Skip to main content

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}