1515use std:: collections:: BTreeMap ;
1616use std:: str:: FromStr ;
1717use std:: sync:: Arc ;
18+ use std:: time:: Instant ;
1819
1920use common_ast:: ast:: CopyStmt ;
2021use common_ast:: ast:: CopyUnit ;
@@ -40,9 +41,13 @@ use common_exception::ErrorCode;
4041use common_exception:: Result ;
4142use common_meta_app:: principal:: OnErrorMode ;
4243use common_meta_app:: principal:: StageInfo ;
44+ use common_storage:: init_stage_operator;
45+ use common_storage:: StageFileInfo ;
46+ use common_storage:: StageFileStatus ;
4347use common_storage:: StageFilesInfo ;
4448use common_users:: UserApiProvider ;
4549use tracing:: debug;
50+ use tracing:: info;
4651
4752use crate :: binder:: location:: parse_uri_location;
4853use crate :: binder:: Binder ;
@@ -181,6 +186,26 @@ impl<'a> Binder {
181186 self . bind_copy_from_query_into_uri ( bind_context, stmt, query, & mut ul)
182187 . await
183188 }
189+ (
190+ CopyUnit :: Query ( query) ,
191+ CopyUnit :: Table {
192+ catalog,
193+ database,
194+ table,
195+ } ,
196+ ) => {
197+ let ( catalog_name, database_name, table_name) =
198+ self . normalize_object_identifier_triple ( catalog, database, table) ;
199+ self . bind_copy_from_query_into_table (
200+ bind_context,
201+ stmt,
202+ query,
203+ & catalog_name,
204+ & database_name,
205+ & table_name,
206+ )
207+ . await
208+ }
184209 ( src, dst) => Err ( ErrorCode :: SyntaxException ( format ! (
185210 "COPY INTO <{}> FROM <{}> is invalid" ,
186211 dst. target( ) ,
@@ -470,6 +495,148 @@ impl<'a> Binder {
470495 } ) ) )
471496 }
472497
498+ /// Bind COPY INFO <table> FROM <query>
499+ async fn bind_copy_from_query_into_table (
500+ & mut self ,
501+ bind_context : & BindContext ,
502+ stmt : & CopyStmt ,
503+ src_query : & Query ,
504+ dst_catalog_name : & str ,
505+ dst_database_name : & str ,
506+ dst_table_name : & str ,
507+ ) -> Result < Plan > {
508+ // Validation mode.
509+ let validation_mode = ValidationMode :: from_str ( stmt. validation_mode . as_str ( ) )
510+ . map_err ( ErrorCode :: SyntaxException ) ?;
511+
512+ // dst
513+ let dst_table = self
514+ . ctx
515+ . get_table ( dst_catalog_name, dst_database_name, dst_table_name)
516+ . await ?;
517+
518+ // src
519+ let ( select_list, location, alias) = check_transform_query ( src_query) ?;
520+ if matches ! ( location, FileLocation :: Uri ( _) ) {
521+ // todo!(youngsofun): need to refactor parser
522+ return Err ( ErrorCode :: SyntaxException (
523+ "copy into table from uri with transform not supported yet" ,
524+ ) ) ;
525+ }
526+
527+ let ( mut stage_info, path) =
528+ parse_file_location ( & self . ctx , location, BTreeMap :: new ( ) ) . await ?;
529+ self . apply_stage_options ( stmt, & mut stage_info) . await ?;
530+ let files_info = StageFilesInfo {
531+ path,
532+ pattern : stmt. pattern . clone ( ) ,
533+ files : stmt. files . clone ( ) ,
534+ } ;
535+
536+ let start = Instant :: now ( ) ;
537+ {
538+ let status = "begin to list files" ;
539+ self . ctx . set_status_info ( status) ;
540+ info ! ( status) ;
541+ }
542+
543+ let operator = init_stage_operator ( & stage_info) ?;
544+ let files = if operator. info ( ) . can_blocking ( ) {
545+ files_info. blocking_list ( & operator, false )
546+ } else {
547+ files_info. list ( & operator, false ) . await
548+ } ?;
549+
550+ let mut all_source_file_infos = files
551+ . into_iter ( )
552+ . map ( |file_with_meta| StageFileInfo :: new ( file_with_meta. path , & file_with_meta. metadata ) )
553+ . collect :: < Vec < _ > > ( ) ;
554+
555+ info ! ( "end to list files: {}" , all_source_file_infos. len( ) ) ;
556+
557+ if !stmt. force {
558+ // Status.
559+ {
560+ let status = "begin to color copied files" ;
561+ self . ctx . set_status_info ( status) ;
562+ info ! ( status) ;
563+ }
564+
565+ all_source_file_infos = self
566+ . ctx
567+ . color_copied_files (
568+ dst_catalog_name,
569+ dst_database_name,
570+ dst_table_name,
571+ all_source_file_infos,
572+ )
573+ . await ?;
574+
575+ info ! ( "end to color copied files: {}" , all_source_file_infos. len( ) ) ;
576+ }
577+
578+ let mut need_copy_file_infos = vec ! [ ] ;
579+ for file in & all_source_file_infos {
580+ if file. status == StageFileStatus :: NeedCopy {
581+ need_copy_file_infos. push ( file. clone ( ) ) ;
582+ }
583+ }
584+
585+ info ! (
586+ "copy: read all files finished, all:{}, need copy:{}, elapsed:{}" ,
587+ all_source_file_infos. len( ) ,
588+ need_copy_file_infos. len( ) ,
589+ start. elapsed( ) . as_secs( )
590+ ) ;
591+
592+ if need_copy_file_infos. is_empty ( ) {
593+ return Err ( ErrorCode :: EmptyData ( "no file need to copy" ) ) ;
594+ }
595+
596+ let ( s_expr, mut from_context) = self
597+ . bind_stage_table (
598+ bind_context,
599+ stage_info. clone ( ) ,
600+ files_info,
601+ alias,
602+ Some ( need_copy_file_infos. clone ( ) ) ,
603+ )
604+ . await ?;
605+
606+ // Generate a analyzed select list with from context
607+ let select_list = self
608+ . normalize_select_list ( & from_context, select_list)
609+ . await ?;
610+ let ( scalar_items, projections) = self . analyze_projection ( & select_list) ?;
611+ let s_expr =
612+ self . bind_projection ( & mut from_context, & projections, & scalar_items, s_expr) ?;
613+ let mut output_context = BindContext :: new ( ) ;
614+ output_context. parent = from_context. parent ;
615+ output_context. columns = from_context. columns ;
616+
617+ let query_plan = Plan :: Query {
618+ s_expr : Box :: new ( s_expr) ,
619+ metadata : self . metadata . clone ( ) ,
620+ bind_context : Box :: new ( output_context) ,
621+ rewrite_kind : None ,
622+ ignore_result : false ,
623+ formatted_ast : None ,
624+ } ;
625+
626+ Ok ( Plan :: Copy ( Box :: new ( CopyPlan :: IntoTableWithTransform {
627+ catalog_name : dst_catalog_name. to_string ( ) ,
628+ database_name : dst_database_name. to_string ( ) ,
629+ table_name : dst_table_name. to_string ( ) ,
630+ table_id : dst_table. get_id ( ) ,
631+ schema : dst_table. schema ( ) ,
632+ from : Box :: new ( query_plan) ,
633+ stage_info : Box :: new ( stage_info) ,
634+ all_source_file_infos,
635+ need_copy_file_infos,
636+ validation_mode,
637+ } ) ) )
638+ }
639+
473640 async fn apply_stage_options ( & mut self , stmt : & CopyStmt , stage : & mut StageInfo ) -> Result < ( ) > {
474641 if !stmt. file_format . is_empty ( ) {
475642 stage. file_format_options = self . try_resolve_file_format ( & stmt. file_format ) . await ?;
0 commit comments