1use serde::{Deserialize, Serialize};
7
8use crate::value::Value;
9
10#[repr(u8)]
18#[derive(
19 Debug,
20 Clone,
21 Copy,
22 PartialEq,
23 Eq,
24 Hash,
25 Serialize,
26 Deserialize,
27 zerompk::ToMessagePack,
28 zerompk::FromMessagePack,
29)]
30#[serde(try_from = "u8", into = "u8")]
31#[msgpack(c_enum)]
32pub enum OpCode {
33 Auth = 0x01,
35 Ping = 0x02,
36 Status = 0x03,
39
40 PointGet = 0x10,
42 PointPut = 0x11,
43 PointDelete = 0x12,
44 VectorSearch = 0x13,
45 RangeScan = 0x14,
46 CrdtRead = 0x15,
47 CrdtApply = 0x16,
48 GraphRagFusion = 0x17,
49 AlterCollectionPolicy = 0x18,
50
51 Sql = 0x20,
53 Ddl = 0x21,
54 Explain = 0x22,
55 CopyFrom = 0x23,
56
57 Set = 0x30,
59 Show = 0x31,
60 Reset = 0x32,
61
62 Begin = 0x40,
64 Commit = 0x41,
65 Rollback = 0x42,
66
67 GraphHop = 0x50,
69 GraphNeighbors = 0x51,
70 GraphPath = 0x52,
71 GraphSubgraph = 0x53,
72 EdgePut = 0x54,
73 EdgeDelete = 0x55,
74 GraphAlgo = 0x56,
75 GraphMatch = 0x57,
76
77 SpatialScan = 0x19,
79
80 TimeseriesScan = 0x1A,
82 TimeseriesIngest = 0x1B,
83
84 TextSearch = 0x60,
86 HybridSearch = 0x61,
87
88 VectorBatchInsert = 0x70,
90 DocumentBatchInsert = 0x71,
91
92 KvScan = 0x72,
94 KvExpire = 0x73,
95 KvPersist = 0x74,
96 KvGetTtl = 0x75,
97 KvBatchGet = 0x76,
98 KvBatchPut = 0x77,
99 KvFieldGet = 0x78,
100 KvFieldSet = 0x79,
101
102 DocumentUpdate = 0x7A,
104 DocumentScan = 0x7B,
105 DocumentUpsert = 0x7C,
106 DocumentBulkUpdate = 0x7D,
107 DocumentBulkDelete = 0x7E,
108
109 VectorInsert = 0x7F,
111 VectorMultiSearch = 0x80,
112 VectorDelete = 0x81,
113
114 ColumnarScan = 0x82,
116 ColumnarInsert = 0x83,
117
118 RecursiveScan = 0x84,
120
121 DocumentTruncate = 0x85,
123 DocumentEstimateCount = 0x86,
124 DocumentInsertSelect = 0x87,
125 DocumentRegister = 0x88,
126 DocumentDropIndex = 0x89,
127
128 KvRegisterIndex = 0x8A,
130 KvDropIndex = 0x8B,
131 KvTruncate = 0x8C,
132
133 VectorSetParams = 0x8D,
135
136 KvIncr = 0x8E,
138 KvIncrFloat = 0x8F,
139 KvCas = 0x90,
140 KvGetSet = 0x91,
141
142 KvRegisterSortedIndex = 0x92,
144 KvDropSortedIndex = 0x93,
145 KvSortedIndexRank = 0x94,
146 KvSortedIndexTopK = 0x95,
147 KvSortedIndexRange = 0x96,
148 KvSortedIndexCount = 0x97,
149 KvSortedIndexScore = 0x98,
150}
151
152impl OpCode {
153 pub fn is_write(&self) -> bool {
155 matches!(
156 self,
157 OpCode::PointPut
158 | OpCode::PointDelete
159 | OpCode::CrdtApply
160 | OpCode::EdgePut
161 | OpCode::EdgeDelete
162 | OpCode::VectorBatchInsert
163 | OpCode::DocumentBatchInsert
164 | OpCode::AlterCollectionPolicy
165 | OpCode::TimeseriesIngest
166 | OpCode::KvExpire
167 | OpCode::KvPersist
168 | OpCode::KvBatchPut
169 | OpCode::KvFieldSet
170 | OpCode::DocumentUpdate
171 | OpCode::DocumentUpsert
172 | OpCode::DocumentBulkUpdate
173 | OpCode::DocumentBulkDelete
174 | OpCode::VectorInsert
175 | OpCode::VectorDelete
176 | OpCode::ColumnarInsert
177 | OpCode::DocumentTruncate
178 | OpCode::DocumentInsertSelect
179 | OpCode::DocumentRegister
180 | OpCode::DocumentDropIndex
181 | OpCode::KvRegisterIndex
182 | OpCode::KvDropIndex
183 | OpCode::KvTruncate
184 | OpCode::VectorSetParams
185 | OpCode::KvIncr
186 | OpCode::KvIncrFloat
187 | OpCode::KvCas
188 | OpCode::KvGetSet
189 | OpCode::KvRegisterSortedIndex
190 | OpCode::KvDropSortedIndex
191 )
192 }
193}
194
195impl From<OpCode> for u8 {
196 fn from(op: OpCode) -> u8 {
197 op as u8
198 }
199}
200
201impl TryFrom<u8> for OpCode {
202 type Error = String;
203
204 fn try_from(value: u8) -> Result<Self, Self::Error> {
205 match value {
206 0x01 => Ok(OpCode::Auth),
207 0x02 => Ok(OpCode::Ping),
208 0x03 => Ok(OpCode::Status),
209 0x10 => Ok(OpCode::PointGet),
210 0x11 => Ok(OpCode::PointPut),
211 0x12 => Ok(OpCode::PointDelete),
212 0x13 => Ok(OpCode::VectorSearch),
213 0x14 => Ok(OpCode::RangeScan),
214 0x15 => Ok(OpCode::CrdtRead),
215 0x16 => Ok(OpCode::CrdtApply),
216 0x17 => Ok(OpCode::GraphRagFusion),
217 0x18 => Ok(OpCode::AlterCollectionPolicy),
218 0x19 => Ok(OpCode::SpatialScan),
219 0x1A => Ok(OpCode::TimeseriesScan),
220 0x1B => Ok(OpCode::TimeseriesIngest),
221 0x20 => Ok(OpCode::Sql),
222 0x21 => Ok(OpCode::Ddl),
223 0x22 => Ok(OpCode::Explain),
224 0x23 => Ok(OpCode::CopyFrom),
225 0x30 => Ok(OpCode::Set),
226 0x31 => Ok(OpCode::Show),
227 0x32 => Ok(OpCode::Reset),
228 0x40 => Ok(OpCode::Begin),
229 0x41 => Ok(OpCode::Commit),
230 0x42 => Ok(OpCode::Rollback),
231 0x50 => Ok(OpCode::GraphHop),
232 0x51 => Ok(OpCode::GraphNeighbors),
233 0x52 => Ok(OpCode::GraphPath),
234 0x53 => Ok(OpCode::GraphSubgraph),
235 0x54 => Ok(OpCode::EdgePut),
236 0x55 => Ok(OpCode::EdgeDelete),
237 0x56 => Ok(OpCode::GraphAlgo),
238 0x57 => Ok(OpCode::GraphMatch),
239 0x60 => Ok(OpCode::TextSearch),
240 0x61 => Ok(OpCode::HybridSearch),
241 0x70 => Ok(OpCode::VectorBatchInsert),
242 0x71 => Ok(OpCode::DocumentBatchInsert),
243 0x72 => Ok(OpCode::KvScan),
244 0x73 => Ok(OpCode::KvExpire),
245 0x74 => Ok(OpCode::KvPersist),
246 0x75 => Ok(OpCode::KvGetTtl),
247 0x76 => Ok(OpCode::KvBatchGet),
248 0x77 => Ok(OpCode::KvBatchPut),
249 0x78 => Ok(OpCode::KvFieldGet),
250 0x79 => Ok(OpCode::KvFieldSet),
251 0x7A => Ok(OpCode::DocumentUpdate),
252 0x7B => Ok(OpCode::DocumentScan),
253 0x7C => Ok(OpCode::DocumentUpsert),
254 0x7D => Ok(OpCode::DocumentBulkUpdate),
255 0x7E => Ok(OpCode::DocumentBulkDelete),
256 0x7F => Ok(OpCode::VectorInsert),
257 0x80 => Ok(OpCode::VectorMultiSearch),
258 0x81 => Ok(OpCode::VectorDelete),
259 0x82 => Ok(OpCode::ColumnarScan),
260 0x83 => Ok(OpCode::ColumnarInsert),
261 0x84 => Ok(OpCode::RecursiveScan),
262 0x85 => Ok(OpCode::DocumentTruncate),
263 0x86 => Ok(OpCode::DocumentEstimateCount),
264 0x87 => Ok(OpCode::DocumentInsertSelect),
265 0x88 => Ok(OpCode::DocumentRegister),
266 0x89 => Ok(OpCode::DocumentDropIndex),
267 0x8A => Ok(OpCode::KvRegisterIndex),
268 0x8B => Ok(OpCode::KvDropIndex),
269 0x8C => Ok(OpCode::KvTruncate),
270 0x8D => Ok(OpCode::VectorSetParams),
271 0x8E => Ok(OpCode::KvIncr),
272 0x8F => Ok(OpCode::KvIncrFloat),
273 0x90 => Ok(OpCode::KvCas),
274 0x91 => Ok(OpCode::KvGetSet),
275 0x92 => Ok(OpCode::KvRegisterSortedIndex),
276 0x93 => Ok(OpCode::KvDropSortedIndex),
277 0x94 => Ok(OpCode::KvSortedIndexRank),
278 0x95 => Ok(OpCode::KvSortedIndexTopK),
279 0x96 => Ok(OpCode::KvSortedIndexRange),
280 0x97 => Ok(OpCode::KvSortedIndexCount),
281 0x98 => Ok(OpCode::KvSortedIndexScore),
282 other => Err(format!("unknown OpCode byte: 0x{other:02X}")),
283 }
284 }
285}
286
287#[repr(u8)]
291#[derive(
292 Debug,
293 Clone,
294 Copy,
295 PartialEq,
296 Eq,
297 Serialize,
298 Deserialize,
299 zerompk::ToMessagePack,
300 zerompk::FromMessagePack,
301)]
302#[msgpack(c_enum)]
303pub enum ResponseStatus {
304 Ok = 0,
306 Partial = 1,
308 Error = 2,
310}
311
312#[derive(
316 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
317)]
318#[serde(tag = "method", rename_all = "snake_case")]
319pub enum AuthMethod {
320 Trust {
321 #[serde(default = "default_username")]
322 username: String,
323 },
324 Password {
325 username: String,
326 password: String,
327 },
328 ApiKey {
329 token: String,
330 },
331}
332
333fn default_username() -> String {
334 "admin".into()
335}
336
337#[derive(
339 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
340)]
341pub struct AuthResponse {
342 pub username: String,
343 pub tenant_id: u32,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct NativeRequest {
354 pub op: OpCode,
356 pub seq: u64,
358 #[serde(flatten)]
360 pub fields: RequestFields,
361}
362
363impl zerompk::ToMessagePack for NativeRequest {
364 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
365 writer.write_array_len(3)?;
366 self.op.write(writer)?;
367 writer.write_u64(self.seq)?;
368 self.fields.write(writer)
369 }
370}
371
372impl<'a> zerompk::FromMessagePack<'a> for NativeRequest {
373 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
374 let len = reader.read_array_len()?;
375 if len != 3 {
376 return Err(zerompk::Error::ArrayLengthMismatch {
377 expected: 3,
378 actual: len,
379 });
380 }
381 let op = OpCode::read(reader)?;
382 let seq = reader.read_u64()?;
383 let fields = RequestFields::read(reader)?;
384 Ok(Self { op, seq, fields })
385 }
386}
387
388#[derive(
393 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
394)]
395#[serde(untagged)]
396pub enum RequestFields {
397 Text(TextFields),
400}
401
402#[derive(Debug, Clone, Default, Serialize, Deserialize)]
406pub struct TextFields {
407 #[serde(skip_serializing_if = "Option::is_none")]
409 pub auth: Option<AuthMethod>,
410
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub sql: Option<String>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub key: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub value: Option<String>,
418
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub collection: Option<String>,
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub document_id: Option<String>,
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub data: Option<Vec<u8>>,
426
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub query_vector: Option<Vec<f32>>,
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub top_k: Option<u64>,
432
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub field: Option<String>,
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub limit: Option<u64>,
438
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub delta: Option<Vec<u8>>,
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub peer_id: Option<u64>,
444
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub vector_top_k: Option<u64>,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub edge_label: Option<String>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub direction: Option<String>,
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub expansion_depth: Option<u64>,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub final_top_k: Option<u64>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub vector_k: Option<f64>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub graph_k: Option<f64>,
460
461 #[serde(skip_serializing_if = "Option::is_none")]
463 pub start_node: Option<String>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub end_node: Option<String>,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub depth: Option<u64>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub from_node: Option<String>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub to_node: Option<String>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub edge_type: Option<String>,
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub properties: Option<serde_json::Value>,
476
477 #[serde(skip_serializing_if = "Option::is_none")]
479 pub query_text: Option<String>,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub vector_weight: Option<f64>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub fuzzy: Option<bool>,
484
485 #[serde(skip_serializing_if = "Option::is_none")]
487 pub ef_search: Option<u64>,
488 #[serde(skip_serializing_if = "Option::is_none")]
490 pub field_name: Option<String>,
491
492 #[serde(skip_serializing_if = "Option::is_none")]
494 pub lower_bound: Option<Vec<u8>>,
495 #[serde(skip_serializing_if = "Option::is_none")]
496 pub upper_bound: Option<Vec<u8>>,
497
498 #[serde(skip_serializing_if = "Option::is_none")]
501 pub mutation_id: Option<u64>,
502
503 #[serde(skip_serializing_if = "Option::is_none")]
505 pub vectors: Option<Vec<BatchVector>>,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub documents: Option<Vec<BatchDocument>>,
508
509 #[serde(skip_serializing_if = "Option::is_none")]
512 pub query_geometry: Option<Vec<u8>>,
513 #[serde(skip_serializing_if = "Option::is_none")]
515 pub spatial_predicate: Option<String>,
516 #[serde(skip_serializing_if = "Option::is_none")]
518 pub distance_meters: Option<f64>,
519
520 #[serde(skip_serializing_if = "Option::is_none")]
523 pub payload: Option<Vec<u8>>,
524 #[serde(skip_serializing_if = "Option::is_none")]
526 pub format: Option<String>,
527 #[serde(skip_serializing_if = "Option::is_none")]
529 pub time_range_start: Option<i64>,
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub time_range_end: Option<i64>,
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub bucket_interval: Option<String>,
536
537 #[serde(skip_serializing_if = "Option::is_none")]
540 pub ttl_ms: Option<u64>,
541 #[serde(skip_serializing_if = "Option::is_none")]
543 pub cursor: Option<Vec<u8>>,
544 #[serde(skip_serializing_if = "Option::is_none")]
546 pub match_pattern: Option<String>,
547 #[serde(skip_serializing_if = "Option::is_none")]
549 pub keys: Option<Vec<Vec<u8>>>,
550 #[serde(skip_serializing_if = "Option::is_none")]
552 pub entries: Option<Vec<(Vec<u8>, Vec<u8>)>>,
553 #[serde(skip_serializing_if = "Option::is_none")]
555 pub fields: Option<Vec<String>>,
556
557 #[serde(skip_serializing_if = "Option::is_none")]
560 pub incr_delta: Option<i64>,
561 #[serde(skip_serializing_if = "Option::is_none")]
563 pub incr_float_delta: Option<f64>,
564 #[serde(skip_serializing_if = "Option::is_none")]
566 pub expected: Option<Vec<u8>>,
567 #[serde(skip_serializing_if = "Option::is_none")]
569 pub new_value: Option<Vec<u8>>,
570
571 #[serde(skip_serializing_if = "Option::is_none")]
574 pub index_name: Option<String>,
575 #[serde(skip_serializing_if = "Option::is_none")]
577 pub sort_columns: Option<Vec<(String, String)>>,
578 #[serde(skip_serializing_if = "Option::is_none")]
580 pub key_column: Option<String>,
581 #[serde(skip_serializing_if = "Option::is_none")]
583 pub window_type: Option<String>,
584 #[serde(skip_serializing_if = "Option::is_none")]
586 pub window_timestamp_column: Option<String>,
587 #[serde(skip_serializing_if = "Option::is_none")]
589 pub window_start_ms: Option<u64>,
590 #[serde(skip_serializing_if = "Option::is_none")]
592 pub window_end_ms: Option<u64>,
593 #[serde(skip_serializing_if = "Option::is_none")]
595 pub top_k_count: Option<u32>,
596 #[serde(skip_serializing_if = "Option::is_none")]
598 pub score_min: Option<Vec<u8>>,
599 #[serde(skip_serializing_if = "Option::is_none")]
601 pub score_max: Option<Vec<u8>>,
602
603 #[serde(skip_serializing_if = "Option::is_none")]
606 pub updates: Option<Vec<(String, Vec<u8>)>>,
607 #[serde(skip_serializing_if = "Option::is_none")]
609 pub filters: Option<Vec<u8>>,
610
611 #[serde(skip_serializing_if = "Option::is_none")]
614 pub vector: Option<Vec<f32>>,
615 #[serde(skip_serializing_if = "Option::is_none")]
617 pub vector_id: Option<u32>,
618
619 #[serde(skip_serializing_if = "Option::is_none")]
621 pub policy: Option<serde_json::Value>,
622
623 #[serde(skip_serializing_if = "Option::is_none")]
626 pub algorithm: Option<String>,
627 #[serde(skip_serializing_if = "Option::is_none")]
629 pub match_query: Option<String>,
630 #[serde(skip_serializing_if = "Option::is_none")]
632 pub algo_params: Option<serde_json::Value>,
633
634 #[serde(skip_serializing_if = "Option::is_none")]
637 pub index_paths: Option<Vec<String>>,
638 #[serde(skip_serializing_if = "Option::is_none")]
640 pub source_collection: Option<String>,
641
642 #[serde(skip_serializing_if = "Option::is_none")]
645 pub field_position: Option<u64>,
646 #[serde(skip_serializing_if = "Option::is_none")]
648 pub backfill: Option<bool>,
649
650 #[serde(skip_serializing_if = "Option::is_none")]
653 pub m: Option<u64>,
654 #[serde(skip_serializing_if = "Option::is_none")]
656 pub ef_construction: Option<u64>,
657 #[serde(skip_serializing_if = "Option::is_none")]
659 pub metric: Option<String>,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub index_type: Option<String>,
663}
664
665impl zerompk::ToMessagePack for TextFields {
666 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
667 use crate::json_msgpack::JsonValue;
668 writer.write_array_len(81)?;
669 self.auth.write(writer)?;
670 self.sql.write(writer)?;
671 self.key.write(writer)?;
672 self.value.write(writer)?;
673 self.collection.write(writer)?;
674 self.document_id.write(writer)?;
675 self.data.write(writer)?;
676 self.query_vector.write(writer)?;
677 self.top_k.write(writer)?;
678 self.field.write(writer)?;
679 self.limit.write(writer)?;
680 self.delta.write(writer)?;
681 self.peer_id.write(writer)?;
682 self.vector_top_k.write(writer)?;
683 self.edge_label.write(writer)?;
684 self.direction.write(writer)?;
685 self.expansion_depth.write(writer)?;
686 self.final_top_k.write(writer)?;
687 self.vector_k.write(writer)?;
688 self.graph_k.write(writer)?;
689 self.start_node.write(writer)?;
690 self.end_node.write(writer)?;
691 self.depth.write(writer)?;
692 self.from_node.write(writer)?;
693 self.to_node.write(writer)?;
694 self.edge_type.write(writer)?;
695 self.properties
696 .as_ref()
697 .map(|v| JsonValue(v.clone()))
698 .write(writer)?;
699 self.query_text.write(writer)?;
700 self.vector_weight.write(writer)?;
701 self.fuzzy.write(writer)?;
702 self.ef_search.write(writer)?;
703 self.field_name.write(writer)?;
704 self.lower_bound.write(writer)?;
705 self.upper_bound.write(writer)?;
706 self.mutation_id.write(writer)?;
707 self.vectors.write(writer)?;
708 self.documents.write(writer)?;
709 self.query_geometry.write(writer)?;
710 self.spatial_predicate.write(writer)?;
711 self.distance_meters.write(writer)?;
712 self.payload.write(writer)?;
713 self.format.write(writer)?;
714 self.time_range_start.write(writer)?;
715 self.time_range_end.write(writer)?;
716 self.bucket_interval.write(writer)?;
717 self.ttl_ms.write(writer)?;
718 self.cursor.write(writer)?;
719 self.match_pattern.write(writer)?;
720 self.keys.write(writer)?;
721 self.entries.write(writer)?;
722 self.fields.write(writer)?;
723 self.incr_delta.write(writer)?;
724 self.incr_float_delta.write(writer)?;
725 self.expected.write(writer)?;
726 self.new_value.write(writer)?;
727 self.index_name.write(writer)?;
728 self.sort_columns.write(writer)?;
729 self.key_column.write(writer)?;
730 self.window_type.write(writer)?;
731 self.window_timestamp_column.write(writer)?;
732 self.window_start_ms.write(writer)?;
733 self.window_end_ms.write(writer)?;
734 self.top_k_count.write(writer)?;
735 self.score_min.write(writer)?;
736 self.score_max.write(writer)?;
737 self.updates.write(writer)?;
738 self.filters.write(writer)?;
739 self.vector.write(writer)?;
740 self.vector_id.write(writer)?;
741 self.policy
742 .as_ref()
743 .map(|v| JsonValue(v.clone()))
744 .write(writer)?;
745 self.algorithm.write(writer)?;
746 self.match_query.write(writer)?;
747 self.algo_params
748 .as_ref()
749 .map(|v| JsonValue(v.clone()))
750 .write(writer)?;
751 self.index_paths.write(writer)?;
752 self.source_collection.write(writer)?;
753 self.field_position.write(writer)?;
754 self.backfill.write(writer)?;
755 self.m.write(writer)?;
756 self.ef_construction.write(writer)?;
757 self.metric.write(writer)?;
758 self.index_type.write(writer)
759 }
760}
761
762impl<'a> zerompk::FromMessagePack<'a> for TextFields {
763 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
764 use crate::json_msgpack::JsonValue;
765 let len = reader.read_array_len()?;
766 if len != 81 {
767 return Err(zerompk::Error::ArrayLengthMismatch {
768 expected: 81,
769 actual: len,
770 });
771 }
772 Ok(Self {
773 auth: Option::<AuthMethod>::read(reader)?,
774 sql: Option::<String>::read(reader)?,
775 key: Option::<String>::read(reader)?,
776 value: Option::<String>::read(reader)?,
777 collection: Option::<String>::read(reader)?,
778 document_id: Option::<String>::read(reader)?,
779 data: Option::<Vec<u8>>::read(reader)?,
780 query_vector: Option::<Vec<f32>>::read(reader)?,
781 top_k: Option::<u64>::read(reader)?,
782 field: Option::<String>::read(reader)?,
783 limit: Option::<u64>::read(reader)?,
784 delta: Option::<Vec<u8>>::read(reader)?,
785 peer_id: Option::<u64>::read(reader)?,
786 vector_top_k: Option::<u64>::read(reader)?,
787 edge_label: Option::<String>::read(reader)?,
788 direction: Option::<String>::read(reader)?,
789 expansion_depth: Option::<u64>::read(reader)?,
790 final_top_k: Option::<u64>::read(reader)?,
791 vector_k: Option::<f64>::read(reader)?,
792 graph_k: Option::<f64>::read(reader)?,
793 start_node: Option::<String>::read(reader)?,
794 end_node: Option::<String>::read(reader)?,
795 depth: Option::<u64>::read(reader)?,
796 from_node: Option::<String>::read(reader)?,
797 to_node: Option::<String>::read(reader)?,
798 edge_type: Option::<String>::read(reader)?,
799 properties: Option::<JsonValue>::read(reader)?.map(|v| v.0),
800 query_text: Option::<String>::read(reader)?,
801 vector_weight: Option::<f64>::read(reader)?,
802 fuzzy: Option::<bool>::read(reader)?,
803 ef_search: Option::<u64>::read(reader)?,
804 field_name: Option::<String>::read(reader)?,
805 lower_bound: Option::<Vec<u8>>::read(reader)?,
806 upper_bound: Option::<Vec<u8>>::read(reader)?,
807 mutation_id: Option::<u64>::read(reader)?,
808 vectors: Option::<Vec<BatchVector>>::read(reader)?,
809 documents: Option::<Vec<BatchDocument>>::read(reader)?,
810 query_geometry: Option::<Vec<u8>>::read(reader)?,
811 spatial_predicate: Option::<String>::read(reader)?,
812 distance_meters: Option::<f64>::read(reader)?,
813 payload: Option::<Vec<u8>>::read(reader)?,
814 format: Option::<String>::read(reader)?,
815 time_range_start: Option::<i64>::read(reader)?,
816 time_range_end: Option::<i64>::read(reader)?,
817 bucket_interval: Option::<String>::read(reader)?,
818 ttl_ms: Option::<u64>::read(reader)?,
819 cursor: Option::<Vec<u8>>::read(reader)?,
820 match_pattern: Option::<String>::read(reader)?,
821 keys: Option::<Vec<Vec<u8>>>::read(reader)?,
822 entries: Option::<Vec<(Vec<u8>, Vec<u8>)>>::read(reader)?,
823 fields: Option::<Vec<String>>::read(reader)?,
824 incr_delta: Option::<i64>::read(reader)?,
825 incr_float_delta: Option::<f64>::read(reader)?,
826 expected: Option::<Vec<u8>>::read(reader)?,
827 new_value: Option::<Vec<u8>>::read(reader)?,
828 index_name: Option::<String>::read(reader)?,
829 sort_columns: Option::<Vec<(String, String)>>::read(reader)?,
830 key_column: Option::<String>::read(reader)?,
831 window_type: Option::<String>::read(reader)?,
832 window_timestamp_column: Option::<String>::read(reader)?,
833 window_start_ms: Option::<u64>::read(reader)?,
834 window_end_ms: Option::<u64>::read(reader)?,
835 top_k_count: Option::<u32>::read(reader)?,
836 score_min: Option::<Vec<u8>>::read(reader)?,
837 score_max: Option::<Vec<u8>>::read(reader)?,
838 updates: Option::<Vec<(String, Vec<u8>)>>::read(reader)?,
839 filters: Option::<Vec<u8>>::read(reader)?,
840 vector: Option::<Vec<f32>>::read(reader)?,
841 vector_id: Option::<u32>::read(reader)?,
842 policy: Option::<JsonValue>::read(reader)?.map(|v| v.0),
843 algorithm: Option::<String>::read(reader)?,
844 match_query: Option::<String>::read(reader)?,
845 algo_params: Option::<JsonValue>::read(reader)?.map(|v| v.0),
846 index_paths: Option::<Vec<String>>::read(reader)?,
847 source_collection: Option::<String>::read(reader)?,
848 field_position: Option::<u64>::read(reader)?,
849 backfill: Option::<bool>::read(reader)?,
850 m: Option::<u64>::read(reader)?,
851 ef_construction: Option::<u64>::read(reader)?,
852 metric: Option::<String>::read(reader)?,
853 index_type: Option::<String>::read(reader)?,
854 })
855 }
856}
857
858#[derive(Debug, Clone, Serialize, Deserialize)]
860pub struct BatchVector {
861 pub id: String,
862 pub embedding: Vec<f32>,
863 #[serde(skip_serializing_if = "Option::is_none")]
864 pub metadata: Option<serde_json::Value>,
865}
866
867impl zerompk::ToMessagePack for BatchVector {
868 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
869 use crate::json_msgpack::JsonValue;
870 writer.write_array_len(3)?;
871 writer.write_string(&self.id)?;
872 self.embedding.write(writer)?;
873 self.metadata
874 .as_ref()
875 .map(|v| JsonValue(v.clone()))
876 .write(writer)
877 }
878}
879
880impl<'a> zerompk::FromMessagePack<'a> for BatchVector {
881 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
882 use crate::json_msgpack::JsonValue;
883 let len = reader.read_array_len()?;
884 if len != 3 {
885 return Err(zerompk::Error::ArrayLengthMismatch {
886 expected: 3,
887 actual: len,
888 });
889 }
890 let id = reader.read_string()?.into_owned();
891 let embedding = Vec::<f32>::read(reader)?;
892 let metadata = Option::<JsonValue>::read(reader)?.map(|v| v.0);
893 Ok(Self {
894 id,
895 embedding,
896 metadata,
897 })
898 }
899}
900
901#[derive(Debug, Clone, Serialize, Deserialize)]
903pub struct BatchDocument {
904 pub id: String,
905 pub fields: serde_json::Value,
906}
907
908impl zerompk::ToMessagePack for BatchDocument {
909 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
910 use crate::json_msgpack::JsonValue;
911 writer.write_array_len(2)?;
912 writer.write_string(&self.id)?;
913 JsonValue(self.fields.clone()).write(writer)
914 }
915}
916
917impl<'a> zerompk::FromMessagePack<'a> for BatchDocument {
918 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
919 use crate::json_msgpack::JsonValue;
920 let len = reader.read_array_len()?;
921 if len != 2 {
922 return Err(zerompk::Error::ArrayLengthMismatch {
923 expected: 2,
924 actual: len,
925 });
926 }
927 let id = reader.read_string()?.into_owned();
928 let fields = JsonValue::read(reader)?.0;
929 Ok(Self { id, fields })
930 }
931}
932
933#[derive(
937 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
938)]
939pub struct NativeResponse {
940 pub seq: u64,
942 pub status: ResponseStatus,
944 #[serde(skip_serializing_if = "Option::is_none")]
946 pub columns: Option<Vec<String>>,
947 #[serde(skip_serializing_if = "Option::is_none")]
949 pub rows: Option<Vec<Vec<Value>>>,
950 #[serde(skip_serializing_if = "Option::is_none")]
952 pub rows_affected: Option<u64>,
953 pub watermark_lsn: u64,
955 #[serde(skip_serializing_if = "Option::is_none")]
957 pub error: Option<ErrorPayload>,
958 #[serde(skip_serializing_if = "Option::is_none")]
960 pub auth: Option<AuthResponse>,
961}
962
963#[derive(
965 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
966)]
967pub struct ErrorPayload {
968 pub code: String,
970 pub message: String,
972}
973
974impl NativeResponse {
975 pub fn ok(seq: u64) -> Self {
977 Self {
978 seq,
979 status: ResponseStatus::Ok,
980 columns: None,
981 rows: None,
982 rows_affected: None,
983 watermark_lsn: 0,
984 error: None,
985 auth: None,
986 }
987 }
988
989 pub fn from_query_result(seq: u64, qr: crate::result::QueryResult, lsn: u64) -> Self {
991 Self {
992 seq,
993 status: ResponseStatus::Ok,
994 columns: Some(qr.columns),
995 rows: Some(qr.rows),
996 rows_affected: Some(qr.rows_affected),
997 watermark_lsn: lsn,
998 error: None,
999 auth: None,
1000 }
1001 }
1002
1003 pub fn error(seq: u64, code: impl Into<String>, message: impl Into<String>) -> Self {
1005 Self {
1006 seq,
1007 status: ResponseStatus::Error,
1008 columns: None,
1009 rows: None,
1010 rows_affected: None,
1011 watermark_lsn: 0,
1012 error: Some(ErrorPayload {
1013 code: code.into(),
1014 message: message.into(),
1015 }),
1016 auth: None,
1017 }
1018 }
1019
1020 pub fn auth_ok(seq: u64, username: String, tenant_id: u32) -> Self {
1022 Self {
1023 seq,
1024 status: ResponseStatus::Ok,
1025 columns: None,
1026 rows: None,
1027 rows_affected: None,
1028 watermark_lsn: 0,
1029 error: None,
1030 auth: Some(AuthResponse {
1031 username,
1032 tenant_id,
1033 }),
1034 }
1035 }
1036
1037 pub fn status_row(seq: u64, message: impl Into<String>) -> Self {
1039 Self {
1040 seq,
1041 status: ResponseStatus::Ok,
1042 columns: Some(vec!["status".into()]),
1043 rows: Some(vec![vec![Value::String(message.into())]]),
1044 rows_affected: Some(1),
1045 watermark_lsn: 0,
1046 error: None,
1047 auth: None,
1048 }
1049 }
1050}
1051
1052pub const MAX_FRAME_SIZE: u32 = 16 * 1024 * 1024;
1056
1057pub const FRAME_HEADER_LEN: usize = 4;
1059
1060pub const DEFAULT_NATIVE_PORT: u16 = 6433;
1062
1063#[cfg(test)]
1064mod tests {
1065 use super::*;
1066
1067 #[test]
1068 fn opcode_repr() {
1069 assert_eq!(OpCode::Auth as u8, 0x01);
1070 assert_eq!(OpCode::Sql as u8, 0x20);
1071 assert_eq!(OpCode::Begin as u8, 0x40);
1072 assert_eq!(OpCode::GraphHop as u8, 0x50);
1073 assert_eq!(OpCode::TextSearch as u8, 0x60);
1074 assert_eq!(OpCode::VectorBatchInsert as u8, 0x70);
1075 }
1076
1077 #[test]
1078 fn opcode_is_write() {
1079 assert!(OpCode::PointPut.is_write());
1080 assert!(OpCode::PointDelete.is_write());
1081 assert!(OpCode::CrdtApply.is_write());
1082 assert!(OpCode::EdgePut.is_write());
1083 assert!(!OpCode::PointGet.is_write());
1084 assert!(!OpCode::Sql.is_write());
1085 assert!(!OpCode::VectorSearch.is_write());
1086 assert!(!OpCode::Ping.is_write());
1087 }
1088
1089 #[test]
1090 fn response_status_repr() {
1091 assert_eq!(ResponseStatus::Ok as u8, 0);
1092 assert_eq!(ResponseStatus::Partial as u8, 1);
1093 assert_eq!(ResponseStatus::Error as u8, 2);
1094 }
1095
1096 #[test]
1097 fn native_response_ok() {
1098 let r = NativeResponse::ok(42);
1099 assert_eq!(r.seq, 42);
1100 assert_eq!(r.status, ResponseStatus::Ok);
1101 assert!(r.error.is_none());
1102 }
1103
1104 #[test]
1105 fn native_response_error() {
1106 let r = NativeResponse::error(1, "42P01", "collection not found");
1107 assert_eq!(r.status, ResponseStatus::Error);
1108 let e = r.error.unwrap();
1109 assert_eq!(e.code, "42P01");
1110 assert_eq!(e.message, "collection not found");
1111 }
1112
1113 #[test]
1114 fn native_response_from_query_result() {
1115 let qr = crate::result::QueryResult {
1116 columns: vec!["id".into(), "name".into()],
1117 rows: vec![vec![
1118 Value::String("u1".into()),
1119 Value::String("Alice".into()),
1120 ]],
1121 rows_affected: 0,
1122 };
1123 let r = NativeResponse::from_query_result(5, qr, 100);
1124 assert_eq!(r.seq, 5);
1125 assert_eq!(r.watermark_lsn, 100);
1126 assert_eq!(r.columns.as_ref().unwrap().len(), 2);
1127 assert_eq!(r.rows.as_ref().unwrap().len(), 1);
1128 }
1129
1130 #[test]
1131 fn native_response_status_row() {
1132 let r = NativeResponse::status_row(3, "OK");
1133 assert_eq!(r.columns.as_ref().unwrap(), &["status"]);
1134 assert_eq!(r.rows.as_ref().unwrap()[0][0].as_str(), Some("OK"));
1135 }
1136
1137 #[test]
1138 fn msgpack_roundtrip_request() {
1139 let req = NativeRequest {
1140 op: OpCode::Sql,
1141 seq: 1,
1142 fields: RequestFields::Text(TextFields {
1143 sql: Some("SELECT 1".into()),
1144 ..Default::default()
1145 }),
1146 };
1147 let bytes = zerompk::to_msgpack_vec(&req).unwrap();
1148 let decoded: NativeRequest = zerompk::from_msgpack(&bytes).unwrap();
1149 assert_eq!(decoded.op, OpCode::Sql);
1150 assert_eq!(decoded.seq, 1);
1151 }
1152
1153 #[test]
1154 fn msgpack_roundtrip_response() {
1155 let resp = NativeResponse::from_query_result(
1156 7,
1157 crate::result::QueryResult {
1158 columns: vec!["x".into()],
1159 rows: vec![vec![Value::Integer(42)]],
1160 rows_affected: 0,
1161 },
1162 99,
1163 );
1164 let bytes = zerompk::to_msgpack_vec(&resp).unwrap();
1165 let decoded: NativeResponse = zerompk::from_msgpack(&bytes).unwrap();
1166 assert_eq!(decoded.seq, 7);
1167 assert_eq!(decoded.watermark_lsn, 99);
1168 assert_eq!(decoded.rows.unwrap()[0][0].as_i64(), Some(42));
1169 }
1170
1171 #[test]
1172 fn auth_method_variants() {
1173 let trust = AuthMethod::Trust {
1174 username: "admin".into(),
1175 };
1176 let bytes = zerompk::to_msgpack_vec(&trust).unwrap();
1177 let decoded: AuthMethod = zerompk::from_msgpack(&bytes).unwrap();
1178 match decoded {
1179 AuthMethod::Trust { username } => assert_eq!(username, "admin"),
1180 _ => panic!("expected Trust variant"),
1181 }
1182
1183 let pw = AuthMethod::Password {
1184 username: "user".into(),
1185 password: "secret".into(),
1186 };
1187 let bytes = zerompk::to_msgpack_vec(&pw).unwrap();
1188 let decoded: AuthMethod = zerompk::from_msgpack(&bytes).unwrap();
1189 match decoded {
1190 AuthMethod::Password { username, password } => {
1191 assert_eq!(username, "user");
1192 assert_eq!(password, "secret");
1193 }
1194 _ => panic!("expected Password variant"),
1195 }
1196 }
1197}