# -*- 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 traceback
import urllib3
import copy
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 APIRequestStringFormatWithPrefix

from .commands.load import loadPlugin
from .commands.load import loadJDBCDriver
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.connectdb import connectDb, disconnectDb
from .commands.start import executeFile
from .commands.host import executeLocalCommand
from .commands.spool import spool
from .commands.echo import echo_input
from .commands.setOptions import setOptions
from .commands.cliSleep import cliSleep
from .commands.userNameSpace import userNameSpace
from .commands.whenever import setWheneverAction
from .commands.ssh import executeSshRequest
from .commands.ssh import rewriteSshRequest
from .commands.compare import executeCompareRequest
from .commands.helper import showHelp
from .commands.data import executeDataRequest
from .commands.monitor import executeMonitorRequest

from .common import rewriteHintStatement
from .common import rewriteSQLStatement
from .common import rewriteAPIStatement
from .common import rewriteConnectRequest
from .common import parseSQLHints
from .common import parseAPIHints
from .common import sortresult
from .testcliexception import TestCliException
from .globalvar 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.scenarioId = ''
        self.scenarioName = ''

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

        # 数据库连接
        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 "****"

    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())
                        if dataValue is not None:
                            dataValue = binascii.b2a_hex(dataValue)
                            dataValue = dataValue.decode()
                            return "0x" + dataValue
                        else:
                            return None
                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("SQL_FETCHSIZE"))
            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):
        """
        返回内容：
            错误情况下：
            {
                "type": "error",
                "message": apiErrorMessage
            }
            正确情况下：
            {
                "type": "result",
                "title": None,
                "rows": None,
                "headers": None,
                "columnTypes": None,
                "status": content
            }
        """
        httpHandler = self.cliHandler.httpHandler

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

        headers = {}
        fields = {}
        operator = None
        outputTarget = None
        contentFrom = None

        if "headers" in apiRequest:
            headers = apiRequest["headers"]
        if "httpFields" in apiRequest:
            # fields字段需要复制下来
            # 因为后续会不断更新fields的内容，甚至包括二进制的内容（以file文件上传的时候）
            # 这些二进制的内容没有必要（也做不到）记录到扩展日志中
            fields = copy.copy(apiRequest["httpFields"])

        multiPartBoundary = None
        if "multipart" in apiRequest:
            if "Content-Type" in headers.keys() and headers["Content-Type"] is not None:
                contentType = str(headers["Content-Type"])
                contentTypeList = contentType.split(';')
                for contentType in contentTypeList:
                    contentType = str(contentType).strip()
                    if contentType.startswith("boundary"):
                        multiPartBoundary = re.sub(r"boundary(\s+)?=(\s+)?", "", contentType).strip()
            else:
                yield {
                    "type": "error",
                    "message": "Testcli-0000: " +
                               "Missed part Content-Disposition:name in multiPart header."
                }
                return
            if multiPartBoundary is None:
                yield {
                    "type": "error",
                    "message": "Testcli-0000: " +
                               "Missed multiPart boundary in header."
                }
                return
            for multiPart in apiRequest["multipart"]:
                if "headers" in multiPart.keys() and multiPart["headers"] is not None:
                    multiPartHeader = multiPart["headers"]
                    if "Content-Disposition" in multiPartHeader.keys() \
                            and multiPartHeader["Content-Disposition"] is not None:
                        contentDisposition = multiPartHeader["Content-Disposition"]
                        contentDispositions = contentDisposition.split(";")
                        contentDispositionName = None
                        contentDispositionFileName = None
                        for contentDisposition in contentDispositions:
                            contentDisposition = str(contentDisposition).strip()
                            if str(contentDisposition).startswith("filename"):
                                contentDispositionFileName = \
                                    re.sub(r"filename(\s+)?=(\s+)?", "", contentDisposition).strip()
                                if contentDispositionFileName.startswith('"') and \
                                        contentDispositionFileName.endswith('"'):
                                    contentDispositionFileName = contentDispositionFileName[1:-1]
                                if contentDispositionFileName.startswith("'") and \
                                        contentDispositionFileName.endswith("'"):
                                    contentDispositionFileName = contentDispositionFileName[1:-1]
                            if str(contentDisposition).startswith("name"):
                                contentDispositionName = \
                                    re.sub(r"name(\s+)?=(\s+)?", "", contentDisposition).strip()
                                if contentDispositionName.startswith('"') and \
                                        contentDispositionName.endswith('"'):
                                    contentDispositionName = contentDispositionName[1:-1]
                                if contentDispositionName.startswith("'") and \
                                        contentDispositionName.endswith("'"):
                                    contentDispositionName = contentDispositionName[1:-1]
                        if contentDispositionName is None:
                            yield {
                                "type": "error",
                                "message": "Testcli-0000: " +
                                           "Missed part Content-Disposition:name in multiPart header."
                            }
                            return
                        if contentDispositionFileName is None:
                            # 上传的内容是一个文本内容
                            multiPartContent = "".join(multiPart["contents"])
                            fields[contentDispositionName] = (None, multiPartContent)
                        else:
                            # 上传的内容是一个文件
                            contentFrom = None
                            if "operate" in multiPart.keys() and multiPart["operate"] is not None:
                                if len(multiPart["operate"]) == 1:
                                    multiPartOperate = multiPart["operate"][0]
                                    if "content" in multiPartOperate.keys():
                                        contentFrom = multiPartOperate["content"].strip()
                            if contentFrom is None:
                                # 将当前的文本作为上传数据的一部分
                                multiPartContent = "".join(multiPart["contents"])
                                body = multiPartContent.encode(self.testOptions.get("SCRIPT_ENCODING"))
                                fields[contentDispositionName] = (contentDispositionFileName, body)
                            else:
                                # 如果指定了数据从文件中获取，则不再分析contents的内容
                                if not os.path.exists(contentFrom):
                                    yield {
                                        "type": "error",
                                        "message": "Testcli-0000: " + "File [" + str(contentFrom) + "] does not exist."
                                    }
                                    return
                                with open(contentFrom, mode="rb") as f:
                                    body = f.read()
                                fields[contentDispositionName] = (contentDispositionFileName, body)
                            pass
                    else:
                        yield {
                            "type": "error",
                            "message": "Testcli-0000: " + "Missed part Content-Disposition in multiPart content."
                        }
                        return
                else:
                    yield {
                        "type": "error",
                        "message": "Testcli-0000: " + "Missed part header in multiPart content."
                    }
                    return

        if "operate" in apiRequest.keys() and apiRequest["operate"] is not None:
            if len(apiRequest["operate"]) == 1:
                operate = apiRequest["operate"][0]
                operator = operate["operator"]
                if operator in [">", ">>"]:
                    outputTarget = operate["content"].strip()
                if operator in ["<"]:
                    contentFrom = operate["content"].strip()

        if contentFrom is not None:
            # 如果指定了数据从文件中获取，则不再分析contents的内容
            with open(contentFrom, mode="rb") as f:
                body = f.read()
        else:
            if "contents" in apiRequest:
                body = "".join(apiRequest["contents"])
            else:
                body = ""
            if len(fields) != 0:
                # 如果填写了fields字段，且body中仅仅包含一个换行符，则标记body为空字符串
                # Python的urllib3不容许同时出现fields和body内容
                if body.strip() == "":
                    body = None
            if body is not None:
                body = body.encode(self.testOptions.get("SCRIPT_ENCODING"))
                # 重置Header的Content-Length
                headers["Content-Length"] = len(body)
            else:
                # 重置Header的Content-Length
                headers["Content-Length"] = 0
        try:
            if self.timeout == -1:
                timeoutLimit = None
            else:
                timeoutLimit = self.timeout
            args = {
                "method": httpMethod,
                "url": httpRequestTarget,
                "fields": fields,
                "headers": headers,
                "timeout": timeoutLimit,
            }
            if body is not None:
                args["body"] = body
            if multiPartBoundary is not None:
                args["multipart_boundary"] = multiPartBoundary
            ret = httpHandler.request(**args)
            result = {"status": ret.status}
            if outputTarget is not None:
                result["content"] = None
                # 将API执行结果输出到文件中
                mode = None
                if operator == ">":
                    mode = "wb"
                if operator == ">>":
                    mode = "ab"
                with open(outputTarget, mode=mode) as f:
                    f.write(ret.data)
            else:
                data = ret.data.decode('utf-8')
                try:
                    data = json.loads(data)
                except JSONDecodeError:
                    # 返回的对象不是一个JSON
                    pass
                result["content"] = data
            result = json.dumps(obj=result,
                                sort_keys=True,
                                indent=4,
                                separators=(',', ': '),
                                ensure_ascii=False)
            yield {
                "type": "result",
                "title": None,
                "rows": None,
                "headers": None,
                "columnTypes": None,
                "status": result
            }
            return
        except urllib3.exceptions.MaxRetryError as ue:
            if self.timeOutMode == "COMMAND":
                if self.testOptions.get("API_TIMEOUT") != "-1":
                    message = "Request timeout limit command threshold [" + \
                              str(self.testOptions.get("API_TIMEOUT")) + "] reached."
                else:
                    message = str(ue)
            else:
                if self.testOptions.get("SCRIPT_TIMEOUT") != "-1":
                    message = "Request timeout limit script threshold [" + \
                              str(self.testOptions.get("SCRIPT_TIMEOUT")) + "] reached."
                else:
                    message = str(ue)
            yield {
                "type": "error",
                "message": "Testcli-0000: " + message
            }
            return
        except Exception as ex:
            yield {
                "type": "error",
                "message": "Testcli-0000: " + str(ex)
            }
            return

    def executeSQLStatement(self, sql: str, sqlHints):
        """
        返回内容：
            错误情况下：
            {
                "type": "error",
                "message": sqlErrorMessage
            }
            正确情况下：
            {
                "type": "result",
                "title": title,
                "rows": result,
                "headers": headers,
                "columnTypes": columnTypes,
                "status": status
            }
        """

        # 进入到SQL执行阶段, 开始执行SQL语句
        if self.sqlConn:
            # 打开游标
            self.sqlCursor = self.sqlConn.cursor()
        else:
            # 进入到SQL执行阶段，不是特殊命令, 数据库连接也不存在, 直接报错
            if self.testOptions.get("WHENEVER_ERROR") == "EXIT":
                raise TestCliException("Not Connected. ")
            else:
                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,)]
                        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(pattern=sqlFilter, string=''.join(str(item)), flags=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)

                    # 返回SQL结果
                    if sqlStatus == 1:
                        yield {
                            "type": "error",
                            "message": sqlErrorMessage
                        }
                    else:
                        yield {
                            "type": "result",
                            "title": title,
                            "rows": result,
                            "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-0000: Script Timeout " \
                                             "(" + str(self.scriptTimeOut) + \
                                             ") expired. Abort this command."
                    else:
                        sqlErrorMessage = "Testcli-0000: 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:",
                                    "dm.jdbc.driver.DMException:",
                                    ]:
                    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)
                yield {"type": "error", "message": sqlErrorMessage}

    def runStatement(self, statement: str,
                     commandScriptFile: str = "Console",
                     nameSpace: str = None):

        # 如果没有提供nameSpace，则使用系统默认的nameSpace
        if nameSpace is None:
            nameSpace = self.testOptions.get("NAMESPACE")

        # 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 nameSpace == "SQL":
                print("[DEBUG] SQL Command=[" + str(statement) + "]")
            elif nameSpace == "API":
                print("[DEBUG] API Command=[" + str(statement) + "]")

        # 开始解析语句
        try:
            ret_CommandSplitResults = []
            ret_CommandSplitResultsWithComments = []
            ret_CommandHints = []

            # 解析前要保留用户的nameSpace，解析后要把这个内容还原
            # 主要是考虑了执行脚本中，脚本内部含有的nameSpace切换不应该影响到外部的脚本
            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) + "】")

                # 如果发生了语句解析错误，且错误信息是缺少EOF，则是认为语句没有结束导致，不是正常的错误
                if ret_errorCode != 0:
                    missedSQLSlash = False
                    if re.search(pattern=r'missing.*<EOF>', string=ret_errorMsg):
                        # 语句没有结束
                        isFinished = False
                    if re.search(pattern=r'missing.*SQL_SLASH', string=ret_errorMsg):
                        # 语句没有结束
                        missedSQLSlash = True
                        isFinished = False
                    if re.search(pattern=r'expecting.*<EOF>', string=ret_errorMsg):
                        # 语句没有结束
                        isFinished = False
                    if not isFinished and self.testOptions.get("NAMESPACE") == "SQL":
                        statementFinished = False
                        if nPos == (len(statementLines) - 1):
                            # 都已经到最后一行了，不需要继续等待了
                            statementFinished = True
                        if not currentStatement.strip().startswith("_") and \
                                currentStatement.strip().endswith(';') and \
                                not missedSQLSlash:
                            # 遇到了分号，且不是复合语句，那么直接结束
                            statementFinished = True
                        if currentStatement.strip().endswith("\n/"):
                            # 遇到了顶行的/，不管是不是复合语句，直接结束
                            statementFinished = True
                        if statementFinished:
                            if currentStatement.strip().startswith("_"):
                                # 内部语句，且语句已经结束
                                ret_CommandSplitResults.append(
                                    {'name': 'PARSE_ERROR',
                                     'statement': currentStatement,
                                     'reason': ret_errorMsg}
                                )
                                # 解析前的语句
                                ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                                # 所有的提示信息
                                ret_CommandHints.append(currentHints)
                                # 清空语句的变量
                                currentHints = []
                                currentStatement = None
                                currentStatementWithComments = None
                            else:
                                if currentStatement.strip().endswith("\n/"):
                                    currentStatement = currentStatement[:-2]
                                elif currentStatement.strip().endswith(";"):
                                    currentStatement = currentStatement[:-1]
                                ret_CommandSplitResults.append(
                                    {'name': 'SQL_UNKNOWN',
                                     'statement': currentStatement,
                                     'reason': ret_errorMsg}
                                )
                                # 解析前的语句
                                ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                                # 所有的提示信息
                                ret_CommandHints.append(currentHints)
                                # 清空语句的变量
                                currentHints = []
                                currentStatement = None
                                currentStatementWithComments = None
                        continue

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

                # 语句已经结束, 但是输入的内容有错误信息
                if isFinished and ret_errorCode != 0:
                    ret_CommandSplitResults.append(
                        {'name': 'PARSE_ERROR',
                         'statement': currentStatement,
                         'reason': ret_errorMsg}
                    )
                    # 解析前的语句
                    ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                    # 所有的提示信息
                    ret_CommandHints.append(currentHints)
                    # 清空语句的变量
                    currentHints = []
                    currentStatement = None
                    currentStatementWithComments = None
                    continue

                # 语句已经结束，没有任何错误，但解析的结果为空
                if ret_CommandSplitResult is None:
                    # 空行，可能包含注释，保留注释内容，带到下一个有意义的段落
                    # Hint的两种写法
                    # 1.  -- [Hint]  hintsomething
                    # 2.  -- [hitsomething]
                    pattern = r"^(\s+)?([-/][-/])(\s+)?\[(\s+)?Hint(\s+)?\](.*)"
                    matchObj = re.match(pattern, statementLine, re.IGNORECASE)
                    if matchObj:
                        currentHints.append(matchObj.group(6).strip())
                    else:
                        pattern = r"^(\s+)?([-/][-/])(\s+)?\[(.*)\].*"
                        matchObj = re.match(pattern, statementLine, re.IGNORECASE)
                        if matchObj:
                            currentHints.append(matchObj.group(4).strip())
                    # 解析后的语句
                    ret_CommandSplitResults.append(None)
                    # 解析前的语句
                    ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                    # 对于非有效语句，Hint不在当前语句中体现，而是要等到下次有意义的语句进行处理
                    ret_CommandHints.append([])
                    # 清空除了Hint以外的其他语句变量
                    currentStatement = None
                    currentStatementWithComments = None
                    continue

                # 切换解析的命名空间
                if ret_CommandSplitResult["name"] == "USE":
                    self.testOptions.set("NAMESPACE", ret_CommandSplitResult["nameSpace"])

                # 对于SQL的CONNECT命令做特殊处理， 以支持外部环境变量带来的问题
                if self.testOptions.get("NAMESPACE") == "SQL":
                    if ret_CommandSplitResult["name"] == "CONNECT":
                        # 对于数据库连接命令，如果没有给出连接详细信息，并且指定了环境变量，附属环境变量到连接命令后
                        if "driver" not in ret_CommandSplitResult:
                            connectionURL = None
                            if "SQLCLI_CONNECTION_URL" in os.environ:
                                connectionURL = str(os.environ["SQLCLI_CONNECTION_URL"]).strip('"').strip("'").strip()
                            elif "TESTCLI_CONNECTION_URL" in os.environ:
                                connectionURL = str(os.environ["TESTCLI_CONNECTION_URL"]).strip('"').strip("'").strip()
                            if connectionURL is not None:
                                # 拼接链接字符串后重新解析
                                (isFinished, ret_CommandSplitResult, ret_errorCode, ret_errorMsg) \
                                    = SQLAnalyze(currentStatement + "@" + connectionURL)

                # 语句正常结束，且其中包含了正式的内容
                ret_CommandSplitResults.append(ret_CommandSplitResult)
                # 解析前的语句
                ret_CommandSplitResultsWithComments.append(currentStatementWithComments)
                # 所有的提示信息
                ret_CommandHints.append(currentHints)
                # 清空语句的变量
                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

            # 记录命令开始时间
            startTime = time.time()
            try:
                self.sqlTimeOut = int(self.testOptions.get("SQL_TIMEOUT"))
            except ValueError:
                self.sqlTimeOut = -1
            try:
                self.apiTimeOut = int(self.testOptions.get("API_TIMEOUT"))
            except ValueError:
                self.apiTimeOut = -1
            try:
                self.scriptTimeOut = int(self.testOptions.get("SCRIPT_TIMEOUT"))
            except ValueError:
                self.scriptTimeOut = -1

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

            # 如果是空语句，不需要执行，但可能是完全注释行
            # 也可能是一个解析错误的语句
            if ret_CommandSplitResults[pos] is None:
                if self.loopMode and not self.loopCondition:
                    pass
                else:
                    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

            # 返回Command的解析信息
            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-0000: 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

            # 处理Hints信息
            if self.testOptions.get("NAMESPACE") == "SQL":
                commandHintList = parseSQLHints(list(ret_CommandHints[pos]))
            elif self.testOptions.get("NAMESPACE") == "API":
                commandHintList = parseAPIHints(list(ret_CommandHints[pos]))
            else:
                commandHintList = {}
            for commandHintKey, commandHintValue in commandHintList.items():
                # 根据语句中的变量或者其他定义信息来重写当前语句
                if type(commandHintValue) == str:
                    # 只针对字符串进行处理，暂时不对其他数据类型进行处理
                    commandHintNewValue, rewrotedCommandHintValueList = rewriteHintStatement(
                        cls=self.cliHandler,
                        statement=commandHintValue,
                        commandScriptFile=commandScriptFile
                    )
                    if len(rewrotedCommandHintValueList) != 0:
                        commandHintList[commandHintKey] = commandHintNewValue
                        yield {
                            "type": "parse",
                            "rawCommand": None,
                            "formattedCommand": None,
                            "rewrotedCommand":
                                ["REWROTED Hint> " + str(commandHintKey) + ":" + str(commandHintNewValue), ],
                            "script": commandScriptFile
                        }
            if "ScenarioName" in commandHintList:
                scenarioName = str(commandHintList["ScenarioName"])
                scenarioId = str(commandHintList["ScenarioId"])
                if scenarioName.strip().upper() == 'END':
                    self.scenarioName = ""
                    self.scenarioId = ""
                else:
                    self.scenarioName = scenarioName
                    self.scenarioId = scenarioId

            # 处理各种命令
            if "TESTCLI_DEBUG" in os.environ:
                print("[DEBUG] parsedObject=[" + str(parseObject) + "]")
            sqlKeyWords = ["SELECT", "DELETE", "UPDATE", "CREATE", "INSERT",
                           "DROP", "COMMIT", "ROLLBACK",
                           "PROCEDURE", "DECLARE", "BEGIN",
                           "SQL_UNKNOWN"]
            if parseObject["name"] == "HELP":
                # 显示帮助信息
                for commandResult in showHelp(
                        topicName=parseObject["topic"],
                ):
                    yield commandResult
            elif parseObject["name"] == "ECHO":
                # 将后续内容回显到指定的文件中
                for commandResult in echo_input(
                        cls=self.cliHandler,
                        fileName=parseObject["param"],
                        block=parseObject["block"],
                ):
                    yield commandResult
            elif parseObject["name"] == "START":
                # 执行脚本前记录当前执行的脚本名称
                savedExecuteScript = self.cliHandler.executeScript
                self.cliHandler.executeScript = parseObject["script"]
                # 执行脚本文件
                for commandResult in executeFile(
                        cls=self.cliHandler,
                        scriptFile=parseObject["script"],
                        argv=parseObject["argv"],
                ):
                    yield commandResult
                # 执行脚本后还原当前执行脚本的名称
                self.cliHandler.executeScript = savedExecuteScript
            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
                ):
                    if commandResult["type"] == "result":
                        lastCommandResult.clear()
                        lastCommandResult["status"] = commandResult["status"]
                        lastCommandResult["errorCode"] = 0
                    if commandResult["type"] == "error":
                        lastCommandResult["status"] = commandResult["message"]
                        lastCommandResult["errorCode"] = 1
                    yield commandResult
            elif parseObject["name"] == "CONNECT":
                # 根据语句中的变量或者其他定义信息来重写当前语句
                connectRequestObject, rewrotedCommandList = rewriteConnectRequest(
                    cls=self.cliHandler,
                    connectRequestObject=parseObject,
                    commandScriptFile=commandScriptFile
                )
                if len(rewrotedCommandList) != 0:
                    # 如果命令被发生了改写，要打印改写记录
                    yield {
                        "type": "parse",
                        "rawCommand": None,
                        "formattedCommand": None,
                        "rewrotedCommand": rewrotedCommandList,
                        "script": commandScriptFile
                    }

                # 执行CONNECT命令
                for commandResult in connectDb(
                        cls=self.cliHandler,
                        connectProperties=connectRequestObject
                ):
                    if commandResult["type"] == "result":
                        lastCommandResult.clear()
                        lastCommandResult["status"] = commandResult["status"]
                        lastCommandResult["errorCode"] = 0
                    if commandResult["type"] == "error":
                        lastCommandResult["message"] = commandResult["message"]
                        lastCommandResult["errorCode"] = 1
                    yield commandResult
            elif parseObject["name"] == "SET":
                # 执行SET命令
                for commandResult in setOptions(
                        cls=self.cliHandler,
                        options=parseObject
                ):
                    if commandResult["type"] == "result":
                        lastCommandResult.clear()
                        lastCommandResult["status"] = commandResult["status"]
                        lastCommandResult["errorCode"] = 0
                    if commandResult["type"] == "error":
                        lastCommandResult["status"] = commandResult["message"]
                        lastCommandResult["errorCode"] = 1
                    yield commandResult
            elif parseObject["name"] == "DISCONNECT":
                # 执行DISCONNECT命令
                if self.testOptions.get("NAMESPACE") == "SQL":
                    for commandResult in disconnectDb(
                            cls=self.cliHandler
                    ):
                        if commandResult["type"] == "result":
                            lastCommandResult.clear()
                            lastCommandResult["status"] = commandResult["status"]
                            lastCommandResult["errorCode"] = 0
                        if commandResult["type"] == "error":
                            lastCommandResult["status"] = commandResult["message"]
                            lastCommandResult["errorCode"] = 1
                        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 = rewriteSQLStatement(
                    cls=self.cliHandler,
                    statement=sqlCommand,
                    commandScriptFile=commandScriptFile
                )
                if len(rewrotedCommandList) != 0:
                    # 如果命令被发生了改写，要打印改写记录
                    yield {
                        "type": "parse",
                        "rawCommand": None,
                        "formattedCommand": None,
                        "rewrotedCommand": rewrotedCommandList,
                        "script": commandScriptFile
                    }

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

                # 执行HTTP请求
                for result in self.executeAPIStatement(
                        apiRequest=parseObject,
                        apiHints=commandHintList):
                    if result["type"] == "result":
                        lastCommandResult.clear()
                        data = json.loads(result["status"])
                        lastCommandResult["content"] = data["content"]
                        lastCommandResult["status"] = data["status"]
                        lastCommandResult["errorCode"] = 0
                    if result["type"] == "error":
                        lastCommandResult["message"] = result["message"]
                        lastCommandResult["errorCode"] = 1
                    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":
                    try:
                        self.loopCondition = not evalExpression(self.cliHandler, parseObject["UNTIL"])
                        self.loopMode = True
                        self.loopStartPos = pos
                    except Exception as ex:
                        self.loopMode = False
                        yield {
                            "type": "error",
                            "message": "Loop condition expression error.. SyntaxError =>[" + str(ex) + "]"
                        }
            elif parseObject["name"] in ["WHENEVER"]:
                for result in setWheneverAction(
                        cls=self.cliHandler,
                        action=parseObject["action"],
                        exitCode=parseObject["exitCode"]
                ):
                    yield result
            elif parseObject["name"] in ["SSH"]:
                parseObject, rewrotedCommandList = rewriteSshRequest(
                    cls=self.cliHandler,
                    requestObject=parseObject,
                    commandScriptFile=commandScriptFile
                )
                if len(rewrotedCommandList) != 0:
                    # 如果命令被发生了改写，要打印改写记录
                    yield {
                        "type": "parse",
                        "rawCommand": None,
                        "formattedCommand": None,
                        "rewrotedCommand": rewrotedCommandList,
                        "script": commandScriptFile
                    }

                for result in executeSshRequest(
                        requestObject=parseObject,
                ):
                    yield result
            elif parseObject["name"] in ["JOB"]:
                for result in self.cliHandler.JobHandler.processRequest(
                        cls=self.cliHandler,
                        requestObject=parseObject,
                ):
                    yield result
            elif parseObject["name"] in ["COMPARE"]:
                for result in executeCompareRequest(
                        cls=self.cliHandler,
                        requestObject=parseObject,
                        commandScriptFile=commandScriptFile
                ):
                    yield result
            elif parseObject["name"] in ["DATA"]:
                for result in executeDataRequest(
                        cls=self.cliHandler,
                        requestObject=parseObject,
                ):
                    yield result
            elif parseObject["name"] in ["PARSE_ERROR"]:
                yield {"type": "error",
                       "message": "TestCli parse error:  " + str(parseObject["reason"])}
            elif parseObject["name"] in ["MONITOR"]:
                for result in executeMonitorRequest(
                        cls=self.cliHandler,
                        requestObject=parseObject,
                ):
                    if result["type"] == "result":
                        lastCommandResult["rows"] = result["rows"]
                        lastCommandResult["headers"] = result["headers"]
                        lastCommandResult["status"] = result["status"]
                        lastCommandResult["errorCode"] = 0
                    if result["type"] == "error":
                        lastCommandResult["rows"] = []
                        lastCommandResult["headers"] = []
                        lastCommandResult["status"] = result["message"]
                        lastCommandResult["errorCode"] = 1
                    yield result
            else:
                raise TestCliException("TestCli parse error:  unknown parseObject [" + str(parseObject) + "]")

            # 如果需要，打印语句执行时间
            endTime = time.time()
            lastCommandResult["elapsed"] = endTime - startTime

            # 记录命令的日志信息
            if self.testOptions.get("SILENT").upper() == 'OFF':
                if parseObject["name"] == "START":
                    commandStatus = ""
                    commandErrorCode = ""
                else:
                    if "status" in lastCommandResult:
                        commandStatus = lastCommandResult["status"]
                    else:
                        commandStatus = ""
                    if "errorCode" in lastCommandResult:
                        commandErrorCode = lastCommandResult["errorCode"]
                    else:
                        commandErrorCode = ""
                yield {
                    "type": "statistics",
                    "startedTime": startTime,
                    "elapsed": endTime - startTime,
                    "processName": self.workerName,
                    "rawCommand": ret_CommandSplitResultsWithComments[pos],
                    "commandType": parseObject["name"],
                    "command": json.dumps(obj=parseObject, sort_keys=True, ensure_ascii=False),
                    "commandStatus": commandStatus,
                    "errorCode": commandErrorCode,
                    "scenarioId": self.scenarioId,
                    "scenarioName": self.scenarioName
                }

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