#!/bin/env python # -*- coding: utf-8 -*- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. ########################################################################### # @file common.py # @date 2020-07-14 19:11:39 # @brief common function ############################################################################ """ common test tool """ import re import time import palo_client import palo_config import palo_logger import palo_job import palo_types import util config = palo_config.config LOG = palo_logger.Logger.getLogger() L = palo_logger.StructedLogMessage def get_client(host=None): """get a new client""" if host is None: host = config.fe_host client = palo_client.get_client(host, config.fe_query_port, user=config.fe_user, password=config.fe_password, http_port=config.fe_http_port) return client def create_workspace(database_name): """get new palo client connect, and create db, and use and return client""" client = palo_client.get_client(config.fe_host, config.fe_query_port, user=config.fe_user, password=config.fe_password, http_port=config.fe_http_port) client.clean(database_name) client.create_database(database_name) client.use(database_name) return client def check2(client1, sql1, client2=None, sql2=None, forced=False): """ check2 client can be PaloClient or mysql.cursor """ if client2 is None and sql2 is None: assert 0 == 1, 'the input of check2() is wrong' elif client2 is None: client2 = client1 elif sql2 is None: sql2 = sql1 ret1 = client1.execute(sql1) ret2 = client2.execute(sql2) util.check(ret1, ret2, forced) return True def get_explain_rollup(client, sql): """ Get explain table """ result = client.execute('EXPLAIN ' + sql) if result is None: return None rollup_flag = 'TABLE: ' explain_rollup = list() for element in result: message = element[0].lstrip() profile = message.split(',') for msg in profile: if msg.startswith(rollup_flag): LOG.info(L('explain', msg=msg)) table = msg[len(rollup_flag):].rstrip(' ') pattern = re.compile(r'[(](.*?)[)]', re.S) rollup = re.findall(pattern, table) explain_rollup.append(rollup[0]) return explain_rollup def assert_stop_routine_load(ret, client, stop_job=None, info=''): """assert, if not stop routine load """ ret = client.show_routine_load(stop_job) if not ret and stop_job is not None: try: client.stop_routine_load(stop_job) except Exception as e: print(str(e)) assert ret, info def wait_commit(client, routine_load_job_name, committed_expect_num, timeout=600): """wait task committed""" print('expect commited rows: %s\n' % committed_expect_num) while timeout > 0: ret = client.show_routine_load(routine_load_job_name,) routine_load_job = palo_job.RoutineLoadJob(ret[0]) loaded_rows = routine_load_job.get_loaded_rows() print(loaded_rows) if str(loaded_rows) == str(committed_expect_num): return True state = routine_load_job.get_state() if state != 'RUNNING': print('routine load job\' state is not running, it\'s %s' % state) return False timeout -= 3 time.sleep(3) return False def execute_ignore_error(func, *argc, **kwargs): """execute ignore sql error""" try: func(*argc, **kwargs) except Exception as e: LOG.info(L('execute ignore error', msg=str(e))) print(e) def execute_retry_when_msg(msg, func, *argc, **kwargs): """当SQL返回的结果包含msg,则重试""" retry = 20 while retry > 0: try: ret = func(*argc, **kwargs) return ret except Exception as e: if msg in str(e): time.sleep(3) retry -= 1 LOG.info(L("get error msg", msg=str(e))) return False def check_by_file(expect_file, table_name=None, sql=None, client=None, database_name=None, **kwargs): """ 通过csv校验文件,验证表或sql的结果。 根据sql执行返回的列的类型,读取csv文件转化为相应的结构,进行校验 expect_file: 校验文件 table_name:表名,被校验的表 sql: sql,被校验的sql查询,表和sql不能同时为None client: 执行sql的client,如果为None则重新创建连接 database_name: 数据库名称 **kwargs:可以指定sql或表中的类型,可在复杂类型中使用,例: k2=palo_types.ARRAY_INT 如array返回为字符串类型,对于array等可单独指定类型校验 """ LOG.info(L('check file', file=expect_file)) if table_name is None and sql is None: assert 0 == 1, 'there is no table and query to be checked' if client is None: client = get_client() if database_name: client.use(database_name) if table_name: if database_name: table_name = '%s.%s' % (database_name, table_name) sql = 'select * from %s' % table_name cursor, ret = client.execute(sql, return_cursor=True) result_info = cursor.description column_name_list = util.get_attr(result_info, 0) column_type_list = util.get_attr(result_info, 1) for col_name, col_type in kwargs.items(): try: idx = column_name_list.index(col_name) column_type_list[idx] = col_type except Exception as e: print(type(col_name), type(column_name_list[1])) print("%s is not the column in %s" % (col_name, column_name_list)) expect_ret = palo_types.convert_csv_to_ret(expect_file, column_type_list) # 判断sql结果是否含有复杂类型,如果有,则需要对复杂类型进行处理 if max(column_type_list) > 1000: ret = palo_types.convert_ret_complex_type(ret, column_type_list) util.check(ret, expect_ret, True) return True def check_by_sql(tested_sql, expect_sql, client=None, database_name=None, **kwargs): """ 适用于复杂类型的两个sql结果校验。将两个sql分别执行,格式化处理,保存到文件中进行校验 通过**kwargs指定列的类型 tested_sql: 被测sql expect_sql: 校验sql,预期正确的结果 """ if client is None: client = get_client() if database_name: client.use(database_name) cursor, expect_ret = client.execute(expect_sql, return_cursor=True) # 根据cursor获取返回结果的列名和类型code result_info = cursor.description column_name_list = util.get_attr(result_info, 0) column_type_list = util.get_attr(result_info, 1) tested_ret = client.execute(tested_sql) for col_name, col_type in kwargs.items(): try: idx = column_name_list.index(col_name) column_type_list[idx] = col_type except Exception as e: print(type(col_name), type(column_name_list[1])) print("%s is not the column in %s" % (col_name, column_name_list)) if len(kwargs) != 0: processed_expect_ret = palo_types.convert_ret_complex_type(expect_ret, column_type_list) processed_tested_ret = palo_types.convert_ret_complex_type(tested_ret, column_type_list) util.check(processed_tested_ret, processed_expect_ret, True) else: util.check(tested_ret, expect_ret, True) return True