# -*- coding: utf-8 -*-
import datetime
import time
from json import JSONDecodeError

import click
import json
import os
import re
import platform
import binascii
import decimal
import random
import traceback
from .sqlclijdbc import SQLCliJDBCException
from .sqlclijdbc import SQLCliJDBCLargeObject
from .sqlclijdbc import SQLCliJDBCTimeOutException
from .sqlparse import SQLAnalyze
from .apiparse import APIAnalyze
from .sqlparse import SQLFormatWithPrefix
from .apiparse import APIFormatWithPrefix

from .commands.load import loadPlugin
from .commands.load import loadDriver
from .commands.load import loadMap
from .commands.exit import exitApplication
from .commands.session import sessionManage
from .commands.assertExpression import assertExpression
from .commands.assertExpression import evalExpression
from .commands.embeddScript import executeEmbeddScript
from .commands.host import executeLocalCommand
from .commands.setOptions import setOptions
from .commands.cliSleep import cliSleep

from .testcliexception import TestCliException
from .global_var import lastCommandResult


class CmdExecute(object):
    def __init__(self):
        # 程序处理选项
        self.testOptions = None

        # 主程序处理句柄
        self.cliHandler = None

        # 当前Executeor的WorkerName
        self.workerName = None

        # 程序Echo输出句柄
        self.echofile = None

        # 脚本启动的时间
        self.startTime = time.time()

        # 当前执行的命令脚本
        self.script = None

        # 命令重写处理
        self.mappingHandler = None

        # Scenario名称，如果当前未指定，则重复上一个命令的Scenario信息
        self.scenario = ''

        # Transaction信息
        self.transaction = ""

        # 当前脚本的TimeOut设置
        self.sqlTimeOut = -1          # SQL执行的超时时间设置
        self.apiTimeOut = -1          # API执行的超时时间设置
        self.scriptTimeOut = -1       # 脚本执行的超时时间设置
        self.timeout = -1             # 当前SQL的超时时间设置
        self.timeOutMode = None       # COMMAND|SCRIPT|NONE

        # 记录最后命令返回的结果
        self.lastJsonCommandResult = None
        self.lastElapsedTime = 0

        # 数据库连接
        self.sqlConn = None
        self.sqlCursor = None
        self.apiConn = None

        # 记录语句是否当前处于条件表达式判定中
        self.ifMode = False
        self.ifCondition = False
        # 记录语句是否在循环过程中
        self.loopMode = False
        self.loopCondition = False
        self.loopStartPos = 0

    def setStartTime(self, startTime):
        self.startTime = startTime

    def getStartTime(self):
        return self.startTime

    def jqparse(self, obj, path='.'):
        class DecimalEncoder(json.JSONEncoder):
            def default(self, o):
                if isinstance(o, decimal.Decimal):
                    return float(o)
                super(DecimalEncoder, self).default(o)

        if self is None:
            pass
        if obj is None:
            if "TESTCLI_DEBUG" in os.environ:
                click.secho("[DEBUG] JQ Parse Error: obj is None")
            return "****"
        try:
            obj = json.loads(obj) if isinstance(obj, str) else obj
            find_str, find_map = '', ['["%s"]', '[%s]', '%s', '.%s']
            for im in path.split('.'):
                if not im:
                    continue
                if obj is None:
                    if "TESTCLI_DEBUG" in os.environ:
                        click.secho("[DEBUG] JQ Parse Error: obj is none")
                    return "****"
                if isinstance(obj, (list, tuple, str)):
                    if im.startswith('[') and im.endswith(']'):
                        im = im[1:-1]
                    if ':' in im:
                        slice_default = [0, len(obj), 1]
                        obj, quota = obj[slice(
                            *[int(sli) if sli else slice_default[i] for i, sli in
                              enumerate(im.split(':'))])], 1
                    else:
                        obj, quota = obj[int(im)], 1
                else:
                    if im in obj:
                        obj, quota = obj.get(im), 0
                    elif im.endswith('()'):
                        obj, quota = list(getattr(obj, im[:-2])()), 3
                    else:
                        if im.isdigit():
                            obj, quota = obj[int(im)], 1
                        else:
                            raise KeyError(im)
                find_str += find_map[quota] % im
            return obj if isinstance(obj, str) else json.dumps(obj,
                                                               sort_keys=True,
                                                               ensure_ascii=False,
                                                               cls=DecimalEncoder)
        except (IndexError, KeyError, ValueError) as je:
            if "TESTCLI_DEBUG" in os.environ:
                click.secho("[DEBUG] JQ Parse Error: " + repr(je))
            return "****"

    @staticmethod
    def sortresult(result):
        # 无法使用系统默认的排序函数，这里空值总是置于最后
        for i in range(len(result) - 1, 0, -1):
            for j in range(i - 1, -1, -1):
                bNeedExchange = False
                for k in range(0, len(result[i])):
                    if len(result[i]) != len(result[j]):
                        return
                    if result[i][k] is None and result[j][k] is None:
                        # 两边都是空值
                        continue
                    if result[i][k] is None and result[j][k] is not None:
                        # 左边空值， 右侧不空， 按照左侧大值来考虑
                        break
                    if result[j][k] is None and result[i][k] is not None:
                        # 右侧空值， 左边不空， 按照右侧大值来考虑
                        bNeedExchange = True
                        break
                    if not isinstance(result[i][k], type(result[j][k])):
                        if str(result[i][k]) < str(result[j][k]):
                            bNeedExchange = True
                            break
                        if str(result[i][k]) > str(result[j][k]):
                            break
                    else:
                        if result[i][k] < result[j][k]:
                            bNeedExchange = True
                            break
                        if result[i][k] > result[j][k]:
                            break
                if bNeedExchange:
                    result[j], result[i] = result[i], result[j]

    def getcommandResult(self, cursor, rowcount):
        """
            返回的内容：
                title           输出的前提示信息
                result          结果数据集
                headers         表头信息
                columnTypes     结果字段类型
                status          输出的后提示信息
                FetchStatus     是否输出完成
                rowcount        共返回记录行数
                Warning         警告信息
        """
        title = headers = None
        fetchStatus = True

        def format_column(p_column, p_columntype):
            if type(p_column) == float:
                return self.testOptions.get("FLOAT_FORMAT") % p_column
            elif type(p_column) in (bool, str, int):
                return p_column
            elif type(p_column) == list:
                return p_column
            elif type(p_column) == datetime.date:
                columnFormat = self.testOptions.get("DATE_FORMAT")
                if platform.system().lower() in ['windows', 'darwin']:
                    columnFormat = columnFormat.replace("%04Y", "%Y")
                else:
                    columnFormat = columnFormat.replace("%Y", "%04Y")
                return p_column.strftime(columnFormat)
            elif type(p_column) == datetime.datetime:
                if p_columntype in ["TIMESTAMP WITH TIME ZONE",
                                    "TIMESTAMP WITH LOCAL TIME ZONE"]:
                    columnFormat = self.testOptions.get("DATETIME-TZ_FORMAT")
                else:
                    columnFormat = self.testOptions.get("DATETIME_FORMAT")
                if platform.system().lower() in ['windows', 'darwin']:
                    columnFormat = columnFormat.replace("%04Y", "%Y")
                else:
                    columnFormat = columnFormat.replace("%Y", "%04Y")
                return p_column.strftime(columnFormat)
            elif type(p_column) == datetime.time:
                return p_column.strftime(self.testOptions.get("TIME_FORMAT"))
            elif type(p_column) == bytearray:
                if p_columntype == "BLOB":
                    columnTrimedLength = int(self.testOptions.get("LOB_LENGTH"))
                    bColumnFullOutput = True
                    if len(p_column) > columnTrimedLength:
                        bColumnFullOutput = False
                        p_column = p_column[:columnTrimedLength]
                    # 转换为16进制，并反算成ASCII
                    p_column = binascii.b2a_hex(p_column)
                    p_column = p_column.decode()
                    if not bColumnFullOutput:
                        # 用...的方式提醒输出没有结束，只是由于格式控制导致不显示
                        return "0x" + p_column + "..."
                    else:
                        return "0x" + p_column
                else:
                    # 转换为16进制，并反算成ASCII
                    p_column = binascii.b2a_hex(p_column)
                    p_column = p_column.decode()
                    return "0x" + p_column
            elif type(p_column) == decimal.Decimal:
                if self.testOptions.get("DECIMAL_FORMAT") != "":
                    return self.testOptions.get("DECIMAL_FORMAT") % p_column
                else:
                    return p_column
            elif type(p_column) == SQLCliJDBCLargeObject:
                trimedLength = int(self.testOptions.get("LOB_LENGTH"))
                if trimedLength < 4:
                    trimedLength = 4
                if trimedLength > p_column.getObjectLength():
                    if p_column.getColumnTypeName().upper().find("CLOB") != -1:
                        dataValue = p_column.getData(1, p_column.getObjectLength())
                        return dataValue
                    elif p_column.getColumnTypeName().upper().find("BLOB") != -1:
                        dataValue = p_column.getData(1, p_column.getObjectLength())
                        dataValue = binascii.b2a_hex(dataValue)
                        dataValue = dataValue.decode()
                        return "0x" + dataValue
                else:
                    if p_column.getColumnTypeName().upper().find("CLOB") != -1:
                        dataValue = "Len:" + str(p_column.getObjectLength()) + ";" + \
                                      "Content:[" + \
                                      p_column.getData(1, trimedLength - 3) + "..." + \
                                      p_column.getData(p_column.getObjectLength() - 2, 3) + \
                                      "]"
                        return dataValue
                    elif p_column.getColumnTypeName().upper().find("BLOB") != -1:
                        dataValue = "Len:" + str(p_column.getObjectLength()) + ";" + \
                                      "Content:0x[" + \
                                      binascii.b2a_hex(p_column.getData(1, trimedLength - 3)).decode() + "..." + \
                                      binascii.b2a_hex(p_column.getData(p_column.getObjectLength() - 2, 3)).decode() + \
                                      "]"
                        return dataValue
            elif isinstance(p_column, type(None)):
                return p_column
            else:
                # 其他类型直接返回
                raise SQLCliJDBCException("TestCli-0000: Unknown column type [" +
                                          str(p_columntype) + ":" + str(type(p_column)) +
                                          "] in format_column")

        # cursor.description is not None for queries that return result sets,
        # e.g. SELECT.
        result = []
        columnTypes = []
        if cursor.description is not None:
            headers = [x[0] for x in cursor.description]
            columnTypes = [x[1] for x in cursor.description]
            if cursor.warnings is not None:
                status = "{0} row{1} selected with warnings."
            else:
                status = "{0} row{1} selected."
            arraySize = int(self.testOptions.get("ARRAYSIZE"))
            rowset = cursor.fetchmany(arraySize)
            for row in rowset:
                collatedRow = []
                for nColumnPos in range(0, len(row)):
                    column = row[nColumnPos]
                    columntype = columnTypes[nColumnPos]
                    # 对于空值直接返回
                    if column is None:
                        collatedRow.append(None)
                        continue

                    # 处理各种数据类型
                    if columnTypes[nColumnPos] == "STRUCT":
                        columnValue = "STRUCTURE("
                        for pos in range(0, len(column)):
                            m_ColumnType = str(type(column[pos]))
                            if pos == 0:
                                if type(column[pos]) == str:
                                    columnValue = columnValue + "'" + str(column[pos]) + "'"
                                elif type(column[pos]) == datetime.date:
                                    columnValue = columnValue + "DATE '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif type(column[pos]) == datetime.datetime:
                                    columnValue = columnValue + "TIMESTAMP '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif isinstance(column[pos], type(None)):
                                    columnValue = columnValue + "<null>"
                                else:
                                    columnValue = columnValue + \
                                                    str(format_column(column[pos], m_ColumnType))
                            else:
                                if type(column[pos]) == str:
                                    columnValue = columnValue + ",'" + str(column[pos]) + "'"
                                elif type(column[pos]) == datetime.date:
                                    columnValue = columnValue + ",DATE '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif type(column[pos]) == datetime.datetime:
                                    columnValue = columnValue + ",TIMESTAMP '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif isinstance(column[pos], type(None)):
                                    columnValue = columnValue + ",<null>"
                                else:
                                    columnValue = columnValue + "," + \
                                                    str(format_column(column[pos], m_ColumnType))
                        columnValue = columnValue + ")"
                        collatedRow.append(columnValue)
                    elif columnTypes[nColumnPos] == "ARRAY":
                        columnValue = "ARRAY["
                        if self.testOptions.get('OUTPUT_SORT_ARRAY') == "ON":
                            # 保证Array的输出每次都一样顺序
                            # 需要注意可能有NULL值导致字符数组无法排序的情况, column是一个一维数组
                            column.sort(key=lambda x: (x is None, x))
                        for pos in range(0, len(column)):
                            m_ColumnType = str(type(column[pos]))
                            if pos == 0:
                                if type(column[pos]) == str:
                                    columnValue = columnValue + "'" + str(column[pos]) + "'"
                                elif type(column[pos]) == datetime.date:
                                    columnValue = columnValue + "DATE '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif type(column[pos]) == datetime.datetime:
                                    columnValue = columnValue + "TIMESTAMP '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif isinstance(column[pos], type(None)):
                                    columnValue = columnValue + "<null>"
                                else:
                                    columnValue = columnValue + \
                                                    str(format_column(column[pos], m_ColumnType))
                            else:
                                if type(column[pos]) == str:
                                    columnValue = columnValue + ",'" + str(column[pos]) + "'"
                                elif type(column[pos]) == datetime.date:
                                    columnValue = columnValue + ",DATE '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif type(column[pos]) == datetime.datetime:
                                    columnValue = columnValue + ",TIMESTAMP '" + \
                                                    format_column(column[pos], m_ColumnType) + "'"
                                elif isinstance(column[pos], type(None)):
                                    columnValue = columnValue + ",<null>"
                                else:
                                    columnValue = columnValue + "," + \
                                                    str(format_column(column[pos], m_ColumnType))
                        columnValue = columnValue + "]"
                        collatedRow.append(columnValue)
                    else:
                        collatedRow.append(format_column(column, columntype))
                collatedRow = tuple(collatedRow)
                result.append(collatedRow)
            rowcount = rowcount + len(rowset)
            if len(rowset) < arraySize:
                # 已经没有什么可以取的了, 游标结束
                fetchStatus = False
        else:
            if cursor.warnings is not None:
                status = "{0} row{1} affected with warnings."
            else:
                status = "{0} row{1} affected."
            rowcount = 0 if cursor.rowcount == -1 else cursor.rowcount
            result = None
            fetchStatus = False

        # 只要不是最后一次打印，不再返回status内容
        if fetchStatus:
            status = None

        if self.testOptions.get('FEEDBACK').upper() == 'ON' and status is not None:
            status = status.format(rowcount, "" if rowcount in [0, 1] else "s")
        else:
            status = None
        return title, result, headers, columnTypes, status, fetchStatus, rowcount, cursor.warnings

    '''
        执行指定的API请求

        输入：
            apiRequest             JSON对象，请求的内容
            apiHints               提示信息            
    '''
    def executeAPIStatement(self, apiRequest, apiHints, startTime):
        httpHandler = self.cliHandler.httpHandler

        httpMethod = apiRequest["httpMethod"]
        httpRequestTarget = apiRequest["httpRequestTarget"]

        headers = {}
        fields = {}
        if "headers" in apiRequest:
            headers = apiRequest["headers"]
        if "httpFields" in apiRequest:
            fields = apiRequest["httpFields"]

        if "contents" in apiRequest:
            body = "".join(apiRequest["contents"])
        else:
            body = ""
        ret = httpHandler.request(
            method=httpMethod,
            url=httpRequestTarget,
            headers=headers,
            fields=fields,
            body=body
        )
        data = ret.data.decode('utf-8')
        try:
            data = json.loads(data)
            data = json.dumps(obj=data,
                              sort_keys=True,
                              indent=4,
                              separators=(',', ': '),
                              ensure_ascii=False)
        except JSONDecodeError:
            pass
        # 返回的对象不是一个JSON
        yield {
            "type":         "result",
            "title":        None,
            "rows":         None,
            "headers":      None,
            "columnTypes":  None,
            "status":       data
        }

    def executeSQLStatement(self, sql: str, sqlHints, startTime):
        """
        返回内容：

        """

        # 进入到SQL执行阶段, 开始执行SQL语句
        if self.sqlConn:
            # 打开游标
            self.sqlCursor = self.sqlConn.cursor()
        else:
            # 进入到SQL执行阶段，不是特殊命令, 数据库连接也不存在, 直接报错
            if self.testOptions.get("WHENEVER_SQLERROR") == "EXIT":
                raise TestCliException("Not Connected. ")
            else:
                self.lastJsonCommandResult = None
                yield {
                    "type": "error",
                    "message": "Not connected. "
                }

        # 执行正常的SQL语句
        if self.sqlCursor is not None:
            try:
                # 执行数据库的SQL语句
                if "SQL_DIRECT" in sqlHints.keys():
                    self.sqlCursor.execute_direct(sql, TimeOutLimit=self.timeout)
                elif "SQL_PREPARE" in sqlHints.keys():
                    self.sqlCursor.execute(sql, TimeOutLimit=self.timeout)
                else:
                    if self.testOptions.get("SQL_EXECUTE").upper() == "DIRECT":
                        self.sqlCursor.execute_direct(sql, TimeOutLimit=self.timeout)
                    else:
                        self.sqlCursor.execute(sql, TimeOutLimit=self.timeout)

                rowcount = 0
                sqlStatus = 0
                while True:
                    (title, result, headers, columnTypes, status,
                     fetchStatus, fetchedRowCount, sqlWarnings) = \
                        self.getcommandResult(self.sqlCursor, rowcount)
                    rowcount = fetchedRowCount
                    if "TESTCLI_DEBUG" in os.environ:
                        print("[DEBUG] headers=" + str(headers))
                        if result is not None:
                            for rowPos in range(0, len(result)):
                                for cellPos in range(0, len(result[rowPos])):
                                    print("[DEBUG] Cell[" + str(rowPos) + ":" +
                                          str(cellPos) + "]=[" + str(result[rowPos][cellPos]) + "]")

                    sqlErrorMessage = ""
                    # 如果Hints中有order字样，对结果进行排序后再输出
                    if "Order" in sqlHints.keys() and result is not None:
                        if "TESTCLI_DEBUG" in os.environ:
                            print("[DEBUG] Apply Sort for this result 2.")
                        # 不能用sorted函数，需要考虑None出现在列表中特定元素的问题
                        # l =  [(-32767,), (32767,), (None,), (0,)]
                        # l = sorted(l, key=lambda x: (x is None, x))
                        # '<' not supported between instances of 'NoneType' and 'int'
                        self.sortresult(result)

                    # 如果Hint中存在LogFilter，则结果集中过滤指定的输出信息
                    if "LogFilter" in sqlHints.keys() and result is not None:
                        for sqlFilter in sqlHints["LogFilter"]:
                            for item in result[:]:
                                if "TESTCLI_DEBUG" in os.environ:
                                    print("[DEBUG] Apply Filter: " + str(''.join(str(item))) +
                                          " with " + sqlFilter)
                                if re.match(sqlFilter, ''.join(str(item)), re.IGNORECASE):
                                    result.remove(item)
                                    continue

                    # 如果Hint中存在LogMask,则掩码指定的输出信息
                    if "LogMask" in sqlHints.keys() and result is not None:
                        for i in range(0, len(result)):
                            rowResult = list(result[i])
                            bDataChanged = False
                            for j in range(0, len(rowResult)):
                                if rowResult[j] is None:
                                    continue
                                output = str(rowResult[j])
                                for sqlMaskListString in sqlHints["LogMask"]:
                                    sqlMaskList = sqlMaskListString.split("=>")
                                    if len(sqlMaskList) == 2:
                                        sqlMaskPattern = sqlMaskList[0]
                                        sqlMaskTarget = sqlMaskList[1]
                                        if "TESTCLI_DEBUG" in os.environ:
                                            print("[DEBUG] Apply Mask: " + output +
                                                  " with " + sqlMaskPattern + "=>" + sqlMaskTarget)
                                        try:
                                            beforeReplace = str(rowResult[j])
                                            nIterCount = 0
                                            while True:
                                                # 循环多次替代，一直到没有可替代为止
                                                afterReplace = re.sub(sqlMaskPattern, sqlMaskTarget,
                                                                      beforeReplace, re.IGNORECASE)
                                                if afterReplace == beforeReplace or nIterCount > 99:
                                                    newOutput = afterReplace
                                                    break
                                                beforeReplace = afterReplace
                                                nIterCount = nIterCount + 1
                                            if newOutput != output:
                                                bDataChanged = True
                                                rowResult[j] = newOutput
                                        except re.error:
                                            if "TESTCLI_DEBUG" in os.environ:
                                                print('[DEBUG] traceback.print_exc():\n%s'
                                                      % traceback.print_exc())
                                                print('[DEBUG] traceback.format_exc():\n%s'
                                                      % traceback.format_exc())
                                    else:
                                        if "TESTCLI_DEBUG" in os.environ:
                                            print("[DEBUG] LogMask Hint Error: " + sqlHints["LogMask"])
                            if bDataChanged:
                                result[i] = tuple(rowResult)

                    # 保存之前的运行结果
                    if result is None:
                        rowCount = 0
                    else:
                        rowCount = len(result)
                    self.lastJsonCommandResult = {"desc": headers,
                                                  "rows": rowCount,
                                                  "elapsed": time.time() - startTime,
                                                  "result": result,
                                                  "status": 0,
                                                  "warnings": sqlWarnings}

                    # 返回SQL结果
                    if sqlStatus == 1:
                        yield {
                            "type": "error",
                            "message": sqlErrorMessage
                        }
                    else:
                        if self.testOptions.get('TERMOUT').upper() != 'OFF':
                            yield {
                                "type": "result",
                                "title": title,
                                "rows": result,
                                "headers": headers,
                                "columnTypes": columnTypes,
                                "status": status
                            }
                        else:
                            yield {
                                "type": "result",
                                "title": title,
                                "rows": [],
                                "headers": headers,
                                "columnTypes": columnTypes,
                                "status": status
                            }
                    if not fetchStatus:
                        break
            except SQLCliJDBCTimeOutException:
                # 处理超时时间问题
                if sql.upper() not in ["_EXIT", "_QUIT"]:
                    if self.timeOutMode == "SCRIPT":
                        sqlErrorMessage = "TestCli-000: Script Timeout " \
                                             "(" + str(self.scriptTimeOut) + \
                                             ") expired. Abort this command."
                    else:
                        sqlErrorMessage = "TestCli-000: SQL Timeout " \
                                             "(" + str(self.sqlTimeOut) + \
                                             ") expired. Abort this command."
                    yield {"type": "error", "message": sqlErrorMessage}
            except (SQLCliJDBCException, Exception) as je:
                sqlErrorMessage = str(je).strip()
                for errorPrefix in ["java.util.concurrent.ExecutionException:", ]:
                    if sqlErrorMessage.startswith(errorPrefix):
                        sqlErrorMessage = sqlErrorMessage[len(errorPrefix):].strip()
                for errorPrefix in ['java.sql.SQLSyntaxErrorException:',
                                    "java.sql.SQLException:",
                                    "java.sql.SQLInvalidAuthorizationSpecException:",
                                    "java.sql.SQLDataException:",
                                    "java.sql.SQLTransactionRollbackException:",
                                    "java.sql.SQLTransientConnectionException:",
                                    "java.sql.SQLFeatureNotSupportedException",
                                    "com.microsoft.sqlserver.jdbc.",
                                    "org.h2.jdbc.JdbcSQLSyntaxErrorException:"
                                    ]:
                    if sqlErrorMessage.startswith(errorPrefix):
                        sqlErrorMessage = sqlErrorMessage[len(errorPrefix):].strip()

                # 如果Hint中存在LogFilter，则输出的消息中过滤指定的输出信息
                if "LogFilter" in sqlHints.keys():
                    sqlMultiLineErrorMessage = sqlErrorMessage.split('\n')
                    bErrorMessageHasChanged = False
                    for sqlFilter in sqlHints["LogFilter"]:
                        for item in sqlMultiLineErrorMessage[:]:
                            if re.match(sqlFilter, ''.join(str(item)), re.IGNORECASE):
                                sqlMultiLineErrorMessage.remove(item)
                                bErrorMessageHasChanged = True
                                continue
                    if bErrorMessageHasChanged:
                        sqlErrorMessage = "\n".join(sqlMultiLineErrorMessage)

                # 如果Hint中存在LogMask,则掩码指定的输出信息
                if "LogMask" in sqlHints.keys():
                    sqlMultiLineErrorMessage = sqlErrorMessage.split('\n')
                    bErrorMessageHasChanged = False
                    for sqlMaskListString in sqlHints["LogMask"]:
                        sqlMaskList = sqlMaskListString.split("=>")
                        if len(sqlMaskList) == 2:
                            sqlMaskPattern = sqlMaskList[0]
                            sqlMaskTarget = sqlMaskList[1]
                            for pos2 in range(0, len(sqlMultiLineErrorMessage)):
                                newOutput = re.sub(sqlMaskPattern, sqlMaskTarget,
                                                   sqlMultiLineErrorMessage[pos2],
                                                   re.IGNORECASE)
                                if newOutput != sqlMultiLineErrorMessage[pos2]:
                                    bErrorMessageHasChanged = True
                                    sqlMultiLineErrorMessage[pos2] = newOutput
                        else:
                            if "TESTCLI_DEBUG" in os.environ:
                                raise TestCliException("LogMask Hint Error: " + sqlHints["LogMask"])
                    if bErrorMessageHasChanged:
                        sqlErrorMessage = "\n".join(sqlMultiLineErrorMessage)

                self.lastJsonCommandResult = {"elapsed": time.time() - startTime,
                                              "message": sqlErrorMessage,
                                              "status": -1,
                                              "rows": 0}

                # 发生了JDBC的SQL语法错误
                if self.testOptions.get("WHENEVER_SQLERROR") == "EXIT":
                    raise TestCliException(sqlErrorMessage)
                else:
                    yield {"type": "error", "message": sqlErrorMessage}

    '''
        重写指定的语句
        在正式执行之前，对语句进行重新，替换掉MAPPING或者环境变量中的信息
        输入：
            statement              原语句
            commandScriptFile      语句所在的脚本文件（用于MAPPING，做映射使用)
        输出：
            statement              修改后的语句，可能和原语句相同
            rewrotedCommandHistory 改写历史记录
    '''
    def rewriteRunStatement(self, statement: str, commandScriptFile: str):
        # 命令可能会被多次改写
        rewrotedCommandHistory = []

        # 如果打开了回写，并且指定了输出文件，且SQL被改写过，输出改写后的SQL
        if self.testOptions.get("TESTREWRITE").upper() == 'ON':
            while True:
                beforeRewriteStatement = statement
                afterRewriteStatement = self.mappingHandler.RewriteSQL(commandScriptFile, beforeRewriteStatement)
                if beforeRewriteStatement != afterRewriteStatement:  # 命令已经发生了改变
                    # 记录被改写的命令
                    if self.testOptions.get("NAMESPACE") == "SQL":
                        rewrotedCommandHistory.append(
                            SQLFormatWithPrefix("Your SQL has been changed to:\n" + afterRewriteStatement, 'REWROTED '))
                    if self.testOptions.get("NAMESPACE") == "API":
                        rewrotedCommandHistory.append(
                            APIFormatWithPrefix("Your API has been changed to:\n" + afterRewriteStatement, 'REWROTED '))
                    statement = afterRewriteStatement
                else:
                    # 一直循环到没有任何东西可以被替换
                    break

        # 保留原脚本
        rawStatement = statement

        while True:
            # 替换脚本中的变量信息
            # 替换： 一： 系统的环境变量,即{{env}}
            # 替换： 二:  系统内嵌脚本中的变量{{eval}}
            match_obj = re.search(r"{{(.*?)}}", statement, re.IGNORECASE | re.DOTALL)
            if match_obj:
                searchResult = str(match_obj.group(0))
                varName = str(match_obj.group(1)).strip()
                # 尝试本地变量
                try:
                    evalResult = evalExpression(self.cliHandler, varName)
                    statement = statement.replace(searchResult, str(evalResult))
                except NameError:
                    # 非环境变量
                    pass
                # 尝试环境变量
                if varName in os.environ:
                    statement = statement.replace(searchResult, os.environ[varName])
            else:
                # 没有任何可以替换的了
                break

        # 语句发生了变化
        if rawStatement != statement:
            # 记录被变量信息改写的命令
            if self.testOptions.get("NAMESPACE") == "SQL":
                rewrotedCommandHistory.append(
                    SQLFormatWithPrefix("Your SQL has been changed to:\n" + statement, 'REWROTED '))
            if self.testOptions.get("NAMESPACE") == "API":
                rewrotedCommandHistory.append(
                    APIFormatWithPrefix("Your API has been changed to:\n" + statement, 'REWROTED '))

        return statement, rewrotedCommandHistory

    '''
        处理命令行的Hint信息
        输入：
            commandHints            字符串方式的Hint输入
        输出:
            commandHintList         用Key-Value方式返回的Hint信息                          
    '''
    @staticmethod
    def parseHints(commandHints: list):
        commandHintList = {}

        for commandHint in commandHints:
            # [Hint]  Scenario:XXXX   -- 相关SQL的Scenariox信息，仅仅作为日志信息供查看
            match_obj = re.search(
                r"^Scenario:(.*)", commandHint, re.IGNORECASE | re.DOTALL)
            if match_obj:
                senario = match_obj.group(1)
                # 如果只有一个内容， 规则是:Scenario:ScenarioName
                commandHintList["Scenario"] = senario
                continue

            # [Hint]  order           -- SQLCli将会把随后的SQL语句进行排序输出，原程序的输出顺序被忽略
            match_obj = re.search(r"^order", commandHint, re.IGNORECASE | re.DOTALL)
            if match_obj:
                commandHintList["Order"] = True
                continue

            # [Hint]  LogFilter      -- SQLCli会过滤随后显示的输出信息，对于符合过滤条件的，将会被过滤
            match_obj = re.search(
                r"^LogFilter(\s+)(.*)", commandHint, re.IGNORECASE | re.DOTALL)
            if match_obj:
                # 可能有多个Filter信息
                sqlFilter = match_obj.group(5).strip()
                if "LogFilter" in commandHintList:
                    commandHintList["LogFilter"].append(sqlFilter)
                else:
                    commandHintList["LogFilter"] = [sqlFilter, ]
                continue

            # [Hint]  LogMask      -- SQLCli会掩码随后显示的输出信息，对于符合掩码条件的，将会被掩码
            match_obj = re.search(
                r"^LogMask(\s+)(.*)", commandHint, re.IGNORECASE | re.DOTALL)
            if match_obj:
                sqlMask = match_obj.group(5).strip()
                if "LogMask" in commandHintList:
                    commandHintList["LogMask"].append(sqlMask)
                else:
                    commandHintList["LogMask"] = [sqlMask]
                continue

            # [Hint]  SQL_DIRECT   -- SQLCli执行的时候将不再尝试解析语句，而是直接解析执行
            match_obj = re.search(r"^SQL_DIRECT", commandHint, re.IGNORECASE | re.DOTALL)
            if match_obj:
                commandHintList["SQL_DIRECT"] = True
                continue

            # [Hint]  SQL_PREPARE   -- SQLCli执行的时候将首先尝试解析语句，随后执行
            match_obj = re.search(r"^SQL_PREPARE", commandHint, re.IGNORECASE | re.DOTALL)
            if match_obj:
                commandHintList["SQL_PREPARE"] = True
                continue
        return commandHintList

    def runStatement(self, statement: str, commandScriptFile: str, nameSpace: str):
        # Remove spaces and EOL
        statement = statement.strip()
        formattedCommand = None
        if not statement:  # Empty string
            return

        # 记录脚本的文件名
        self.script = commandScriptFile

        # DEBUG模式下，打印当前计划要执行的语句
        if "TESTCLI_DEBUG" in os.environ:
            if self.testOptions.get("NAMESPACE") == "SQL":
                print("[DEBUG] SQL Command=[" + str(statement) + "]")
            elif self.testOptions.get("NAMESPACE") == "API":
                print("[DEBUG] API Command=[" + str(statement) + "]")

        # 开始解析语句
        try:
            ret_CommandSplitResults = []
            ret_CommandSplitResultsWithComments = []
            ret_CommandHints = []
            defaultNameSpace = self.testOptions.get("NAMESPACE")
            # 将所有的语句分拆开，按照行，依次投喂给解析器，以获得被Antlr分拆后的运行结果
            currentStatement = None
            currentStatementWithComments = None
            currentHints = []
            statementLines = statement.split('\n')
            for nPos in range(0, len(statementLines)):
                statementLine = statementLines[nPos]

                # 将上次没有结束的行和当前行放在一起, 再次看是否已经结束
                if currentStatement is None:
                    currentStatement = statementLine
                else:
                    currentStatement = currentStatement + '\n' + statementLine
                if currentStatementWithComments is None:
                    currentStatementWithComments = statementLine
                else:
                    currentStatementWithComments = currentStatementWithComments + '\n' + statementLine

                # 调用解析器解析语句
                if self.testOptions.get("NAMESPACE") == "SQL":
                    (isFinished, ret_CommandSplitResult, ret_errorCode, ret_errorMsg) \
                        = SQLAnalyze(currentStatement)
                elif self.testOptions.get("NAMESPACE") == "API":
                    (isFinished, ret_CommandSplitResult, ret_errorCode, ret_errorMsg) \
                        = APIAnalyze(currentStatement)
                else:
                    raise TestCliException("不支持的运行空间【" + str(nameSpace) + "】")

                # 语句没有结束
                if not isFinished:
                    # 如果语句还没有结束，则需要等待下一行输入
                    # 如果到了文件末尾，就没有必要继续等待，直接返回
                    if nPos == (len(statementLines) - 1):
                        if self.testOptions.get("NAMESPACE") == "SQL":
                            ret_CommandSplitResults.append(
                                {'name': 'SQL_UNKNOWN',
                                 'statement': currentStatement,
                                 'reason': "missing SQL_END at '<EOF>'"}
                            )
                        elif self.testOptions.get("NAMESPACE") == "API":
                            ret_CommandSplitResults.append(
                                {'name': 'API_UNKNOWN',
                                 'statement': currentStatement,
                                 'reason': "missing HTTP_END at '<EOF>'"}
                            )
                        # 解析前的语句
                        ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                        # 所有的提示信息
                        ret_CommandHints.append(currentHints)
                        # 清空语句的变量
                        currentStatement = None
                        currentStatementWithComments = None
                    continue

                # 语句已经结束
                if ret_CommandSplitResult is None:
                    # 解析的结果为空，可能的原因有：
                    #    这是一个空行，或者仅仅包含注释，那继续等待下一个有意义的行
                    #    这行已经发生了解析错误，但是由于我们的解析规则并不覆盖所有的数据库语句，所以就假设其为数据库语句
                    if ret_errorCode != 0:
                        # 语句已经出错
                        if self.testOptions.get("NAMESPACE") == "SQL":
                            # 对于SQL语句，不认识的语句要当作一个SQL使用
                            #   如果语句的最后一个字符是SQL终止符号，则认为语句结束
                            #   如果语句的最后一个字符不是SQL终止符号，继续等待
                            if currentStatement.strip().endswith(';') or currentStatement.strip().endswith('\n/'):
                                ret_CommandSplitResults.append(
                                    {'name': 'SQL_UNKNOWN',
                                     'statement': currentStatement,
                                     'reason': ret_errorMsg}
                                )
                                # 解析前的语句
                                ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                                # 所有的提示信息
                                ret_CommandHints.append(currentHints)
                                # 清空语句的变量
                                currentStatement = None
                                currentStatementWithComments = None
                            continue
                        elif self.testOptions.get("NAMESPACE") == "API":
                            # 对于API语句，不认识的语句肯定是出问题了
                            ret_CommandSplitResults.append(
                                {'name': 'API_UNKNOWN',
                                 'statement': currentStatement,
                                 'reason': ret_errorMsg}
                            )
                            # 解析前的语句
                            ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                            # 所有的提示信息
                            ret_CommandHints.append(currentHints)
                            # 清空语句的变量
                            currentStatement = None
                            currentStatementWithComments = None

                    # 没有任何有效的语句，可能是空行或者完全的注释
                    # 对于注释中的Hint信息需要保留下来
                    pattern = ""
                    if self.testOptions.get("NAMESPACE") == "SQL":
                        pattern = r"^(\s+)?--(\s+)?\[(\s+)?Hint(\s+)?\](.*)"
                    elif self.testOptions.get("NAMESPACE") == "API":
                        pattern = r"^(\s+)?//(\s+)?\[(\s+)?Hint(\s+)?\](.*)"
                    matchObj = re.match(pattern, statementLine, re.IGNORECASE)
                    if matchObj:
                        currentHints.append(matchObj.group(5).strip())
                    # 解析后的语句
                    ret_CommandSplitResults.append(None)
                    # 解析前的语句
                    ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                    # 对于非有效语句，Hint不在当前语句中体现，而是要等到下次有意义的语句进行处理
                    ret_CommandHints.append([])
                    # 清空语句的变量
                    currentStatement = None
                    currentStatementWithComments = None
                    continue
                else:
                    # 解析出来了真实有效的内容
                    if ret_CommandSplitResult["name"] == "USE":
                        self.testOptions.set("NAMESPACE", ret_CommandSplitResult["nameSpace"])
                    if self.testOptions.get("NAMESPACE") == "SQL":
                        # 对于SQL的CONNECT命令做特殊处理
                        if ret_CommandSplitResult["name"] == "CONNECT":
                            # 对于数据库连接命令，如果没有给出连接详细信息，并且指定了环境变量，附属环境变量到连接命令后
                            if "driver" not in ret_CommandSplitResult and "SQLCLI_CONNECTION_URL" in os.environ:
                                (isFinished, ret_CommandSplitResult, ret_errorCode, ret_errorMsg) \
                                    = SQLAnalyze(currentStatement + "@" + os.environ["SQLCLI_CONNECTION_URL"])
                    # 语句已经结束, 记录语句解析的结果
                    # 解析后的语句
                    ret_CommandSplitResults.append(ret_CommandSplitResult)
                    # 解析前的语句
                    ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                    # 所有的提示信息
                    ret_CommandHints.append(currentHints)
                    # 清空语句的变量
                    currentStatement = None
                    currentStatementWithComments = None
                    continue
        except Exception:
            if "TESTCLI_DEBUG" in os.environ:
                print('traceback.print_exc():\n%s' % traceback.print_exc())
                print('traceback.format_exc():\n%s' % traceback.format_exc())
            raise TestCliException("TestCli-000 Internal error. Parse failed.")

        # 解析过程中设置的NAMESPACE，解析后要还原，好保证执行的正确
        self.testOptions.set("NAMESPACE", defaultNameSpace)

        # 开始执行语句
        pos = 0
        while True:
            if pos == len(ret_CommandSplitResults):
                break

            # 记录命令开始时间
            start = time.time()

            # 首先打印原有语句
            if self.testOptions.get("NAMESPACE") == "SQL":
                formattedCommand = SQLFormatWithPrefix(ret_CommandSplitResultsWithComments[pos])
            if self.testOptions.get("NAMESPACE") == "API":
                formattedCommand = APIFormatWithPrefix(ret_CommandSplitResultsWithComments[pos])

            # 如果是空语句，不需要执行，但可能是完全注释行
            # 也可能是一个解析错误的语句
            if ret_CommandSplitResults[pos] is None:
                yield {
                    "type": "parse",
                    "rawCommand": ret_CommandSplitResults[pos],
                    "formattedCommand": formattedCommand,
                    "rewrotedCommand": [],
                    "script": commandScriptFile
                }
                pos = pos + 1
                continue
            else:
                parseObject = dict(ret_CommandSplitResults[pos])

            # 如果在循环模式下，且循环条件已经不满足，则直接跳出
            if parseObject["name"] != "LOOP":
                if self.loopMode and not self.loopCondition:
                    pos = pos + 1
                    continue

            # 返回SQL的解析信息
            if commandScriptFile != "Console":
                # 如果是控制台显示，不再回显原命令，没有任何实际意义
                yield {
                    "type": "parse",
                    "rawCommand": ret_CommandSplitResults[pos],
                    "formattedCommand": formattedCommand,
                    "rewrotedCommand": [],
                    "script": commandScriptFile
                }

            # 处理超时时间问题
            if self.scriptTimeOut > 0:
                if self.scriptTimeOut <= time.time() - self.getStartTime():
                    commandErrorMessage = "TestCli-000: Script Timeout " \
                                         "(" + str(round(self.scriptTimeOut, 2)) + \
                                         ") expired. Abort this Script."
                    yield {"type": "error", "message": commandErrorMessage}
                    raise EOFError
                else:
                    if self.testOptions.get("NAMESPACE") == "SQL":
                        if self.sqlTimeOut > 0:
                            if self.scriptTimeOut - (time.time() - self.getStartTime()) < self.sqlTimeOut:
                                # 脚本超时剩余时间更少，执行较少的那个超时控制
                                self.timeOutMode = "SCRIPT"
                                self.timeout = self.scriptTimeOut - (time.time() - self.getStartTime())
                            else:
                                self.timeOutMode = "COMMAND"
                                self.timeout = self.sqlTimeOut
                        else:
                            self.timeOutMode = "SCRIPT"
                            self.timeout = self.scriptTimeOut - (time.time() - self.getStartTime())
                    if self.testOptions.get("NAMESPACE") == "API":
                        if self.apiTimeOut > 0:
                            if self.scriptTimeOut - (time.time() - self.getStartTime()) < self.apiTimeOut:
                                # 脚本超时剩余时间更少，执行较少的那个超时控制
                                self.timeOutMode = "SCRIPT"
                                self.timeout = self.scriptTimeOut - (time.time() - self.getStartTime())
                            else:
                                self.timeOutMode = "COMMAND"
                                self.timeout = self.apiTimeOut
                        else:
                            self.timeOutMode = "SCRIPT"
                            self.timeout = self.scriptTimeOut - (time.time() - self.getStartTime())
            elif self.testOptions.get("NAMESPACE") == "SQL" and self.sqlTimeOut > 0:
                # 没有设置SCRIPT的超时时间，只设置了COMMAND的超时时间
                self.timeOutMode = "COMMAND"
                self.timeout = self.sqlTimeOut
            elif self.testOptions.get("NAMESPACE") == "API" and self.apiTimeOut > 0:
                # 没有设置SCRIPT的超时时间，只设置了COMMAND的超时时间
                self.timeOutMode = "COMMAND"
                self.timeout = self.apiTimeOut
            else:
                # 什么超时时间都没有设置
                self.timeOutMode = None
                self.timeout = -1

            # 处理各种命令
            sqlKeyWords = ["SELECT", "DELETE", "UPDATE", "CREATE", "INSERT",
                           "DROP", "COMMIT", "ROLLBACK",
                           "PROCEDURE", "DECLARE", "BEGIN",
                           "SQL_UNKNOWN"]
            if parseObject["name"] == "ECHO":
                # 将后续内容回显到指定的文件中
                for commandResult in self.cliHandler.echo_input(
                        cls=self.cliHandler,
                        fileName=parseObject["param"],
                        block=parseObject["block"],
                ):
                    yield commandResult
            elif parseObject["name"] == "START":
                # 执行脚本文件
                for commandResult in self.cliHandler.execute_from_file(
                        cls=self.cliHandler,
                        scriptFileList=parseObject["scriptList"],
                        loopTimes=parseObject["loopTimes"],
                ):
                    yield commandResult
            elif parseObject["name"] in ["EXIT", "QUIT"]:
                # 执行脚本文件
                if "exitValue" in parseObject.keys():
                    exitValue = parseObject["exitValue"]
                else:
                    exitValue = 0
                for commandResult in exitApplication(
                        cls=self.cliHandler,
                        exitValue=exitValue
                ):
                    yield commandResult
            elif parseObject["name"] == "CONNECT":
                # 执行CONNECT命令
                if self.testOptions.get("NAMESPACE") == "SQL":
                    for commandResult in self.cliHandler.connect_db(
                            cls=self.cliHandler,
                            connectProperties=parseObject
                    ):
                        yield commandResult
                else:
                    yield {
                        "type": "error",
                        "message": "affadsfsad",
                        "script": commandScriptFile
                    }
            elif parseObject["name"] == "SET":
                # 执行SET命令
                for commandResult in setOptions(
                        cls=self.cliHandler,
                        options=parseObject
                ):
                    yield commandResult
            elif parseObject["name"] == "DISCONNECT":
                # 执行DISCONNECT命令
                if self.testOptions.get("NAMESPACE") == "SQL":
                    for commandResult in self.cliHandler.disconnect_db(
                            cls=self.cliHandler
                    ):
                        yield commandResult
                else:
                    yield {
                        "type": "error",
                        "message": "affadsfsad",
                        "script": commandScriptFile
                    }
            elif parseObject["name"] in sqlKeyWords:
                if self.ifMode and not self.ifCondition:
                    pos = pos + 1
                    continue
                sqlCommand = parseObject["statement"]
                # 根据语句中的变量或者其他定义信息来重写当前语句
                sqlCommand, rewrotedCommandList = self.rewriteRunStatement(
                    statement=sqlCommand,
                    commandScriptFile=commandScriptFile
                )
                if len(rewrotedCommandList) != 0:
                    # 如果命令被发生了改写，要打印改写记录
                    yield {
                        "type": "parse",
                        "rawCommand": None,
                        "formattedCommand": None,
                        "rewrotedCommand": rewrotedCommandList,
                        "script": commandScriptFile
                    }

                # 处理Hints信息
                commandHintList = self.parseHints(list(ret_CommandHints[pos]))

                # 执行SQL语句
                startTime = time.time()
                for result in self.executeSQLStatement(
                        sql=sqlCommand,
                        sqlHints=commandHintList,
                        startTime=0):
                    endTime = time.time()
                    lastCommandResult["elapsed"] = endTime - startTime
                    if result["type"] == "result":
                        lastCommandResult["rows"] = result["rows"]
                        lastCommandResult["headers"] = result["headers"]
                    else:
                        lastCommandResult["rows"] = []
                        lastCommandResult["headers"] = []
                    yield result
            elif parseObject["name"] in ["USE"]:
                for commandResult in self.cliHandler.set_nameSpace(
                        cls=self.cliHandler,
                        nameSpace=parseObject["nameSpace"]
                ):
                    yield commandResult
            elif parseObject["name"] in ["SLEEP"]:
                for commandResult in cliSleep(
                        cls=self.cliHandler,
                        sleepTime=parseObject["sleepTime"]
                ):
                    yield commandResult
            elif parseObject["name"] in ["SPOOL"]:
                for commandResult in self.cliHandler.spool(
                        cls=self.cliHandler,
                        fileName=parseObject["file"]
                ):
                    yield commandResult
            elif parseObject["name"] in ["SESSION"]:
                for commandResult in sessionManage(
                        cls=self.cliHandler,
                        action=parseObject["action"],
                        sessionName=parseObject["sessionName"]
                ):
                    yield commandResult
            elif parseObject["name"] in ["SCRIPT"]:
                for commandResult in executeEmbeddScript(
                        cls=self.cliHandler,
                        block=parseObject["block"]
                ):
                    yield commandResult
            elif parseObject["name"] in ["ASSERT"]:
                for commandResult in assertExpression(
                        cls=self.cliHandler,
                        expression=parseObject["expression"]
                ):
                    yield commandResult
            elif parseObject["name"] in ["LOAD"]:
                if parseObject["option"] == "PLUGIN":
                    for commandResult in loadPlugin(
                            pluginFile=parseObject["pluginFile"]
                    ):
                        yield commandResult
                elif parseObject["option"] == "DRIVER":
                    for commandResult in loadDriver(
                            cls=self.cliHandler,
                            driverName=parseObject["driverName"],
                            driverFile=parseObject["driverFile"]
                    ):
                        yield commandResult
                elif parseObject["option"] == "MAP":
                    for commandResult in loadMap(
                            cls=self.cliHandler,
                            mapFile=parseObject["mapFile"]
                    ):
                        yield commandResult
            elif parseObject["name"] in ["HTTP"]:
                if self.ifMode and not self.ifCondition:
                    pos = pos + 1
                    continue
                httpRequestTarget = parseObject["httpRequestTarget"]
                # 根据语句中的变量或者其他定义信息来重写当前语句
                httpRequestTarget, rewrotedCommandList = self.rewriteRunStatement(
                    statement=httpRequestTarget,
                    commandScriptFile=commandScriptFile
                )
                if len(rewrotedCommandList) != 0:
                    # 如果命令被发生了改写，要打印改写记录
                    yield {
                        "type": "parse",
                        "rawCommand": None,
                        "formattedCommand": None,
                        "rewrotedCommand": rewrotedCommandList,
                        "script": commandScriptFile
                    }
                    parseObject["httpRequestTarget"] = httpRequestTarget

                # 处理Hints信息
                commandHintList = self.parseHints(list(ret_CommandHints[pos]))

                # 执行HTTP请求
                for result in self.executeAPIStatement(
                        apiRequest=parseObject,
                        apiHints=commandHintList,
                        startTime=0):
                    yield result
            elif parseObject["name"] in ["HOST"]:
                # 执行主机操作系统命令
                for result in executeLocalCommand(
                        cls=self.cliHandler,
                        command=parseObject["script"]
                ):
                    yield result
            elif parseObject["name"] in ["ENDIF"]:
                self.ifMode = False
                self.ifCondition = False
            elif parseObject["name"] in ["IF"]:
                expression = parseObject["expression"]
                try:
                    ret = evalExpression(self.cliHandler, expression)
                    if type(ret) == bool:
                        self.ifMode = True
                        if ret:
                            self.ifCondition = True
                        else:
                            self.ifCondition = False
                    else:
                        yield {
                            "type": "error",
                            "message": "Set condition fail. SyntaxError =>[not a bool expression]"
                        }
                except (SyntaxError, NameError) as ae:
                    yield {
                        "type": "error",
                        "message": "Set condition fail. SyntaxError =>[" + str(ae) + "]"
                    }
            elif parseObject["name"] in ["LOOP"]:
                if parseObject["rule"] == "END":
                    if not self.loopCondition:
                        self.loopMode = False
                    else:
                        pos = self.loopStartPos
                        continue
                elif parseObject["rule"] == "BREAK":
                    self.loopCondition = False
                elif parseObject["rule"] == "CONTINUE":
                    pos = self.loopStartPos
                    continue
                elif parseObject["rule"] == "BEGIN":
                    self.loopMode = True
                    self.loopStartPos = pos
                    self.loopCondition = not evalExpression(self.cliHandler, parseObject["UNTIL"])
            elif parseObject["name"] in ["UNKNOWN"]:
                yield {"type": "error",
                       "message": "TESTCLI_0000:  " + parseObject["reason"]}
            else:
                raise TestCliException("FDDFSFDFSDSFD " + str(parseObject))

            # 如果需要，打印语句执行时间
            end = time.time()
            self.lastElapsedTime = end - start

            # 记录SQL日志信息
            if self.testOptions.get("SILENT").upper() == 'OFF':
                yield {
                    "type": "statistics",
                    "elapsed": self.lastElapsedTime,
                }

            # 开始执行下一个语句
            pos = pos + 1
