fix(memory): 定宽时间戳存储 + rowid 确定性排序 + 遗留数据归一化
- formatTime 改用固定 30 字节布局(9 位小数秒),让 TEXT 列的字典序与时间序一致(RFC3339Nano 会去掉尾随零产生变宽字符串,秒内会反序) - memory_items / project / project_chapter 的 ORDER BY 全部补 rowid 兜底,消除同 tick 打平时的顺序不确定 - 开库时幂等归一化变宽的历史值;无法解析的行原样保留,不阻塞开库 - 回归测试:变宽排序反例、同一时间戳插入序、归一化幂等与跳过
This commit is contained in:
1 parent
1ac126fc9e
commit
31086ed1ab
10 files changed
+745
-61
No files matched your search
@@ -79,7 +79,8 @@ type rowScanner interface {
|
||||
// internal/db). A nil db returns ErrNilDB. log may be nil, in which case a
|
||||
// discard logger is used; the active logger is tagged with source
|
||||
// "memory/projectindex" so every record is traceable. ctx controls schema
|
||||
// creation and must be honored by the caller.
|
||||
// creation and must be honored by the caller. Legacy variable-width timestamps
|
||||
// are normalized in place (see normalizeTimestamps).
|
||||
func New(ctx context.Context, db *sql.DB, log *slog.Logger) (*Store, error) {
|
||||
if db == nil {
|
||||
return nil, ErrNilDB
|
||||
@@ -94,6 +95,9 @@ func New(ctx context.Context, db *sql.DB, log *slog.Logger) (*Store, error) {
|
||||
return nil, fmt.Errorf("projectindex: apply schema: %w", err)
|
||||
}
|
||||
}
|
||||
if err := normalizeTimestamps(ctx, db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Store{db: db, log: log}, nil
|
||||
}
|
||||
|
||||
@@ -106,12 +110,14 @@ func discardLogger(log *slog.Logger) *slog.Logger {
|
||||
}
|
||||
|
||||
// ListProjects returns every project index entry in insertion order (created_at
|
||||
// ASC, id ASC). An empty (non-nil) slice is returned when there are none, so
|
||||
// callers can range over it unconditionally. A read error is wrapped with %w so
|
||||
// callers can use errors.Is against the package sentinels.
|
||||
// ASC, rowid ASC) — rowid breaks created_at ties so projects written within the
|
||||
// same clock tick keep their insertion order. An empty (non-nil) slice is
|
||||
// returned when there are none, so callers can range over it unconditionally. A
|
||||
// read error is wrapped with %w so callers can use errors.Is against the package
|
||||
// sentinels.
|
||||
func (s *Store) ListProjects(ctx context.Context) ([]Project, error) {
|
||||
rows, err := s.db.QueryContext(ctx, `SELECT id, title, desc, created_at, updated_at
|
||||
FROM project ORDER BY created_at ASC, id ASC`)
|
||||
FROM project ORDER BY created_at ASC, rowid ASC`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("projectindex: list projects: %w", err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user