Skip to content

Commit 74867bd

Browse files
Reduce amount of code in AsyncPgConnection functions that have query type as generic parameter (#153)
Reduce amount of code in functions that have query type as generic parameter The total amount of LLVM lines in the lemmy_db_schema crate reduced by 34% as reported by the commit author --------- Co-authored-by: Georg Semmler <[email protected]>
1 parent 86cf7cd commit 74867bd

File tree

1 file changed

+72
-33
lines changed

1 file changed

+72
-33
lines changed

src/pg/mod.rs

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -156,18 +156,10 @@ impl AsyncConnection for AsyncPgConnection {
156156
T: AsQuery + 'query,
157157
T::Query: QueryFragment<Self::Backend> + QueryId + 'query,
158158
{
159-
let connection_future = self.connection_future.as_ref().map(|rx| rx.resubscribe());
160159
let query = source.as_query();
161-
let load_future = self.with_prepared_statement(query, |conn, stmt, binds| async move {
162-
let res = conn.query_raw(&stmt, binds).await.map_err(ErrorHelper)?;
163-
164-
Ok(res
165-
.map_err(|e| diesel::result::Error::from(ErrorHelper(e)))
166-
.map_ok(PgRow::new)
167-
.boxed())
168-
});
160+
let load_future = self.with_prepared_statement(query, load_prepared);
169161

170-
drive_future(connection_future, load_future).boxed()
162+
self.run_with_connection_future(load_future)
171163
}
172164

173165
fn execute_returning_count<'conn, 'query, T>(
@@ -177,19 +169,8 @@ impl AsyncConnection for AsyncPgConnection {
177169
where
178170
T: QueryFragment<Self::Backend> + QueryId + 'query,
179171
{
180-
let connection_future = self.connection_future.as_ref().map(|rx| rx.resubscribe());
181-
let execute = self.with_prepared_statement(source, |conn, stmt, binds| async move {
182-
let binds = binds
183-
.iter()
184-
.map(|b| b as &(dyn ToSql + Sync))
185-
.collect::<Vec<_>>();
186-
187-
let res = tokio_postgres::Client::execute(&conn, &stmt, &binds as &[_])
188-
.await
189-
.map_err(ErrorHelper)?;
190-
Ok(res as usize)
191-
});
192-
drive_future(connection_future, execute).boxed()
172+
let execute = self.with_prepared_statement(source, execute_prepared);
173+
self.run_with_connection_future(execute)
193174
}
194175

195176
fn transaction_state(&mut self) -> &mut AnsiTransactionManager {
@@ -212,6 +193,35 @@ impl Drop for AsyncPgConnection {
212193
}
213194
}
214195

196+
async fn load_prepared(
197+
conn: Arc<tokio_postgres::Client>,
198+
stmt: Statement,
199+
binds: Vec<ToSqlHelper>,
200+
) -> QueryResult<BoxStream<'static, QueryResult<PgRow>>> {
201+
let res = conn.query_raw(&stmt, binds).await.map_err(ErrorHelper)?;
202+
203+
Ok(res
204+
.map_err(|e| diesel::result::Error::from(ErrorHelper(e)))
205+
.map_ok(PgRow::new)
206+
.boxed())
207+
}
208+
209+
async fn execute_prepared(
210+
conn: Arc<tokio_postgres::Client>,
211+
stmt: Statement,
212+
binds: Vec<ToSqlHelper>,
213+
) -> QueryResult<usize> {
214+
let binds = binds
215+
.iter()
216+
.map(|b| b as &(dyn ToSql + Sync))
217+
.collect::<Vec<_>>();
218+
219+
let res = tokio_postgres::Client::execute(&conn, &stmt, &binds as &[_])
220+
.await
221+
.map_err(ErrorHelper)?;
222+
Ok(res as usize)
223+
}
224+
215225
#[inline(always)]
216226
fn update_transaction_manager_status<T>(
217227
query_result: QueryResult<T>,
@@ -335,14 +345,22 @@ impl AsyncPgConnection {
335345
Ok(())
336346
}
337347

348+
fn run_with_connection_future<'a, R: 'a>(
349+
&self,
350+
future: impl Future<Output = QueryResult<R>> + Send + 'a,
351+
) -> BoxFuture<'a, QueryResult<R>> {
352+
let connection_future = self.connection_future.as_ref().map(|rx| rx.resubscribe());
353+
drive_future(connection_future, future).boxed()
354+
}
355+
338356
fn with_prepared_statement<'a, T, F, R>(
339357
&mut self,
340358
query: T,
341-
callback: impl FnOnce(Arc<tokio_postgres::Client>, Statement, Vec<ToSqlHelper>) -> F + Send + 'a,
359+
callback: fn(Arc<tokio_postgres::Client>, Statement, Vec<ToSqlHelper>) -> F,
342360
) -> BoxFuture<'a, QueryResult<R>>
343361
where
344362
T: QueryFragment<diesel::pg::Pg> + QueryId,
345-
F: Future<Output = QueryResult<R>> + Send,
363+
F: Future<Output = QueryResult<R>> + Send + 'a,
346364
R: Send,
347365
{
348366
// we explicilty descruct the query here before going into the async block
@@ -352,14 +370,9 @@ impl AsyncPgConnection {
352370
// which both are `Send`.
353371
// We also collect the query id (essentially an integer) and the safe_to_cache flag here
354372
// so there is no need to even access the query in the async block below
355-
let is_safe_to_cache_prepared = query.is_safe_to_cache_prepared(&diesel::pg::Pg);
356373
let mut query_builder = PgQueryBuilder::default();
357-
let sql = query
358-
.to_sql(&mut query_builder, &Pg)
359-
.map(|_| query_builder.finish());
360374

361375
let mut bind_collector = RawBytesBindCollector::<diesel::pg::Pg>::new();
362-
let query_id = T::query_id();
363376

364377
// we don't resolve custom types here yet, we do that later
365378
// in the async block below as we might need to perform lookup
@@ -368,16 +381,42 @@ impl AsyncPgConnection {
368381
// We apply this workaround to prevent requiring all the diesel
369382
// serialization code to beeing async
370383
let mut metadata_lookup = PgAsyncMetadataLookup::new();
371-
let collect_bind_result =
372-
query.collect_binds(&mut bind_collector, &mut metadata_lookup, &Pg);
373384

385+
// The code that doesn't need the `T` generic parameter is in a separate function to reduce LLVM IR lines
386+
self.with_prepared_statement_after_sql_built(
387+
callback,
388+
query.is_safe_to_cache_prepared(&Pg),
389+
T::query_id(),
390+
query.to_sql(&mut query_builder, &Pg),
391+
query.collect_binds(&mut bind_collector, &mut metadata_lookup, &Pg),
392+
query_builder,
393+
bind_collector,
394+
metadata_lookup,
395+
)
396+
}
397+
398+
fn with_prepared_statement_after_sql_built<'a, F, R>(
399+
&mut self,
400+
callback: fn(Arc<tokio_postgres::Client>, Statement, Vec<ToSqlHelper>) -> F,
401+
is_safe_to_cache_prepared: QueryResult<bool>,
402+
query_id: Option<std::any::TypeId>,
403+
to_sql_result: QueryResult<()>,
404+
collect_bind_result: QueryResult<()>,
405+
query_builder: PgQueryBuilder,
406+
mut bind_collector: RawBytesBindCollector<Pg>,
407+
metadata_lookup: PgAsyncMetadataLookup,
408+
) -> BoxFuture<'a, QueryResult<R>>
409+
where
410+
F: Future<Output = QueryResult<R>> + Send + 'a,
411+
R: Send,
412+
{
374413
let raw_connection = self.conn.clone();
375414
let stmt_cache = self.stmt_cache.clone();
376415
let metadata_cache = self.metadata_cache.clone();
377416
let tm = self.transaction_state.clone();
378417

379418
async move {
380-
let sql = sql?;
419+
let sql = to_sql_result.map(|_| query_builder.finish())?;
381420
let is_safe_to_cache_prepared = is_safe_to_cache_prepared?;
382421
collect_bind_result?;
383422
// Check whether we need to resolve some types at all

0 commit comments

Comments
 (0)