1use sqlx::{
2 Executor,
3 Postgres,
4};
5use sqlxo_traits::{
6 Creatable,
7 CreateModel,
8 QueryContext,
9};
10
11use crate::{
12 blocks::{
13 InsertHead,
14 SqlWriter,
15 },
16 Buildable,
17 ExecutablePlan,
18 FetchablePlan,
19 Planable,
20};
21
22#[allow(dead_code)]
23pub trait BuildableInsertQuery<C>: Buildable<C, Plan: Planable<C>>
24where
25 C: QueryContext,
26{
27}
28
29pub struct InsertQueryPlan<'a, C: QueryContext>
30where
31 C::Model: Creatable,
32{
33 pub(crate) table: &'a str,
34 pub(crate) create_model: <C::Model as Creatable>::CreateModel,
35 pub(crate) insert_marker_field: Option<&'static str>,
36}
37
38impl<'a, C> InsertQueryPlan<'a, C>
39where
40 C: QueryContext,
41 C::Model: Creatable,
42{
43 fn to_query_builder(&self) -> sqlx::QueryBuilder<'static, Postgres> {
44 let head = InsertHead::new(self.table);
45 let mut w = SqlWriter::new(head);
46
47 self.create_model
48 .apply_inserts(w.query_builder_mut(), self.insert_marker_field);
49
50 w.into_builder()
51 }
52
53 #[cfg(any(test, feature = "test-utils"))]
54 pub fn sql(&self) -> String {
55 use sqlx::Execute;
56 self.to_query_builder().build().sql().to_string()
57 }
58}
59
60#[async_trait::async_trait]
61impl<'a, C> ExecutablePlan<C> for InsertQueryPlan<'a, C>
62where
63 C: QueryContext,
64 C::Model: Creatable,
65{
66 async fn execute<'e, E>(&self, exec: E) -> Result<u64, sqlx::Error>
67 where
68 E: Executor<'e, Database = Postgres>,
69 {
70 let rows = self
71 .to_query_builder()
72 .build()
73 .execute(exec)
74 .await?
75 .rows_affected();
76
77 Ok(rows)
78 }
79}
80
81#[async_trait::async_trait]
82impl<'a, C> FetchablePlan<C> for InsertQueryPlan<'a, C>
83where
84 C: QueryContext,
85 C::Model: Creatable,
86{
87 async fn fetch_one<'e, E>(&self, exec: E) -> Result<C::Model, sqlx::Error>
88 where
89 E: Executor<'e, Database = Postgres>,
90 {
91 self.to_query_builder()
92 .push(" RETURNING *")
93 .build_query_as::<C::Model>()
94 .fetch_one(exec)
95 .await
96 }
97
98 async fn fetch_all<'e, E>(
99 &self,
100 exec: E,
101 ) -> Result<Vec<C::Model>, sqlx::Error>
102 where
103 E: Executor<'e, Database = Postgres>,
104 {
105 self.to_query_builder()
106 .push(" RETURNING *")
107 .build_query_as::<C::Model>()
108 .fetch_all(exec)
109 .await
110 }
111
112 async fn fetch_optional<'e, E>(
113 &self,
114 exec: E,
115 ) -> Result<Option<C::Model>, sqlx::Error>
116 where
117 E: Executor<'e, Database = Postgres>,
118 {
119 self.to_query_builder()
120 .push(" RETURNING *")
121 .build_query_as::<C::Model>()
122 .fetch_optional(exec)
123 .await
124 }
125}
126
127impl<'a, C> Planable<C> for InsertQueryPlan<'a, C>
128where
129 C: QueryContext,
130 C::Model: Creatable,
131{
132}
133
134pub struct InsertQueryBuilder<'a, C: QueryContext>
135where
136 C::Model: Creatable,
137{
138 pub(crate) table: &'a str,
139 pub(crate) create_model: Option<<C::Model as Creatable>::CreateModel>,
140 pub(crate) insert_marker_field: Option<&'static str>,
141}
142
143impl<'a, C> InsertQueryBuilder<'a, C>
144where
145 C: QueryContext,
146 C::Model: Creatable,
147{
148 pub fn model(
149 mut self,
150 model: <C::Model as Creatable>::CreateModel,
151 ) -> Self {
152 self.create_model = Some(model);
153 self
154 }
155}
156
157impl<'a, C> Buildable<C> for InsertQueryBuilder<'a, C>
158where
159 C: QueryContext,
160 C::Model: Creatable,
161{
162 type Plan = InsertQueryPlan<'a, C>;
163
164 fn from_ctx() -> Self {
165 Self {
166 table: C::TABLE,
167 create_model: None,
168 insert_marker_field: <C::Model as Creatable>::INSERT_MARKER_FIELD,
169 }
170 }
171
172 fn build(self) -> Self::Plan {
173 let create_model = self
174 .create_model
175 .expect("create model must be set with .model()");
176
177 InsertQueryPlan {
178 table: self.table,
179 create_model,
180 insert_marker_field: self.insert_marker_field,
181 }
182 }
183}
184
185impl<'a, C> BuildableInsertQuery<C> for InsertQueryBuilder<'a, C>
186where
187 C: QueryContext,
188 C::Model: Creatable,
189{
190}