use crate::{
Query,
expression::{Operator, PostfixOperator, UnaryOperator},
instr::{RpnInstr, TemporalKind},
param::Param,
query::SortKind,
span::{Span, TextSource, TextSpan},
};
#[derive(Debug, Clone, Copy)]
pub struct MergeBase {
pub data: u32,
pub param: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SubqueryBase {
pub project: u32,
pub from: u16,
pub filters: u32,
pub having: u16,
pub distinct: bool,
pub group_by: u16,
pub order_by: u16,
pub limit: u16,
pub offset: u16,
}
#[derive(Debug, Clone, Copy)]
pub struct JsonContainsLower {
pub rhs: usize,
pub path: &'static str,
pub negated: bool,
pub compound: bool,
}
impl SubqueryBase {
pub fn total(self) -> usize {
self.project as usize
+ self.from as usize
+ self.filters as usize
+ self.having as usize
+ self.group_by as usize
+ self.order_by as usize
+ self.limit as usize
+ self.offset as usize
}
}
fn remap_instr(instr: RpnInstr, base: MergeBase) -> RpnInstr {
match instr {
RpnInstr::Column { span, table } => RpnInstr::Column {
span: span.offset(base.data),
table: table.map(|v| v.offset(base.data)),
},
RpnInstr::Table { span } => RpnInstr::Table {
span: span.offset(base.data),
},
RpnInstr::Param { id } => RpnInstr::Param {
id: id + base.param,
},
RpnInstr::Raw { span, params } => RpnInstr::Raw {
span: span.offset(base.data),
params,
},
RpnInstr::Alias { span, inner } => RpnInstr::Alias {
span: span.offset(base.data),
inner,
},
RpnInstr::JsonExtractText { inner, path } => RpnInstr::JsonExtractText {
inner,
path: path.offset(base.data),
},
RpnInstr::JsonContains {
inner,
rhs,
path,
negated,
compound,
} => RpnInstr::JsonContains {
inner,
rhs,
path: path.offset(base.data),
negated,
compound,
},
RpnInstr::JsonContainsKey {
inner,
path,
negated,
} => RpnInstr::JsonContainsKey {
inner,
path: path.offset(base.data),
negated,
},
RpnInstr::JsonLength { inner, path } => RpnInstr::JsonLength {
inner,
path: path.offset(base.data),
},
RpnInstr::Temporal { inner, kind } => RpnInstr::Temporal { inner, kind },
other => other,
}
}
fn remap_param(param: Param, data_offset: u32) -> Param {
match param {
Param::Text(Some(span)) => Param::Text(Some(span.offset(data_offset))),
Param::Blob(Some(span)) => Param::Blob(Some(span.offset(data_offset))),
other => other,
}
}
#[derive(Debug)]
pub struct LowerCtx<'a> {
pub instrs: &'a mut Vec<RpnInstr>,
pub params: &'a mut Vec<Param>,
pub data: &'a mut Vec<u8>,
}
pub trait Params {
fn push_param(&mut self, param: Param) -> u32;
}
impl Params for Vec<Param> {
fn push_param(&mut self, param: Param) -> u32 {
let index = self.len() as u32;
self.push(param);
index
}
}
pub trait Data {
fn intern_text(&mut self, s: &str) -> TextSpan;
fn text(&self, span: TextSpan) -> &str;
fn blob(&self, span: Span) -> &[u8];
fn intern_blob(&mut self, b: &[u8]) -> Span;
}
impl Data for Vec<u8> {
fn text(&self, span: TextSpan) -> &str {
match span.0 {
TextSource::StaticText(value) => value,
TextSource::Text(span) => {
let bytes = self.blob(span);
unsafe { std::str::from_utf8_unchecked(bytes) }
}
}
}
fn blob(&self, span: Span) -> &[u8] {
&self[span.start as usize..(span.start + span.len) as usize]
}
fn intern_text(&mut self, s: &str) -> TextSpan {
let start: u32 = self.len() as u32;
let len: u32 = s.len() as u32;
self.extend_from_slice(s.as_bytes());
TextSpan::new(start, len)
}
fn intern_blob(&mut self, b: &[u8]) -> Span {
let start = self.len() as u32;
let len = b.len() as u32;
self.extend_from_slice(b);
Span::new(start, len)
}
}
pub trait Instructions {
fn push_subquery(&mut self, base: SubqueryBase);
fn push_alias(&mut self, span: TextSpan, inner: usize);
fn push_seperated(&mut self, count: usize);
fn push_column(&mut self, table: Option<TextSpan>, span: TextSpan);
fn push_table(&mut self, name: TextSpan);
fn push_binary(&mut self, op: Operator, lhs: usize, rhs: usize);
fn push_unary(&mut self, op: UnaryOperator, rhs: usize);
fn push_between(&mut self, negated: bool, lhs: usize, low: usize, high: usize);
fn push_param(&mut self, id: u32);
fn push_raw(&mut self, span: TextSpan, params: usize);
fn push_cross_join(&mut self, count: usize);
fn push_assignment(&mut self, lhs: usize, rhs: usize);
}
impl Instructions for Vec<RpnInstr> {
fn push_alias(&mut self, span: TextSpan, inner: usize) {
self.push(RpnInstr::Alias { span, inner });
}
fn push_column(&mut self, table: Option<TextSpan>, span: TextSpan) {
self.push(RpnInstr::Column { span, table });
}
fn push_seperated(&mut self, count: usize) {
self.push(RpnInstr::Seperated { count });
}
fn push_cross_join(&mut self, count: usize) {
self.push(RpnInstr::CrossJoin { count });
}
fn push_table(&mut self, span: TextSpan) {
self.push(RpnInstr::Table { span });
}
fn push_binary(&mut self, op: Operator, lhs: usize, rhs: usize) {
self.push(RpnInstr::Binary { op, lhs, rhs });
}
fn push_assignment(&mut self, lhs: usize, rhs: usize) {
self.push(RpnInstr::Assignement { lhs, rhs });
}
fn push_unary(&mut self, op: UnaryOperator, rhs: usize) {
self.push(RpnInstr::Unary { op, rhs });
}
fn push_subquery(&mut self, base: SubqueryBase) {
self.push(RpnInstr::Subquery(base));
}
fn push_between(&mut self, negated: bool, lhs: usize, low: usize, high: usize) {
self.push(RpnInstr::Between {
negated,
lhs,
low,
high,
});
}
fn push_param(&mut self, id: u32) {
self.push(RpnInstr::Param { id });
}
fn push_raw(&mut self, span: TextSpan, params: usize) {
self.push(RpnInstr::Raw { span, params });
}
}
impl<'a> LowerCtx<'a> {
pub fn lower_param(&mut self, param: Param) -> usize {
let id = self.params.push_param(param);
self.instrs.push_param(id);
1
}
pub fn lower_raw(&mut self, sql: &str, data: Vec<u8>, params: Vec<Param>) -> usize {
let data_offset = self.data.len() as u32;
self.data.extend_from_slice(&data);
let span = self.data.intern_text(sql);
let count = params.len();
for param in params {
let _ = self.lower_param(remap_param(param, data_offset));
}
self.instrs.push_raw(span, count);
count + 1
}
pub fn lower_column(&mut self, table: Option<&'static str>, name: &'static str) -> usize {
let span = TextSpan::new_static(name);
let table = table.map(TextSpan::new_static);
self.instrs.push_column(table, span);
1
}
pub fn lower_table(&mut self, name: &'static str) -> usize {
let span = TextSpan::new_static(name);
self.instrs.push_table(span);
1
}
pub fn lower_star(&mut self) -> usize {
self.instrs.push(RpnInstr::Star);
1
}
pub fn lower_alias(&mut self, alias: &'static str, inner: usize) -> usize {
let span = TextSpan::new_static(alias);
self.instrs.push_alias(span, inner);
inner + 1
}
pub fn lower_subquery_ref(&mut self, sub: &Query) -> usize {
let merge = MergeBase {
data: self.data.len() as u32,
param: self.params.len() as u32,
};
self.data.extend_from_slice(&sub.data);
self.params.extend(sub.params.iter().copied());
let base = SubqueryBase {
project: sub.project.len().try_into().expect("project overflow"),
from: sub.from.len().try_into().expect("from overflow"),
filters: sub.filters.len().try_into().expect("filters overflow"),
having: sub.havings.len().try_into().expect("havings overflow"),
distinct: sub.distinct,
group_by: sub.group_by.len().try_into().expect("group by overflow"),
order_by: sub.order_by.len().try_into().expect("order by overflow"),
limit: sub.limit.len().try_into().expect("limit overflow"),
offset: sub.offset.len().try_into().expect("offset overflow"),
};
self.instrs.extend(
sub.project
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.from
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.filters
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.group_by
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.havings
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.order_by
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.limit
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
self.instrs.extend(
sub.offset
.iter()
.copied()
.map(|instr| remap_instr(instr, merge)),
);
let total = base.total();
self.instrs.push_subquery(base);
total + 1
}
pub fn lower_order_by(&mut self, kind: SortKind, inner: usize) -> usize {
self.instrs.push(RpnInstr::OrderBy { sort: kind, inner });
inner + 1
}
pub fn lower_exists(&mut self, negated: bool, inner: usize) -> usize {
self.instrs.push(RpnInstr::Exists { negated, inner });
inner + 1
}
pub fn lower_lit_int(&mut self, value: i64) -> usize {
self.instrs.push(RpnInstr::LitInt { value });
1
}
pub fn lower_null(&mut self) -> usize {
self.instrs.push(RpnInstr::Null);
1
}
pub fn lower_fn(&mut self, name: &'static str, args: usize) -> usize {
self.instrs.push(RpnInstr::Fn { name, args });
args + 1
}
pub fn lower_json_extract_text(&mut self, inner: usize, path: &'static str) -> usize {
self.instrs.push(RpnInstr::JsonExtractText {
inner,
path: TextSpan::new_static(path),
});
inner + 1
}
pub fn lower_json_contains(&mut self, inner: usize, spec: JsonContainsLower) -> usize {
self.instrs.push(RpnInstr::JsonContains {
inner,
rhs: spec.rhs,
path: TextSpan::new_static(spec.path),
negated: spec.negated,
compound: spec.compound,
});
inner + spec.rhs + 1
}
pub fn lower_json_contains_key(
&mut self,
inner: usize,
path: &'static str,
negated: bool,
) -> usize {
self.instrs.push(RpnInstr::JsonContainsKey {
inner,
path: TextSpan::new_static(path),
negated,
});
inner + 1
}
pub fn lower_json_length(&mut self, inner: usize, path: &'static str) -> usize {
self.instrs.push(RpnInstr::JsonLength {
inner,
path: TextSpan::new_static(path),
});
inner + 1
}
pub fn lower_temporal(&mut self, inner: usize, kind: TemporalKind) -> usize {
self.instrs.push(RpnInstr::Temporal { inner, kind });
inner + 1
}
pub fn lower_in(&mut self, negated: bool, lhs: usize, rhs: usize) -> usize {
self.instrs.push(RpnInstr::In { negated, lhs, rhs });
lhs + rhs + 1
}
pub(crate) fn lower_postfix(&mut self, operator: PostfixOperator, lhs: usize) -> usize {
self.instrs.push(RpnInstr::Postfix { op: operator, lhs });
lhs + 1
}
}