@@ -8,7 +8,7 @@ use anyhow::Context;
8
8
use base64:: { engine, Engine as _} ;
9
9
use chrono:: Utc ;
10
10
use futures:: future:: BoxFuture ;
11
- use futures:: { FutureExt , TryStreamExt } ;
11
+ use futures:: { FutureExt , StreamExt , TryStreamExt } ;
12
12
use itertools:: Itertools ;
13
13
use native_tls:: { Certificate , TlsConnector } ;
14
14
use postgres_native_tls:: MakeTlsConnector ;
@@ -30,7 +30,8 @@ use windmill_common::error::{self, Error};
30
30
use windmill_common:: worker:: { to_raw_value, Connection , CLOUD_HOSTED } ;
31
31
use windmill_parser:: { Arg , Typ } ;
32
32
use windmill_parser_sql:: {
33
- parse_db_resource, parse_pg_statement_arg_indices, parse_pgsql_sig, parse_sql_blocks,
33
+ parse_db_resource, parse_pg_statement_arg_indices, parse_pgsql_sig, parse_s3_mode,
34
+ parse_sql_blocks,
34
35
} ;
35
36
use windmill_queue:: { CanceledBy , MiniPulledJob } ;
36
37
@@ -53,6 +54,14 @@ struct PgDatabase {
53
54
root_certificate_pem : Option < String > ,
54
55
}
55
56
57
+ #[ derive( Clone ) ]
58
+ struct S3Mode {
59
+ client : AuthedClient ,
60
+ object_key : String ,
61
+ storage : Option < String > ,
62
+ workspace_id : String ,
63
+ }
64
+
56
65
lazy_static ! {
57
66
pub static ref CONNECTION_CACHE : Arc <Mutex <Option <( String , tokio_postgres:: Client ) >>> =
58
67
Arc :: new( Mutex :: new( None ) ) ;
@@ -68,6 +77,7 @@ fn do_postgresql_inner<'a>(
68
77
column_order : Option < & ' a mut Option < Vec < String > > > ,
69
78
siz : & ' a AtomicUsize ,
70
79
skip_collect : bool ,
80
+ s3 : Option < S3Mode > ,
71
81
) -> error:: Result < BoxFuture < ' a , anyhow:: Result < Box < RawValue > > > > {
72
82
let mut query_params = vec ! [ ] ;
73
83
@@ -106,6 +116,29 @@ fn do_postgresql_inner<'a>(
106
116
. execute_raw ( & query, query_params)
107
117
. await
108
118
. map_err ( to_anyhow) ?;
119
+ } else if let Some ( ref s3) = s3 {
120
+ let rows = client
121
+ . query_raw ( & query, query_params)
122
+ . await ?
123
+ . map_err ( to_anyhow)
124
+ . map ( |row_result| {
125
+ row_result. and_then ( |row| {
126
+ postgres_row_to_json_value ( row)
127
+ . map_err ( to_anyhow)
128
+ . and_then ( |ref v| serde_json:: to_string ( v) . map_err ( to_anyhow) )
129
+ } )
130
+ } ) ;
131
+
132
+ s3. client
133
+ . upload_s3_file (
134
+ s3. workspace_id . as_str ( ) ,
135
+ s3. object_key . clone ( ) ,
136
+ s3. storage . clone ( ) ,
137
+ rows,
138
+ )
139
+ . await ?;
140
+
141
+ return Ok ( serde_json:: value:: to_raw_value ( & s3. object_key ) ?) ;
109
142
} else {
110
143
let rows = client
111
144
. query_raw ( & query, query_params)
@@ -171,6 +204,12 @@ pub async fn do_postgresql(
171
204
let pg_args = build_args_values ( job, client, conn) . await ?;
172
205
173
206
let inline_db_res_path = parse_db_resource ( & query) ;
207
+ let s3 = parse_s3_mode ( & query) . map ( |s3_mode| S3Mode {
208
+ client : client. clone ( ) ,
209
+ storage : s3_mode. storage ,
210
+ object_key : format ! ( "{}/{}.txt" , s3_mode. folder_key, job. id) ,
211
+ workspace_id : job. workspace_id . clone ( ) ,
212
+ } ) ;
174
213
175
214
let db_arg = if let Some ( inline_db_res_path) = inline_db_res_path {
176
215
Some (
@@ -321,6 +360,7 @@ pub async fn do_postgresql(
321
360
None ,
322
361
& size,
323
362
annotations. return_last_result && i < queries. len ( ) - 1 ,
363
+ s3. clone ( ) ,
324
364
)
325
365
} )
326
366
. collect :: < error:: Result < Vec < _ > > > ( ) ?;
@@ -347,6 +387,7 @@ pub async fn do_postgresql(
347
387
Some ( column_order) ,
348
388
& size,
349
389
false ,
390
+ s3,
350
391
) ?
351
392
} ;
352
393
0 commit comments