11import os
2+ from typing import Optional , List
23
34import pytest
45import yatest .common
78
89
910class KqpRun (object ):
10- def __init__ (self , config_file , scheme_file , udfs_dir = None ):
11- self .kqprun_binary = yql_utils .yql_binary_path ('ydb/tests/tools/kqprun/kqprun' )
11+ def __init__ (self , config_file : str , scheme_file : str , udfs_dir : Optional [ str ] = None , path_prefix : str = "" ):
12+ self .kqprun_binary : str = yql_utils .yql_binary_path ('ydb/tests/tools/kqprun/kqprun' )
1213
13- self .config_file = yql_utils .yql_source_path (config_file )
14- self .scheme_file = yql_utils .yql_source_path (scheme_file )
14+ self .config_file : str = yql_utils .yql_source_path (config_file )
15+ self .scheme_file : str = yql_utils .yql_source_path (scheme_file )
1516
16- self .res_dir = yql_utils .get_yql_dir (prefix = ' kqprun_' )
17+ self .res_dir : str = yql_utils .get_yql_dir (prefix = f' { path_prefix } kqprun_' )
1718
1819 if udfs_dir is None :
19- self .udfs_dir = yql_utils .get_udfs_path ()
20+ self .udfs_dir : str = yql_utils .get_udfs_path ()
2021 else :
21- self .udfs_dir = udfs_dir
22+ self .udfs_dir : str = udfs_dir
2223
23- def __res_file_path (self , name ):
24+ self .tables : List [str ] = []
25+ self .queries : List [str ] = []
26+
27+ def __res_file_path (self , name : str ) -> str :
2428 return os .path .join (self .res_dir , name )
2529
26- def yql_exec (self , program = None , program_file = None , verbose = False , check_error = True , var_templates = None , tables = None ):
30+ def add_table (self , name : str , content : List [str ], attrs : Optional [str ] = None ):
31+ table_path = self .__res_file_path (f'table_{ len (self .tables )} .yson' )
32+ with open (table_path , 'w' ) as table :
33+ for row in content :
34+ table .write (f'{ row } \n ' )
35+
36+ if attrs is not None :
37+ with open (f'{ table_path } .attr' , 'w' ) as table_attrs :
38+ table_attrs .write (attrs )
39+
40+ self .tables .append (f'yt./Root/plato.{ name } @{ table_path } ' )
41+
42+ def add_query (self , sql : str ):
43+ query_path = self .__res_file_path (f'query_{ len (self .queries )} .sql' )
44+ with open (query_path , 'w' ) as query :
45+ query .write (sql )
46+
47+ self .queries .append (query_path )
48+
49+ def yql_exec (self , verbose : bool = False , check_error : bool = True , var_templates : Optional [List [str ]] = None ,
50+ yql_program : Optional [str ] = None , yql_tables : List [yql_utils .Table ] = []) -> yql_utils .YQLExecResult :
2751 udfs_dir = self .udfs_dir
2852
2953 config_file = self .config_file
30- program_file = yql_utils .prepare_program (program , program_file , self .res_dir , ext = 'sql' )[1 ]
3154 scheme_file = self .scheme_file
3255
3356 results_file = self .__res_file_path ('results.txt' )
57+ ast_file = self .__res_file_path ('ast.txt' )
58+ plan_file = self .__res_file_path ('plan.json' )
3459 log_file = self .__res_file_path ('log.txt' )
3560
3661 cmd = self .kqprun_binary + ' '
@@ -39,46 +64,60 @@ def yql_exec(self, program=None, program_file=None, verbose=False, check_error=T
3964 '--emulate-yt '
4065 '--exclude-linked-udfs '
4166 '--execution-case query '
42- '--app-config=%(config_file)s '
43- '--script-query=%(program_file)s '
44- '--scheme-query=%(scheme_file)s '
45- '--result-file=%(results_file)s '
46- '--log-file=%(log_file)s '
47- '--udfs-dir=%(udfs_dir)s '
67+ f'--app-config={ config_file } '
68+ f'--scheme-query={ scheme_file } '
69+ f'--result-file={ results_file } '
70+ f'--script-ast-file={ ast_file } '
71+ f'--script-plan-file={ plan_file } '
72+ f'--log-file={ log_file } '
73+ f'--udfs-dir={ udfs_dir } '
4874 '--result-format full-proto '
49- '--result-rows-limit 0 ' % locals ()
75+ '--plan-format json '
76+ '--result-rows-limit 0 '
5077 )
5178
5279 if var_templates is not None :
5380 for var_template in var_templates :
54- cmd += '--var-template %s ' % var_template
81+ cmd += f'--var-template { var_template } '
82+
83+ for query in self .queries :
84+ cmd += f'--script-query={ query } '
85+
86+ if yql_program is not None :
87+ program_file = yql_utils .prepare_program (yql_program , None , self .res_dir , ext = 'sql' )[1 ]
88+ cmd += f'--script-query={ program_file } '
5589
56- if tables is not None :
57- for table in tables :
58- if table .format != 'yson' :
59- pytest .skip ('skip tests containing tables with a non-yson attribute format' )
60- cmd += '--table=yt./Root/%s@%s ' % (table .full_name , table .yqlrun_file )
90+ for table in self .tables :
91+ cmd += f'--table={ table } '
92+
93+ for table in yql_tables :
94+ if table .format != 'yson' :
95+ pytest .skip ('skip tests containing tables with a non-yson attribute format' )
96+ cmd += f'--table=yt./Root/{ table .full_name } @{ table .yqlrun_file } '
6197
6298 proc_result = yatest .common .process .execute (cmd .strip ().split (), check_exit_code = False , cwd = self .res_dir )
6399 if proc_result .exit_code != 0 and check_error :
64- assert 0 , (
65- 'Command\n %(command)s\n finished with exit code %(code)d, stderr:\n \n %(stderr)s\n \n log file:\n %(log_file)s'
66- % {
67- 'command' : cmd ,
68- 'code' : proc_result .exit_code ,
69- 'stderr' : proc_result .std_err ,
70- 'log_file' : yql_utils .read_res_file (log_file )[1 ],
71- }
72- )
100+ assert 0 , f'Command\n { cmd } \n finished with exit code { proc_result .exit_code } , stderr:\n \n { proc_result .std_err } \n \n log file:\n { yql_utils .read_res_file (log_file )[1 ]} '
73101
74102 results , log_results = yql_utils .read_res_file (results_file )
103+ ast , log_ast = yql_utils .read_res_file (ast_file )
104+ plan , log_plan = yql_utils .read_res_file (plan_file )
75105 err , log_err = yql_utils .read_res_file (log_file )
76106
77107 if verbose :
78- yql_utils .log ('PROGRAM:' )
79- yql_utils .log (program )
108+ yql_utils .log ('QUERIES:' )
109+ if yql_program is not None :
110+ yql_utils .log (yql_program )
111+
112+ for query in self .queries :
113+ yql_utils .log (yql_program )
114+
80115 yql_utils .log ('RESULTS:' )
81116 yql_utils .log (log_results )
117+ yql_utils .log ('AST:' )
118+ yql_utils .log (log_ast )
119+ yql_utils .log ('PLAN:' )
120+ yql_utils .log (log_plan )
82121 yql_utils .log ('ERROR:' )
83122 yql_utils .log (log_err )
84123
@@ -87,11 +126,11 @@ def yql_exec(self, program=None, program_file=None, verbose=False, check_error=T
87126 proc_result .std_err ,
88127 results ,
89128 results_file ,
90- None ,
91- None ,
92- None ,
93- None ,
94- program ,
129+ ast ,
130+ ast_file ,
131+ plan ,
132+ plan_file ,
133+ yql_program ,
95134 proc_result ,
96135 None ,
97136 )
0 commit comments