# Easy Sqlite Toolkit
# Author Zhj<2022-12-06>

import typing
import fcntl
import os
import sqlite3
import time
import traceback
import re
import copy
import weakref
from functools import reduce
from contextlib import contextmanager

import public
from .gcmanager import gc_enable, gc_disable
from .tools import is_number
from .exceptions import HintException, PanelError


_BASE_DIR = '/www/server/panel'


# 记录错误日志
def _log(e):
    if isinstance(e, HintException):
        return

    # 日志目录
    log_dir = '{}/logs/sqlite_easy/{}'.format(_BASE_DIR, time.strftime('%Y%m'))

    # 确保目录已创建
    if not os.path.exists(log_dir):
        os.makedirs(log_dir, 0o600)

    # 记录错误日志
    with open('{}/{}.log'.format(log_dir, time.strftime('%d')), 'a') as fp:
        fp.write('[{}]{}\n'.format(time.strftime('%Y-%m-%d %X'), str(e)))

        # 如果是异常，打印异常堆栈信息
        if isinstance(e, BaseException):
            fp.write('{}\n'.format(traceback.format_exc()))


# 日志打印
def _log2(s, log_file='sqlite_client.log'):
    log_dir = '{}/logs'.format(_BASE_DIR)

    # 确保目录已创建
    if not os.path.exists(log_dir):
        os.makedirs(log_dir, 0o600)

    with open('{}/{}'.format(log_dir, log_file), 'a') as fp:
        fp.write('[{}]{}\n'.format(time.strftime('%Y-%m-%d %X'), s))


def _to_tuple(p):
    '''
        @name 将输入参数转为元组
        @author Zhj<2022-07-16>
        @param  p<mixed> 输入参数
        @return tuple
    '''
    # 输入参数为元组时 直接返回
    if isinstance(p, tuple):
        return p

    # 输入参数为列表时 转为元组
    if isinstance(p, list):
        return tuple(p)

    # 其他情况 直接创建一个元组 并将参数放入其中
    return (p,)


# 正则表达式列表
# 匹配SQL字段名称的正则表达式
match_field_reg = re.compile(r'^(?:`?(\w+)`?\.)?`?(\w+)`?$', flags=re.IGNORECASE)

# 匹配表名的正则表达式
match_table_name_reg = re.compile(r'^([\w`]+)(?:(?:\s+AS\s+|\s+)([\w`]+))?$', flags=re.IGNORECASE)

# 匹配【?】号的正则表达式
search_question_reg = re.compile(r'\?')

# 匹配查询字段名称的正则表达式
match_row_key_reg = re.compile(r'^(?:distinct\s+)?(?:[\w`]+\.)?([\w`]+)(?:(?:\s+AS\s+|\s+)([\w`]+))?$', flags=re.IGNORECASE)

# 匹配查询字段别名的正则表达式
search_row_key_reg = re.compile(r'(?:\s+AS\s+|\s+)([\w`]+)$', flags=re.IGNORECASE)

# 匹配SQL逻辑操作符【AND】【OR】的正则表达式
search_logic_opt_reg = re.compile(r'\s+(?:AND|OR)\s+', flags=re.IGNORECASE)

# 匹配以【1 AND 】开头的WHERE语句的正则表达式
match_where_str_begin_reg = re.compile(r'^1\s+(?:AND|OR)\s+', flags=re.IGNORECASE)

# 匹配引号【'】【"】的正则表达式
search_quote_reg = re.compile(r'(?<!\\)([\'"])')

# 匹配子查询的正则表达式
search_subquery_reg = re.compile(r'^(\([\s\S]+?\))(?:(?:\s+AS\s+|\s+)([\w`]+))?$', flags=re.IGNORECASE)


def _add_backtick_for_field(field):
    '''
        @name 给字段名添加反引号
        @author Zhj<2022-07-16>
        @param  field<string> 字段名
        @return string
    '''
    return match_field_reg.sub(lambda m: '{}{}'.format(
        '' if m.group(1) is None else '`%s`.' % m.group(1),
        '`%s`' % m.group(2)
    ), str(field).strip())


def _format_cursor(cursor):
    '''
        @name 将cursor对象转换为list对象
        @author Zhj<2023-04-25>
        @param  cursor<sqlite.Cursor> 游标对象
        @return list
    '''
    gc_disable()
    cols = tuple(map(lambda x: x[0], cursor.description))
    data = []
    for row in cursor:
        d = {}
        i = 0
        for col in cols:
            d[col] = row[i]
            i += 1
        data.append(d)
    cursor.close()
    gc_enable()
    return data


@contextmanager
def _auto_repair_context(db_path):
    '''
        @name 自动修复sqlite数据库的上下文环境
        @author Zhj<2023-03-15>
        @param  db_path<string> sqlite数据库绝对路径
        @return void
    '''

    # os.chdir(_BASE_DIR)
    # sys.path.insert(0, _BASE_DIR)
    #
    # import core.include.public as public
    # path = os.getcwd()
    # monitor_backpath = path + '/backupDB/monitor_mgr_bak.db'
    # safety_backpath = path + '/backupDB/safety_bak.db'
    try:
        yield
    except BaseException as e:
        if str(db_path).startswith(':memory:'):
            raise e

        if str(e) in ['database disk image is malformed', 'file is not a database']:
            # TODO 修复数据库
            # public.ExecShell('\cp -rf %s %s' % (monitor_backpath,db_path))
            # public.ExecShell('\cp -rf %s %s' % (safety_backpath,db_path))
            pass

        raise e


class Where:
    '''
        @name where条件收集类
        @author Zhj<2022-07-16>
    '''
    __slots__ = ['__WHERE_STR', '__BIND_PARAMS']

    def __init__(self):
        self.__WHERE_STR = '1'
        self.__BIND_PARAMS = ()

    # def __del__(self):
    #     self.clear()

    def add(self, condition, bind_params=(), logic='AND'):
        '''
            @name 添加where条件
            @author Zhj<2022-07-16>
            @param  condition<string>       where条件
            @param  bind_params<tuple|list> 绑定参数
            @param  logic<string>           逻辑运算符 AND|OR
            @return self
        '''
        self.__judge_the_logic(logic)

        # 当检测condition为字段名且存在参数绑定时，自动添加"=?"
        if match_field_reg.match(condition) and len(_to_tuple(bind_params)) > 0:
            condition = '{} = ?'.format(_add_backtick_for_field(condition))

        where_str = ' {} ({})' if search_logic_opt_reg.search(condition) else ' {} {}'
        self.__WHERE_STR += where_str.format(logic.upper(), str(condition))
        self.__BIND_PARAMS += _to_tuple(bind_params)
        return self

    def add_where_in(self, field, vals, logic='AND', not_in=False):
        '''
            @name 添加where IN查询条件
            @param  field<string>       字段名
            @param  vals<list|tuple>    查询条件
            @param  logic<string>       逻辑运算符 AND|OR
            @param  not_in<bool>        是否为NOT IN
            @return self
        '''
        self.__judge_the_logic(logic)

        if isinstance(vals, str) or isinstance(vals, int) or isinstance(vals, float):
            vals = [vals]

        # 空列表
        # (IN)构建where 0
        # (NOT IN)构建where 1
        if len(vals) == 0:
            self.__WHERE_STR += ' {} {}'.format(logic.upper(), 1 if not_in else 0)
            return self

        # 元组转列表
        if isinstance(vals, tuple):
            vals = list(vals)

        # 去重
        vals = list(set(vals))

        # 构造where条件
        tmp = []
        where_params = ()

        # 绑定参数过多时使用字符串拼接
        is_to_more_vals = len(vals) > 300

        if is_to_more_vals:
            for val in vals:
                if is_number(val):
                    tmp.append(str(val))
                    continue

                tmp.append("'{}'".format(search_quote_reg.sub(r'\\\1', str(val))))
        else:
            for val in vals:
                tmp.append('?')
                where_params += (val,)

        self.__WHERE_STR += ' {} {} {} ({})'.format(
            logic.upper(),
            _add_backtick_for_field(field),
            'IN' if not not_in else 'NOT IN',
            ','.join(tmp)
        )
        self.__BIND_PARAMS += where_params

        return self

    def add_nest(self, where_obj, logic='AND'):
        self.__judge_the_logic(logic)

        if not isinstance(where_obj, Where):
            raise RuntimeError('parameter where_obj must a type of Where')

        self.__WHERE_STR += ' {} ({})'.format(logic.upper(), where_obj.to_string())
        self.__BIND_PARAMS += where_obj.get_bind_params()

        return self

    def __judge_the_logic(self, logic):
        if logic.upper() not in ['AND', 'OR']:
            raise RuntimeError('parameter logic must be AND or OR')

    def to_string(self):
        where_str = match_where_str_begin_reg.sub('', self.__WHERE_STR)

        if len(where_str) == 0:
            return '1'

        return where_str

    def get_bind_params(self):
        return self.__BIND_PARAMS

    def build(self):
        '''
            @name 获取where条件表达式和绑定参数
            @author Zhj<2022-07-16>
            @return (where条件<string>, 绑定参数<tuple>)
        '''
        where_str = match_where_str_begin_reg.sub('', self.__WHERE_STR)

        if len(where_str) == 0:
            return '', ()

        return ' WHERE {}'.format(where_str), self.__BIND_PARAMS

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__WHERE_STR = '1'
        self.__BIND_PARAMS = ()

        return self


class Limit:
    '''
        @name limit条件收集类
        @author Zhj<2022-07-16>
    '''
    __slots__ = ['__LIMIT', '__SKIP']

    def __init__(self):
        self.__LIMIT = None
        self.__SKIP = None

    # def __del__(self):
    #     self.clear()

    def set_limit(self, limit):
        '''
            @name 设置limit
            @author Zhj<2022-07-16>
            @param  limit<integer>  查询行数
            @return self
        '''
        self.__LIMIT = int(limit)
        return self

    def set_skip(self, skip):
        '''
            @name 设置skip
            @author Zhj<2022-07-16>
            @param  skip<integer> 跳过的行数
            @return self
        '''
        self.__SKIP = int(skip)
        return self

    def build(self):
        '''
            @name 获取limit条件表达式
            @author Zhj<2022-17-16>
            @return string
        '''
        if self.__LIMIT is None:
            return ''

        if self.__SKIP is None:
            return ' LIMIT {}'.format(str(self.__LIMIT))

        return ' LIMIT {},{}'.format(str(self.__SKIP), str(self.__LIMIT))

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__LIMIT = None
        self.__SKIP = None

        return self


class Order:
    '''
        @name order排序条件收集类
        @author ZHj<2022-07-16>
    '''
    __slots__ = ['__ORDERS', '__BIND_PARAMS']

    def __init__(self):
        self.__ORDERS = []
        self.__BIND_PARAMS = ()

    # def __del__(self):
    #     self.clear()

    def add_order(self, field, ordering='ASC', params=()):
        '''
            @name 添加排序条件
            @author Zhj<2022-07-16>
            @param  field<string>       字段名
            @param  ordering<string>    排序方式 ASC|DESC
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__ORDERS.append('{} {}'.format(
            _add_backtick_for_field(field),
            ordering.upper()
        ))
        self.__BIND_PARAMS += _to_tuple(params)
        return self

    def build(self):
        '''
            @name 获取排序条件表达式和绑定参数
            @author Zhj<2022-07-16>
            @return (排序条件表达式<string>, 绑定参数<tuple>)
        '''
        if len(self.__ORDERS) == 0:
            return '', ()

        return ' ORDER BY {}'.format(', '.join(self.__ORDERS)), self.__BIND_PARAMS

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__ORDERS = []
        self.__BIND_PARAMS = ()
        return self


class Field:
    '''
        @name 查询字段收集类
        @author Zhj<2022-07-16>
    '''
    __slots__ = ['__FIELDS']

    def __init__(self):
        self.__FIELDS = []

    # def __del__(self):
    #     self.clear()

    def set_fields(self, *fields):
        '''
            @name 设置查询字段
            @author Zhj<2022-07-17>
            @param  fields<tuple>   查询字段列表
            @return self
        '''
        self.__FIELDS = list(
            map(lambda field: _add_backtick_for_field(field),
                filter(lambda x: x is not None, fields)
                )
        )
        return self

    def add_fields(self, *fields):
        '''
            @name 添加查询字段
            @author Zhj<2022-07-16>
            @param  fields<tuple>   查询字段列表
            @return self
        '''
        self.__FIELDS += list(
            map(lambda field: _add_backtick_for_field(field),
                filter(lambda x: x is not None, fields)
                )
        )
        return self

    def build(self):
        '''
            @name 获取查询字段列表
            @author Zhj<2022-07-16>
            @return string
        '''
        if len(self.__FIELDS) == 0:
            return '*'

        return ', '.join(list(set(self.__FIELDS)))

    def is_empty(self):
        '''
            @name 检查是否为空
            @author Zhj<2022-07-20>
            @return bool
        '''
        return len(self.__FIELDS) == 0

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__FIELDS = []
        return self


class Group:
    '''
        @name 分组条件收集类
        @author Zhj<2022-07-16>
    '''
    __slots__ = ['__GROUPS', '__BIND_PARAMS']

    def __init__(self):
        self.__GROUPS = []
        self.__BIND_PARAMS = ()

    # def __del__(self):
    #     self.clear()

    def add_group(self, condition, params=()):
        '''
            @name 添加分组条件
            @author Zhj<2022-07-16>
            @param  condition<string>   分组条件
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__GROUPS.append(str(condition).strip())
        self.__BIND_PARAMS += _to_tuple(params)
        return self

    def build(self):
        '''
            @name 获取分组条件表达式和绑定参数
            @author Zhj<2022-07-16>
            @return (分组条件表达式<string>, 绑定参数<tuple>)
        '''
        if len(self.__GROUPS) == 0:
            return '', ()

        return ' GROUP BY {}'.format(', '.join(self.__GROUPS)), self.__BIND_PARAMS

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__GROUPS = []
        self.__BIND_PARAMS = ()
        return self


class Having:
    '''
        @name 分组筛选条件收集类
        @author Zhj<2022-07-16>
    '''
    __slots__ = ['__HAVINGS', '__BIND_PARAMS']

    def __init__(self):
        self.__HAVINGS = []
        self.__BIND_PARAMS = ()

    # def __del__(self):
    #     self.clear()

    def add_having(self, condition, params=()):
        '''
            @name 添加分组筛选条件
            @author Zhj<2022-07-16>
            @param  condition<string>   分组筛选条件
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__HAVINGS.append(str(condition).strip())
        self.__BIND_PARAMS += _to_tuple(params)
        return self

    def build(self):
        '''
            @name 获取分组筛选条件表达式和绑定参数
            @author Zhj<2022-07-16>
            @return (分组筛选条件表达式<string>, 绑定参数<tuple>)
        '''
        if len(self.__HAVINGS) == 0:
            return '', ()

        return ' HAVING {}'.format(', '.join(self.__HAVINGS)), self.__BIND_PARAMS

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__HAVINGS = []
        self.__BIND_PARAMS = ()
        return self


class Join:
    '''
        @name 关联条件收集类
        @author Zhj<2022-07-16>
    '''
    __slots__ = ['__JOINS']

    def __init__(self):
        self.__JOINS = []

    # def __del__(self):
    #     self.clear()

    def add_join(self, expression, condition, join_type='INNER', table_prefix=''):
        '''
            @name 添加关联条件
            @author Zhj<2022-07-16>
            @param  expression<string>      表达式
            @param  condition<string>       关联条件
            @param  join_type<string>       关联方式 INNER|LEFT|RIGHT
            @param  table_prefix<string>    表前缀
            @return self
        '''
        m = match_table_name_reg.match(expression)

        if m:
            expression = '{}{}'.format(
                _add_backtick_for_field('{}{}'.format(table_prefix, m.group(1).strip('`'))),
                '' if m.group(2) is None else ' AS {}'.format(_add_backtick_for_field(m.group(2)))
            )

        self.__JOINS.append('{} JOIN {} ON {}'.format(
            join_type.upper(),
            str(expression).strip(),
            str(condition).strip()
        ))
        return self

    def build(self):
        '''
            @name 获取关联条件表达式
            @author Zhj<2022-07-16>
            @return string
        '''
        if len(self.__JOINS) == 0:
            return ''

        return ' ' + ' '.join(self.__JOINS)

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__JOINS = []
        return self


class Update:
    '''
        @name Update条件
        @author Zhj<2022-07-17>
    '''
    __slots__ = ['__UPDATES', '__BIND_PARAMS']

    def __init__(self):
        self.__UPDATES = []
        self.__BIND_PARAMS = ()

    # def __del__(self):
    #     self.clear()

    def add(self, field, value):
        '''
            @name 添加更新条件
            @author Zhj<2022-07-17>
            @param  field<string>           字段名
            @param  value<integer|string>   值
            @return self
        '''
        self.__UPDATES.append('{} = ?'.format(_add_backtick_for_field(field)))
        self.__BIND_PARAMS += _to_tuple(value)
        return self

    def increment(self, field, step=1):
        '''
            @name 自增
            @author Zhj<2022-07-17>
            @param  field<string>    字段名
            @param  step<integer>    值
            @return self
        '''
        self.__UPDATES.append('{field} = {field} + {step}'.format(
            field=_add_backtick_for_field(field),
            step=str(int(step))
        ))
        return self

    def decrement(self, field, step=1):
        '''
            @name 自减
            @author Zhj<2022-07-17>
            @param  field<string>    字段名
            @param  step<integer>    值
            @return self
        '''
        self.__UPDATES.append('{field} = {field} - {step}'.format(
            field=_add_backtick_for_field(field),
            step=str(int(step))
        ))
        return self

    def exp(self, field, exp):
        '''
            @name 添加原生表达式
            @author Zhj<2022-12-08>
            @param field<string>  字段名
            @param exp<string>    原生表达式
            @return:
        '''
        self.__UPDATES.append('{field} = {exp}'.format(
            field=_add_backtick_for_field(field),
            exp=str(exp)
        ))
        return self

    def build(self):
        '''
            @name 获取更新表达式和绑定参数
            @author Zhj<2022-07-17>
            @return self
        '''
        if self.is_empty():
            return '', ()

        return ', '.join(self.__UPDATES), self.__BIND_PARAMS

    def is_empty(self):
        '''
            @name 检查update条件是否为空
            @author Zhj<2022-07-17>
            @return bool
        '''
        return len(self.__UPDATES) == 0

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-07-17>
            @return self
        '''
        self.__UPDATES = []
        self.__BIND_PARAMS = ()
        return self


class Duplicate(Update):
    '''
        @name Duplicate条件
        @author Zhj<2024-12-19>
    '''
    def __init__(self):
        Update.__init__(self)

    def build(self):
        if self.is_empty():
            return '', ()

        raw_sql, binds = Update.build(self)

        return ' ON CONFLICT DO UPDATE SET ' + raw_sql, binds


class AlterTable:
    __slots__ = ['__weakref__', '__QUERY', '__COLUMNS', '__RENAME_TABLE', '__ALTERS']

    def __init__(self, query):
        if not isinstance(query, SqliteEasy):
            raise RuntimeError('参数query必须是一个SqliteEasy类型')
        self.__QUERY = weakref.proxy(query)
        self.__COLUMNS = []
        self.__RENAME_TABLE = None
        self.__ALTERS = []

    # def __del__(self):
    #     self.clear()
    #     self.__COLUMNS = []
    #     self.__ALTERS = []
    #     self.__QUERY = None

    def rename_table(self, new_table_name):
        '''
            @name 更新表名
            @author Zhj<2022-09-21>
            @param  new_table_name<string> 新表名
            @return self
        '''
        self.__RENAME_TABLE = ' RENAME TO {}'.format(_add_backtick_for_field(new_table_name))
        return self

    def rename_column(self, col_name, new_col_name):
        '''
            @name 更新字段名
            @author Zhj<2022-09-21>
            @param  col_name<string>        当前字段名
            @param  new_col_name<string>    新字段名
            @return self
        '''
        if self.__column_exists(col_name):
            self.__ALTERS.append(' RENAME COLUMN {} TO {}'.format(_add_backtick_for_field(col_name), _add_backtick_for_field(new_col_name)))
            self.__COLUMNS.remove(col_name)
            self.__COLUMNS.append(new_col_name)
        return self

    def add_column(self, col_name, prop, force=False):
        '''
            @name 新增字段
            @author Zhj<2022-09-21>
            @param  col_name<string> 字段名
            @param  prop<string>     字段属性
            @param  force<?bool>     是否强制新增(删除旧的字段)[可选]
            @return self
        '''
        if force:
            self.drop_column(col_name)

        if not self.__column_exists(col_name):
            self.__ALTERS.append(' ADD COLUMN {} {}'.format(_add_backtick_for_field(col_name), prop))
            self.__COLUMNS.append(col_name)

        return self

    def drop_column(self, col_name):
        '''
            @name 删除字段
            @author Zhj<2022-09-21>
            @param  col_name<string> 字段名
            @return self
        '''
        if self.__column_exists(col_name):
            self.__ALTERS.append(' DROP COLUMN {}'.format(_add_backtick_for_field(col_name)))
            self.__COLUMNS.remove(col_name)
        return self

    def __column_exists(self, col_name):
        '''
            @name 检查字段是否已经存在
            @author Zhj<2022-09-21>
            @param  col_name<string> 字段名
            @return bool
        '''
        if len(self.__COLUMNS) == 0:
            self.__COLUMNS = self.__QUERY.get_columns()

        return col_name in self.__COLUMNS

    def build(self, table_name):
        '''
            @name 构建语句
            @author Zhj<2022-09-21>
            @param  table_name<string> 表名
            @return string|None
        '''
        if self.is_empty():
            return None

        ret = "begin;\n"
        ret += "\n".join(list(map(lambda x: 'ALTER TABLE {}{};'.format(table_name, x), self.__ALTERS)))
        ret += "ALTER TABLE {}{};\n".format(table_name,
                                            self.__RENAME_TABLE) if self.__RENAME_TABLE is not None else "\n"
        ret += 'commit;'

        return ret

    def is_empty(self):
        '''
            @name 检查更新条件是否为空
            @author Zhj<2022-09-21>
            @return bool
        '''
        return self.__RENAME_TABLE is None and len(self.__ALTERS) == 0

    def clear(self):
        '''
            @name 清空收集数据
            @author Zhj<2022-09-21>
            @return self
        '''
        self.__ALTERS = []
        return self


class DbConnection:
    '''
        @name Sqlite数据库连接类(相比Db类更加底层)
        @author Zhj<2022-12-13>
    '''
    __slots__ = ['__DB_NAME', '__DB_PATH', '__DB_LOCK_FILE', '__CONN', '__DEBUG_LOG', 'ENGINE']

    def __init__(self, db_name, engine=None):
        '''
            @name 初始化函数
            @author Zhj<2022-12-14>
            @param db_name<string>  数据库名称(全路径 不包含.db)
            @return void
        '''

        self.ENGINE = sqlite3 if engine is None else engine
        self.__DB_NAME = db_name
        self.__DB_PATH = '{}.db'.format(db_name)

        if str(db_name).startswith(':memory:'):
           self.__DB_PATH = ':memory:'
        else:
            if os.path.exists(db_name):
                self.__DB_PATH = db_name

        # 数据库连接对象
        self.__CONN: typing.Optional[sqlite3.Connection] = None

        # 数据库并发文件锁
        self.__DB_LOCK_FILE = None
        if self.__DB_PATH != ':memory:':
            self.__DB_LOCK_FILE = '/tmp/aap_locks/{}.lock'.format(self.__DB_PATH.replace('/', '____'))
            dirname = os.path.dirname(self.__DB_LOCK_FILE)
            if not os.path.exists(dirname):
                os.makedirs(dirname, 0o775)
            if not os.path.exists(self.__DB_LOCK_FILE):
                with open(self.__DB_LOCK_FILE, 'ab'):
                    pass

        # 连接数据库
        self.connect()

    # # 析构函数
    # def __del__(self):
    #     try:
    #         # 关闭数据库连接
    #         self.close()
    #     except BaseException as e:
    #         _log(e)

    # 数据库读操作并发锁
    @contextmanager
    def __rlock(self):
        if self.__DB_LOCK_FILE is not None:
            with open(self.__DB_LOCK_FILE, 'rb') as fp:
                fcntl.flock(fp.fileno(), fcntl.LOCK_SH)
                yield
        else:
            yield

    # 数据库写操作并发锁
    @contextmanager
    def __wlock(self):
        if self.__DB_LOCK_FILE is not None:
            with open(self.__DB_LOCK_FILE, 'rb+') as fp:
                fcntl.flock(fp.fileno(), fcntl.LOCK_EX)
                yield
        else:
            yield

    # 获取sqlite连接对象
    def conn_obj(self) -> sqlite3.Connection:
        '''
            @name 获取sqlite数据库连接对象<sqlite3.Connection>
            @author Zhj<2022-12-22>
            @return sqlite3.Connection|None
        '''
        return self.__CONN

    # 连接sqlite
    def connect(self):
        if isinstance(self.__CONN, self.ENGINE.Connection):
            return self.__CONN

        # 连接数据库(写)
        self.__CONN = self.ENGINE.connect(self.__DB_PATH, timeout=15, check_same_thread=False)
        self.__CONN.text_factory = str
        self.__CONN.isolation_level = 'IMMEDIATE'

    # 关闭连接
    def close(self):
        '''
            @name 关闭sqlite连接
        '''
        # 关闭sqlite数据库连接
        if isinstance(self.__CONN, self.ENGINE.Connection):
            try:
                self.__CONN.close()
            except BaseException as e:
                _log(e)

    # 开启事务(sqlite自动开启，无需手动调用)
    def start_transaction(self):
        '''
            @name 开启事务
            @return bool
        '''
        return True

    # 提交事务
    def commit(self) -> bool:
        '''
            @name 提交事务
            @return bool
        '''
        if isinstance(self.__CONN, self.ENGINE.Connection) and self.__CONN.in_transaction:
            self.__CONN.commit()
            return True

        return False

    # 回滚事务
    def rollback(self) -> bool:
        '''
            @name 回滚事务
            @return bool
        '''
        if isinstance(self.__CONN, self.ENGINE.Connection) and self.__CONN.in_transaction:
            self.__CONN.rollback()
            return True

        return False

    # 重试辅助函数
    def __sqlite_retry_help(self, fn, *args, **kwargs):
        retries = 25  # 尝试最大次数
        retry_interval = 20  # 每次尝试间隔时间/ms

        while retries > 0:
            try:
                return fn(*args, **kwargs)
            except (
            SystemError, KeyError, self.ENGINE.InterfaceError, self.ENGINE.InternalError, self.ENGINE.OperationalError) as e:
                # 数据库操作错误，不是锁协议错误，直接抛出异常
                if isinstance(e, self.ENGINE.OperationalError) and str(e) not in ['locking protocol',
                                                                              'database is locked']:
                    raise e

                # 最后一次尝试失败后直接raise异常
                if retries == 1:
                    raise e

                # 打印异常信息
                _log(e)
                e = None
                del (e,)

                time.sleep(retry_interval * 0.001)
            finally:
                retries -= 1

    # 查询
    def query(self, sql, params=(), take_first=False):
        '''
            @name 执行查询SQL
            @param sql<string>          sql语句
            @param params<list|tuple>   绑定参数[可选]
            @param take_first<bool>     是否只获取一行数据[可选 默认获取所有行]
            @return list|dict|None
        '''
        # 读锁
        with self.__rlock():
            s_time = time.time()
            e_time = s_time

            try:
                ret = self.__sqlite_retry_help(self.__query_help, sql, params, take_first)
                e_time = time.time()
                return ret
            finally:
                if e_time - s_time > 1:
                    _log2('{}s {}'.format(round(e_time - s_time, 2),
                                          reduce(lambda x, y: search_question_reg.sub(
                                              str(y) if is_number(y) else "'%s'" % y, x, 1), params, sql)),
                          'sqlite_slow.log')

    # 查询(辅助函数)
    def __query_help(self, sql, params=(), take_first=False):
        with _auto_repair_context(self.__DB_PATH):
            try:
                # 执行SQL
                # 获取游标对象
                cur = self.__CONN.execute(sql, _to_tuple(params))
            except:
                # 打印sql语句与绑定参数
                _log('{}\nbindings: {}\ndb: {}'.format(sql, params, self.__DB_NAME))

                cur = None
                del (cur,)

                # 抛出异常
                raise

            try:
                s_time = time.time()

                ret = _format_cursor(cur)

                e_time = time.time()

                if e_time - s_time > 1:
                    _log2('{}s format_cursor'.format(round(e_time - s_time, 2)), 'sqlite_slow.log')

                del (cur,)

                # 只获取首行数据
                if take_first:
                    if len(ret) == 0:
                        return None
                    return ret[0]

                return list(ret)
            except:
                # 打印sql语句与绑定参数
                _log('{}\nbindings: {}\ndb: {}'.format(sql, params, self.__DB_NAME))

                cur = None
                del (cur,)

                raise

    # 执行单条SQL语句
    def execute(self, sql, params=(), get_rowid=False):
        '''
            @name 执行SQL
            @param sql<string>          sql语句
            @param params<tuple|list>   绑定参数
            @param get_rowid<bool>      获取插入ID
            @return int 影响行数或插入ID
        '''
        # 写锁
        with self.__wlock():
            return self.__sqlite_retry_help(self.__execute_help, sql, params, get_rowid)

    # 执行单条SQL语句(辅助函数)
    def __execute_help(self, sql, params=(), get_rowid=False):
        with _auto_repair_context(self.__DB_PATH):
            try:
                # 获取游标对象
                cur = self.__CONN.cursor()

                # 执行SQL
                cur.execute(sql, _to_tuple(params))
            except:
                # 打印sql语句与绑定参数
                _log('{}\nbindings: {}\ndb: {}'.format(sql, params, self.__DB_NAME))

                cur = None
                del (cur,)

                # 抛出异常
                raise

            try:
                # 获取新插入数据ID
                if get_rowid:
                    return cur.lastrowid

                # 返回受影响的行数
                return cur.rowcount
            finally:
                # 主动关闭Cursor
                cur.close()

                cur = None
                del (cur,)

    # 执行批量写入
    def execute_many(self, sql, params=()):
        '''
            @name 批量插入
            @param sql<string>          sql语句
            @param params<tuple|list>   绑定参数
            @return int 影响行数
        '''
        # 写锁
        with self.__wlock():
            return self.__sqlite_retry_help(self.__execute_many_help, sql, params)

    # 执行批量写入(辅助函数)
    def __execute_many_help(self, sql, params=()):
        with _auto_repair_context(self.__DB_PATH):
            try:
                # 获取游标对象
                cur = self.__CONN.cursor()

                # 执行SQL
                cur.executemany(sql, params)
            except:
                # 打印sql语句与绑定参数
                _log('{}\nbindings: {}\ndb: {}'.format(sql, params, self.__DB_NAME))

                cur = None
                del (cur,)

                # 抛出异常
                raise

            rowcount = cur.rowcount

            # 主动关闭Cursor
            cur.close()

            cur = None
            del (cur,)

            return rowcount

    # 执行多条SQL语句
    def execute_script(self, sql):
        '''
            @name 批量执行SQL
            @param sql<string> sql语句集合
            @return bool
        '''
        # 写锁
        with self.__wlock():
            return self.__sqlite_retry_help(self.__execute_script_help, sql)

    # 执行多条SQL语句(辅助函数)
    def __execute_script_help(self, sql):
        with _auto_repair_context(self.__DB_PATH):
            try:
                # 获取游标对象
                cur = self.__CONN.cursor()

                # 执行SQL
                cur.executescript(sql)
            except:
                # 打印sql语句与绑定参数
                _log('{}\ndb: {}'.format(sql, self.__DB_NAME))

                cur = None
                del (cur,)

                # 抛出异常
                raise

            # 主动关闭Cursor
            cur.close()

            cur = None
            del (cur,)

            return True

    # 执行vacuum整理数据库空间
    def vacuum(self):
        '''
            @name 执行vaccum整理数据库空间
            @author Zhj<2022-12-22>
            @return int
        '''
        # 写锁
        with self.__wlock():
            return self.execute('vacuum')

    # 备份数据库
    def backup(self, dest_conn) -> bool:
        '''
            @name 将当前数据库备份到目标数据库
            @author Zhj<2022-12-22>
            @param dest_conn<DbConnection> 目标数据库连接对象
            @return bool
        '''
        if dest_conn.conn_obj() is None:
            raise PanelError('dest_conn not connect to sqlite.')

        # 写锁
        with self.__wlock():
            with _auto_repair_context(self.__DB_PATH):
                # 开始备份(全量备份)
                self.__CONN.backup(dest_conn.conn_obj())

                return True

    # 导出数据库
    def dump(self, dest_file: str, row_check_func: typing.Optional[callable] = None) -> bool:
        """
            @name 导出数据库
            @author Zhj<2024-08-08>
            @param dest_file<str>               目标文件
            @param row_check_func<?callable>    语句行检查函数 row_check_func(row: str) -> bool
            @return bool
        """
        # 写锁
        with self.__wlock():
            with open(dest_file, 'w') as fp:
                for row in self.__CONN.iterdump():
                    # 当传入了语句行检查函数时，执行检查函数
                    if row_check_func is not None and not row_check_func(row):
                        continue

                    # 导出数据行
                    fp.write(row + '\n')

        return True


class Snapshot:
    __slots__ = ['pk', 'from_sub_query', 'table', 'prefix', 'alias', 'where', 'limit', 'order',
                 'field', 'group', 'having', 'join', 'update', 'duplicate']


class Db:
    '''
        @name Sqlite数据库连接类
        @author Zhj<2022-07-18>
    '''
    __slots__ = ['__DB_NAME', '__DB_CONN', '__AUTO_COMMIT', '__AUTO_VACUUM', '__NEED_VACUUM', '__DEBUG_LOG', '__QUERIES', 'ENGINE']

    __DB_ROOT_DIR = '{}/data/'.format(_BASE_DIR)

    def __init__(self, db_name, engine=None, auto_connect: bool = True):
        '''
            @name 初始化函数
            @author Zhj<2022-12-14>
            @param db_name<string>  数据库名称
            @param brandnew<bool>   是否开启一个全新连接
            @return void
        '''
        self.ENGINE = sqlite3 if engine is None else engine
        if str(db_name).startswith(':memory:'):
            self.__DB_NAME = db_name  # 内存数据库
        else:
            self.__DB_NAME = os.path.join(self.__DB_ROOT_DIR, re.sub(r'\.db$', '', db_name))  # 数据库名称(全路径 不带.db)
        self.__DB_CONN: typing.Optional[DbConnection] = None  # 数据库连接对象
        self.__AUTO_COMMIT = True  # 是否自动提交事务(默认自动提交)
        self.__AUTO_VACUUM = False  # 是否自动释放空间
        self.__NEED_VACUUM = False  # 事务提交后是否需要释放空间
        self.__DEBUG_LOG = False  # 是否开启调试日志
        self.__QUERIES = []  # 查询构造器数量

        if auto_connect:
            self._connect()

    def __del__(self):
        self.close()
        del (self.__DB_CONN,)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_trackback):
        self.close()

    def _connect(self):
        '''
            @name   连接Sqlite数据库
            @author Zhj<2022-07-16>
            @return self
        '''
        self.__DB_CONN = DbConnection(db_name=self.__DB_NAME, engine=self.ENGINE)
        return self

    def close(self):
        '''
            @name 将数据库连接返回连接池
            @author Zhj<2022-07-16>
            @return self
        '''
        try:
            # 释放数据库连接
            if self.__DB_CONN is None:
                return
            self.__DB_CONN.close()
            self.__DB_CONN = None
        except BaseException as e:
            _log(e)

    def client(self):
        '''
            @name 获取数据库连接对象
            @return SqliteClient
        '''
        return self.__DB_CONN

    def db_name(self):
        '''
            @name 获取数据库名称
            @author Zhj<2022-09-07>
            @return string
        '''
        return self.__DB_NAME

    def auto_vacuum(self, auto_vacuum=True):
        '''
            @name 设置是否自动释放空间
            @author Zhj<2022-09-27>
            @param  auto_vacuum<?bool> 是否自动释放空间
            @return self
        '''
        self.__AUTO_VACUUM = auto_vacuum
        return self

    def need_vacuum(self, need_vacuum=True):
        '''
            @name 设置是否需要清理空间
            @author Zhj<2022-09-02>
            @param need_vacuum<?bool> 是否需要清理空间
            @return self
        '''
        self.__NEED_VACUUM = need_vacuum
        return self

    def autocommit(self, autocommit=True):
        '''
            @name 设置自动提交事务状态
            @author Zhj<2022-07-17>
            @param  autocommit<bool>    是否自动提交事务
            @return self
        '''
        self.__AUTO_COMMIT = autocommit
        return self

    def is_autocommit(self):
        '''
            @name 是否自动提交事务
            @return bool
        '''
        return self.__AUTO_COMMIT

    def is_autovacuum(self):
        '''
            @name 是否自动释放空间
            @return bool
        '''
        return self.__AUTO_VACUUM

    def commit(self):
        '''
            @name 提交事务
            @author Zhj<2022-07-17>
            @return bool
        '''
        if self.__DB_CONN is None:
            return False

        # 记录commit语句执行开始时间
        s_time = time.time()

        # 提交事务
        ret = self.__DB_CONN.commit()

        # 写日志
        if self.is_debug():
            self.debug_log('commit', self.db_name(), time.time() - s_time)

        # 开启了自动释放空间时
        # 释放空间
        if self.__NEED_VACUUM and self.is_autovacuum():
            self.__NEED_VACUUM = False
            self.vacuum()

        return ret

    def rollback(self):
        '''
            @name 回滚事务
            @author Zhj<2022-07-17>
            @return bool
        '''
        if self.__DB_CONN is None:
            return False

        self.__NEED_VACUUM = False

        # 记录rollback语句执行开始时间
        s_time = time.time()

        ret = self.__DB_CONN.rollback()

        # 写日志
        if self.is_debug():
            self.debug_log('rollback', self.db_name(), time.time() - s_time)

        return ret

    def query(self):
        '''
            @name 获取查询构造器对象
            @author Zhj<2022-07-18>
            @return SqliteEasy
        '''
        self.__QUERIES.append(None)
        return SqliteEasy(self)

    def query_done(self):
        '''
            @name 标记查询构造器已关闭
            @author Zhj<2023-02-08>
            @return None
        '''
        del (self.__QUERIES[-1:],)

    def queries(self):
        '''
            @name 查看当前查询构造器数量
            @author Zhj<2023-02-08>
            @return int
        '''
        return len(self.__QUERIES)

    def vacuum(self):
        '''
            @name 释放空间
            @author Zhj<2022-12-22>
            @return int
        '''
        # 记录vacuum语句执行开始时间
        s_time = time.time()

        # 执行vacuum
        ret = self.__DB_CONN.vacuum()

        # 写日志
        if self.is_debug():
            self.debug_log('vacuum', self.db_name(), time.time() - s_time)

        return ret

    def backup(self, dest_db):
        '''
            @name 备份数据库(>=py3.7)
            @author Zhj<2022-12-22>
            @param  dest_db<Db>     目标数据库连接对象
            @return bool
        '''
        if not isinstance(dest_db, Db):
            raise PanelError('dest_db must a Db object.')

        # 记录执行备份开始时间
        s_time = time.time()

        # 开始备份
        ret = self.__DB_CONN.backup(dest_db.client())

        # 写日志
        if self.is_debug():
            self.debug_log('backup to {}'.format(dest_db.db_name()), self.db_name(), time.time() - s_time)

        return ret

    def dump(self, dest_file: str, row_check_func: typing.Optional[callable] = None) -> bool:
        '''
            @name 导出数据库(>=py3.7)
            @author Zhj<2024-08-08>
            @param dest_file<str>               目标文件
            @param row_check_func<?callable>    语句行检查函数 row_check_func(row: str) -> bool
            @return bool
        '''
        # 记录执行导出开始时间
        s_time = time.time()

        # 开始导出
        ret = self.__DB_CONN.dump(dest_file, row_check_func)

        # 写日志
        if self.is_debug():
            self.debug_log('dump to {}'.format(dest_file), self.db_name(), time.time() - s_time)

        return ret

    def debug(self, debug_state=True):
        '''
            @name 设置调试
            @param debug_state<bool> 调试状态
            @return self
        '''
        self.__DEBUG_LOG = debug_state
        return self

    def is_debug(self):
        '''
            @name 检查当前是否开启了调试日志
            @return bool
        '''
        return self.__DEBUG_LOG

    def debug_log(self, content, db_name, cost_time):
        '''
            @name 写查询日志
            @author Zhj<2022-09-27>
            @param  content<string>     sql语句
            @param  db_name<string>     数据库名称
            @param  cost_time<float>    执行耗时/s
            @return void
        '''
        # 获取当前日期时间
        cur_datetime = time.strftime('%Y-%m-%d %X')

        # sql查询日志目录
        log_dir = '{}/logs/sql_log/{}'.format(_BASE_DIR, time.strftime('%Y%m'))

        # 目录不存在时创建
        if not os.path.exists(log_dir):
            os.makedirs(log_dir, 0o600)

        with open('{}/{}.log'.format(log_dir, cur_datetime[8:10]), 'a') as fp:
            fp.write('[{}]{} {}ms {}\n'.format(cur_datetime, db_name, round(cost_time * 1000, 2), content))

    def synchronous_off(self):
        '''
            @name 关闭同步
            @author Zhj<2023-02-17>
            @return self
        '''
        self.__DB_CONN.execute_script('PRAGMA synchronous=0;')
        return self

    def synchronous_off_wal(self):
        '''
            @name 设置WAL日志模式并关闭同步
            @author Zhj<2023-02-17>
            @return self
        '''
        self.__DB_CONN.execute_script('PRAGMA journal_mode=wal;\nPRAGMA synchronous=0;\nPRAGMA temp_store=memory;\nPRAGMA mmap_size=30000000000;')
        return self

    def integrity_check(self):
        '''
            @name 检查数据库是否完整
            @author Zhj<2023-03-15>
            @return bool
        '''
        try:
            ret = self.__DB_CONN.query('PRAGMA integrity_check', take_first=True)
            return ret['integrity_check'] == 'ok'
        except:
            return False


class SqliteEasy:
    '''
        @name Sqlite查询类
        @author Zhj<2022-07-15>
    '''
    __slots__ = ['__weakref__', '__DB', '__DB_TABLE', '__FETCH_SQL', '__EXPLAIN', '__PK', '__OPT_PREFIX', '__OPT_ALIAS',
                 '__OPT_WHERE', '__OPT_LIMIT', '__OPT_ORDER', '__OPT_FIELD', '__OPT_GROUP', '__OPT_HAVING',
                 '__OPT_JOIN', '__OPT_UPDATE', '__OPT_DUPLICATE', '__OPT_ALTER_TABLE', '__FROM_SUB_QUERY',
                 '__CONFLICT_OPTIONS']

    def __init__(self, db: typing.Optional[Db] = None):
        self.__DB = None  # 数据库对象
        self.__DB_TABLE = None  # 表名(包含前缀)
        self.__FETCH_SQL = False  # 是否输出sql语句
        self.__EXPLAIN = False  # 分析sql语句
        self.__PK = None  # 主键字段名
        self.__OPT_PREFIX = 'bt_'  # 表前缀
        self.__OPT_ALIAS = None  # 表别名
        self.__OPT_WHERE = Where()  # where条件
        self.__OPT_LIMIT = Limit()  # limit条件
        self.__OPT_ORDER = Order()  # order条件
        self.__OPT_FIELD = Field()  # field条件
        self.__OPT_GROUP = Group()  # group条件
        self.__OPT_HAVING = Having()  # having条件
        self.__OPT_JOIN = Join()  # 联表条件
        self.__OPT_UPDATE = Update()  # update条件
        self.__OPT_DUPLICATE = Duplicate()  # Duplicate
        self.__OPT_ALTER_TABLE = AlterTable(self)  # 更新表结构条件
        self.__FROM_SUB_QUERY = False  # 是否通过子查询
        self.__CONFLICT_OPTIONS = (
            'ROLLBACK',     # 回滚
            'ABORT',        # 撤销
            'FAIL',         # 抛出失败异常
            'IGNORE',       # 忽略
            'REPLACE',      # 覆盖
        )  # 发生约束冲突时，额外的处理选项

        if db is not None:
            self.__DB = db

    def __del__(self):
        self.close()

        self.__OPT_WHERE = None
        self.__OPT_LIMIT = None
        self.__OPT_ORDER = None
        self.__OPT_FIELD = None
        self.__OPT_GROUP = None
        self.__OPT_HAVING = None
        self.__OPT_JOIN = None
        self.__OPT_UPDATE = None
        self.__OPT_ALTER_TABLE = None

        del (self.__OPT_WHERE,
             self.__OPT_LIMIT,
             self.__OPT_ORDER,
             self.__OPT_FIELD,
             self.__OPT_GROUP,
             self.__OPT_HAVING,
             self.__OPT_JOIN,
             self.__OPT_UPDATE,
             self.__OPT_ALTER_TABLE,)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.close()

    def set_db(self, db):
        '''
            @name 设置数据库对象
            @author Zhj<2022-07-18>
            @param  db<Db>  数据库对象
            @return self
        '''
        if not isinstance(db, Db):
            raise PanelError('db must a instance of core.include.sqlite_server.Db')

        self.__DB = db
        return self

    def get_db(self):
        '''
            @name 获取数据库对象
            @author Zhj<2022-07-18>
            @return Db|None
        '''
        return self.__DB

    def set_pk(self, pk):
        '''
            @name 设置主键字段名
            @author Zhj<2022-07-19>
            @param  pk<string> 主键字段名
            @return self
        '''
        self.__PK = pk
        return self

    def set_where_obj(self, where_obj):
        '''
            @name 设置Where对象
            @author Zhj<2022-07-19>
            @param  where_obj<Where> Where对象
            @return self
        '''
        if not isinstance(where_obj, Where):
            raise PanelError('where_obj must a instance of core.include.sqlite_server.Where')
        self.__OPT_WHERE = where_obj
        return self

    def get_where_obj(self):
        '''
            @name 获取Where对象
            @return Where
        '''
        return self.__OPT_WHERE

    def set_limit_obj(self, limit_obj):
        '''
            @name 设置Limit对象
            @author Zhj<2022-07-19>
            @param  limit_obj<Limit> Limit对象
            @return self
        '''
        if not isinstance(limit_obj, Limit):
            raise PanelError('limit_obj must a instance of core.include.sqlite_server.Limit')
        self.__OPT_LIMIT = limit_obj
        return self

    def set_order_obj(self, order_obj):
        '''
            @name 设置Order对象
            @author Zhj<2022-07-19>
            @param  order_obj<Order> Order对象
            @return self
        '''
        if not isinstance(order_obj, Order):
            raise PanelError('order_obj must a instance of core.include.sqlite_server.Order')
        self.__OPT_ORDER = order_obj
        return self

    def set_field_obj(self, field_obj):
        '''
            @name 设置Field对象
            @author Zhj<2022-07-19>
            @param  field_obj<Field> Field对象
            @return self
        '''
        if not isinstance(field_obj, Field):
            raise PanelError('field_obj must a instance of core.include.sqlite_server.Field')
        self.__OPT_FIELD = field_obj
        return self

    def set_group_obj(self, group_obj):
        '''
            @name 设置Group对象
            @author Zhj<2022-07-19>
            @param  group_obj<Group> Group对象
            @return self
        '''
        if not isinstance(group_obj, Group):
            raise PanelError('group_obj must a instance of core.include.sqlite_server.Group')
        self.__OPT_GROUP = group_obj
        return self

    def set_having_obj(self, having_obj):
        '''
            @name 设置Having对象
            @author Zhj<2022-07-19>
            @param  having_obj<Having> Having对象
            @return self
        '''
        if not isinstance(having_obj, Having):
            raise PanelError('having_obj must a instance of core.include.sqlite_server.Having')
        self.__OPT_HAVING = having_obj
        return self

    def set_join_obj(self, join_obj):
        '''
            @name 设置Join对象
            @author Zhj<2022-07-19>
            @param  join_obj<Join> Join对象
            @return self
        '''
        if not isinstance(join_obj, Join):
            raise PanelError('join_obj must a instance of core.include.sqlite_server.Join')
        self.__OPT_JOIN = join_obj
        return self

    def set_update_obj(self, update_obj):
        '''
            @name 设置Update对象
            @author Zhj<2022-07-19>
            @param  update_obj<Update> Update对象
            @return self
        '''
        if not isinstance(update_obj, Update):
            raise PanelError('update_obj must a instance of core.include.sqlite_server.Update')
        self.__OPT_UPDATE = update_obj
        return self

    def set_duplicate_obj(self, duplicate_obj):
        '''
            @name 设置Duplicated对象
            @author Zhj<2022-07-19>
            @param  duplicate_obj<Duplicate> Duplicate对象
            @return self
        '''
        if not isinstance(duplicate_obj, Duplicate):
            raise PanelError('duplicate_obj must a instance of core.include.sqlite_server.Duplicate')
        self.__OPT_DUPLICATE = duplicate_obj
        return self

    def set_alter_table_obj(self, alter_table_obj):
        '''
            @name 设置AlterTable对象
            @author Zhj<2022-07-19>
            @param  alter_table_obj<AlterTable> AlterTable对象
            @return self
        '''
        if not isinstance(alter_table_obj, AlterTable):
            raise PanelError('alter_table_obj must a instance of core.include.sqlite_server.AlterTable')
        self.__OPT_ALTER_TABLE = alter_table_obj
        return self

    def close(self):
        '''
            @name 关闭游标并将数据库连接返回连接池
            @author Zhj<2022-07-16>
            @return self
        '''
        try:
            self.__DB.query_done()
            self.__DB = None

            # 清理查询条件
            self.__clear()
        except:
            pass

    def autocommit(self, autocommit=True):
        '''
            @name 设置自动提交事务状态
            @author Zhj<2022-07-17>
            @param  autocommit<bool>    是否自动提交事务
            @return self
        '''
        if self.__DB is not None:
            self.__DB.autocommit(autocommit)

        return self

    def commit(self):
        '''
            @name 提交事务
            @author Zhj<2022-07-17>
            @return bool
        '''
        if self.__DB is None:
            return False

        # 提交事务
        return self.__DB.commit()

    def rollback(self):
        '''
            @name 回滚事务
            @author Zhj<2022-07-17>
            @return bool
        '''
        if self.__DB is None:
            return False

        # 回滚事务
        return self.__DB.rollback()

    def vacuum(self):
        """
            @name 释放空间
            @author Zhj<2023-11-13>
            @return bool
        """
        if self.__DB is None:
            return False

        return self.__DB.vacuum()

    def fetch_sql(self, fetch_sql=True):
        '''
            @name 设置是否输入sql原生语句
            @author Zhj<2022-07-18>
            @param  fetch_sql<bool> 是否输入sql原生语句
            @return self
        '''
        self.__FETCH_SQL = fetch_sql
        return self

    def explain(self, explain=True):
        '''
            @name 设置EXPLAIN
            @author Zhj<2022-07-20>
            @param  explain<bool>   是否开启EXPLAIN
            @return self
        '''
        self.__EXPLAIN = explain
        return self

    def prefix(self, prefix):
        '''
            @name 设置表前缀
            @author Zhj<2022-07-16>
            @param  prefix<string> 表前缀
            @return self
        '''
        self.__OPT_PREFIX = prefix
        return self

    def name(self, table_name):
        '''
            @name 设置表名(不包含前缀)
            @author Zhj<2022-07-16>
            @param  table_name<string>  表名(不包含前缀)
            @return self
        '''
        return self.table(table_name, False)

    def table(self, table_name, is_fullname=True):
        '''
            @name 设置表名(包含前缀)
            @param table_name<string>   表名
            @param is_fullname<bool>    是否完整表名(不包含前缀)
            @return self
        '''
        m = match_table_name_reg.match(table_name)

        if m is None or m.group(1) is None:
            raise PanelError('Invalid table name：{}'.format(table_name))

        self.__DB_TABLE = m.group(1).strip('`')

        # 添加前缀
        if not is_fullname and self.__OPT_PREFIX is not None:
            self.__DB_TABLE = self.__OPT_PREFIX + self.__DB_TABLE

        if m.group(2) is not None:
            self.__OPT_ALIAS = m.group(2).strip('`')

        # 重置主键字段名
        self.__PK = None

        return self

    # 使用子查询
    def from_sub_query(self, sub_query):
        # 当传入查询构造器对象时，将其转为SQL语句
        if isinstance(sub_query, SqliteEasy):
            sub_query = sub_query.build_sql(True)

        m = search_subquery_reg.search(str(sub_query))

        if m is None or m.group(1) is None:
            raise PanelError('Invalid sub-query：{}'.format(sub_query))

        self.__DB_TABLE = m.group(1)

        if m.group(2) is not None:
            self.__OPT_ALIAS = m.group(2).strip('`')

        # 重置主键字段名
        self.__PK = None

        self.__FROM_SUB_QUERY = True

        return self

    def alias(self, alias):
        '''
            @name 设置表别名
            @author Zhj<2022-07-16>
            @param  alias<string> 表别名
            @return self
        '''
        self.__OPT_ALIAS = alias
        return self

    def field(self, *fields):
        '''
            @name 添加查询字段
            @author Zhj<2022-07-15>
            @param  fields<tuple>   查询字段列表
            @return self
        '''
        self.__OPT_FIELD.add_fields(*fields)
        return self

    def join(self, exp, condition, join_type='INNER', add_table_prefix=True):
        '''
            @name 添加关联条件
            @author Zhj<2022-07-17>
            @param  exp<string>             表达式
            @param  condition<string>       关联条件
            @param  join_type<string>       关联方式 INNER|LEFT|RIGHT
            @param  add_table_prefix<bool>  是否添加表前缀[可选 默认自动添加]
            @return self
        '''
        table_prefix = ''

        if add_table_prefix and self.__OPT_PREFIX is not None:
            table_prefix = self.__OPT_PREFIX

        self.__OPT_JOIN.add_join(exp, condition, join_type, table_prefix)
        return self

    def inner_join(self, exp, condition, add_table_prefix=True):
        '''
            @name 添加内连接关联条件
            @param  exp<string>          表达式
            @param  condition<string>    关联条件
            @param  add_table_prefix<bool>  是否添加表前缀[可选 默认自动添加]
            @return self
        '''
        return self.join(exp, condition, 'INNER', add_table_prefix)

    def left_join(self, exp, condition, add_table_prefix=True):
        '''
            @name 添加左连接关联条件
            @author Zhj<2022-07-17>
            @param  exp<string>          表达式
            @param  condition<string>    关联条件
            @param  add_table_prefix<bool>  是否添加表前缀[可选 默认自动添加]
            @return self
        '''
        return self.join(exp, condition, 'LEFT', add_table_prefix)

    def right_join(self, exp, condition, add_table_prefix=True):
        '''
            @name 添加右连接关联条件
            @author Zhj<2022-07-17>
            @param  exp<string>          表达式
            @param  condition<string>    关联条件
            @param  add_table_prefix<bool>  是否添加表前缀[可选 默认自动添加]
            @return self
        '''
        return self.join(exp, condition, 'RIGHT', add_table_prefix)

    def where(self, condition, params=()):
        '''
            @name 添加where条件 AND
            @author Zhj<2022-07-16>
            @param  condition<string>   where条件
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__OPT_WHERE.add(condition, params)
        return self

    def where_or(self, condition, params=()):
        '''
            @name 添加where条件 OR
            @author Zhj<2022-07-16>
            @param  condition<string>   where条件
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__OPT_WHERE.add(condition, params, 'OR')
        return self

    def where_in(self, field, vals, logic='AND'):
        '''
            @name 添加where条件 IN
            @author Zhj<2022-07-16>
            @param  field<string>       字段名
            @param  vals<list|tuple>    查询参数列表
            @param  logic<string>       逻辑运算符 AND|OR
            @return self
        '''
        self.__OPT_WHERE.add_where_in(field, vals, logic)
        return self

    def where_not_in(self, field, vals, logic='AND'):
        '''
            @name 添加where条件 IN
            @author Zhj<2022-07-16>
            @param  field<string>       字段名
            @param  vals<list|tuple>    查询参数列表
            @param  logic<string>       逻辑运算符 AND|OR
            @return self
        '''
        self.__OPT_WHERE.add_where_in(field, vals, logic, True)
        return self

    # 嵌套where
    @contextmanager
    def where_nest(self, logic: str = 'and'):
        query = SqliteEasy(self.__DB)
        yield query
        self.__OPT_WHERE.add_nest(query.get_where_obj(), logic)

    def group(self, condition, params=()):
        '''
            @name 添加分组条件
            @author Zhj<2022-07-17>
            @param  condition<string>   分组条件
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__OPT_GROUP.add_group(condition, params)
        return self

    def having(self, condition, params=()):
        '''
            @name 添加分组筛选条件
            @author Zhj<2022-07-17>
            @param  condition<string>   分组筛选条件
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__OPT_HAVING.add_having(condition, params)
        return self

    def order(self, field, ordering='ASC', params=()):
        '''
            @name 添加排序条件
            @author Zhj<2022-07-17>
            @param  field<string>       字段名
            @param  ordering<string>    排序方式 ASC|DESC
            @param  params<list|tuple>  绑定参数
            @return self
        '''
        self.__OPT_ORDER.add_order(field, ordering, params)
        return self

    def limit(self, limit, skip=None):
        '''
            @name 设置返回行数
            @author Zhj<2022-07-16>
            @param  limit<integer>  返回的行数
            @param  skip<?integer>  跳过的行数
            @return self
        '''
        self.__OPT_LIMIT.set_limit(limit)

        if skip is not None:
            self.__OPT_LIMIT.set_skip(skip)

        return self

    def skip(self, skip):
        '''
            @name 设置跳过的行数
            @author Zhj<2022-07-16>
            @param  skip<integer> 跳过的行数
            @return self
        '''
        self.__OPT_LIMIT.set_skip(skip)
        return self

    def query(self, raw_sql, params=(), take_first=False, clear_conditions=True):
        '''
            @name 查询
            @author Zhj<2022-07-17>
            @param  raw_sql<string>         sql语句
            @param  params<list|tuple>      绑定参数[可选]
            @param  take_first<bool>        是否只获取一行数据[可选 默认获取所有行]
            @param  clear_conditions<?bool> 是否清空查询条件[可选 默认清空]
            @return list|dict|None
        '''
        if self.__DB is None:
            return None

        # 记录语句执行开始时间
        s_time = time.time()

        # 执行sql语句
        ret = self.__DB.client().query(raw_sql, _to_tuple(params), take_first)

        # 写日志
        if self.__DB.is_debug():
            self.__DB.debug_log(self.__to_raw_sql(raw_sql, _to_tuple(params)), self.__DB.db_name(),
                                time.time() - s_time)

        # 自动提交事务
        # if self.__is_autocommit():
        #     self.commit()

        # 清空查询条件
        if clear_conditions:
            self.__clear()

        return ret

    def execute(self, raw_sql, params=(), get_rowid=False, clear_conditions=True):
        '''
            @name 执行一条sql语句并返回影响的行数
            @author Zhj<2022-07-18>
            @param  raw_sql<string>         sql语句
            @param  params<list|tuple>      绑定参数[可选]
            @param  get_rowid<bool>         获取插入ID[可选 默认返回影响的行数]
            @param  clear_conditions<?bool> 是否清空查询条件[可选 默认清空]
            @return integer
        '''
        if self.__DB is None:
            return 0

        # 记录语句执行开始时间
        s_time = time.time()

        # 执行sql语句
        ret = self.__DB.client().execute(raw_sql, _to_tuple(params), get_rowid)

        # 写日志
        if self.__DB.is_debug():
            self.__DB.debug_log(self.__to_raw_sql(raw_sql, _to_tuple(params)), self.__DB.db_name(),
                                time.time() - s_time)

        # 自动提交事务
        if self.__is_autocommit():
            self.commit()

        # 清空查询条件
        if clear_conditions:
            self.__clear()

        return ret

    def execute_script(self, sql_script, clear_conditions=True):
        '''
            @name 执行多条sql语句(注意：执行这个方法会自动提交之前的事务，本次执行不会加入事务，事务请编写在sql脚本中)
            @author Zhj<2022-07-18>
            @param  sql_script<string>      sql语句
            @param  clear_conditions<?bool> 是否清空查询条件[可选 默认清空]
            @return bool
        '''
        if self.__DB is None:
            return False

        # 记录语句执行开始时间
        s_time = time.time()

        # 执行sql语句
        self.__DB.client().execute_script(sql_script)

        # 写日志
        if self.__DB.is_debug():
            self.__DB.debug_log(sql_script, self.__DB.db_name(), time.time() - s_time)

        # 清空查询条件
        if clear_conditions:
            self.__clear()

        return True

    def insert(self, data, option=None, get_rowid=True):
        '''
            @name 插入一条数据
            @author Zhj<2022-07-17>
            @param  data<dict|list>     插入数据
            @param  option<?string>     额外选项[可选]
            @param  get_rowid<bool>     获取插入ID[可选 默认返回影响的行数]
            @return integer|string
        '''
        if self.__DB_TABLE is None:
            raise PanelError('Insert failed: table name not provide.')

        # 传入dict类型之外的情况
        if not isinstance(data, dict):
            # list类型：尝试insert_all
            if isinstance(data, list):
                return self.insert_all(data, option=option)

            # 其它类型：抛出异常提示
            raise PanelError('Insert failed: parameter "data" type must be dict.')

        # 检查冲突选项是否正确
        if option is not None and str(option).upper() not in self.__CONFLICT_OPTIONS:
            raise PanelError('option must be one of: {}'.format(', '.join(self.__CONFLICT_OPTIONS)))

        placeholders = []
        ks = data.keys()
        params = ()

        for k in ks:
            placeholders.append('?')
            params += (data[k],)

        # build deplicate sql and bind params
        duplicate_sql, duplicate_binds = self.__OPT_DUPLICATE.build()

        params += duplicate_binds

        raw_sql = 'INSERT{} INTO {} ({}) VALUES ({}){}'.format(
            ' OR {}'.format(str(option).upper()) if option is not None else '',
            _add_backtick_for_field(self.__DB_TABLE),
            ', '.join(list(map(lambda x: _add_backtick_for_field(x), ks))),
            ','.join(placeholders),
            duplicate_sql
        )

        # 输出sql原生语句
        if self.__FETCH_SQL:
            return self.__to_raw_sql(raw_sql, params)

        # 输出SQL语句分析信息
        if self.__EXPLAIN:
            return self.explain_raw_sql(raw_sql, params)

        # 执行sql语句
        return self.execute(raw_sql, params, get_rowid)

    def insert_all(self, data_list, clear_conditions=True, option=None):
        '''
            @name 批量插入数据
            @author Zhj<2022-07-17>
            @param  data_list<list>         批量插入数据
            @param  clear_conditions<?bool> 是否清空查询条件[可选 默认清空]
            @param  option<?string>         额外选项[可选]
            @return integer
        '''
        if self.__DB is None or len(data_list) == 0:
            return 0

        # 检查冲突选项是否正确
        if option is not None and str(option).upper() not in self.__CONFLICT_OPTIONS:
            raise PanelError('option must be one of: {}'.format(', '.join(self.__CONFLICT_OPTIONS)))

        ks = data_list[0].keys()

        # build deplicate sql and bind params
        duplicate_sql, duplicate_binds = self.__OPT_DUPLICATE.build()

        # 生成sql语句
        raw_sql = 'INSERT{} INTO {} ({}) VALUES ({}){}'.format(
            ' OR {}'.format(str(option).upper()) if option is not None else '',
            _add_backtick_for_field(self.__DB_TABLE),
            ', '.join(list(map(lambda x: _add_backtick_for_field(x), ks))),
            ','.join(list(map(lambda x: '?', ks))),
            duplicate_sql
        )

        # 绑定参数
        params = list(map(lambda x: _to_tuple(list(map(lambda y: x[y], ks))), data_list))

        params += duplicate_binds

        # 记录语句执行开始时间
        s_time = time.time()

        # 执行sql语句
        ret = self.__DB.client().execute_many(raw_sql, params)

        # 写日志
        if self.__DB.is_debug():
            # 构造一个真实的批量写入sql
            debug_raw_sql = raw_sql
            i = len(params) - 1
            pad_str = ', ({})'.format(','.join(list(map(lambda x: '?', ks))))

            while i:
                debug_raw_sql += pad_str
                i -= 1

            # 将绑定参数扁平化
            debug_params = ()
            for p in params:
                debug_params += p

            self.__DB.debug_log(self.__to_raw_sql(debug_raw_sql, _to_tuple(debug_params)), self.__DB.db_name(),
                                time.time() - s_time)

        # 自动提交事务
        if self.__is_autocommit():
            self.commit()

        # 清空查询条件
        if clear_conditions:
            self.__clear()

        # 返回新增的行数
        return ret

    def increment(self, field, step=1):
        '''
            @name 自增数值
            @author Zhj<2022-07-17>
            @param  field<string>   字段名
            @param  step<integer>   值
            @return self
        '''
        self.__OPT_UPDATE.increment(field, step)
        return self

    def decrement(self, field, step=1):
        '''
            @name 自减数值
            @author Zhj<2022-07-17>
            @param  field<string>   字段名
            @param  step<integer>   值
            @return self
        '''
        self.__OPT_UPDATE.decrement(field, step)
        return self

    def exp(self, field, exp):
        '''
            @name 使用原生表达式更新
            @param field<string>    字段名
            @param exp<string>      原生表达式
            @return self
        '''
        self.__OPT_UPDATE.exp(field, exp)
        return self

    def update(self, data=None):
        '''
            @name 更新表数据
            @author Zhj<2022-07-17>
            @param  data<?dict>     更新数据
            @return integer|string
        '''
        if data is not None:
            # 更新数据不是字典类型 返回0
            if not isinstance(data, dict):
                return 0

            for k, v in data.items():
                self.__OPT_UPDATE.add(k, v)

        # 更新条件为空 返回0
        if self.__OPT_UPDATE.is_empty():
            return 0

        update_str, update_params = self.__OPT_UPDATE.build()
        join_str = self.__OPT_JOIN.build()
        where_str, where_params = self.__OPT_WHERE.build()
        limit_str = self.__OPT_LIMIT.build()

        raw_sql = 'UPDATE {table_name}{join_condition} SET {exprission}' \
                  '{where_condition}{limit_condition}'.format_map({
            'table_name': self.__build_table_name(True),
            'join_condition': join_str,
            'exprission': update_str,
            'where_condition': where_str,
            'limit_condition': limit_str,
        })
        bind_params = update_params + where_params

        # 输出原生sql语句
        if self.__FETCH_SQL:
            return self.__to_raw_sql(raw_sql, bind_params)

        # 输出SQL语句分析信息
        if self.__EXPLAIN:
            return self.explain_raw_sql(raw_sql, bind_params)

        # 执行sql语句
        return self.execute(raw_sql, bind_params)

    def delete(self):
        '''
            @name 删除表数据
            @author Zhj<2022-07-17>
            @return integer|string
        '''
        join_str = self.__OPT_JOIN.build()
        where_str, where_params = self.__OPT_WHERE.build()
        limit_str = self.__OPT_LIMIT.build()

        raw_sql = 'DELETE FROM {table_name}{join_condition}{where_condition}{limit_condition}'.format(
            table_name=self.__build_table_name(True),
            join_condition=join_str,
            where_condition=where_str,
            limit_condition=limit_str
        )

        # 输出原生sql语句
        if self.__FETCH_SQL:
            return self.__to_raw_sql(raw_sql, where_params)

        # 输出SQL语句分析信息
        if self.__EXPLAIN:
            return self.explain_raw_sql(raw_sql, where_params)

        # 执行sql语句
        ret = self.execute(raw_sql, where_params)

        # 自动提交事务时
        # 且开启了自动释放空间时
        # 释放空间
        if self.__is_autocommit() and self.__is_autovacuum():
            self.__DB.vacuum()
        # 否则
        # 记录本次事务提交后需要释放空间
        else:
            self.__DB.need_vacuum()

        return ret

    def find(self):
        '''
            @name 查询一行数据
            @author Zhj<2022-07-17>
            @return dict|None|string
        '''
        self.__OPT_LIMIT.set_limit(1)

        # 构建sql语句和绑定参数
        raw_sql, bind_params = self.__build_sql()

        # 输出原生sql语句
        if self.__FETCH_SQL:
            return self.__to_raw_sql(raw_sql, bind_params)

        # 输出SQL语句分析信息
        if self.__EXPLAIN:
            return self.explain_raw_sql(raw_sql, bind_params)

        return self.query(raw_sql, bind_params, True)

    def select(self):
        '''
            @name 查询多行数据
            @author Zhj<2022-07-17>
            @return list|None|string
        '''
        # 构建sql语句和绑定参数
        raw_sql, bind_params = self.__build_sql()

        # 输出原生sql语句
        if self.__FETCH_SQL:
            return self.__to_raw_sql(raw_sql, bind_params)

        # 输出SQL语句分析信息
        if self.__EXPLAIN:
            return self.explain_raw_sql(raw_sql, bind_params)

        return self.query(raw_sql, bind_params)

    def value(self, field):
        '''
            @name 获取某个字段的值
            @author Zhj<2022-07-17>
            @param  field<string>   字段名
            @return string|integer|None
        '''
        self.__OPT_FIELD.set_fields(field)
        ret = self.find()

        # 输出原生sql语句或SQL语句分析信息
        if self.__FETCH_SQL or self.__EXPLAIN:
            return ret

        if ret is None:
            return None

        return ret.get(self.__get_row_key(field), None)

    def column(self, field, dict_key=None):
        '''
            @name 获取指定字段的值列表或字典
            @author Zhj<2022-07-17>
            @param  field<string|None>   字段名
            @param  dict_key<?string>               字典键(字段名)
            @return list|dict|string
        '''
        if self.__OPT_FIELD.is_empty() and field is not None:
            self.__OPT_FIELD.set_fields(*filter(lambda x: x is not None, [field, dict_key]))

        ret = self.select()

        # 输出原生sql语句或SQL语句分析信息
        if self.__FETCH_SQL or self.__EXPLAIN:
            return ret

        if ret is None:
            # 指定了键时 返回字典
            if dict_key is not None:
                return {}

            return []

        # 返回列表
        if dict_key is None:
            return list(map(lambda x: x.get(self.__get_row_key(field), None), ret))

        # 返回字典
        d = {}
        f_k = None if field is None else self.__get_row_key(field)
        d_k = self.__get_row_key(dict_key)

        for item in ret:
            k = item.get(d_k, None)

            if k is None:
                continue

            d[k] = item if f_k is None else item.get(f_k, None)

        return d

    def count(self):
        '''
            @name 统计行数
            @author Zhj<2022-07-17>
            @return integer
        '''
        ret = self.value('COUNT(*)')

        # 输出原生sql语句或SQL语句分析信息
        if self.__FETCH_SQL or self.__EXPLAIN:
            return ret

        if ret is None:
            ret = 0

        return int(ret)

    def avg(self, field, precsicion=None):
        '''
            @name 统计平均值
            @author Zhj<2022-07-17>
            @param  field<string>       字段名
            @param  precision<?integer> 小数点精度
            @return float
        '''
        field = 'AVG({})'.format(_add_backtick_for_field(field))

        if precsicion is not None and is_number(precsicion):
            field = 'ROUND({},{})'.format(field, precsicion)

        ret = self.value(field)

        # 输出原生sql语句或SQL语句分析信息
        if self.__FETCH_SQL or self.__EXPLAIN:
            return ret

        if ret is None:
            ret = 0

        return float(ret)

    def sum(self, field, precsicion=None):
        '''
            @name 统计总和
            @author Zhj<2022-07-17>
            @param  field<string>       字段名
            @param  precision<?integer> 小数点精度
            @return integer
        '''
        field = 'SUM({})'.format(_add_backtick_for_field(field))

        if precsicion is not None and is_number(precsicion):
            field = 'ROUND({},{})'.format(field, precsicion)

        ret = self.value(field)

        # 输出原生sql语句或SQL语句分析信息
        if self.__FETCH_SQL or self.__EXPLAIN:
            return ret

        if ret is None:
            ret = 0

        return int(ret)

    def exists(self):
        '''
            @name 检查数据是否存在
            @author Zhj<2022-07-18>
            @return bool
        '''
        self.__OPT_LIMIT.set_limit(1)

        # 构建sql语句和绑定参数
        raw_sql, bind_params = self.__build_sql()

        k = 'bt__exists'

        raw_sql = 'SELECT EXISTS({}) AS `{}`'.format(raw_sql, k)

        # 输出原生sql语句
        if self.__FETCH_SQL:
            return self.__to_raw_sql(raw_sql, bind_params)

        # 输出SQL语句分析信息
        if self.__EXPLAIN:
            return self.explain_raw_sql(raw_sql, bind_params)

        # 执行sql语句
        ret = self.query(raw_sql, bind_params, True)

        if ret is None or not isinstance(ret, dict):
            return False

        return True if int(ret.get(k, 0)) == 1 else False

    # 设置insert时唯一索引重复时的更新操作
    def duplicate(self, update: typing.Dict[str, str]):
        self.__OPT_DUPLICATE.clear()
        for k, v in update.items():
            self.__OPT_DUPLICATE.exp(k, v)
        return self

    # TODO 强制索引
    def force_index(self, index_name: str):
        pass

    def build_sql(self, sub_query=False, alias=None):
        '''
            @name 构建sql查询语句(合并绑定参数)
            @author Zhj<2022-07-17>
            @param  sub_query<bool> 是否为子查询
            @return string
        '''
        raw_sql = self.__to_raw_sql(*self.__build_sql())

        # 子查询
        if sub_query:
            raw_sql = '(%s)' % raw_sql

            # 别名
            if alias is not None:
                raw_sql = '%s AS %s' % (raw_sql, _add_backtick_for_field(alias))

        return raw_sql

    def get_pk(self):
        '''
            @name 获取主键字段名
            @author Zhj<2022-07-18>
            @return string|None
        '''
        if self.__PK is not None:
            return self.__PK

        ret = self.query('PRAGMA TABLE_INFO({})'.format(self.__build_table_name(False)), take_first=True,
                         clear_conditions=False)

        if ret is None:
            return None

        self.__PK = ret.get('name', None)

        return self.__PK

    def get_columns(self):
        '''
            @name 获取所有列名
            @author Zhj<2022-09-21>
            @return list
        '''
        ret = self.query('PRAGMA TABLE_INFO({})'.format(self.__build_table_name(False)), clear_conditions=False)

        return [column['name'] for column in ret]

    def add_column(self, col_name, prop, force=False):
        '''
            @name 新建字段
            @author Zhj<2022-09-21>
            @param  col_name<string> 字段名
            @param  prop<string>     字段属性
            @param  force<?bool>     是否强制新增(删除旧的字段)[可选]
            @return self
        '''
        self.__OPT_ALTER_TABLE.add_column(col_name, prop, force)
        return self

    def drop_column(self, col_name):
        '''
            @name 删除字段
            @author Zhj<2022-09-21>
            @param  col_name<string> 字段名
            @return self
        '''
        self.__OPT_ALTER_TABLE.drop_column(col_name)
        return self

    def rename_column(self, col_name, new_col_name):
        '''
            @name 更新字段名
            @author Zhj<2022-09-21>
            @param  col_name<string>        当前字段名
            @param  new_col_name<string>    新字段名
            @return self
        '''
        self.__OPT_ALTER_TABLE.rename_column(col_name, new_col_name)
        return self

    def alter_table(self):
        '''
            @name 更新表结构
            @author Zhj<2022-09-21>
            @return bool
        '''
        if self.__OPT_ALTER_TABLE.is_empty():
            return False

        raw_sql = self.__OPT_ALTER_TABLE.build(self.__build_table_name(False))

        self.execute_script(raw_sql)

        return True

    def add_index(self, idx_name, idx_col):
        '''
            @name 创建索引
            @author Zhj<2022-10-11>
            @param  idx_name<string>        索引名称
            @param  idx_col<string|list>    字段名称
            @return self
        '''
        if not isinstance(idx_col, list):
            idx_col = [idx_col]

        self.execute('CREATE INDEX IF NOT EXISTS {} ON {} ({})'.format(
            _add_backtick_for_field(idx_name),
            self.__build_table_name(False),
            ','.join(list(map(lambda x: _add_backtick_for_field(x), idx_col)))
        ))

        return self

    def drop_index(self, idx_name):
        '''
            @name 删除索引
            @author Zhj<2022-10-11>
            @param  idx_name<string> 索引名称
            @return self
        '''
        self.execute('DROP INDEX IF EXISTS {} ON {}'.format(
            _add_backtick_for_field(idx_name),
            self.__build_table_name(False)
        ))

        return self

    def fork(self):
        '''
            @name 克隆一个查询构造器
            @author Zhj<2022-07-19>
            @return SqliteEasy|None
        '''
        if self.__DB is None:
            return None

        query = self.__DB.query()
        query.set_pk(self.__PK)
        if self.__FROM_SUB_QUERY:
            query.from_sub_query(self.__DB_TABLE)
        else:
            query.table(self.__DB_TABLE)
        query.prefix(self.__OPT_PREFIX)
        query.alias(self.__OPT_ALIAS)
        query.set_where_obj(copy.deepcopy(self.__OPT_WHERE))
        query.set_limit_obj(copy.deepcopy(self.__OPT_LIMIT))
        query.set_order_obj(copy.deepcopy(self.__OPT_ORDER))
        query.set_field_obj(copy.deepcopy(self.__OPT_FIELD))
        query.set_group_obj(copy.deepcopy(self.__OPT_GROUP))
        query.set_having_obj(copy.deepcopy(self.__OPT_HAVING))
        query.set_join_obj(copy.deepcopy(self.__OPT_JOIN))
        query.set_update_obj(copy.deepcopy(self.__OPT_UPDATE))
        query.set_duplicate_obj(copy.deepcopy(self.__OPT_DUPLICATE))

        return query

    def snapshot(self):
        '''
            @name 创建查询构造器快照
            @author Zhj<2025-01-07>
            @return SqliteEasy|None
        '''
        snapshot = Snapshot()
        snapshot.pk = self.__PK
        snapshot.from_sub_query = self.__FROM_SUB_QUERY
        snapshot.table = self.__DB_TABLE
        snapshot.prefix = self.__OPT_PREFIX
        snapshot.alias = self.__OPT_ALIAS
        snapshot.where = copy.deepcopy(self.__OPT_WHERE)
        snapshot.limit = copy.deepcopy(self.__OPT_LIMIT)
        snapshot.order = copy.deepcopy(self.__OPT_ORDER)
        snapshot.field = copy.deepcopy(self.__OPT_FIELD)
        snapshot.group = copy.deepcopy(self.__OPT_GROUP)
        snapshot.having = copy.deepcopy(self.__OPT_HAVING)
        snapshot.join = copy.deepcopy(self.__OPT_JOIN)
        snapshot.update = copy.deepcopy(self.__OPT_UPDATE)
        snapshot.duplicate = copy.deepcopy(self.__OPT_DUPLICATE)
        return snapshot

    def restore_from_snapshot(self, snapshot):
        '''
            @name 通过快照还原查询构造器
            @author Zhj<2025-01-07>
            @return self
        '''
        if not isinstance(snapshot, Snapshot):
            raise PanelError('snapshot must a instance of core.include.sqlite_server.Snapshot')
        self.set_pk(snapshot.pk)
        if snapshot.from_sub_query:
            self.from_sub_query(snapshot.table)
        else:
            self.table(snapshot.table)
        self.prefix(snapshot.prefix)
        self.alias(snapshot.alias)
        self.set_where_obj(copy.deepcopy(snapshot.where))
        self.set_limit_obj(copy.deepcopy(snapshot.limit))
        self.set_order_obj(copy.deepcopy(snapshot.order))
        self.set_field_obj(copy.deepcopy(snapshot.field))
        self.set_group_obj(copy.deepcopy(snapshot.group))
        self.set_having_obj(copy.deepcopy(snapshot.having))
        self.set_join_obj(copy.deepcopy(snapshot.join))
        self.set_update_obj(copy.deepcopy(snapshot.update))
        self.set_duplicate_obj(copy.deepcopy(snapshot.duplicate))
        # self.set_alter_table_obj(copy.deepcopy(snapshot.alter_table))
        return self

    def explain_raw_sql(self, raw_sql, bind_params=()):
        '''
            @name 分析SQL语句
            @author Zhj<2022-07-21>
            @param  raw_sql<string>         SQL语句
            @param  bind_params<list|tuple> 绑定参数
            @return string|None
        '''
        ret = self.query('EXPLAIN QUERY PLAN {}'.format(raw_sql), _to_tuple(bind_params))

        if ret is None:
            return None

        return "\n".join(list(map(lambda x: x.get('detail', ''), ret)))

    def __to_raw_sql(self, raw_sql, bind_params=()):
        '''
            @name 将sql语句和绑定参数合并
            @author Zhj<2022-07-18>
            @param  raw_sql<string>     sql语句(含有参数绑定占位符)
            @param  bind_params<tuple>  绑定参数
            @return string
        '''
        return reduce(lambda x, y: search_question_reg.sub(str(y) if is_number(y) else "'%s'" % y, x, 1), bind_params, repr(raw_sql)[1:-1])

    def __build_sql(self):
        '''
            @name 构建sql查询语句与绑定参数
            @author Zhj<2022-07-18>
            @return (sql语句<string>, 绑定参数<tuple>)
        '''
        fields = self.__OPT_FIELD.build()
        join_condition = self.__OPT_JOIN.build()
        where_condition, where_params = self.__OPT_WHERE.build()
        group_condition, group_params = self.__OPT_GROUP.build()
        having_condition, having_params = self.__OPT_HAVING.build()
        order_condition, order_params = self.__OPT_ORDER.build()
        limit_condition = self.__OPT_LIMIT.build()

        # 查询语句
        raw_sql = 'SELECT {fields} FROM {table_name}' \
                  '{join_condition}{where_condition}{group_condition}' \
                  '{order_condition}{having_condition}{limit_condition}'.format_map({
            'fields': fields,
            'table_name': self.__build_table_name(),
            'join_condition': join_condition,
            'where_condition': where_condition,
            'group_condition': group_condition,
            'having_condition': having_condition,
            'order_condition': order_condition,
            'limit_condition': limit_condition,
        })

        # 绑定参数
        bind_params = ()

        bind_params += where_params
        bind_params += group_params
        bind_params += having_params
        bind_params += order_params

        return raw_sql, bind_params

    def __build_table_name(self, contain_alias=True):
        '''
            @name 构建表名
            @author Zhj<2022-07-17>
            @param  contain_alias<bool> 是否包含别名
            @return string
        '''
        table_name = self.__DB_TABLE if self.__FROM_SUB_QUERY else _add_backtick_for_field(self.__DB_TABLE)
        table_alias = ''

        if contain_alias:
            table_alias = '' if self.__OPT_ALIAS is None else ' AS {}'.format(
                _add_backtick_for_field(self.__OPT_ALIAS)
            )

        return '{}{}'.format(table_name, table_alias)

    def __get_row_key(self, field):
        '''
            @name 获取字段名对应的字典键名
            @author Zhj<2022-07-18>
            @param  field<string>   字段名
            @return string|None
        '''
        # 移除字符串两段空白字符
        field = str(field).strip()

        m = match_row_key_reg.match(field)

        # 不符合字段规则
        # 返回None
        if m is None:
            # 检查有无设置别名
            m = search_row_key_reg.search(field)

            # 设置了别名
            if m:
                return m.group(1).strip('`')

            return field

        # 设置了别名时
        # 获取别名
        if m.group(2) is not None:
            return m.group(2).strip('`')

        # 获取字段名 不包含前缀表名
        return m.group(1).strip('`')

    def __is_autocommit(self):
        '''
            @name 检查是否自动提交事务
            @author Zhj<2022-07-18>
            @return bool
        '''
        if self.__DB is None:
            return False

        return self.__DB.is_autocommit()

    def __is_autovacuum(self):
        '''
            @name 检查是否自动释放空间
            @author Zhj<2022-09-27>
            @return bool
        '''
        if self.__DB is None:
            return False

        return self.__DB.is_autovacuum()

    def __clear(self):
        '''
            @name 清空查询条件
            @author Zhj<2022-07-17>
            @return void
        '''
        self.__PK = None
        self.__OPT_ALIAS = None
        self.__OPT_FIELD.clear()
        self.__OPT_JOIN.clear()
        self.__OPT_WHERE.clear()
        self.__OPT_GROUP.clear()
        self.__OPT_HAVING.clear()
        self.__OPT_ORDER.clear()
        self.__OPT_LIMIT.clear()
        self.__OPT_UPDATE.clear()
        self.__OPT_DUPLICATE.clear()
