1use super::{AbendId, PlaceId, SymId};
5use crate::sql::{HostType, fingerprint};
6use crate::{codec_enum, codec_struct};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct SqlEntry<P = PlaceId> {
11 pub ordinal: u32,
12 pub verb: SymId,
14 pub statement: SqlStatement<P>,
15 pub text: SymId,
17 pub fingerprint: u32,
18 pub with_hold: bool,
20}
21
22#[derive(Clone, Debug, PartialEq, Eq)]
24pub enum SqlStatement<P = PlaceId> {
25 Query { inputs: Vec<HostPlace<P>>, into: Vec<HostPlace<P>> },
26 Change { delete: bool, inputs: Vec<HostPlace<P>>, current_of: Option<SymId> },
27 Open { cursor: SymId, inputs: Vec<HostPlace<P>> },
28 Fetch { cursor: SymId, into: Vec<HostPlace<P>> },
29 Close { cursor: SymId },
30 Commit,
31 Rollback,
32 Declaration,
34 Unsupported(SymId),
36}
37
38#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct HostPlace<P = PlaceId> {
42 pub var: P,
43 pub member: Option<(u32, u32)>,
44 pub ty: Result<HostType, AbendId>,
46 pub indicator: Option<(P, u32)>,
47}
48
49#[derive(Clone, Debug, Default, PartialEq, Eq)]
51pub struct Sqlca<P = PlaceId> {
52 pub fields: Vec<(SqlcaField, P, HostType)>,
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum SqlcaField {
57 CaId,
58 CaBc,
59 Code,
60 ErrMl,
61 ErrMc,
62 ErrP,
63 State,
64 ErrD(u8),
66 Warn(u8),
68}
69
70codec_struct!(SqlEntry { ordinal, verb, statement, text, fingerprint, with_hold });
71codec_enum!(SqlStatement {
72 Query { inputs, into } = 0,
73 Change { delete, inputs, current_of } = 1,
74 Open { cursor, inputs } = 2,
75 Fetch { cursor, into } = 3,
76 Close { cursor } = 4,
77 Commit = 5,
78 Rollback = 6,
79 Declaration = 7,
80 Unsupported(what) = 8,
81});
82codec_struct!(HostPlace { var, member, ty, indicator } check host_place_valid);
83codec_struct!(Sqlca { fields } check sqlca_valid);
84codec_enum!(SqlcaField {
85 CaId = 0,
86 CaBc = 1,
87 Code = 2,
88 ErrMl = 3,
89 ErrMc = 4,
90 ErrP = 5,
91 State = 6,
92 ErrD(n) = 7,
93 Warn(n) = 8,
94});
95
96fn not_a_structure(ty: &HostType) -> Result<(), String> {
98 match ty {
99 HostType::Structure(_) => Err("a host structure where lowering gives its members".into()),
100 _ => Ok(()),
101 }
102}
103
104fn host_place_valid(place: &HostPlace) -> Result<(), String> {
105 place.ty.as_ref().map_or(Ok(()), not_a_structure)
106}
107
108fn sqlca_valid(sqlca: &Sqlca) -> Result<(), String> {
109 for (field, _, ty) in &sqlca.fields {
110 match *field {
111 SqlcaField::ErrD(n) if !(1..=6).contains(&n) => return Err(format!("SQLERRD({n}) is not an SQLCA field")),
112 SqlcaField::Warn(n) if n > 10 => return Err(format!("SQLWARN{n} is not an SQLCA field")),
113 _ => not_a_structure(ty)?,
114 }
115 }
116 Ok(())
117}
118
119pub(super) fn table_valid(table: &[SqlEntry], symbols: &[String]) -> Result<(), String> {
122 let symbol = |id: SymId| symbols.get(id as usize).ok_or_else(|| format!("symbol {id} of a table of {}", symbols.len()));
123 for (k, entry) in (1u32..).zip(table) {
124 if entry.ordinal != k {
125 return Err(format!("SQL entry {k} has ordinal {}", entry.ordinal));
126 }
127 let text = symbol(entry.text)?;
128 if entry.fingerprint != fingerprint(text) {
129 return Err(format!("SQL entry {k} has fingerprint {:08X}, not its text's {:08X}", entry.fingerprint, fingerprint(text)));
130 }
131 let held = match &entry.statement {
132 SqlStatement::Open { cursor, .. } => text.starts_with(&format!("DECLARE {} CURSOR WITH HOLD FOR ", symbol(*cursor)?)),
133 _ => false,
134 };
135 if entry.with_hold != held {
136 return Err(format!("SQL entry {k} has WITH HOLD {} for its text", if entry.with_hold { "set" } else { "clear" }));
137 }
138 }
139 Ok(())
140}