wasi_pg_client/query/
pipeline.rs1use std::sync::Arc;
17
18use crate::protocol::{BackendMessage, FrontendMessage, TransactionStatus};
19
20use crate::connection::{Connection, ConnectionState};
21use crate::error::{PgError, PgServerError, Result};
22use crate::query::params::encode_params_text;
23use crate::query::result::{CommandTag, QueryResult};
24use crate::query::row::{FieldDescription, Row};
25use crate::query::{read_data_row, read_row_description};
26use crate::transport::AsyncTransport;
27
28#[derive(Debug)]
34pub(crate) enum PipelineOp {
35 Query {
37 sql: String,
38 params: Vec<Option<Vec<u8>>>,
39 },
40 Execute {
42 sql: String,
43 params: Vec<Option<Vec<u8>>>,
44 },
45}
46
47#[derive(Debug, Clone)]
49#[non_exhaustive]
50pub enum PipelineResult {
51 Query(QueryResult),
53 Execute(CommandTag),
55}
56
57#[non_exhaustive]
67pub struct Pipeline<'a> {
68 conn: &'a mut Connection,
69 ops: Vec<PipelineOp>,
70}
71
72impl<'a> Pipeline<'a> {
73 pub(crate) fn new(conn: &'a mut Connection) -> Self {
74 Self {
75 conn,
76 ops: Vec::new(),
77 }
78 }
79
80 pub fn query(mut self, sql: &str, params: &[&dyn crate::types::ToSql]) -> Result<Self> {
82 let values = encode_params_text(params)?;
83 self.ops.push(PipelineOp::Query {
84 sql: sql.to_string(),
85 params: values,
86 });
87 Ok(self)
88 }
89
90 pub fn execute(mut self, sql: &str, params: &[&dyn crate::types::ToSql]) -> Result<Self> {
92 let values = encode_params_text(params)?;
93 self.ops.push(PipelineOp::Execute {
94 sql: sql.to_string(),
95 params: values,
96 });
97 Ok(self)
98 }
99
100 #[must_use = "pipeline errors should be checked"]
107 pub async fn finish(self) -> Result<Vec<PipelineResult>> {
108 let conn = self.conn;
109 conn.transition(ConnectionState::ActiveExtendedQuery)?;
110
111 for op in &self.ops {
113 match op {
114 PipelineOp::Query { sql, params } | PipelineOp::Execute { sql, params } => {
115 conn.codec
116 .encode_and_write(
117 &mut conn.transport,
118 &FrontendMessage::Parse {
119 name: String::new(),
120 sql: sql.clone(),
121 param_types: vec![],
122 },
123 )
124 .await?;
125
126 conn.codec
127 .encode_and_write(
128 &mut conn.transport,
129 &FrontendMessage::Bind {
130 portal: String::new(),
131 statement: String::new(),
132 param_formats: vec![crate::protocol::FormatCode::Text],
133 params: params.clone(),
134 result_formats: vec![crate::protocol::FormatCode::Binary],
135 },
136 )
137 .await?;
138
139 conn.codec
142 .encode_and_write(
143 &mut conn.transport,
144 &FrontendMessage::Describe {
145 variant: b'P',
146 name: String::new(),
147 },
148 )
149 .await?;
150
151 conn.codec
152 .encode_and_write(
153 &mut conn.transport,
154 &FrontendMessage::Execute {
155 portal: String::new(),
156 max_rows: 0,
157 },
158 )
159 .await?;
160 }
161 }
162 }
163
164 conn.codec
166 .encode_and_write(&mut conn.transport, &FrontendMessage::Sync)
167 .await?;
168
169 conn.transport.flush().await.map_err(PgError::Transport)?;
171
172 let mut results = Vec::with_capacity(self.ops.len());
174 let mut current_op = 0;
175 let mut current_columns: Option<Arc<Vec<FieldDescription>>> = None;
176 let mut current_rows: Vec<Row> = Vec::new();
177
178 loop {
179 let msg = conn.codec.read_message(&mut conn.transport).await?;
180 if conn.handle_async_message(&msg) {
181 continue;
182 }
183 match msg {
184 BackendMessage::ParseComplete => {}
185 BackendMessage::BindComplete => {}
186 BackendMessage::NoData => {
187 }
189 BackendMessage::RowDescription(body) => {
190 current_columns = Some(Arc::new(read_row_description(body)?));
191 }
192 BackendMessage::DataRow(body) => {
193 let values = read_data_row(body)?;
194 current_rows.push(Row::new(
195 current_columns.clone().unwrap_or_default(),
196 values,
197 ));
198 }
199 BackendMessage::CommandComplete(body) => {
200 let tag = CommandTag::new(body.tag().unwrap_or("").into());
201
202 match self.ops.get(current_op) {
204 Some(PipelineOp::Query { .. }) => {
205 results.push(PipelineResult::Query(QueryResult::new(
206 std::mem::take(&mut current_rows),
207 tag,
208 current_columns.take().unwrap_or_default(),
209 )));
210 }
211 Some(PipelineOp::Execute { .. }) => {
212 results.push(PipelineResult::Execute(tag));
213 }
214 None => {}
215 }
216 current_op += 1;
217 }
218 BackendMessage::EmptyQueryResponse => {
219 match self.ops.get(current_op) {
220 Some(PipelineOp::Query { .. }) => {
221 results.push(PipelineResult::Query(QueryResult::new(
222 Vec::new(),
223 CommandTag::new("".into()),
224 Arc::new(Vec::new()),
225 )));
226 }
227 Some(PipelineOp::Execute { .. }) => {
228 results.push(PipelineResult::Execute(CommandTag::new("".into())));
229 }
230 None => {}
231 }
232 current_op += 1;
233 }
234 BackendMessage::ErrorResponse(body) => {
235 let server_err = PgServerError::from_error_body(&body).map_err(PgError::Io)?;
236 conn.read_until_ready().await?;
237 conn.state = ConnectionState::Idle;
238 return Err(PgError::Server(Box::new(server_err)));
239 }
240 BackendMessage::ReadyForQuery(body) => {
241 conn.transaction_status = TransactionStatus::from_u8(body.status())
242 .unwrap_or(TransactionStatus::Idle);
243 conn.state = ConnectionState::Idle;
244 break;
245 }
246 _ => {}
247 }
248 }
249
250 Ok(results)
251 }
252}
253
254impl Connection {
259 pub fn pipeline(&mut self) -> Pipeline<'_> {
263 Pipeline::new(self)
264 }
265}