datacraft_query/native_query.rs
1//! The native saved-query vocabulary (M3) — split from the port root
2//! (step 36.7) to keep every file under the 500-line size gate, then
3//! moved with the query layer (step 76.3, item L37). The decodable
4//! MSysQueries subset and its QBE projection live here; the command
5//! layer's port root re-exports the vocabulary and owns the trait.
6
7use serde::{Deserialize, Serialize};
8
9/// Query flavor of a native saved query (step 15.3, M3).
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum NativeQueryKind {
13 /// Select query (object flags 0x00).
14 Select,
15 /// Crosstab query (0x10).
16 Crosstab,
17 /// Delete query (0x20).
18 Delete,
19 /// Update query (0x30).
20 Update,
21 /// Append query (0x40).
22 Append,
23 /// Make-table query (0x50).
24 MakeTable,
25 /// Data-definition query (0x60).
26 DataDefinition,
27 /// Pass-through query (0x70).
28 Passthrough,
29 /// Union query (0x80).
30 Union,
31}
32
33/// One column of a native saved query (M3).
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct NativeQueryColumn {
36 /// Source table, when the column names one.
37 pub table: Option<String>,
38 /// Source field or output alias.
39 pub field: Option<String>,
40 /// Column expression, when the column is computed.
41 pub expression: Option<String>,
42 /// Whether the column sorts descending.
43 pub descending: bool,
44}
45
46/// Join flavor of a native query (MSysQueries attribute 7 flag).
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "snake_case")]
49pub enum NativeJoinKind {
50 /// Inner join (flag 1).
51 Inner,
52 /// Left outer join (flag 2): the left operand's rows are preserved.
53 Left,
54 /// Right outer join (flag 3): the right operand's rows are preserved.
55 Right,
56}
57
58/// One JOIN expression of a native query.
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct NativeJoin {
61 /// Equality expression verbatim (e.g. `Table1.col1 = Table3.col1`).
62 pub expression: String,
63 /// Outer/inner flavor.
64 pub kind: NativeJoinKind,
65}
66
67/// One ORDER BY entry of a native query (MSysQueries attribute 11).
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct NativeOrder {
70 /// Field reference verbatim (qualified or bare).
71 pub field: String,
72 /// Whether the entry sorts descending.
73 pub descending: bool,
74}
75
76/// One native saved query as stored in the Jet/ACE file (M3, read-only).
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78pub struct NativeQuery {
79 /// Query name from MSysObjects.
80 pub name: String,
81 /// Query flavor from the object flags.
82 pub kind: NativeQueryKind,
83 /// FROM tables in attribute order.
84 pub tables: Vec<String>,
85 /// Selected columns.
86 pub columns: Vec<NativeQueryColumn>,
87 /// WHERE expressions verbatim (attribute 8).
88 pub criteria: Vec<String>,
89 /// GROUP BY expressions verbatim (attribute 9) — a documented rest
90 /// for execution (step 27.1, M12).
91 #[serde(default)]
92 pub group_by: Vec<String>,
93 /// HAVING expressions verbatim (attribute 10) — a documented rest.
94 #[serde(default)]
95 pub having: Vec<String>,
96 /// JOIN expressions with their flavors (attribute 7).
97 pub joins: Vec<NativeJoin>,
98 /// ORDER BY entries (attribute 11).
99 pub order_by: Vec<NativeOrder>,
100 /// Select-type bits of attribute 1 (`0x01` star, `0x02` distinct,
101 /// `0x08` distinct row, `0x10` top, `0x20` percent).
102 #[serde(default)]
103 pub select_flags: i64,
104 /// Parameter names (attribute 2) — parameters stay a documented rest.
105 #[serde(default)]
106 pub parameters: Vec<String>,
107}
108
109impl NativeQuery {
110 /// Maps the decodable subset onto a QBE query definition: select
111 /// queries over exactly one table, plain field columns, no criteria,
112 /// grouping, joins or computed columns. This is the read-only design
113 /// grid's projection only; execution goes through
114 /// [`crate::native_exec`] (step 27.1, M12).
115 pub fn query_def(&self) -> Option<crate::qbe::QueryDef> {
116 if self.kind != NativeQueryKind::Select
117 || self.tables.len() != 1
118 || !self.criteria.is_empty()
119 || !self.group_by.is_empty()
120 || !self.having.is_empty()
121 || !self.joins.is_empty()
122 {
123 return None;
124 }
125 let mut fields = Vec::with_capacity(self.columns.len());
126 for column in &self.columns {
127 if column.expression.is_some() {
128 return None;
129 }
130 let field = column.field.as_ref()?;
131 fields.push(crate::qbe::QbeField {
132 name: field.clone(),
133 shown: true,
134 sort: if column.descending {
135 crate::qbe::SortOrder::Descending
136 } else {
137 crate::qbe::SortOrder::Ascending
138 },
139 criteria: Vec::new(),
140 });
141 }
142 Some(crate::qbe::QueryDef {
143 name: self.name.clone(),
144 table: self.tables[0].clone(),
145 fields,
146 })
147 }
148}