sqlxo/
insert.rs

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}