entries批注

pull/18/head
Warmlight 2 months ago
parent 9b093e7aa4
commit bb93bced04

@ -5,10 +5,14 @@ Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
# 导入正则表达式模块
import re
# 从lib.core.agent模块导入agent对象用于处理注入过程中的各种细节
from lib.core.agent import agent
# 从lib.core.bigarray模块导入BigArray类用于高效存储和处理大量数据
from lib.core.bigarray import BigArray
# 从lib.core.common模块导入各种常用函数和常量如Backend、clearConsoleLine、getLimitRange等
from lib.core.common import Backend
from lib.core.common import clearConsoleLine
from lib.core.common import getLimitRange
@ -25,33 +29,47 @@ from lib.core.common import singleTimeLogMessage
from lib.core.common import singleTimeWarnMessage
from lib.core.common import unArrayizeValue
from lib.core.common import unsafeSQLIdentificatorNaming
# 从lib.core.convert模块导入getConsoleLength函数用于获取控制台字符串的长度
from lib.core.convert import getConsoleLength
from lib.core.convert import getUnicode
# 从lib.core.data模块导入conf和kb对象用于存储配置信息和知识库信息
from lib.core.data import conf
from lib.core.data import kb
# 从lib.core.data模块导入logger对象用于记录日志信息
from lib.core.data import logger
# 从lib.core.data模块导入queries对象用于存储各种数据库的查询语句
from lib.core.data import queries
# 从lib.core.dicts模块导入DUMP_REPLACEMENTS字典用于替换转储数据中的特殊字符
from lib.core.dicts import DUMP_REPLACEMENTS
# 从lib.core.enums模块导入各种枚举类型如CHARSET_TYPE、DBMS、EXPECTED、PAYLOAD等
from lib.core.enums import CHARSET_TYPE
from lib.core.enums import DBMS
from lib.core.enums import EXPECTED
from lib.core.enums import PAYLOAD
# 从lib.core.exception模块导入各种自定义异常类
from lib.core.exception import SqlmapConnectionException
from lib.core.exception import SqlmapMissingMandatoryOptionException
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedFeatureException
# 从lib.core.settings模块导入各种配置常量
from lib.core.settings import CHECK_ZERO_COLUMNS_THRESHOLD
from lib.core.settings import CURRENT_DB
from lib.core.settings import METADB_SUFFIX
from lib.core.settings import NULL
from lib.core.settings import PLUS_ONE_DBMSES
from lib.core.settings import UPPER_CASE_DBMSES
# 从lib.request模块导入inject函数用于执行SQL注入
from lib.request import inject
# 从lib.utils.hash模块导入attackDumpedTable函数用于对转储的表数据进行攻击
from lib.utils.hash import attackDumpedTable
# 从lib.utils.pivotdumptable模块导入pivotDumpTable函数用于执行透视转储表操作
from lib.utils.pivotdumptable import pivotDumpTable
# 导入第三方six库用于兼容Python 2和Python 3
from thirdparty import six
# 导入第三方six库的zip函数并重命名为_zip
from thirdparty.six.moves import zip as _zip
# 定义Entries类用于封装枚举数据库条目的相关功能
class Entries(object):
"""
This class defines entries' enumeration functionalities for plugins.
@ -60,9 +78,12 @@ class Entries(object):
def __init__(self):
pass
# 定义dumpTable方法用于转储指定表的条目
def dumpTable(self, foundData=None):
# 强制执行数据库枚举,确保已获取数据库类型
self.forceDbmsEnum()
# 如果没有指定数据库或指定为当前数据库,则获取当前数据库
if conf.db is None or conf.db == CURRENT_DB:
if conf.db is None:
warnMsg = "missing database parameter. sqlmap is going "
@ -71,65 +92,81 @@ class Entries(object):
logger.warning(warnMsg)
conf.db = self.getCurrentDb()
# 如果指定了数据库
elif conf.db is not None:
# 如果数据库是属于大写数据库类型,则将其转换为大写
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
conf.db = conf.db.upper()
# 如果数据库名包含逗号,则抛出异常,因为只允许一个数据库名
if ',' in conf.db:
errMsg = "only one database name is allowed when enumerating "
errMsg += "the tables' columns"
raise SqlmapMissingMandatoryOptionException(errMsg)
# 如果数据库名匹配排除模式,则跳过
if conf.exclude and re.search(conf.exclude, conf.db, re.I) is not None:
infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
singleTimeLogMessage(infoMsg)
return
# 对数据库名进行安全处理
conf.db = safeSQLIdentificatorNaming(conf.db) or ""
# 如果指定了表
if conf.tbl:
# 如果表名是属于大写数据库类型,则将其转换为大写
if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES:
conf.tbl = conf.tbl.upper()
# 将表名拆分为列表
tblList = conf.tbl.split(',')
# 如果没有指定表
else:
# 获取所有表
self.getTables()
# 如果已缓存表信息
if len(kb.data.cachedTables) > 0:
# 获取表列表
tblList = list(six.itervalues(kb.data.cachedTables))
# 如果表列表嵌套,则解包
if tblList and isListLike(tblList[0]):
tblList = tblList[0]
# 如果指定了数据库但未能获取表信息
elif conf.db and not conf.search:
errMsg = "unable to retrieve the tables "
errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
raise SqlmapNoneDataException(errMsg)
else:
return
# 对表名列表中的表名进行安全处理
for tbl in tblList:
tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True)
# 遍历表列表
for tbl in tblList:
# 如果检测到键盘中断,则跳出循环
if kb.dumpKeyboardInterrupt:
break
# 如果表名匹配排除模式,则跳过
if conf.exclude and re.search(conf.exclude, tbl, re.I) is not None:
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl)
singleTimeLogMessage(infoMsg)
continue
# 设置当前表名
conf.tbl = tbl
# 初始化已转储的表数据
kb.data.dumpedTable = {}
# 如果没有传入已发现的列数据
if foundData is None:
# 清空缓存的列信息
kb.data.cachedColumns = {}
# 获取列信息,仅获取列名,并设置转储模式
self.getColumns(onlyColNames=True, dumpMode=True)
# 如果传入了已发现的列数据,则直接使用
else:
kb.data.cachedColumns = foundData
try:
# 根据数据库类型设置转储表名
if Backend.isDbms(DBMS.INFORMIX):
kb.dumpTable = "%s:%s" % (conf.db, tbl)
elif Backend.isDbms(DBMS.SQLITE):
@ -139,6 +176,7 @@ class Entries(object):
else:
kb.dumpTable = "%s.%s" % (conf.db, tbl)
# 如果未能获取列信息,则跳过当前表
if safeSQLIdentificatorNaming(conf.db) not in kb.data.cachedColumns or safeSQLIdentificatorNaming(tbl, True) not in kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]:
warnMsg = "unable to enumerate the columns for table '%s'" % unsafeSQLIdentificatorNaming(tbl)
if METADB_SUFFIX.upper() not in conf.db.upper():
@ -148,12 +186,16 @@ class Entries(object):
continue
# 获取当前表的列信息
columns = kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]
# 对列名列表进行排序
colList = sorted(column for column in columns if column)
# 如果指定了排除模式,则从列名列表中排除匹配的列
if conf.exclude:
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
# 如果没有可用的列名,则跳过当前表
if not colList:
warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(tbl)
if METADB_SUFFIX.upper() not in conf.db.upper():
@ -161,9 +203,11 @@ class Entries(object):
warnMsg += " (no usable column names)"
logger.warning(warnMsg)
continue
# 设置全局变量 kb.dumpColumns 为当前表需要转储的列名列表
kb.dumpColumns = [unsafeSQLIdentificatorNaming(_) for _ in colList]
# 将列名列表转换为逗号分隔的字符串
colNames = colString = ','.join(column for column in colList)
# 获取转储表的根查询
rootQuery = queries[Backend.getIdentifiedDbms()].dump_table
infoMsg = "fetching entries"
@ -173,18 +217,22 @@ class Entries(object):
if METADB_SUFFIX.upper() not in conf.db.upper():
infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
# 遍历列名列表,对每个列名进行预处理
for column in colList:
_ = agent.preprocessField(tbl, column)
if _ != column:
colString = re.sub(r"\b%s\b" % re.escape(column), _.replace("\\", r"\\"), colString)
# 初始化条目计数
entriesCount = 0
# 如果存在可用的注入技术,或使用了--direct参数
if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct:
# 初始化条目列表
entries = []
query = None
# 根据数据库类型构建查询语句
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL):
query = rootQuery.inband.query % (colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())))
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA):
@ -247,9 +295,10 @@ class Entries(object):
query = rootQuery.inband.query % (colString, conf.db, tbl)
query = agent.whereQuery(query)
# 如果没有获取到条目,并且查询语句存在,并且没有检测到键盘中断
if not entries and query and not kb.dumpKeyboardInterrupt:
try:
# 执行查询语句,获取条目信息
entries = inject.getValue(query, blind=False, time=False, dump=True)
except KeyboardInterrupt:
entries = None
@ -257,7 +306,7 @@ class Entries(object):
clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg)
# 如果成功获取到条目
if not isNoneValue(entries):
if isinstance(entries, six.string_types):
entries = [entries]
@ -266,6 +315,7 @@ class Entries(object):
entriesCount = len(entries)
# 遍历每个列名和条目,更新转储的表数据信息
for index, column in enumerate(colList):
if column not in kb.data.dumpedTable:
kb.data.dumpedTable[column] = {"length": len(column), "values": BigArray()}
@ -273,19 +323,20 @@ class Entries(object):
for entry in entries:
if entry is None or len(entry) == 0:
continue
# 如果条目是字符串类型
if isinstance(entry, six.string_types):
colEntry = entry
# 否则,获取指定索引的条目
else:
colEntry = unArrayizeValue(entry[index]) if index < len(entry) else u''
maxLen = max(getConsoleLength(column), getConsoleLength(DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry))))
# 更新最大长度
if maxLen > kb.data.dumpedTable[column]["length"]:
kb.data.dumpedTable[column]["length"] = maxLen
# 添加条目值
kb.data.dumpedTable[column]["values"].append(colEntry)
# 如果没有转储表数据,并且可以使用盲注方式,且不是直接模式
if not kb.data.dumpedTable and isInferenceAvailable() and not conf.direct:
infoMsg = "fetching number of "
if conf.col:
@ -293,7 +344,8 @@ class Entries(object):
infoMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming(tbl)
infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming(conf.db)
logger.info(infoMsg)
# 构建盲注获取条目计数的查询语句
if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL):
query = rootQuery.blind.count % (tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())))
elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.MAXDB, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MCKOI, DBMS.EXTREMEDB, DBMS.RAIMA):
@ -307,21 +359,22 @@ class Entries(object):
query = agent.whereQuery(query)
# 执行盲注查询,获取条目计数
count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS)
lengths = {}
entries = {}
# 如果条目计数为0
if count == 0:
warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming(tbl)
warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming(conf.db)
warnMsg += "appears to be empty"
logger.warning(warnMsg)
# 初始化长度和条目
for column in colList:
lengths[column] = len(column)
entries[column] = []
# 如果未能获取条目计数
elif not isNumPosStrValue(count):
warnMsg = "unable to retrieve the number of "
if conf.col:
@ -331,7 +384,7 @@ class Entries(object):
logger.warning(warnMsg)
continue
# 对于特定数据库
elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX, DBMS.MCKOI, DBMS.RAIMA):
if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.RAIMA):
table = tbl
@ -339,7 +392,7 @@ class Entries(object):
table = "%s.%s" % (conf.db, tbl) if conf.db else tbl
elif Backend.isDbms(DBMS.INFORMIX):
table = "%s:%s" % (conf.db, tbl) if conf.db else tbl
# 如果是mssql并且没有强制透视
if Backend.isDbms(DBMS.MSSQL) and not conf.forcePivoting:
warnMsg = "in case of table dumping problems (e.g. column entry order) "
warnMsg += "you are advised to rerun with '--force-pivoting'"
@ -369,7 +422,7 @@ class Entries(object):
clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg)
# 如果没有获取到条目,且没有检测到键盘中断
if not entries and not kb.dumpKeyboardInterrupt:
try:
retVal = pivotDumpTable(table, colList, count, blind=True)
@ -382,12 +435,12 @@ class Entries(object):
if retVal:
entries, lengths = retVal
# 对于其他数据库
else:
emptyColumns = []
plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES
indexRange = getLimitRange(count, plusOne=plusOne)
# 如果列的数量小于行数,且大于阈值,则进行空列检查
if len(colList) < len(indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD:
debugMsg = "checking for empty columns"
logger.debug(infoMsg)
@ -398,7 +451,7 @@ class Entries(object):
debugMsg = "column '%s' of table '%s' will not be " % (column, kb.dumpTable)
debugMsg += "dumped as it appears to be empty"
logger.debug(debugMsg)
try:
for index in indexRange:
for column in colList:
@ -409,7 +462,7 @@ class Entries(object):
if column not in entries:
entries[column] = BigArray()
# 根据不同的数据库类型,构建盲注查询语句
if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE, DBMS.CLICKHOUSE):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index)
elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.DERBY, DBMS.ALTIBASE,):
@ -428,10 +481,10 @@ class Entries(object):
query = rootQuery.blind.query % (agent.preprocessField(tbl, column), conf.db, tbl, index)
query = agent.whereQuery(query)
# 执行盲注查询,获取值
value = NULL if column in emptyColumns else inject.getValue(query, union=False, error=False, dump=True)
value = '' if value is None else value
# 更新最大长度和条目值
lengths[column] = max(lengths[column], len(DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value))))
entries[column].append(value)
@ -440,14 +493,15 @@ class Entries(object):
clearConsoleLine()
warnMsg = "Ctrl+C detected in dumping phase"
logger.warning(warnMsg)
# 遍历获取到的条目将结果保存到kb.data.dumpedTable中
for column, columnEntries in entries.items():
length = max(lengths[column], len(column))
kb.data.dumpedTable[column] = {"length": length, "values": columnEntries}
# 获取总行数
entriesCount = len(columnEntries)
# 如果没有转储表数据或者条目数为0且有权限标识
if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag):
warnMsg = "unable to retrieve the entries "
if conf.col:
@ -456,15 +510,18 @@ class Entries(object):
warnMsg += "in database '%s'%s" % (unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "")
logger.warning(warnMsg)
else:
# 保存转储的信息,包括总行数、表名和数据库名
kb.data.dumpedTable["__infos__"] = {"count": entriesCount,
"table": safeSQLIdentificatorNaming(tbl, True),
"db": safeSQLIdentificatorNaming(conf.db)}
try:
# 对转储的数据进行攻击
attackDumpedTable()
except (IOError, OSError) as ex:
errMsg = "an error occurred while attacking "
errMsg += "table dump ('%s')" % getSafeExString(ex)
logger.critical(errMsg)
# 将转储的数据传递给dumper
conf.dumper.dbTableValues(kb.data.dumpedTable)
except SqlmapConnectionException as ex:
@ -473,40 +530,46 @@ class Entries(object):
logger.critical(errMsg)
finally:
# 清空全局变量
kb.dumpColumns = None
kb.dumpTable = None
# 定义dumpAll方法用于转储所有数据库中的所有表的所有条目
def dumpAll(self):
# 如果指定了数据库,但没有指定表,则只转储该数据库下的表
if conf.db is not None and conf.tbl is None:
self.dumpTable()
return
# 如果是MySQL数据库且没有information_schema
if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema:
errMsg = "information_schema not available, "
errMsg += "back-end DBMS is MySQL < 5.0"
raise SqlmapUnsupportedFeatureException(errMsg)
infoMsg = "sqlmap will dump entries of all tables from all databases now"
logger.info(infoMsg)
# 清空表和列的全局变量
conf.tbl = None
conf.col = None
# 获取所有表
self.getTables()
# 如果有缓存的表信息
if kb.data.cachedTables:
if isinstance(kb.data.cachedTables, list):
kb.data.cachedTables = {None: kb.data.cachedTables}
# 遍历数据库和表
for db, tables in kb.data.cachedTables.items():
conf.db = db
for table in tables:
if conf.exclude and re.search(conf.exclude, table, re.I) is not None:
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table)
logger.info(infoMsg)
continue
try:
conf.tbl = table
kb.data.cachedColumns = {}
@ -517,45 +580,57 @@ class Entries(object):
infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming(table)
logger.info(infoMsg)
# 定义dumpFoundColumn方法用于转储已发现的列
def dumpFoundColumn(self, dbs, foundCols, colConsider):
message = "do you want to dump found column(s) entries? [Y/n] "
# 询问用户是否要转储已发现的列
if not readInput(message, default='Y', boolean=True):
return
dumpFromDbs = []
message = "which database(s)?\n[a]ll (default)\n"
message = "which database(s)?\
[a]ll (default)\
"
# 构建数据库选项
for db, tblData in dbs.items():
if tblData:
message += "[%s]\n" % unsafeSQLIdentificatorNaming(db)
message += "[%s]\
" % unsafeSQLIdentificatorNaming(db)
message += "[q]uit"
# 接收用户选择
choice = readInput(message, default='a')
# 处理用户选择
if not choice or choice in ('a', 'A'):
dumpFromDbs = list(dbs.keys())
elif choice in ('q', 'Q'):
return
else:
dumpFromDbs = choice.replace(" ", "").split(',')
# 遍历数据库
for db, tblData in dbs.items():
if db not in dumpFromDbs or not tblData:
continue
conf.db = db
dumpFromTbls = []
message = "which table(s) of database '%s'?\n" % unsafeSQLIdentificatorNaming(db)
message += "[a]ll (default)\n"
message = "which table(s) of database '%s'?\
" % unsafeSQLIdentificatorNaming(db)
message += "[a]ll (default)\
"
# 构建表选项
for tbl in tblData:
message += "[%s]\n" % tbl
message += "[%s]\
" % tbl
message += "[s]kip\n"
message += "[s]kip\
"
message += "[q]uit"
# 接收用户选择
choice = readInput(message, default='a')
# 处理用户选择
if not choice or choice in ('a', 'A'):
dumpFromTbls = tblData
elif choice in ('s', 'S'):
@ -564,80 +639,120 @@ class Entries(object):
return
else:
dumpFromTbls = choice.replace(" ", "").split(',')
# 遍历表
for table, columns in tblData.items():
if table not in dumpFromTbls:
continue
conf.tbl = table
colList = [_ for _ in columns if _]
# 如果指定了排除模式,则排除匹配的列
if conf.exclude:
colList = [_ for _ in colList if re.search(conf.exclude, _, re.I) is None]
# 设置需要转储的列
conf.col = ','.join(colList)
kb.data.cachedColumns = {}
kb.data.dumpedTable = {}
data = self.dumpTable(dbs)
# 如果成功转储了数据则传递给dumper
if data:
conf.dumper.dbTableValues(data)
def dumpFoundTables(self, tables):
# 1. 定义一个消息,询问用户是否要转储发现的表条目
message = "do you want to dump found table(s) entries? [Y/n] "
# 2. 使用 readInput 函数获取用户输入,默认值为 'Y' (是),并将其转换为布尔值
# 如果用户输入 'n' (否) 或者其他非 'y' 的值,则返回 False否则返回 True。
# 如果用户输入为否,则直接返回,不进行后续的转储操作
if not readInput(message, default='Y', boolean=True):
return
# 3. 初始化一个空列表 dumpFromDbs用于存储用户选择要转储的数据库
dumpFromDbs = []
message = "which database(s)?\n[a]ll (default)\n"
# 4. 构建一个消息,用于提示用户选择要转储的数据库,其中 [a]ll (default) 表示默认选择全部数据库
message = "which database(s)?\
[a]ll (default)\
"
# 5. 遍历传入的 tables 字典,该字典的键是数据库名,值是该数据库下的表列表
for db, tablesList in tables.items():
# 6. 如果该数据库有表,则将数据库名添加到消息中,并进行安全 SQL 标识符命名处理
if tablesList:
message += "[%s]\n" % unsafeSQLIdentificatorNaming(db)
message += "[%s]\
" % unsafeSQLIdentificatorNaming(db)
# 7. 在消息中添加一个选项 [q]uit允许用户退出
message += "[q]uit"
# 8. 使用 readInput 函数获取用户输入,默认值为 'a',表示选择全部数据库
choice = readInput(message, default='a')
# 9. 如果用户没有输入或者输入为 'a' (或 'A'),则将所有数据库添加到 dumpFromDbs 列表中
if not choice or choice.lower() == 'a':
dumpFromDbs = list(tables.keys())
# 10. 如果用户输入为 'q' (或 'Q'),则直接返回,不进行后续的转储操作
elif choice.lower() == 'q':
return
# 11. 否则,将用户输入的数据库名按逗号分割,并添加到 dumpFromDbs 列表中
else:
dumpFromDbs = choice.replace(" ", "").split(',')
# 12. 遍历 tables 字典,键是数据库名,值是该数据库下的表列表
for db, tablesList in tables.items():
# 13. 如果当前数据库不在 dumpFromDbs 列表中,或者当前数据库没有表,则跳过该数据库
if db not in dumpFromDbs or not tablesList:
continue
# 14. 将当前数据库名赋值给 conf.db (全局配置参数)
conf.db = db
# 15. 初始化一个空列表 dumpFromTbls用于存储用户选择要转储的表
dumpFromTbls = []
message = "which table(s) of database '%s'?\n" % unsafeSQLIdentificatorNaming(db)
message += "[a]ll (default)\n"
# 16. 构建一个消息,用于提示用户选择当前数据库下要转储的表
message = "which table(s) of database '%s'?\
" % unsafeSQLIdentificatorNaming(db)
# 17. 在消息中添加一个选项 [a]ll (default),表示默认选择全部表
message += "[a]ll (default)\
"
# 18. 遍历当前数据库下的表列表,将表名添加到消息中,并进行安全 SQL 标识符命名处理
for tbl in tablesList:
message += "[%s]\n" % unsafeSQLIdentificatorNaming(tbl)
message += "[%s]\
" % unsafeSQLIdentificatorNaming(tbl)
message += "[s]kip\n"
# 19. 在消息中添加一个选项 [s]kip允许用户跳过当前数据库的表
message += "[s]kip\
"
# 20. 在消息中添加一个选项 [q]uit允许用户退出
message += "[q]uit"
# 21. 使用 readInput 函数获取用户输入,默认值为 'a',表示选择全部表
choice = readInput(message, default='a')
# 22. 如果用户没有输入或者输入为 'a' (或 'A'),则将所有表添加到 dumpFromTbls 列表中
if not choice or choice.lower() == 'a':
dumpFromTbls = tablesList
# 23. 如果用户输入为 's' (或 'S'),则跳过当前数据库的表,继续处理下一个数据库
elif choice.lower() == 's':
continue
# 24. 如果用户输入为 'q' (或 'Q'),则直接返回,不进行后续的转储操作
elif choice.lower() == 'q':
return
# 25. 否则,将用户输入的表名按逗号分割,并添加到 dumpFromTbls 列表中
else:
dumpFromTbls = choice.replace(" ", "").split(',')
# 26. 遍历 dumpFromTbls 列表,该列表存储当前数据库下要转储的表名
for table in dumpFromTbls:
# 27. 将当前表名赋值给 conf.tbl (全局配置参数)
conf.tbl = table
# 28. 清空 kb.data.cachedColumns (全局配置参数) 缓存的列信息
kb.data.cachedColumns = {}
# 29. 清空 kb.data.dumpedTable (全局配置参数) 缓存的表数据
kb.data.dumpedTable = {}
# 30. 调用 self.dumpTable() 函数,转储当前表的数据
data = self.dumpTable()
# 31. 如果转储成功 (data 不为空),则将转储的数据传递给 conf.dumper.dbTableValues() 函数进行后续处理
if data:
conf.dumper.dbTableValues(data)
conf.dumper.dbTableValues(data)
Loading…
Cancel
Save