1- use super :: { Blob , FileRange } ;
1+ use super :: { Blob , FileRange , StreamingBlob } ;
22use crate :: { InstanceMetrics , db:: Pool , error:: Result } ;
33use chrono:: { DateTime , Utc } ;
44use futures_util:: stream:: { Stream , TryStreamExt } ;
55use sqlx:: Acquire ;
6- use std:: sync:: Arc ;
6+ use std:: { io , sync:: Arc } ;
77
88pub ( crate ) struct DatabaseBackend {
99 pool : Pool ,
@@ -58,38 +58,27 @@ impl DatabaseBackend {
5858 }
5959 }
6060
61- pub ( super ) async fn get (
61+ pub ( super ) async fn get_stream (
6262 & self ,
6363 path : & str ,
64- max_size : usize ,
6564 range : Option < FileRange > ,
66- ) -> Result < Blob > {
67- // The maximum size for a BYTEA (the type used for `content`) is 1GB, so this cast is safe:
68- // https://www.postgresql.org/message-id/162867790712200946i7ba8eb92v908ac595c0c35aee%40mail.gmail.com
69- let max_size = max_size. min ( i32:: MAX as usize ) as i32 ;
70-
65+ ) -> Result < StreamingBlob > {
7166 struct Result {
7267 path : String ,
7368 mime : String ,
7469 date_updated : DateTime < Utc > ,
7570 compression : Option < i32 > ,
7671 content : Option < Vec < u8 > > ,
77- is_too_big : bool ,
7872 }
7973
8074 let result = if let Some ( r) = range {
81- // when we only want to get a range we can validate already if the range is small enough
82- if ( r. end ( ) - r. start ( ) + 1 ) > max_size as u64 {
83- return Err ( std:: io:: Error :: other ( crate :: error:: SizeLimitReached ) . into ( ) ) ;
84- }
8575 let range_start = i32:: try_from ( * r. start ( ) ) ?;
8676
8777 sqlx:: query_as!(
8878 Result ,
8979 r#"SELECT
9080 path, mime, date_updated, compression,
91- substring(content from $2 for $3) as content,
92- FALSE as "is_too_big!"
81+ substring(content from $2 for $3) as content
9382 FROM files
9483 WHERE path = $1;"# ,
9584 path,
@@ -105,35 +94,35 @@ impl DatabaseBackend {
10594 sqlx:: query_as!(
10695 Result ,
10796 r#"SELECT
108- path, mime, date_updated, compression,
109- (CASE WHEN LENGTH(content) <= $2 THEN content ELSE NULL END) AS content,
110- (LENGTH(content) > $2) AS "is_too_big!"
97+ path,
98+ mime,
99+ date_updated,
100+ compression,
101+ content
111102 FROM files
112103 WHERE path = $1;"# ,
113104 path,
114- max_size,
115105 )
116106 . fetch_optional ( & self . pool )
117107 . await ?
118108 . ok_or ( super :: PathNotFoundError ) ?
119109 } ;
120110
121- if result. is_too_big {
122- return Err ( std:: io:: Error :: other ( crate :: error:: SizeLimitReached ) . into ( ) ) ;
123- }
124-
125111 let compression = result. compression . map ( |i| {
126112 i. try_into ( )
127113 . expect ( "invalid compression algorithm stored in database" )
128114 } ) ;
129- Ok ( Blob {
115+ let content = result. content . unwrap_or_default ( ) ;
116+ let content_len = content. len ( ) ;
117+ Ok ( StreamingBlob {
130118 path : result. path ,
131119 mime : result
132120 . mime
133121 . parse ( )
134122 . unwrap_or ( mime:: APPLICATION_OCTET_STREAM ) ,
135123 date_updated : result. date_updated ,
136- content : result. content . unwrap_or_default ( ) ,
124+ content : Box :: new ( io:: Cursor :: new ( content) ) ,
125+ content_length : content_len,
137126 compression,
138127 } )
139128 }
0 commit comments