mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-12 23:40:03 +08:00
bugfix:修复notice事件扩散问题 (#2132)
* bugfix:修复notice事件扩散问题 * 优化并发调度 * bugfix:修复签到样式 * bugfix:功能调用统计修复 * bugfix:修复私聊时功能调用统计显示已退群问题 * 提高插件适配兼容性 * 优化发送队列 * 修改权限检查设计 * 继续修改权限检查设计 * 完善权限检查设计 * 优化sqlite配置 * 优化数据库初始化 * 代码整理,无用代码清理 * bugfix:修复启动时数据库校验问题 * bugfix:修复预算裁剪过于激进问题
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Literal, Protocol
|
||||
|
||||
Dialect = Literal["sqlite", "postgres", "mysql", "unknown"]
|
||||
|
||||
|
||||
class SchemaOpRisk(str, Enum):
|
||||
SAFE = "safe"
|
||||
GUARDED = "guarded"
|
||||
MANUAL = "manual"
|
||||
|
||||
|
||||
class SchemaOp(Protocol):
|
||||
risk: SchemaOpRisk
|
||||
|
||||
def to_sql(self, dialect: Dialect) -> list[str]: ...
|
||||
|
||||
|
||||
def quote_identifier(identifier: str, dialect: Dialect) -> str:
|
||||
if dialect == "mysql":
|
||||
return f"`{identifier.replace('`', '``')}`"
|
||||
return f'"{identifier.replace(chr(34), chr(34) + chr(34))}"'
|
||||
|
||||
|
||||
def _column_type(column_type: str | dict[str, str], dialect: Dialect) -> str:
|
||||
if isinstance(column_type, dict):
|
||||
return column_type.get(dialect) or column_type.get("default") or "TEXT"
|
||||
return column_type
|
||||
|
||||
|
||||
def _default_sql(default: str | float | bool | None, dialect: Dialect) -> str:
|
||||
if default is None:
|
||||
return ""
|
||||
if isinstance(default, bool):
|
||||
if dialect == "postgres":
|
||||
return " DEFAULT TRUE" if default else " DEFAULT FALSE"
|
||||
return " DEFAULT 1" if default else " DEFAULT 0"
|
||||
if isinstance(default, int | float):
|
||||
return f" DEFAULT {default}"
|
||||
escaped = default.replace("'", "''")
|
||||
return f" DEFAULT '{escaped}'"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class AddColumn:
|
||||
table: str
|
||||
column: str
|
||||
column_type: str | dict[str, str]
|
||||
nullable: bool = True
|
||||
default: str | float | bool | None = None
|
||||
risk: SchemaOpRisk = SchemaOpRisk.SAFE
|
||||
|
||||
def to_sql(self, dialect: Dialect) -> list[str]:
|
||||
table = quote_identifier(self.table, dialect)
|
||||
column = quote_identifier(self.column, dialect)
|
||||
column_type = _column_type(self.column_type, dialect)
|
||||
null_sql = "" if self.nullable else " NOT NULL"
|
||||
default_sql = _default_sql(self.default, dialect)
|
||||
if not self.nullable and not default_sql:
|
||||
return []
|
||||
if dialect == "postgres":
|
||||
return [
|
||||
"ALTER TABLE "
|
||||
f"{table} ADD COLUMN IF NOT EXISTS {column} "
|
||||
f"{column_type}{default_sql}{null_sql}"
|
||||
]
|
||||
return [
|
||||
"ALTER TABLE "
|
||||
f"{table} ADD COLUMN {column} {column_type}{default_sql}{null_sql}"
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class CreateIndex:
|
||||
table: str
|
||||
columns: tuple[str, ...]
|
||||
name: str | None = None
|
||||
if_not_exists: bool = True
|
||||
unique: bool = False
|
||||
where: str | None = None
|
||||
risk: SchemaOpRisk = SchemaOpRisk.SAFE
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
table: str,
|
||||
columns: tuple[str, ...] | list[str],
|
||||
name: str | None = None,
|
||||
if_not_exists: bool = True,
|
||||
unique: bool = False,
|
||||
where: str | None = None,
|
||||
risk: SchemaOpRisk | None = None,
|
||||
) -> None:
|
||||
object.__setattr__(self, "table", table)
|
||||
object.__setattr__(self, "columns", tuple(columns))
|
||||
object.__setattr__(self, "name", name)
|
||||
object.__setattr__(self, "if_not_exists", if_not_exists)
|
||||
object.__setattr__(self, "unique", unique)
|
||||
object.__setattr__(self, "where", where)
|
||||
resolved_risk = risk or (SchemaOpRisk.MANUAL if unique else SchemaOpRisk.SAFE)
|
||||
object.__setattr__(self, "risk", resolved_risk)
|
||||
|
||||
def to_sql(self, dialect: Dialect) -> list[str]:
|
||||
if self.unique or not self.columns:
|
||||
return []
|
||||
name = self.name or f"idx_{self.table}_{'_'.join(self.columns)}"[:62]
|
||||
if dialect == "mysql" and self.where:
|
||||
return []
|
||||
exists_sql = (
|
||||
"IF NOT EXISTS " if self.if_not_exists and dialect != "mysql" else ""
|
||||
)
|
||||
table = quote_identifier(self.table, dialect)
|
||||
index = quote_identifier(name, dialect)
|
||||
columns = ", ".join(
|
||||
quote_identifier(column, dialect) for column in self.columns
|
||||
)
|
||||
where = f" WHERE {self.where}" if self.where and dialect != "mysql" else ""
|
||||
return [f"CREATE INDEX {exists_sql}{index} ON {table}({columns}){where}"]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RenameColumn:
|
||||
table: str
|
||||
old: str
|
||||
new: str
|
||||
risk: SchemaOpRisk = SchemaOpRisk.GUARDED
|
||||
|
||||
def to_sql(self, dialect: Dialect) -> list[str]:
|
||||
table = quote_identifier(self.table, dialect)
|
||||
old = quote_identifier(self.old, dialect)
|
||||
new = quote_identifier(self.new, dialect)
|
||||
return [f"ALTER TABLE {table} RENAME COLUMN {old} TO {new}"]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class DropColumn:
|
||||
table: str
|
||||
column: str
|
||||
risk: SchemaOpRisk = SchemaOpRisk.GUARDED
|
||||
|
||||
def to_sql(self, dialect: Dialect) -> list[str]:
|
||||
table = quote_identifier(self.table, dialect)
|
||||
column = quote_identifier(self.column, dialect)
|
||||
return [f"ALTER TABLE {table} DROP COLUMN {column}"]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class AlterColumnType:
|
||||
table: str
|
||||
column: str
|
||||
column_type: str | dict[str, str]
|
||||
nullable: bool | None = None
|
||||
risk: SchemaOpRisk = SchemaOpRisk.GUARDED
|
||||
|
||||
def to_sql(self, dialect: Dialect) -> list[str]:
|
||||
column_type = _column_type(self.column_type, dialect)
|
||||
table = quote_identifier(self.table, dialect)
|
||||
column = quote_identifier(self.column, dialect)
|
||||
if dialect == "postgres":
|
||||
return [f"ALTER TABLE {table} ALTER COLUMN {column} TYPE {column_type}"]
|
||||
if dialect == "mysql":
|
||||
null_sql = " NULL" if self.nullable else " NOT NULL"
|
||||
if self.nullable is None:
|
||||
null_sql = ""
|
||||
return [
|
||||
f"ALTER TABLE {table} MODIFY COLUMN {column} {column_type}{null_sql}"
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
def normalize_schema_ops(items: list[str | SchemaOp], dialect: Dialect) -> list[str]:
|
||||
sql_list: list[str] = []
|
||||
for item in items:
|
||||
if isinstance(item, str):
|
||||
sql_list.append(item)
|
||||
else:
|
||||
sql_list.extend(item.to_sql(dialect))
|
||||
return sql_list
|
||||
Reference in New Issue
Block a user