Skip to content

feat: 添加 SonnetDB 驱动适配与能力差距跟踪 - #101

Merged
nnhy merged 4 commits into
NewLifeX:masterfrom
maikebing:feat/sonnetdb-provider
Oct 16, 2026
Merged

nnhy merged 4 commits into
NewLifeX:masterfrom
maikebing:feat/sonnetdb-provider

Conversation

@maikebing

Copy link
Copy Markdown
Contributor

SonnetDB 尚未被 XCode 的数据库工厂识别,也没有对应的驱动依赖包。此 PR 增加 SonnetDB 自己的 ADO.NET、SQL 方言和元数据适配,使关系表常用 CRUD、建模、分页、批量写入及事务可以接入现有 XCode 流程。Refs #100;这是 draft,能力差距 issue 保持开放,不宣称全部等价支持。

变更与已完成范围

  • ✅ 新增 DatabaseType.SonnetDB / DbFactory 注册、XCode.SonnetDB net10.0 依赖包、解决方案及正式打包流程。使用官方已发布 SonnetDB 4.0.0,不引入 PostgreSQL 兼容层。
  • ✅ 原生关系表元数据、自增(同步/异步且不依赖物理列顺序)、参数、Unicode/引号/NULL/BLOB、常用 Decimal 和毫秒 DateTime、单结果集 DataSet。
  • ✅ LIMIT/OFFSET / 仅 OFFSET、基础 LINQ / JOIN / ROW_NUMBER、表前缀、批量 Insert / InsertIgnore / Upsert 更新与累加、单主键 maximumRows 有界删除。
  • ✅ 建表、默认值、普通/复合唯一索引、添加/删除普通列、修改可空性/默认值、索引演进、TRUNCATE、ReadCommitted 提交/回滚与读取自身写入。
  • ✅ 拒绝高精度值静默损坏、Decimal 迁移丢精度、不等价 Replace、有界删除降级、虚报隔离和视图类型猜测;注释/长度在自动建模中明确记录未完成。
  • ✅ 必要的公共路径修复:Entity.Delete(Expression, maximumRows) 原来生成参数字典却不传给 Persistence.Delete;改用数据库方言生成可执行条件,并以最大行数及 O'Brien 精准删除回归覆盖。没有扩展公共接口。

数据库缺口:🛠️ 未完成 / 📦 发布依赖

图例:✅ 指对应实测通过,🛠️ 指尚需实现或修复,⛔ 指 4.0.0 不能完整提供,🧪 指验证待完成,📦 指正式发布待完成。只有源码已经合入而正式包不包含时,才写“等待 SonnetDB 发布”;不能用 main 或跳过测试冒充正式交付。

能力 当前状态与依赖
Decimal 参数完整精度 ⛔ 高精度值受损;适配明确拒绝。🛠️ SonnetDB #235 → 🧪 → 📦 → 🔧 XCode
DateTime 亚毫秒精度 ⛔ 被截为毫秒;适配拒绝。🛠️ #236 → 🧪 → 📦 → 🔧
表/列描述 ⛔ 不持久化,直接 API 拒绝,自动迁移有未完成日志。🛠️ #237 → 🧪 → 📦 → 🔧
字符串长度约束/元数据 ⛔ STRING 无界,长度迁移拒绝。🛠️ #240 → 🧪 → 📦 → 🔧
Decimal ADD/ALTER 的 p/s ⛔ 静默变成 (38,28);适配拒绝受影响迁移。🛠️ #241 → 🧪 → 📦 → 🔧
BatchReplace ⛔ 缺少 delete + insert 原子替换;不以 Upsert 代替。🛠️ #242 → 🧪 → 📦 → 🔧
复合/无主键的有界删除 ⛔ 不退回无界 DELETE。🛠️ #243 → 🧪 → 📦 → 🔧
无主键关系表写入 ⛔ 空表可建但 INSERT 拒绝。🛠️ #255 → 🧪 → 📦 → 🔧
大于 Int32 的分页位置 ⛔ XCode Int64 范围尚未完整提供。🛠️ #256 → 🧪 → 📦 → 🔧
无条件全表 Update ⛔ 原生 UPDATE 要求 WHERE。🛠️ #257 → 🧪 → 📦 → 🔧
视图反向列声明 ⛔ Columns 缺少视图列,零行查询类型为 Object;含此类视图的全表元数据枚举也会明确拒绝。🛠️ #261 → 🧪 → 📦 → 🔧
Rank / DenseRank ⛔ XCode 已有窗口接口尚未完整支持。🛠️ #222 → 🧪 → 📦 → 🔧
更高隔离等级 / 真实锁定读 ⛔ 仅开放 ReadCommitted,保留悲观锁缺口。🛠️ #225、#233 → 🧪 → 📦 → 🔧
主键演进 / 事务内 DDL ⛔ 完整迁移合同待补齐。🛠️ #226、#227 → 🧪 → 📦 → 🔧
多结果集 / schema 限定名称 ⛔ 单结果集/表前缀通过不代表这些能力完成。🛠️ #229、#232 → 🧪 → 📦 → 🔧
精确引用标识符 ✅ main 已有 #211 大小写/歧义实现;📦 #230 等待 SonnetDB 正式发布,然后 🧪 XCode 验证和 🔧 集成。仅引用标识符切片,不扩为 measurement 路线
  • 🧪 全部业务实体、缓存/租户/权限/分表组合和大规模跨库迁移的 SonnetDB 专项验收。
  • 🧪 PostgreSQL 真实服务回归(本地未配置对应服务)。
  • 📦 本 PR 合入并发布 XCode 主包及 XCode.SonnetDB;没有发布适配 NuGet 或关闭 feat: SonnetDB 适配与 PostgreSQL / SQLite 能力差距跟踪 #100。

完整范围、接入示例、边界与后续状态见 Doc/SonnetDB.md。SonnetDB 的 RETURNING / UPSERT / 基础 DECIMAL / GetSchema 已随 4.0.0 发布,✅ 不重新列成等待开发或发版。嵌入式 DropDatabase 明确拒绝删除目录;备份/恢复未在本次验证中宣称已通过。

验证

  • ✅ 整个解决方案构建通过,覆盖 XCode 原有目标框架,无错误;保留仓库已有编译警告。
  • ✅ SonnetDB 正式包与官方 v4.0.0 Server:31 项,27 通过、0 失败、4 个未来合同显式跳过。其中 REST / Auto / 精确 HTTP/2 的 3 项 CRUD/事务回归及 1 项远程建库生命周期通过;不是 mock。官方 Windows bundle 下载已核对 SHA-256。
  • ✅ 单独验证依赖包装项目 dotnet pack XCode.SonnetDB/XCode.SonnetDB.csproj -c Release,产物含 net10.0 的 NewLife.XCode / SonnetDB 依赖。依赖包不含编译程序集,沿用现有数据库包装模式。
  • ⚠️ 现有 SQLite / DbBase / DAL / WindowFunction:70 项中 69 通过、1 失败。失败为 SQLiteTests.MetaTest 在未初始化的 Data/Membership.db 中断言必须存在表;原始 280b2a2f 无 Sonnet 改动的隔离 worktree 同样复现。没有隐藏或修补该无关基线问题,也不宣称全套回归通过。
  • 🧪 4 项 skip 分别跟踪 Replace、Decimal DDL 精度、Decimal/DateTime 完整值精度、视图声明元数据;当前对应保护异常已执行通过。skip 不计入能力完成。

未新增引擎依赖、虚拟业务主键或公共 ORM API,不修改 SonnetDB 服务端,不因数据库支持更多模型而扩建 XCode 特有接口。

This was referenced Oct 8, 2026
@nnhy
nnhy marked this pull request as ready for review October 16, 2026 02:53
Copilot AI balanced review requested due to automatic review settings October 16, 2026 13:29
@nnhy
nnhy marked this pull request as draft October 16, 2026 13:31
@nnhy
nnhy marked this pull request as ready for review October 16, 2026 13:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Numeric literal formatting permits SQL injection, and unresolved upsert, schema migration, and database-path issues affect correctness.

7 open findings
What changed in this PR

Adds draft SonnetDB support to XCode’s existing database infrastructure, with explicit capability limits.

Changes:

  • Adds the provider, SQL dialect, metadata adapter, dependency package, and publishing integration.
  • Adds embedded and remote integration tests and capability documentation.
  • Fixes expression-based deletion conditions that previously referenced unpassed parameters.
File Description
XUnitTest.XCode/​XUnitTest.XCode.csproj References the driver package project.
XUnitTest.XCode/​DataAccessLayer/​SonnetDBTests.cs Tests embedded operations and capability guards.
XUnitTest.XCode/​DataAccessLayer/​SonnetDBRemoteTests.cs Tests remote operations and transactions.
XCode/​Entity/​Entity.cs Formats executable deletion conditions.
XCode/​DataAccessLayer/​Database/​SonnetDB.cs Implements SonnetDB database integration.
XCode/​DataAccessLayer/​Common/​DbFactory.cs Registers the provider.
XCode/​DataAccessLayer/​Common/​DatabaseType.cs Adds the database identifier.
XCode.SonnetDB/​XCode.SonnetDB.csproj Packages driver dependencies.
Readme.MD Links capability documentation.
NewLife.XCode.sln Includes the package project.
Doc/​SonnetDB.md Documents setup, validation, and limitations.
ChangeLog.md Records integration and deletion changes.
.github/​workflows/​publish.yml Adds package publishing.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +110 to +113
if (value is Decimal number) return number.ToString(CultureInfo.InvariantCulture);
if (value is Double dbl) return dbl.ToString("R", CultureInfo.InvariantCulture);
if (value is Single single) return single.ToString("R", CultureInfo.InvariantCulture);
return Convert.ToString(value, CultureInfo.InvariantCulture)!;
var source = builder["Data Source"];
var uriSource = source?.StartsWith("sonnetdb+", StringComparison.OrdinalIgnoreCase) == true ? source.Substring("sonnetdb+".Length) : source;
DatabaseName = Uri.TryCreate(uriSource, UriKind.Absolute, out var uri) && (uri.Scheme == "http" || uri.Scheme == "https")
? Uri.UnescapeDataString(uri.AbsolutePath.Trim('/')) : source;
Comment on lines +329 to +330
var keys = table.Indexes.FirstOrDefault(index => index.Unique && index.Columns.Length > 0 && index.Columns.All(name => columns.Any(column => column.ColumnName.Equals(name, StringComparison.OrdinalIgnoreCase))))?.Columns
?? table.PrimaryKeys.Select(column => column.ColumnName).ToArray();
if (expectedDefault != null) expectedDefault = expectedDefault.Substring(" DEFAULT ".Length);
return entityColumn.Identity != dbColumn.Identity || entityColumn.PrimaryKey != dbColumn.PrimaryKey
|| (entityColumn.Nullable && !entityColumn.PrimaryKey && !entityColumn.Identity) != dbColumn.Nullable
|| !String.Equals(expectedDefault?.Trim(), dbColumn.DefaultValue?.Trim(), StringComparison.OrdinalIgnoreCase);
if ((field.DataType == typeof(DateTime) || field.DataType == typeof(DateTimeOffset) || field.DataType == typeof(TimeSpan))
&& !value.StartsWith("'") && value.IndexOf('(') < 0)
value = SonnetDB.Quote(value);
return " DEFAULT " + value;
Comment on lines +513 to +514
if (oldfield != null && field.DataType == typeof(String) && field.Length != oldfield.Length && field.Length > 0 && field.Length < Int32.MaxValue)
throw new NotSupportedException("SonnetDB STRING尚未支持长度约束变更。参见SonnetDB issue #240,尚需实现对应能力并正式发布。");
public override String? CreateIndexSQL(IDataIndex index) => $"CREATE {(index.Unique ? "UNIQUE " : String.Empty)}INDEX {Database.FormatName(index.Name!)} ON {FormatName(index.Table)} ({String.Join(",", index.Table.GetColumns(index.Columns).Select(FormatName))})";

/// <inheritdoc/>
public override String DropIndexSQL(IDataIndex index) => $"DROP INDEX {Database.FormatName(index.Name!)} ON {FormatName(index.Table)}";
@nnhy
nnhy merged commit 2d90912 into NewLifeX:master Oct 16, 2026
8 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants