@@ -19,6 +19,7 @@ use itertools::Itertools;
19
19
use mysql_async:: consts:: ColumnType as MySqlColumnType ;
20
20
use mysql_async:: prelude:: * ;
21
21
use risingwave_common:: array:: arrow:: IcebergArrowConvert ;
22
+ use risingwave_common:: secret:: LocalSecretManager ;
22
23
use risingwave_common:: types:: { DataType , ScalarImpl , StructType } ;
23
24
use risingwave_connector:: source:: iceberg:: {
24
25
extract_bucket_and_file_name, get_parquet_fields, list_data_directory, new_azblob_operator,
@@ -30,10 +31,15 @@ use thiserror_ext::AsReport;
30
31
use tokio_postgres:: types:: Type as TokioPgType ;
31
32
32
33
use super :: { infer_type, Expr , ExprImpl , ExprRewriter , Literal , RwResult } ;
34
+ use crate :: catalog:: catalog_service:: CatalogReadGuard ;
33
35
use crate :: catalog:: function_catalog:: { FunctionCatalog , FunctionKind } ;
36
+ use crate :: catalog:: root_catalog:: SchemaPath ;
34
37
use crate :: error:: ErrorCode :: BindError ;
35
38
use crate :: utils:: FRONTEND_RUNTIME ;
36
39
40
+ const INLINE_ARG_LEN : usize = 6 ;
41
+ const CDC_SOURCE_ARG_LEN : usize = 2 ;
42
+
37
43
/// A table function takes a row as input and returns a table. It is also known as Set-Returning
38
44
/// Function.
39
45
///
@@ -291,45 +297,76 @@ impl TableFunction {
291
297
} )
292
298
}
293
299
294
- pub fn new_postgres_query ( args : Vec < ExprImpl > ) -> RwResult < Self > {
295
- let args = {
296
- if args. len ( ) != 6 {
297
- return Err ( BindError ( "postgres_query function only accepts 6 arguments: postgres_query(hostname varchar, port varchar, username varchar, password varchar, database_name varchar, postgres_query varchar)" . to_owned ( ) ) . into ( ) ) ;
298
- }
299
- let mut cast_args = Vec :: with_capacity ( 6 ) ;
300
- for arg in args {
301
- let arg = arg. cast_implicit ( DataType :: Varchar ) ?;
302
- cast_args. push ( arg) ;
300
+ fn handle_postgres_or_mysql_query_args (
301
+ catalog_reader : & CatalogReadGuard ,
302
+ db_name : & str ,
303
+ schema_path : SchemaPath < ' _ > ,
304
+ args : Vec < ExprImpl > ,
305
+ expect_connector_name : & str ,
306
+ ) -> RwResult < Vec < ExprImpl > > {
307
+ let cast_args = match args. len ( ) {
308
+ INLINE_ARG_LEN => {
309
+ let mut cast_args = Vec :: with_capacity ( INLINE_ARG_LEN ) ;
310
+ for arg in args {
311
+ let arg = arg. cast_implicit ( DataType :: Varchar ) ?;
312
+ cast_args. push ( arg) ;
313
+ }
314
+ cast_args
303
315
}
304
- cast_args
305
- } ;
306
- let evaled_args = {
307
- let mut evaled_args: Vec < String > = Vec :: with_capacity ( 6 ) ;
308
- for arg in & args {
309
- match arg. try_fold_const ( ) {
310
- Some ( Ok ( value) ) => {
311
- let Some ( scalar) = value else {
312
- return Err ( BindError (
313
- "postgres_query function does not accept null arguments" . to_owned ( ) ,
314
- )
315
- . into ( ) ) ;
316
- } ;
317
- evaled_args. push ( scalar. into_utf8 ( ) . into ( ) ) ;
318
- }
319
- Some ( Err ( err) ) => {
320
- return Err ( err) ;
321
- }
322
- None => {
323
- return Err ( BindError (
324
- "postgres_query function only accepts constant arguments" . to_owned ( ) ,
325
- )
326
- . into ( ) ) ;
327
- }
316
+ CDC_SOURCE_ARG_LEN => {
317
+ let source_name = expr_impl_to_string_fn ( & args[ 0 ] ) ?;
318
+ let source_catalog = catalog_reader
319
+ . get_source_by_name ( db_name, schema_path, & source_name) ?
320
+ . 0 ;
321
+ if !source_catalog
322
+ . connector_name ( )
323
+ . eq_ignore_ascii_case ( expect_connector_name)
324
+ {
325
+ return Err ( BindError ( format ! ( "TVF function only accepts `mysql-cdc` and `postgres-cdc` source. Expected: {}, but got: {}" , expect_connector_name, source_catalog. connector_name( ) ) ) . into ( ) ) ;
328
326
}
327
+
328
+ let ( props, secret_refs) = source_catalog. with_properties . clone ( ) . into_parts ( ) ;
329
+ let secret_resolved =
330
+ LocalSecretManager :: global ( ) . fill_secrets ( props, secret_refs) ?;
331
+
332
+ vec ! [
333
+ ExprImpl :: literal_varchar( secret_resolved[ "hostname" ] . clone( ) ) ,
334
+ ExprImpl :: literal_varchar( secret_resolved[ "port" ] . clone( ) ) ,
335
+ ExprImpl :: literal_varchar( secret_resolved[ "username" ] . clone( ) ) ,
336
+ ExprImpl :: literal_varchar( secret_resolved[ "password" ] . clone( ) ) ,
337
+ ExprImpl :: literal_varchar( secret_resolved[ "database.name" ] . clone( ) ) ,
338
+ args. get( 1 )
339
+ . unwrap( )
340
+ . clone( )
341
+ . cast_implicit( DataType :: Varchar ) ?,
342
+ ]
343
+ }
344
+ _ => {
345
+ return Err ( BindError ( "postgres_query function and mysql_query function accept either 2 arguments: (cdc_source_name varchar, query varchar) or 6 arguments: (hostname varchar, port varchar, username varchar, password varchar, database_name varchar, query varchar)" . to_owned ( ) ) . into ( ) ) ;
329
346
}
330
- evaled_args
331
347
} ;
332
348
349
+ Ok ( cast_args)
350
+ }
351
+
352
+ pub fn new_postgres_query (
353
+ catalog_reader : & CatalogReadGuard ,
354
+ db_name : & str ,
355
+ schema_path : SchemaPath < ' _ > ,
356
+ args : Vec < ExprImpl > ,
357
+ ) -> RwResult < Self > {
358
+ let args = Self :: handle_postgres_or_mysql_query_args (
359
+ catalog_reader,
360
+ db_name,
361
+ schema_path,
362
+ args,
363
+ "postgres-cdc" ,
364
+ ) ?;
365
+ let evaled_args = args
366
+ . iter ( )
367
+ . map ( expr_impl_to_string_fn)
368
+ . collect :: < RwResult < Vec < _ > > > ( ) ?;
369
+
333
370
#[ cfg( madsim) ]
334
371
{
335
372
return Err ( crate :: error:: ErrorCode :: BindError (
@@ -411,45 +448,23 @@ impl TableFunction {
411
448
}
412
449
}
413
450
414
- pub fn new_mysql_query ( args : Vec < ExprImpl > ) -> RwResult < Self > {
415
- static MYSQL_ARGS_LEN : usize = 6 ;
416
- let args = {
417
- if args. len ( ) != MYSQL_ARGS_LEN {
418
- return Err ( BindError ( "mysql_query function only accepts 6 arguments: mysql_query(hostname varchar, port varchar, username varchar, password varchar, database_name varchar, mysql_query varchar)" . to_owned ( ) ) . into ( ) ) ;
419
- }
420
- let mut cast_args = Vec :: with_capacity ( MYSQL_ARGS_LEN ) ;
421
- for arg in args {
422
- let arg = arg. cast_implicit ( DataType :: Varchar ) ?;
423
- cast_args. push ( arg) ;
424
- }
425
- cast_args
426
- } ;
427
- let evaled_args = {
428
- let mut evaled_args: Vec < String > = Vec :: with_capacity ( MYSQL_ARGS_LEN ) ;
429
- for arg in & args {
430
- match arg. try_fold_const ( ) {
431
- Some ( Ok ( value) ) => {
432
- let Some ( scalar) = value else {
433
- return Err ( BindError (
434
- "mysql_query function does not accept null arguments" . to_owned ( ) ,
435
- )
436
- . into ( ) ) ;
437
- } ;
438
- evaled_args. push ( scalar. into_utf8 ( ) . into ( ) ) ;
439
- }
440
- Some ( Err ( err) ) => {
441
- return Err ( err) ;
442
- }
443
- None => {
444
- return Err ( BindError (
445
- "mysql_query function only accepts constant arguments" . to_owned ( ) ,
446
- )
447
- . into ( ) ) ;
448
- }
449
- }
450
- }
451
- evaled_args
452
- } ;
451
+ pub fn new_mysql_query (
452
+ catalog_reader : & CatalogReadGuard ,
453
+ db_name : & str ,
454
+ schema_path : SchemaPath < ' _ > ,
455
+ args : Vec < ExprImpl > ,
456
+ ) -> RwResult < Self > {
457
+ let args = Self :: handle_postgres_or_mysql_query_args (
458
+ catalog_reader,
459
+ db_name,
460
+ schema_path,
461
+ args,
462
+ "mysql-cdc" ,
463
+ ) ?;
464
+ let evaled_args = args
465
+ . iter ( )
466
+ . map ( expr_impl_to_string_fn)
467
+ . collect :: < RwResult < Vec < _ > > > ( ) ?;
453
468
454
469
#[ cfg( madsim) ]
455
470
{
@@ -624,3 +639,24 @@ impl Expr for TableFunction {
624
639
unreachable ! ( "Table function should not be converted to ExprNode" )
625
640
}
626
641
}
642
+
643
+ fn expr_impl_to_string_fn ( arg : & ExprImpl ) -> RwResult < String > {
644
+ match arg. try_fold_const ( ) {
645
+ Some ( Ok ( value) ) => {
646
+ let Some ( scalar) = value else {
647
+ return Err ( BindError (
648
+ "postgres_query function and mysql_query function do not accept null arguments"
649
+ . to_owned ( ) ,
650
+ )
651
+ . into ( ) ) ;
652
+ } ;
653
+ Ok ( scalar. into_utf8 ( ) . to_string ( ) )
654
+ }
655
+ Some ( Err ( err) ) => Err ( err) ,
656
+ None => Err ( BindError (
657
+ "postgres_query function and mysql_query function only accept constant arguments"
658
+ . to_owned ( ) ,
659
+ )
660
+ . into ( ) ) ,
661
+ }
662
+ }
0 commit comments