|
@@ -532,7 +532,45 @@ def resource_impact_all_graph(resource_id, include_meta=True):
|
|
|
|
|
|
def clean_type(type_str):
|
|
|
"""清洗SQL类型字符串"""
|
|
|
- return re.sub(r'\(.*?\)', '', type_str).strip().upper()
|
|
|
+ # 提取基本类型,不包括长度或精度信息
|
|
|
+ basic_type = re.sub(r'\(.*?\)', '', type_str).strip().upper()
|
|
|
+
|
|
|
+ # 移除 VARYING 这样的后缀
|
|
|
+ basic_type = re.sub(r'\s+VARYING$', '', basic_type)
|
|
|
+
|
|
|
+ # 标准化常见类型
|
|
|
+ type_mapping = {
|
|
|
+ 'INT': 'INTEGER',
|
|
|
+ 'INT4': 'INTEGER',
|
|
|
+ 'INT8': 'BIGINT',
|
|
|
+ 'SMALLINT': 'SMALLINT',
|
|
|
+ 'BIGINT': 'BIGINT',
|
|
|
+ 'FLOAT4': 'FLOAT',
|
|
|
+ 'FLOAT8': 'DOUBLE',
|
|
|
+ 'REAL': 'FLOAT',
|
|
|
+ 'DOUBLE PRECISION': 'DOUBLE',
|
|
|
+ 'NUMERIC': 'DECIMAL',
|
|
|
+ 'BOOL': 'BOOLEAN',
|
|
|
+ 'CHARACTER': 'CHAR',
|
|
|
+ 'CHAR VARYING': 'VARCHAR',
|
|
|
+ 'CHARACTER VARYING': 'VARCHAR',
|
|
|
+ 'TEXT': 'TEXT',
|
|
|
+ 'DATE': 'DATE',
|
|
|
+ 'TIME': 'TIME',
|
|
|
+ 'TIMESTAMP': 'TIMESTAMP',
|
|
|
+ 'TIMESTAMPTZ': 'TIMESTAMP WITH TIME ZONE',
|
|
|
+ 'BYTEA': 'BINARY',
|
|
|
+ 'JSON': 'JSON',
|
|
|
+ 'JSONB': 'JSONB',
|
|
|
+ 'UUID': 'UUID',
|
|
|
+ 'SERIAL': 'SERIAL',
|
|
|
+ 'SERIAL4': 'SERIAL',
|
|
|
+ 'SERIAL8': 'BIGSERIAL',
|
|
|
+ 'BIGSERIAL': 'BIGSERIAL'
|
|
|
+ }
|
|
|
+
|
|
|
+ # 尝试从映射表中获取标准化的类型
|
|
|
+ return type_mapping.get(basic_type, basic_type)
|
|
|
|
|
|
def clean_field_name(field_name):
|
|
|
"""清洗字段名"""
|
|
@@ -540,119 +578,293 @@ def clean_field_name(field_name):
|
|
|
|
|
|
def select_create_ddl(sql_content):
|
|
|
"""从SQL内容中提取创建表的DDL语句"""
|
|
|
- create_pattern = r'CREATE\s+TABLE.*?;'
|
|
|
- matches = re.findall(create_pattern, sql_content, re.DOTALL | re.IGNORECASE)
|
|
|
- return matches
|
|
|
+ try:
|
|
|
+ # 解析复杂的SQL文件,识别所有的CREATE TABLE语句及其关联的注释
|
|
|
+ # 找到所有以CREATE TABLE开头的语句块,每个语句块包含主语句和相关的注释
|
|
|
+
|
|
|
+ # 首先,分割 SQL 内容按分号
|
|
|
+ statements = []
|
|
|
+ current_statement = ""
|
|
|
+ in_string = False
|
|
|
+ string_quote = None
|
|
|
+
|
|
|
+ for char in sql_content:
|
|
|
+ if char in ["'", '"']:
|
|
|
+ if not in_string:
|
|
|
+ in_string = True
|
|
|
+ string_quote = char
|
|
|
+ elif char == string_quote:
|
|
|
+ in_string = False
|
|
|
+ string_quote = None
|
|
|
+ current_statement += char
|
|
|
+ elif char == ';' and not in_string:
|
|
|
+ current_statement += char
|
|
|
+ if current_statement.strip():
|
|
|
+ statements.append(current_statement.strip())
|
|
|
+ current_statement = ""
|
|
|
+ else:
|
|
|
+ current_statement += char
|
|
|
+
|
|
|
+ if current_statement.strip():
|
|
|
+ statements.append(current_statement.strip())
|
|
|
+
|
|
|
+ # 找出所有的CREATE TABLE语句和关联的注释
|
|
|
+ create_table_statements = []
|
|
|
+ create_index = -1
|
|
|
+ in_table_block = False
|
|
|
+ current_table = None
|
|
|
+ current_block = ""
|
|
|
+
|
|
|
+ for i, stmt in enumerate(statements):
|
|
|
+ if re.search(r'^\s*CREATE\s+TABLE', stmt, re.IGNORECASE):
|
|
|
+ # 如果已经在处理表,先保存当前块
|
|
|
+ if in_table_block and current_block:
|
|
|
+ create_table_statements.append(current_block)
|
|
|
+
|
|
|
+ # 开始新的表块
|
|
|
+ in_table_block = True
|
|
|
+ current_block = stmt
|
|
|
+
|
|
|
+ # 提取表名
|
|
|
+ table_match = re.search(r'CREATE\s+TABLE\s+(?:(?:"[^"]+"|\'[^\']+\'|[^"\'\s\.]+)\.)?(?:"([^"]+)"|\'([^\']+)\'|([^"\'\s\(]+))', stmt, re.IGNORECASE)
|
|
|
+ if table_match:
|
|
|
+ current_table = table_match.group(1) or table_match.group(2) or table_match.group(3)
|
|
|
+ current_table = current_table.strip('"\'') if current_table else ""
|
|
|
+
|
|
|
+ elif in_table_block and (re.search(r'COMMENT\s+ON\s+TABLE', stmt, re.IGNORECASE) or
|
|
|
+ re.search(r'COMMENT\s+ON\s+COLUMN', stmt, re.IGNORECASE)):
|
|
|
+ # 检查注释是否属于当前表
|
|
|
+ if current_table:
|
|
|
+ # 表注释处理
|
|
|
+ if re.search(r'COMMENT\s+ON\s+TABLE', stmt, re.IGNORECASE):
|
|
|
+ table_comment_match = re.search(r'COMMENT\s+ON\s+TABLE\s+[\'"]?(\w+)[\'"]?', stmt, re.IGNORECASE)
|
|
|
+ if table_comment_match:
|
|
|
+ comment_table = table_comment_match.group(1).strip('"\'')
|
|
|
+ if comment_table == current_table:
|
|
|
+ current_block += " " + stmt
|
|
|
+ else:
|
|
|
+ # 这是另一个表的注释,当前表的DDL到此结束
|
|
|
+ create_table_statements.append(current_block)
|
|
|
+ in_table_block = False
|
|
|
+ current_block = ""
|
|
|
+ current_table = None
|
|
|
+ # 列注释处理
|
|
|
+ elif re.search(r'COMMENT\s+ON\s+COLUMN', stmt, re.IGNORECASE):
|
|
|
+ column_comment_match = re.search(r'COMMENT\s+ON\s+COLUMN\s+[\'"]?(\w+)[\'"]?\.', stmt, re.IGNORECASE)
|
|
|
+ if column_comment_match:
|
|
|
+ comment_table = column_comment_match.group(1).strip('"\'')
|
|
|
+ if comment_table == current_table:
|
|
|
+ current_block += " " + stmt
|
|
|
+ else:
|
|
|
+ # 这是另一个表的注释,当前表的DDL到此结束
|
|
|
+ create_table_statements.append(current_block)
|
|
|
+ in_table_block = False
|
|
|
+ current_block = ""
|
|
|
+ current_table = None
|
|
|
+
|
|
|
+ elif in_table_block and re.search(r'^\s*CREATE\s+', stmt, re.IGNORECASE):
|
|
|
+ # 如果遇到新的CREATE语句(不是注释),保存当前块并结束
|
|
|
+ create_table_statements.append(current_block)
|
|
|
+ in_table_block = False
|
|
|
+ current_block = ""
|
|
|
+ current_table = None
|
|
|
+
|
|
|
+ # 添加最后一个块
|
|
|
+ if in_table_block and current_block:
|
|
|
+ create_table_statements.append(current_block)
|
|
|
+
|
|
|
+ # 日志记录
|
|
|
+ logger.debug(f"提取到 {len(create_table_statements)} 个DDL语句")
|
|
|
+ for i, stmt in enumerate(create_table_statements):
|
|
|
+ logger.debug(f"DDL语句 {i+1}: {stmt}")
|
|
|
+
|
|
|
+ return create_table_statements
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"提取DDL语句失败: {str(e)}")
|
|
|
+ logger.error(traceback.format_exc())
|
|
|
+ return []
|
|
|
|
|
|
def table_sql(sql):
|
|
|
- """解析表定义SQL"""
|
|
|
+ """解析表定义SQL,支持带schema和不带schema两种格式"""
|
|
|
try:
|
|
|
- # 提取表名
|
|
|
- table_name_pattern = r'CREATE\s+TABLE\s+(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+))'
|
|
|
- table_name_match = re.search(table_name_pattern, sql, re.IGNORECASE)
|
|
|
+ # 支持以下格式:
|
|
|
+ # 1. CREATE TABLE tablename
|
|
|
+ # 2. CREATE TABLE "tablename"
|
|
|
+ # 3. CREATE TABLE 'tablename'
|
|
|
+ # 4. CREATE TABLE schema.tablename
|
|
|
+ # 5. CREATE TABLE "schema"."tablename"
|
|
|
+ # 6. CREATE TABLE 'schema'.'tablename'
|
|
|
+ # 匹配表名,支持带引号和不带引号的情况
|
|
|
+ table_pattern = r'CREATE\s+TABLE\s+(?:(?:"([^"]+)"|\'([^\']+)\'|([^"\'\s\.]+))\.)?(?:"([^"]+)"|\'([^\']+)\'|([^"\'\s\(]+))'
|
|
|
+ table_match = re.search(table_pattern, sql, re.IGNORECASE)
|
|
|
|
|
|
- if not table_name_match:
|
|
|
+ if not table_match:
|
|
|
+ logger.error(f"无法匹配CREATE TABLE语句: {sql[:100]}...")
|
|
|
return None
|
|
|
|
|
|
- # 获取匹配的表名(从四个捕获组中选择非None的一个)
|
|
|
- table_name = next((g for g in table_name_match.groups() if g is not None), "")
|
|
|
+ # 获取表名
|
|
|
+ schema = table_match.group(1) or table_match.group(2) or table_match.group(3)
|
|
|
+ table_name = table_match.group(4) or table_match.group(5) or table_match.group(6)
|
|
|
+ if not table_name:
|
|
|
+ logger.error("无法解析表名")
|
|
|
+ return None
|
|
|
+
|
|
|
+ logger.debug(f"解析到表名: {table_name}")
|
|
|
|
|
|
- # 提取字段定义
|
|
|
- fields_pattern = r'CREATE\s+TABLE[^(]*\(\s*(.*?)\s*\)'
|
|
|
- fields_match = re.search(fields_pattern, sql, re.DOTALL | re.IGNORECASE)
|
|
|
+ # 提取CREATE TABLE语句的主体部分(括号内的内容)
|
|
|
+ body_pattern = r'CREATE\s+TABLE\s+[^(]*\((.*?)\)(?=\s*;|\s*$)'
|
|
|
+ body_match = re.search(body_pattern, sql, re.DOTALL | re.IGNORECASE)
|
|
|
|
|
|
- if not fields_match:
|
|
|
+ if not body_match:
|
|
|
+ logger.error("无法提取表主体内容")
|
|
|
return None
|
|
|
|
|
|
- fields_text = fields_match.group(1)
|
|
|
+ body_text = body_match.group(1).strip()
|
|
|
+ logger.debug(f"表定义主体部分: {body_text}")
|
|
|
|
|
|
- # 分割字段定义
|
|
|
- field_definitions = []
|
|
|
-
|
|
|
- # 处理字段定义,避免在逗号内的括号中分割
|
|
|
- in_parenthesis = 0
|
|
|
- current_field = ""
|
|
|
+ # 解析字段定义
|
|
|
+ fields = []
|
|
|
+ # 分割字段定义,处理括号嵌套和引号
|
|
|
+ field_defs = []
|
|
|
+ pos = 0
|
|
|
+ in_parentheses = 0
|
|
|
+ in_quotes = False
|
|
|
+ quote_char = None
|
|
|
|
|
|
- for char in fields_text:
|
|
|
- if char == '(':
|
|
|
- in_parenthesis += 1
|
|
|
- current_field += char
|
|
|
- elif char == ')':
|
|
|
- in_parenthesis -= 1
|
|
|
- current_field += char
|
|
|
- elif char == ',' and in_parenthesis == 0:
|
|
|
- field_definitions.append(current_field.strip())
|
|
|
- current_field = ""
|
|
|
- else:
|
|
|
- current_field += char
|
|
|
+ for i, char in enumerate(body_text):
|
|
|
+ if char in ["'", '"', '`'] and (not in_quotes or char == quote_char):
|
|
|
+ in_quotes = not in_quotes
|
|
|
+ if in_quotes:
|
|
|
+ quote_char = char
|
|
|
+ else:
|
|
|
+ quote_char = None
|
|
|
+ elif char == '(' and not in_quotes:
|
|
|
+ in_parentheses += 1
|
|
|
+ elif char == ')' and not in_quotes:
|
|
|
+ in_parentheses -= 1
|
|
|
+ elif char == ',' and in_parentheses == 0 and not in_quotes:
|
|
|
+ field_defs.append(body_text[pos:i].strip())
|
|
|
+ pos = i + 1
|
|
|
|
|
|
- if current_field.strip():
|
|
|
- field_definitions.append(current_field.strip())
|
|
|
-
|
|
|
- # 解析每个字段
|
|
|
- fields = []
|
|
|
- primary_keys = []
|
|
|
+ # 添加最后一个字段定义
|
|
|
+ if pos < len(body_text):
|
|
|
+ field_defs.append(body_text[pos:].strip())
|
|
|
+
|
|
|
+ logger.debug(f"解析出 {len(field_defs)} 个字段定义")
|
|
|
|
|
|
- for field_def in field_definitions:
|
|
|
- # 忽略PRIMARY KEY等约束定义
|
|
|
+ # 处理每个字段定义
|
|
|
+ for field_def in field_defs:
|
|
|
+ # 跳过约束定义
|
|
|
if re.match(r'^\s*(?:PRIMARY|UNIQUE|FOREIGN|CHECK|CONSTRAINT)\s+', field_def, re.IGNORECASE):
|
|
|
- # 提取主键字段
|
|
|
- pk_pattern = r'PRIMARY\s+KEY\s*\(\s*(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+))\s*\)'
|
|
|
- pk_match = re.search(pk_pattern, field_def, re.IGNORECASE)
|
|
|
-
|
|
|
- if pk_match:
|
|
|
- pk = next((g for g in pk_match.groups() if g is not None), "")
|
|
|
- primary_keys.append(pk)
|
|
|
continue
|
|
|
|
|
|
- # 解析常规字段定义
|
|
|
- field_pattern = r'^\s*(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+))\s+([A-Za-z0-9_]+(?:\s*\([^)]*\))?)'
|
|
|
- field_match = re.search(field_pattern, field_def)
|
|
|
+ # 提取字段名和类型
|
|
|
+ field_pattern = r'^\s*(?:"([^"]+)"|\'([^\']+)\'|`([^`]+)`|([a-zA-Z0-9_]+))\s+(.+?)(?:\s+DEFAULT\s+|\s+NOT\s+NULL|\s+REFERENCES|\s*$)'
|
|
|
+ field_match = re.search(field_pattern, field_def, re.IGNORECASE)
|
|
|
|
|
|
if field_match:
|
|
|
- # 提取字段名和类型
|
|
|
- field_name = next((g for g in field_match.groups()[:4] if g is not None), "")
|
|
|
- field_type = field_match.group(5)
|
|
|
-
|
|
|
- # 检查是否为主键
|
|
|
- is_primary = "PRIMARY KEY" in field_def.upper()
|
|
|
- if is_primary:
|
|
|
- primary_keys.append(field_name)
|
|
|
-
|
|
|
- # 检查是否为非空
|
|
|
- not_null = "NOT NULL" in field_def.upper()
|
|
|
-
|
|
|
- # 检查默认值
|
|
|
- default_match = re.search(r'DEFAULT\s+([^,\s]+)', field_def, re.IGNORECASE)
|
|
|
- default_value = default_match.group(1) if default_match else None
|
|
|
-
|
|
|
- # 添加字段信息
|
|
|
- field_info = {
|
|
|
- "name": field_name,
|
|
|
- "type": clean_type(field_type),
|
|
|
- "is_primary": is_primary,
|
|
|
- "not_null": not_null
|
|
|
- }
|
|
|
+ # 提取字段名
|
|
|
+ field_name = field_match.group(1) or field_match.group(2) or field_match.group(3) or field_match.group(4)
|
|
|
+ # 提取类型
|
|
|
+ field_type = field_match.group(5).strip()
|
|
|
|
|
|
- if default_value:
|
|
|
- field_info["default"] = default_value
|
|
|
-
|
|
|
- fields.append(field_info)
|
|
|
+ # 处理类型中可能的括号
|
|
|
+ type_base = re.split(r'\s+', field_type)[0]
|
|
|
+ clean_type_value = clean_type(type_base)
|
|
|
+
|
|
|
+ fields.append((field_name, clean_type_value))
|
|
|
+ logger.debug(f"解析到字段: {field_name}, 类型: {clean_type_value}")
|
|
|
+ else:
|
|
|
+ logger.warning(f"无法解析字段定义: {field_def}")
|
|
|
|
|
|
- # 更新主键标记
|
|
|
- for field in fields:
|
|
|
- if field["name"] in primary_keys and not field["is_primary"]:
|
|
|
- field["is_primary"] = True
|
|
|
+ # 提取表注释
|
|
|
+ table_comment = ""
|
|
|
+ table_comment_pattern = r"COMMENT\s+ON\s+TABLE\s+(?:['\"]?(\w+)['\"]?)\s+IS\s+'([^']+)'"
|
|
|
+ table_comment_match = re.search(table_comment_pattern, sql, re.IGNORECASE)
|
|
|
+
|
|
|
+ if table_comment_match:
|
|
|
+ comment_table = table_comment_match.group(1)
|
|
|
+ if comment_table.strip("'\"") == table_name.strip("'\""):
|
|
|
+ table_comment = table_comment_match.group(2)
|
|
|
+ logger.debug(f"找到表注释: {table_comment}")
|
|
|
+
|
|
|
+ # 提取列注释
|
|
|
+ comments = {}
|
|
|
+ column_comment_pattern = r"COMMENT\s+ON\s+COLUMN\s+['\"]?(\w+)['\"]?\.['\"]?(\w+)['\"]?\s+IS\s+'([^']+)'"
|
|
|
+
|
|
|
+ for match in re.finditer(column_comment_pattern, sql, re.IGNORECASE):
|
|
|
+ comment_table = match.group(1)
|
|
|
+ column_name = match.group(2)
|
|
|
+ comment = match.group(3)
|
|
|
+
|
|
|
+ # 检查表名是否匹配
|
|
|
+ if comment_table.strip("'\"") == table_name.strip("'\""):
|
|
|
+ comments[column_name] = comment
|
|
|
+ logger.debug(f"找到列注释: {column_name} - {comment}")
|
|
|
+ else:
|
|
|
+ logger.debug(f"忽略列注释,表名不匹配: {comment_table} vs {table_name}")
|
|
|
|
|
|
- # 返回结果
|
|
|
- return {
|
|
|
- "table_name": table_name,
|
|
|
- "fields": fields
|
|
|
+ # 检查字段和注释匹配情况
|
|
|
+ logger.debug("========字段和注释匹配情况========")
|
|
|
+ field_names = [f[0] for f in fields]
|
|
|
+ logger.debug(f"字段列表 ({len(field_names)}): {field_names}")
|
|
|
+ logger.debug(f"注释字段 ({len(comments)}): {list(comments.keys())}")
|
|
|
+
|
|
|
+ # 构建返回结果
|
|
|
+ meta_list = []
|
|
|
+ for field_name, field_type in fields:
|
|
|
+ chinese_name = comments.get(field_name, "")
|
|
|
+ meta_list.append({
|
|
|
+ "en_name": field_name,
|
|
|
+ "data_type": field_type,
|
|
|
+ "name": chinese_name if chinese_name else field_name
|
|
|
+ })
|
|
|
+
|
|
|
+ # 检查表是否存在
|
|
|
+ try:
|
|
|
+ status = status_query([table_name])
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"检查表存在状态失败: {str(e)}")
|
|
|
+ status = [False]
|
|
|
+
|
|
|
+ # 构建返回结果
|
|
|
+ result = {
|
|
|
+ table_name: {
|
|
|
+ "exist": status[0] if status else False,
|
|
|
+ "meta": meta_list
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ logger.debug(f"解析结果: {json.dumps(result, ensure_ascii=False)}")
|
|
|
+ return result
|
|
|
+
|
|
|
except Exception as e:
|
|
|
logger.error(f"解析表定义SQL失败: {str(e)}")
|
|
|
+ logger.error(f"异常详情: {e}")
|
|
|
+ import traceback
|
|
|
+ logger.error(traceback.format_exc())
|
|
|
return None
|
|
|
|
|
|
+# 判断英文表名是否在图谱中存在
|
|
|
+def status_query(key_list):
|
|
|
+ query = """
|
|
|
+ unwind $Key_list as name
|
|
|
+ OPTIONAL MATCH (n:data_model {en_name: name})
|
|
|
+ OPTIONAL MATCH (n:data_resource {en_name: name})
|
|
|
+ OPTIONAL MATCH (n:data_metric {en_name: name})
|
|
|
+ WITH name, CASE
|
|
|
+ WHEN n IS NOT NULL THEN True
|
|
|
+ ELSE False
|
|
|
+ END AS exist
|
|
|
+ return collect(exist)AS exist
|
|
|
+ """
|
|
|
+ with neo4j_driver.get_session() as session:
|
|
|
+ result = session.run(query, Key_list=key_list)
|
|
|
+ data = result.value() # 获取单个值
|
|
|
+ return data
|
|
|
+
|
|
|
+
|
|
|
def select_sql(sql_query):
|
|
|
"""解析SELECT查询语句"""
|
|
|
try:
|
|
@@ -720,24 +932,17 @@ def select_sql(sql_query):
|
|
|
from_clause = from_match.group(1).strip()
|
|
|
|
|
|
# 分析FROM子句中的表
|
|
|
- table_pattern = r'(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+))(?:\s+(?:AS\s+)?(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+)))?'
|
|
|
- table_matches = re.finditer(table_pattern, from_clause)
|
|
|
-
|
|
|
- for match in table_matches:
|
|
|
- table_name = next((g for g in match.groups()[:4] if g is not None), "")
|
|
|
- table_alias = next((g for g in match.groups()[4:] if g is not None), table_name)
|
|
|
-
|
|
|
- tables.append({
|
|
|
- "name": table_name,
|
|
|
- "alias": table_alias
|
|
|
- })
|
|
|
+ table_pattern = r'(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+))(?:\s+(?:AS\s+)?(?:`([^`]+)`|"([^"]+)"|\'([^\']+)\'|([a-zA-Z0-9_]+))?'
|
|
|
+ for match in re.finditer(table_pattern, from_clause):
|
|
|
+ table_name = match.group(1) or match.group(2) or match.group(3) or match.group(4)
|
|
|
+ if table_name:
|
|
|
+ tables.append(table_name)
|
|
|
+
|
|
|
+ return tables
|
|
|
|
|
|
- return {
|
|
|
- "fields": parsed_fields,
|
|
|
- "tables": tables
|
|
|
- }
|
|
|
except Exception as e:
|
|
|
logger.error(f"解析SELECT查询语句失败: {str(e)}")
|
|
|
+ logger.error(traceback.format_exc())
|
|
|
return None
|
|
|
|
|
|
def model_resource_list(page, page_size, name_filter=None):
|
|
@@ -835,4 +1040,4 @@ def data_resource_edit(data):
|
|
|
return dict(updated_node["n"])
|
|
|
except Exception as e:
|
|
|
logger.error(f"编辑数据资源失败: {str(e)}")
|
|
|
- raise
|
|
|
+ raise
|