@@ -19,6 +19,7 @@ use itertools::Itertools;
1919use mysql_async:: consts:: ColumnType as MySqlColumnType ;
2020use mysql_async:: prelude:: * ;
2121use risingwave_common:: array:: arrow:: IcebergArrowConvert ;
22+ use risingwave_common:: secret:: LocalSecretManager ;
2223use risingwave_common:: types:: { DataType , ScalarImpl , StructType } ;
2324use risingwave_connector:: source:: iceberg:: {
2425 extract_bucket_and_file_name, get_parquet_fields, list_data_directory, new_azblob_operator,
@@ -30,10 +31,15 @@ use thiserror_ext::AsReport;
3031use tokio_postgres:: types:: Type as TokioPgType ;
3132
3233use super :: { infer_type, Expr , ExprImpl , ExprRewriter , Literal , RwResult } ;
34+ use crate :: catalog:: catalog_service:: CatalogReadGuard ;
3335use crate :: catalog:: function_catalog:: { FunctionCatalog , FunctionKind } ;
36+ use crate :: catalog:: root_catalog:: SchemaPath ;
3437use crate :: error:: ErrorCode :: BindError ;
3538use crate :: utils:: FRONTEND_RUNTIME ;
3639
40+ const INLINE_ARG_LEN : usize = 6 ;
41+ const CDC_SOURCE_ARG_LEN : usize = 2 ;
42+
3743/// A table function takes a row as input and returns a table. It is also known as Set-Returning
3844/// Function.
3945///
@@ -291,45 +297,76 @@ impl TableFunction {
291297 } )
292298 }
293299
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
303315 }
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 ( ) ) ;
328326 }
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 ( ) ) ;
329346 }
330- evaled_args
331347 } ;
332348
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+
333370 #[ cfg( madsim) ]
334371 {
335372 return Err ( crate :: error:: ErrorCode :: BindError (
@@ -411,45 +448,23 @@ impl TableFunction {
411448 }
412449 }
413450
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 < _ > > > ( ) ?;
453468
454469 #[ cfg( madsim) ]
455470 {
@@ -624,3 +639,24 @@ impl Expr for TableFunction {
624639 unreachable ! ( "Table function should not be converted to ExprNode" )
625640 }
626641}
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