jsql-neo 3.5.2 → 4.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.
- package/LICENSE +202 -0
- package/bin/jsql +97 -0
- package/index.js +20 -0
- package/lib/database.js +484 -28
- package/lib/mod.js +316 -0
- package/lib/mysql_compat.js +232 -0
- package/lib/mysql_server.js +514 -0
- package/lib/native_client.js +470 -0
- package/lib/nedb_compat.js +506 -0
- package/lib/plugin.js +35 -0
- package/lib/sql.js +1160 -0
- package/lib/table.js +42 -21
- package/lib/wasm_client.js +176 -8
- package/native/jsql-neo-native.node +0 -0
- package/nativesrc/jsql-neo-core/Cargo.toml +24 -0
- package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +449 -0
- package/nativesrc/jsql-neo-core/src/engine/memory.rs +147 -0
- package/nativesrc/jsql-neo-core/src/engine/mod.rs +41 -0
- package/nativesrc/jsql-neo-core/src/engine/table.rs +664 -0
- package/nativesrc/jsql-neo-core/src/lib.rs +3 -0
- package/nativesrc/jsql-neo-core/src/storage/mod.rs +1 -0
- package/nativesrc/jsql-neo-core/src/storage/persistent.rs +2 -0
- package/nativesrc/jsql-neo-core/src/storage/wal.rs +85 -0
- package/nativesrc/jsql-neo-core/src/types.rs +94 -0
- package/nativesrc/jsql-neo-native/Cargo.lock +606 -0
- package/nativesrc/jsql-neo-native/Cargo.toml +16 -0
- package/nativesrc/jsql-neo-native/jsql-neo-native.node +0 -0
- package/nativesrc/jsql-neo-native/package.json +7 -0
- package/nativesrc/jsql-neo-native/src/lib.rs +281 -0
- package/package.json +9 -1
- package/wasm/jsql_neo_wasm.d.ts +8 -0
- package/wasm/jsql_neo_wasm.js +455 -6
- package/wasm/jsql_neo_wasm_bg.js +22 -0
- package/wasm/jsql_neo_wasm_bg.wasm +0 -0
- package/wasm/jsql_neo_wasm_bg.wasm.d.ts +4 -0
- package/wasm/package.json +1 -8
|
@@ -0,0 +1,664 @@
|
|
|
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
|
+
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 id = self.next_id;
|
|
234
|
+
self.next_id += 1;
|
|
235
|
+
|
|
236
|
+
if let Some(ref pk_key) = pk_auto_key {
|
|
237
|
+
match fields.get(pk_key) {
|
|
238
|
+
Some(v) if v.is_null() => {
|
|
239
|
+
fields.insert(pk_key.clone(), serde_json::Value::Number(id.into()));
|
|
240
|
+
}
|
|
241
|
+
Some(v) => {
|
|
242
|
+
if let Some(n) = v.as_i64() {
|
|
243
|
+
if n > 0 { self.next_id = self.next_id.max((n + 1) as u64); }
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
None => {
|
|
247
|
+
fields.insert(pk_key.clone(), serde_json::Value::Number(id.into()));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let values_start = self.values.len();
|
|
253
|
+
if fields.len() < self.schema.len() {
|
|
254
|
+
for (name, fs) in &self.schema {
|
|
255
|
+
if let Some(v) = fields.remove(name) {
|
|
256
|
+
self.values.push(FieldValue::from_json(v));
|
|
257
|
+
} else {
|
|
258
|
+
self.values.push(default_value(&fs.field_type));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
for name in &self.field_order {
|
|
263
|
+
self.values.push(fields.remove(name).map(FieldValue::from_json).unwrap_or(FieldValue::Null));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let idx = self.rows.len();
|
|
268
|
+
self.pk_index.insert(id, idx);
|
|
269
|
+
self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
|
|
270
|
+
ids.push(id);
|
|
271
|
+
}
|
|
272
|
+
ids
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pub fn add_rows_vec(&mut self, field_names: &[String], batch: Vec<Vec<serde_json::Value>>) -> Vec<u64> {
|
|
276
|
+
let batch_fv: Vec<Vec<FieldValue>> = batch.into_iter()
|
|
277
|
+
.map(|row| row.into_iter().map(FieldValue::from_json).collect())
|
|
278
|
+
.collect();
|
|
279
|
+
self.add_rows_vec_fv(field_names, batch_fv)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
pub fn add_rows_vec_fv(&mut self, field_names: &[String], batch: Vec<Vec<FieldValue>>) -> Vec<u64> {
|
|
283
|
+
let now = now_millis();
|
|
284
|
+
let batch_len = batch.len();
|
|
285
|
+
let mut ids = Vec::with_capacity(batch_len);
|
|
286
|
+
self.pk_index.reserve(batch_len);
|
|
287
|
+
self.rows.reserve(batch_len);
|
|
288
|
+
self.values.reserve(batch_len * self.field_order.len());
|
|
289
|
+
|
|
290
|
+
let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
|
|
291
|
+
self.schema.get(pk).and_then(|fs| {
|
|
292
|
+
if fs.auto_increment { Some(pk.to_string()) } else { None }
|
|
293
|
+
})
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// fast path: field_names match field_order
|
|
297
|
+
if field_names.len() == self.field_order.len()
|
|
298
|
+
&& field_names.iter().zip(&self.field_order).all(|(a, b)| a == b)
|
|
299
|
+
{
|
|
300
|
+
let pk_idx = pk_auto_key.as_ref().and_then(|pk| self.field_index.get(pk.as_str()).copied());
|
|
301
|
+
for mut row_vals in batch {
|
|
302
|
+
let id = self.next_id;
|
|
303
|
+
self.next_id += 1;
|
|
304
|
+
if row_vals.len() < self.field_order.len() {
|
|
305
|
+
row_vals.resize(self.field_order.len(), FieldValue::Null);
|
|
306
|
+
}
|
|
307
|
+
if let Some(pi) = pk_idx {
|
|
308
|
+
if pi < row_vals.len() {
|
|
309
|
+
match row_vals[pi] {
|
|
310
|
+
FieldValue::Null => row_vals[pi] = FieldValue::Int(id as i64),
|
|
311
|
+
FieldValue::Int(e) if e > 0 => {
|
|
312
|
+
self.next_id = self.next_id.max((e + 1) as u64);
|
|
313
|
+
}
|
|
314
|
+
_ => {}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
let values_start = self.values.len();
|
|
319
|
+
self.values.append(&mut row_vals);
|
|
320
|
+
let idx = self.rows.len();
|
|
321
|
+
self.pk_index.insert(id, idx);
|
|
322
|
+
self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
|
|
323
|
+
ids.push(id);
|
|
324
|
+
}
|
|
325
|
+
return ids;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// slow path: remap field names
|
|
329
|
+
let remap: Vec<Option<usize>> = self.field_order.iter()
|
|
330
|
+
.map(|name| field_names.iter().position(|fname| fname == name))
|
|
331
|
+
.collect();
|
|
332
|
+
let explicit_pk_si = pk_auto_key.as_ref()
|
|
333
|
+
.and_then(|pk| field_names.iter().position(|n| n == pk));
|
|
334
|
+
|
|
335
|
+
for mut row_vals in batch {
|
|
336
|
+
let id = self.next_id;
|
|
337
|
+
self.next_id += 1;
|
|
338
|
+
if let Some(si) = explicit_pk_si {
|
|
339
|
+
if let Some(FieldValue::Int(e)) = row_vals.get(si) {
|
|
340
|
+
if *e > 0 { self.next_id = self.next_id.max((*e + 1) as u64); }
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
let values_start = self.values.len();
|
|
344
|
+
for (i, remapped_si) in remap.iter().enumerate() {
|
|
345
|
+
if let Some(si) = *remapped_si {
|
|
346
|
+
if si < row_vals.len() {
|
|
347
|
+
self.values.push(std::mem::take(&mut row_vals[si]));
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if let Some(ref pk_key) = pk_auto_key {
|
|
352
|
+
if self.field_order[i] == *pk_key {
|
|
353
|
+
self.values.push(FieldValue::Int(id as i64));
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if let Some(name) = self.field_order.get(i) {
|
|
358
|
+
if let Some(fs) = self.schema.get(name) {
|
|
359
|
+
if let Some(ref default) = fs.default {
|
|
360
|
+
self.values.push(FieldValue::from_json(default.clone()));
|
|
361
|
+
} else if !fs.nullable {
|
|
362
|
+
self.values.push(default_value(&fs.field_type));
|
|
363
|
+
} else {
|
|
364
|
+
self.values.push(FieldValue::Null);
|
|
365
|
+
}
|
|
366
|
+
} else {
|
|
367
|
+
self.values.push(FieldValue::Null);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
let idx = self.rows.len();
|
|
372
|
+
self.pk_index.insert(id, idx);
|
|
373
|
+
self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at: now, updated_at: now });
|
|
374
|
+
ids.push(id);
|
|
375
|
+
}
|
|
376
|
+
ids
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
pub fn row_matches(&self, r: &RowStore, filter: &HashMap<String, serde_json::Value>) -> bool {
|
|
380
|
+
let vals = self.get_row_values(r);
|
|
381
|
+
filter.iter().all(|(k, v)| {
|
|
382
|
+
self.field_index.get(k)
|
|
383
|
+
.and_then(|&pos| vals.get(pos))
|
|
384
|
+
.map_or(false, |fv| fv.eq_json(v))
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
pub fn get_row(&self, id: u64) -> Option<Row> {
|
|
389
|
+
let idx = *self.pk_index.get(&id)?;
|
|
390
|
+
self.rows.get(idx).map(|r| self.to_row(r))
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
pub fn get_row_json(&self, id: u64) -> Option<String> {
|
|
394
|
+
let idx = *self.pk_index.get(&id)?;
|
|
395
|
+
self.rows.get(idx).map(|r| self.to_row_json_string(r))
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
pub fn get_rows_json(&self, ids: &[u64]) -> String {
|
|
399
|
+
let mut out = String::with_capacity(ids.len() * 128);
|
|
400
|
+
out.push('[');
|
|
401
|
+
let mut first = true;
|
|
402
|
+
for &id in ids {
|
|
403
|
+
if let Some(&idx) = self.pk_index.get(&id) {
|
|
404
|
+
if let Some(rs) = self.rows.get(idx) {
|
|
405
|
+
if !first { out.push(','); }
|
|
406
|
+
first = false;
|
|
407
|
+
out.push_str(&self.to_row_json_string(rs));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
out.push(']');
|
|
412
|
+
out
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
fn to_row_json_compact(&self, rs: &RowStore) -> String {
|
|
416
|
+
let vals = self.get_row_values(rs);
|
|
417
|
+
let mut out = String::with_capacity(64);
|
|
418
|
+
out.push('[');
|
|
419
|
+
out.push_str(itoa::Buffer::new().format(rs.id));
|
|
420
|
+
for v in vals {
|
|
421
|
+
out.push(',');
|
|
422
|
+
v.write_json(&mut out);
|
|
423
|
+
}
|
|
424
|
+
out.push(']');
|
|
425
|
+
out
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
fn to_row_json_string(&self, rs: &RowStore) -> String {
|
|
429
|
+
let vals = self.get_row_values(rs);
|
|
430
|
+
let mut out = String::with_capacity(96);
|
|
431
|
+
out.push_str(r#"{"id":"#);
|
|
432
|
+
out.push_str(itoa::Buffer::new().format(rs.id));
|
|
433
|
+
out.push_str(r#","fields":{"#);
|
|
434
|
+
for (i, name) in self.field_order.iter().enumerate() {
|
|
435
|
+
if i > 0 { out.push(','); }
|
|
436
|
+
write_json_string(&mut out, name);
|
|
437
|
+
out.push(':');
|
|
438
|
+
if let Some(v) = vals.get(i) {
|
|
439
|
+
v.write_json(&mut out);
|
|
440
|
+
} else {
|
|
441
|
+
out.push_str("null");
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
out.push_str("}}");
|
|
445
|
+
out
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
pub fn update_row(&mut self, id: u64, data: HashMap<String, serde_json::Value>) -> bool {
|
|
449
|
+
let idx = match self.pk_index.get(&id) {
|
|
450
|
+
Some(i) => *i,
|
|
451
|
+
None => return false,
|
|
452
|
+
};
|
|
453
|
+
// Use raw index access to update values without borrow conflicts
|
|
454
|
+
if idx < self.rows.len() {
|
|
455
|
+
let row = &mut self.rows[idx];
|
|
456
|
+
let nv = row.num_values;
|
|
457
|
+
let s = row.values_start;
|
|
458
|
+
for (k, v) in data {
|
|
459
|
+
if let Some(&pos) = self.field_index.get(&k) {
|
|
460
|
+
if pos < nv {
|
|
461
|
+
self.values[s + pos] = FieldValue::from_json(v);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
self.rows[idx].updated_at = now_millis();
|
|
466
|
+
true
|
|
467
|
+
} else {
|
|
468
|
+
false
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
pub fn update_rows(&mut self, batch: Vec<(u64, HashMap<String, serde_json::Value>)>) -> usize {
|
|
473
|
+
let mut count = 0;
|
|
474
|
+
let now = now_millis();
|
|
475
|
+
for (id, data) in batch {
|
|
476
|
+
if let Some(&idx) = self.pk_index.get(&id) {
|
|
477
|
+
if idx < self.rows.len() {
|
|
478
|
+
let row = &mut self.rows[idx];
|
|
479
|
+
let nv = row.num_values;
|
|
480
|
+
let s = row.values_start;
|
|
481
|
+
for (k, v) in data {
|
|
482
|
+
if let Some(&pos) = self.field_index.get(&k) {
|
|
483
|
+
if pos < nv {
|
|
484
|
+
self.values[s + pos] = FieldValue::from_json(v);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
self.rows[idx].updated_at = now;
|
|
489
|
+
count += 1;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
count
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
pub fn remove_row(&mut self, id: u64) -> bool {
|
|
497
|
+
let idx = match self.pk_index.get(&id) {
|
|
498
|
+
Some(i) => *i,
|
|
499
|
+
None => return false,
|
|
500
|
+
};
|
|
501
|
+
self.pk_index.remove(&id);
|
|
502
|
+
let last = self.rows.len() - 1;
|
|
503
|
+
if idx != last {
|
|
504
|
+
self.pk_index.insert(self.rows[last].id, idx);
|
|
505
|
+
}
|
|
506
|
+
self.rows.swap_remove(idx);
|
|
507
|
+
true
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
pub fn remove_rows(&mut self, ids: &[u64]) -> usize {
|
|
511
|
+
let mut count = 0;
|
|
512
|
+
let mut indices: Vec<(usize, u64)> = Vec::with_capacity(ids.len());
|
|
513
|
+
for &id in ids {
|
|
514
|
+
if let Some(&idx) = self.pk_index.get(&id) {
|
|
515
|
+
indices.push((idx, id));
|
|
516
|
+
count += 1;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
indices.sort_unstable_by(|a, b| b.0.cmp(&a.0));
|
|
520
|
+
for (idx, id) in indices {
|
|
521
|
+
self.pk_index.remove(&id);
|
|
522
|
+
let last = self.rows.len() - 1;
|
|
523
|
+
if idx != last {
|
|
524
|
+
self.pk_index.insert(self.rows[last].id, idx);
|
|
525
|
+
}
|
|
526
|
+
self.rows.swap_remove(idx);
|
|
527
|
+
}
|
|
528
|
+
count
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
pub fn remove_row_by_idx(&mut self, idx: usize) {
|
|
532
|
+
let last = self.rows.len() - 1;
|
|
533
|
+
if idx < last {
|
|
534
|
+
let swapped_id = self.rows[last].id;
|
|
535
|
+
self.pk_index.insert(swapped_id, idx);
|
|
536
|
+
}
|
|
537
|
+
self.pk_index.remove(&self.rows[idx].id);
|
|
538
|
+
self.rows.swap_remove(idx);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
pub fn count(&self) -> usize {
|
|
542
|
+
self.rows.len()
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ============================================================
|
|
546
|
+
// 磁盘持久化导出 / 导入(供 HybridEngine 使用)
|
|
547
|
+
// ============================================================
|
|
548
|
+
|
|
549
|
+
pub fn export_all(&self) -> String {
|
|
550
|
+
let mut out = String::with_capacity(64 + self.rows.len() * 96);
|
|
551
|
+
out.push_str(r#"{"next_id":"#);
|
|
552
|
+
out.push_str(itoa::Buffer::new().format(self.next_id));
|
|
553
|
+
out.push_str(r#","rows":["#);
|
|
554
|
+
for (i, rs) in self.rows.iter().enumerate() {
|
|
555
|
+
if i > 0 { out.push(','); }
|
|
556
|
+
out.push_str(r#"{"id":"#);
|
|
557
|
+
out.push_str(itoa::Buffer::new().format(rs.id));
|
|
558
|
+
out.push_str(r#","created_at":"#);
|
|
559
|
+
out.push_str(itoa::Buffer::new().format(rs.created_at));
|
|
560
|
+
out.push_str(r#","updated_at":"#);
|
|
561
|
+
out.push_str(itoa::Buffer::new().format(rs.updated_at));
|
|
562
|
+
out.push_str(r#","fields":{"#);
|
|
563
|
+
let vals = self.get_row_values(rs);
|
|
564
|
+
for (fi, name) in self.field_order.iter().enumerate() {
|
|
565
|
+
if fi > 0 { out.push(','); }
|
|
566
|
+
write_json_string(&mut out, name);
|
|
567
|
+
out.push(':');
|
|
568
|
+
if let Some(v) = vals.get(fi) {
|
|
569
|
+
v.write_json(&mut out);
|
|
570
|
+
} else {
|
|
571
|
+
out.push_str("null");
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
out.push_str("}}");
|
|
575
|
+
}
|
|
576
|
+
out.push_str("]}");
|
|
577
|
+
out
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
pub fn import_from_json(&mut self, json: &str) -> Result<(), String> {
|
|
581
|
+
let parsed: serde_json::Value = serde_json::from_str(json)
|
|
582
|
+
.map_err(|e| format!("invalid table file: {}", e))?;
|
|
583
|
+
let next_id = parsed.get("next_id").and_then(|v| v.as_u64()).unwrap_or(1);
|
|
584
|
+
self.next_id = next_id;
|
|
585
|
+
let rows = parsed.get("rows").and_then(|v| v.as_array()).ok_or("table file missing rows")?;
|
|
586
|
+
let pk_auto_key = self.pk_field.as_deref().and_then(|pk| {
|
|
587
|
+
self.schema.get(pk).and_then(|fs| if fs.auto_increment { Some(pk.to_string()) } else { None })
|
|
588
|
+
});
|
|
589
|
+
self.rows.reserve(rows.len());
|
|
590
|
+
self.values.reserve(rows.len() * self.field_order.len());
|
|
591
|
+
self.pk_index.reserve(rows.len());
|
|
592
|
+
for rv in rows {
|
|
593
|
+
let id = rv.get("id").and_then(|v| v.as_u64()).ok_or("row missing id")?;
|
|
594
|
+
let created_at = rv.get("created_at").and_then(|v| v.as_u64()).unwrap_or_else(now_millis);
|
|
595
|
+
let updated_at = rv.get("updated_at").and_then(|v| v.as_u64()).unwrap_or(created_at);
|
|
596
|
+
let fields = rv.get("fields").and_then(|v| v.as_object()).ok_or("row missing fields")?;
|
|
597
|
+
let values_start = self.values.len();
|
|
598
|
+
for name in &self.field_order {
|
|
599
|
+
if let Some(jv) = fields.get(name) {
|
|
600
|
+
self.values.push(FieldValue::from_json(jv.clone()));
|
|
601
|
+
} else if let Some(ref pk_key) = pk_auto_key {
|
|
602
|
+
if *pk_key == *name {
|
|
603
|
+
self.values.push(FieldValue::Int(id as i64));
|
|
604
|
+
} else {
|
|
605
|
+
self.values.push(FieldValue::Null);
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
self.values.push(FieldValue::Null);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
let idx = self.rows.len();
|
|
612
|
+
self.pk_index.insert(id, idx);
|
|
613
|
+
self.rows.push(RowStore { id, values_start, num_values: self.field_order.len(), created_at, updated_at });
|
|
614
|
+
}
|
|
615
|
+
Ok(())
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
pub fn find_rows(&self, filter: &Option<HashMap<String, serde_json::Value>>,
|
|
619
|
+
limit: Option<usize>, offset: Option<usize>,
|
|
620
|
+
order_by: &Option<String>, order: &Option<String>) -> Vec<Row> {
|
|
621
|
+
let mut results: Vec<&RowStore> = self.rows.iter().collect();
|
|
622
|
+
|
|
623
|
+
if let Some(ref f) = filter {
|
|
624
|
+
let filter_indices: Vec<(usize, &serde_json::Value)> = f.iter()
|
|
625
|
+
.filter_map(|(k, v)| self.field_index.get(k).map(|&pos| (pos, v)))
|
|
626
|
+
.collect();
|
|
627
|
+
results.retain(|r| {
|
|
628
|
+
let vals = self.get_row_values(r);
|
|
629
|
+
filter_indices.iter().all(|(pos, v)| {
|
|
630
|
+
vals.get(*pos).map_or(false, |fv| fv.eq_json(v))
|
|
631
|
+
})
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if let Some(ref field) = order_by {
|
|
636
|
+
let desc = order.as_deref() == Some("desc");
|
|
637
|
+
if let Some(&pos) = self.field_index.get(field) {
|
|
638
|
+
results.sort_by(|a, b| {
|
|
639
|
+
let vals_a = self.get_row_values(a);
|
|
640
|
+
let vals_b = self.get_row_values(b);
|
|
641
|
+
let cmp = cmp_field_values(vals_a.get(pos), vals_b.get(pos));
|
|
642
|
+
if desc { cmp.reverse() } else { cmp }
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
let o = offset.unwrap_or(0);
|
|
648
|
+
let l = limit.unwrap_or(usize::MAX);
|
|
649
|
+
results.into_iter().skip(o).take(l).map(|r| self.to_row(r)).collect()
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
fn cmp_field_values(a: Option<&FieldValue>, b: Option<&FieldValue>) -> std::cmp::Ordering {
|
|
654
|
+
match (a, b) {
|
|
655
|
+
(Some(FieldValue::Int(na)), Some(FieldValue::Int(nb))) => na.cmp(nb),
|
|
656
|
+
(Some(FieldValue::Float(na)), Some(FieldValue::Float(nb))) => na.partial_cmp(nb).unwrap_or(std::cmp::Ordering::Equal),
|
|
657
|
+
(Some(FieldValue::String(sa)), Some(FieldValue::String(sb))) => sa.cmp(sb),
|
|
658
|
+
(Some(FieldValue::Bool(ba)), Some(FieldValue::Bool(bb))) => ba.cmp(bb),
|
|
659
|
+
(Some(a), Some(b)) => format!("{:?}", a).cmp(&format!("{:?}", b)),
|
|
660
|
+
(Some(_), None) => std::cmp::Ordering::Greater,
|
|
661
|
+
(None, Some(_)) => std::cmp::Ordering::Less,
|
|
662
|
+
(None, None) => std::cmp::Ordering::Equal,
|
|
663
|
+
}
|
|
664
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pub mod persistent;
|