postrust_core/query/
mod.rs1mod builder;
6
7pub use builder::{QueryBuilder, INSERTED_COLUMN, PARENT_ROW_COLUMN};
8
9use crate::error::Result;
10use crate::plan::{ActionPlan, DbActionPlan};
11use postrust_sql::{SqlFragment, SqlParam};
12
13pub fn build_query(plan: &ActionPlan) -> Result<MainQuery> {
20 match plan {
21 ActionPlan::Db(db_plan) => build_db_query(db_plan),
22 ActionPlan::Info(_) => Ok(MainQuery::empty()),
23 }
24}
25
26pub const MUTATION_RESULT: &str = "pgrst_mutation_result";
28
29fn build_db_query(plan: &DbActionPlan) -> Result<MainQuery> {
31 let mut query = MainQuery::new();
32
33 match plan {
34 DbActionPlan::Read(read_tree) => {
35 query.main = QueryBuilder::build_read(read_tree)?;
36 }
37 DbActionPlan::MutateRead { mutate, read } => {
38 let mutation = QueryBuilder::build_mutate(mutate)?;
39
40 query.main = match read {
46 Some(read_tree) => {
47 let read_sql = QueryBuilder::build_read(read_tree)?;
48 let mut frag = SqlFragment::new();
49 frag.push("WITH ");
50 frag.push(MUTATION_RESULT);
51 frag.push(" AS (");
52 frag.append(mutation);
53 frag.push(") ");
54 frag.append(read_sql);
55 frag
56 }
57 None => mutation,
58 };
59 }
60 DbActionPlan::Call { call, read } => {
61 query.main = QueryBuilder::build_call(call, read.as_ref())?;
62 }
63 }
64
65 Ok(query)
66}
67
68#[derive(Clone, Debug, Default)]
70pub struct MainQuery {
71 pub main: SqlFragment,
73 pub read: Option<SqlFragment>,
75 pub count: Option<SqlFragment>,
77}
78
79impl MainQuery {
80 pub fn new() -> Self {
82 Self::default()
83 }
84
85 pub fn empty() -> Self {
87 Self::default()
88 }
89
90 pub fn has_main(&self) -> bool {
92 !self.main.is_empty()
93 }
94
95 pub fn build_main(self) -> (String, Vec<SqlParam>) {
97 self.main.build()
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::api_request::{QualifiedIdentifier, Range};
105 use crate::plan::{CoercibleField, CoercibleSelectField, MutatePlan, ReadPlan, ReadPlanTree};
106
107 #[test]
113 fn a_write_reads_its_body_and_is_read_from() {
114 let mutate = MutatePlan::Insert {
115 target: QualifiedIdentifier::new("test", "items"),
116 columns: vec![CoercibleField::simple("id", "int8")],
117 body: Some(bytes::Bytes::from(r#"{"id":9001}"#)),
118 on_conflict: None,
119 where_clauses: vec![],
120 returning: vec!["id".into()],
121 pk_cols: vec!["id".into()],
122 apply_defaults: true,
123 reports_inserted: false,
124 };
125 let read = ReadPlan {
126 select: vec![CoercibleSelectField::simple("id", "bigint")],
127 from: QualifiedIdentifier::unqualified(MUTATION_RESULT),
128 from_alias: None,
129 where_clauses: vec![],
130 order: vec![],
131 range: Range::default(),
132 rel_name: String::new(),
133 rel_to_parent: None,
134 rel_join_conds: vec![],
135 rel_join_type: None,
136 rel_select: vec![],
137 depth: 0,
138 };
139
140 let plan = ActionPlan::Db(DbActionPlan::MutateRead {
141 mutate,
142 read: Some(ReadPlanTree::leaf(read)),
143 });
144 let (sql, _) = build_query(&plan).unwrap().build_main();
145
146 assert_eq!(
147 sql,
148 "WITH pgrst_mutation_result AS (\
149 INSERT INTO \"test\".\"items\" (\"id\") \
150 SELECT pgrst_body.\"id\" \
151 FROM (SELECT $1::json AS json_data) pgrst_payload, \
152 LATERAL (SELECT \"id\" FROM json_to_record(pgrst_payload.json_data) \
153 AS _(\"id\" int8)) pgrst_body \
154 RETURNING \"items\".\"id\") \
155 SELECT \"id\" FROM \"pgrst_mutation_result\""
156 );
157 }
158}