dirsql 0.4.133__tar.gz → 0.4.135__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (504) hide show
  1. {dirsql-0.4.133 → dirsql-0.4.135}/Cargo.lock +2 -2
  2. {dirsql-0.4.133 → dirsql-0.4.135}/Cargo.toml +1 -1
  3. {dirsql-0.4.133 → dirsql-0.4.135}/PKG-INFO +1 -1
  4. {dirsql-0.4.133/packages/rust → dirsql-0.4.135}/docs/reference/cli.md +5 -0
  5. {dirsql-0.4.133/packages/rust → dirsql-0.4.135}/docs/reference/config.md +2 -2
  6. {dirsql-0.4.133/packages/rust → dirsql-0.4.135}/docs/reference/sdk.md +3 -1
  7. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/Cargo.toml +1 -1
  8. dirsql-0.4.135/packages/python/changelog.d/2026-11-04-exact-json-floats.md +1 -0
  9. {dirsql-0.4.133 → dirsql-0.4.135/packages/python}/docs/reference/cli.md +5 -0
  10. {dirsql-0.4.133 → dirsql-0.4.135/packages/python}/docs/reference/config.md +2 -2
  11. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/reference/sdk.md +3 -1
  12. dirsql-0.4.135/packages/python/e2e-attestations/bot-1580-abs-dotdot-1104.json +7 -0
  13. dirsql-0.4.135/packages/python/e2e-attestations/bot-1586-float-roundtrip-1104.json +7 -0
  14. dirsql-0.4.135/packages/python/e2e-attestations/bot-1587-hook-key-case-1104.json +7 -0
  15. dirsql-0.4.135/packages/python/e2e-attestations/bot-1593-non-utf8-text-1104.json +7 -0
  16. dirsql-0.4.135/packages/python/e2e-attestations/bot-1594-duplicate-columns-1104.json +7 -0
  17. dirsql-0.4.135/packages/python/migrations.d/2026-11-04-exact-json-floats.md +21 -0
  18. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/Cargo.toml +1 -1
  19. {dirsql-0.4.133/packages/python → dirsql-0.4.135/packages/rust}/docs/reference/cli.md +5 -0
  20. {dirsql-0.4.133/packages/python → dirsql-0.4.135/packages/rust}/docs/reference/config.md +2 -2
  21. {dirsql-0.4.133 → dirsql-0.4.135/packages/rust}/docs/reference/sdk.md +3 -1
  22. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/db.rs +220 -27
  23. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/path_table.rs +63 -12
  24. {dirsql-0.4.133 → dirsql-0.4.135}/README.md +0 -0
  25. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/__init__.py +0 -0
  26. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/_async.py +0 -0
  27. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/_dirsql.pyi +0 -0
  28. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/__init__.py +0 -0
  29. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/discover_plugins/__init__.py +0 -0
  30. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  31. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  32. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  33. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  34. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/main.py +0 -0
  35. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/cli/resolve_config_extensions.py +0 -0
  36. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/plan_entry_path.py +0 -0
  37. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/py.typed +0 -0
  38. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/read_config.py +0 -0
  39. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/resolve_configs_extension_specs.py +0 -0
  40. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/resolve_extension.py +0 -0
  41. {dirsql-0.4.133 → dirsql-0.4.135}/dirsql/resolve_package.py +0 -0
  42. {dirsql-0.4.133 → dirsql-0.4.135}/docs/.claude/CLAUDE.md +0 -0
  43. {dirsql-0.4.133 → dirsql-0.4.135}/docs/.vitepress/config.ts +0 -0
  44. {dirsql-0.4.133 → dirsql-0.4.135}/docs/.vitepress/theme/index.ts +0 -0
  45. {dirsql-0.4.133 → dirsql-0.4.135}/docs/.vitepress/theme/lang.ts +0 -0
  46. {dirsql-0.4.133 → dirsql-0.4.135}/docs/AGENTS.md +0 -0
  47. {dirsql-0.4.133 → dirsql-0.4.135}/docs/context/.gitattributes +0 -0
  48. {dirsql-0.4.133 → dirsql-0.4.135}/docs/context/recipes.md +0 -0
  49. {dirsql-0.4.133 → dirsql-0.4.135}/docs/context/troubleshooting.md +0 -0
  50. {dirsql-0.4.133 → dirsql-0.4.135}/docs/context/usage.md +0 -0
  51. {dirsql-0.4.133 → dirsql-0.4.135}/docs/explanation.md +0 -0
  52. {dirsql-0.4.133 → dirsql-0.4.135}/docs/getting-started.md +0 -0
  53. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/agent-skill.md +0 -0
  54. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/columns-from-paths.md +0 -0
  55. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/define-tables.md +0 -0
  56. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/embed.md +0 -0
  57. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/extract-from-contents.md +0 -0
  58. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/load-extension.md +0 -0
  59. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/parse-files-into-columns.md +0 -0
  60. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/persist.md +0 -0
  61. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/query-json.md +0 -0
  62. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/query-without-config.md +0 -0
  63. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/react-to-changes.md +0 -0
  64. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/search-by-meaning.md +0 -0
  65. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/search-indexes.md +0 -0
  66. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/skip-files.md +0 -0
  67. {dirsql-0.4.133 → dirsql-0.4.135}/docs/howto/write-a-plugin.md +0 -0
  68. {dirsql-0.4.133 → dirsql-0.4.135}/docs/index.md +0 -0
  69. {dirsql-0.4.133 → dirsql-0.4.135}/docs/package.json +0 -0
  70. {dirsql-0.4.133 → dirsql-0.4.135}/docs/plugins.md +0 -0
  71. {dirsql-0.4.133 → dirsql-0.4.135}/docs/pnpm-lock.yaml +0 -0
  72. {dirsql-0.4.133 → dirsql-0.4.135}/docs/pnpm-workspace.yaml +0 -0
  73. {dirsql-0.4.133 → dirsql-0.4.135}/docs/reference/columns.md +0 -0
  74. {dirsql-0.4.133 → dirsql-0.4.135}/docs/reference/glob.md +0 -0
  75. {dirsql-0.4.133 → dirsql-0.4.135}/docs/reference/hooks.md +0 -0
  76. {dirsql-0.4.133 → dirsql-0.4.135}/docs/reference/http-api.md +0 -0
  77. {dirsql-0.4.133 → dirsql-0.4.135}/docs/reference/path-tables.md +0 -0
  78. {dirsql-0.4.133 → dirsql-0.4.135}/docs/speed.json +0 -0
  79. {dirsql-0.4.133 → dirsql-0.4.135}/docs/speed.md +0 -0
  80. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/CHANGELOG.md +0 -0
  81. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/MIGRATIONS.md +0 -0
  82. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/README.md +0 -0
  83. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  84. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  85. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  86. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  87. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  88. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  89. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  90. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  91. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  92. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  93. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  94. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  95. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  96. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  97. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  98. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  99. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  100. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  101. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  102. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  103. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  104. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  105. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  106. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  107. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
  108. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
  109. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
  110. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
  111. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  112. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  113. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-02-readme-docs-host.md +0 -0
  114. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  115. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-03-path-table-directory-one-level.md +0 -0
  116. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  117. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-brace-groups-follow-bash.md +0 -0
  118. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-cli-context.md +0 -0
  119. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-gitignore-requires-repo.md +0 -0
  120. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-path-table-node-modules-named.md +0 -0
  121. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-path-table-symlinks.md +0 -0
  122. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-path-table-trailing-slash.md +0 -0
  123. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-05-unbalanced-brackets-literal.md +0 -0
  124. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-config-braces.md +0 -0
  125. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-config-directory.md +0 -0
  126. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-config-dotnames.md +0 -0
  127. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-config-gitignore.md +0 -0
  128. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-config-globs-anchor-at-the-config.md +0 -0
  129. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-config-links.md +0 -0
  130. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-one-glob-compiler.md +0 -0
  131. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-plugin-no-default-records.md +0 -0
  132. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-watch-broadcast.md +0 -0
  133. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-06-zero-match-config-table-warning.md +0 -0
  134. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-bracket-slash-literal.md +0 -0
  135. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-dotdot-and-double-slashes.md +0 -0
  136. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-dotdot-as-written.md +0 -0
  137. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-glob-walks-node-modules.md +0 -0
  138. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-literal-gitignore.md +0 -0
  139. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-on-file-ndjson.md +0 -0
  140. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-parallel-hooks.md +0 -0
  141. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-persist-cache-outside-root.md +0 -0
  142. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-posix-classes-unicode.md +0 -0
  143. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-07-spread-few-large-files.md +0 -0
  144. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-08-small-walks-stay-on-one-thread.md +0 -0
  145. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-10-08-watch-requires-named-table.md +0 -0
  146. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-11-02-watch-dirs-created-mid-registration.md +0 -0
  147. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-11-02-watch-live-on-return.md +0 -0
  148. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/2026-11-04-query-bind-params.md +0 -0
  149. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/changelog.d/README.md +0 -0
  150. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/conftest.py +0 -0
  151. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/.claude/CLAUDE.md +0 -0
  152. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/.vitepress/config.ts +0 -0
  153. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  154. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  155. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/AGENTS.md +0 -0
  156. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/context/.gitattributes +0 -0
  157. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/context/recipes.md +0 -0
  158. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/context/troubleshooting.md +0 -0
  159. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/context/usage.md +0 -0
  160. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/explanation.md +0 -0
  161. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/getting-started.md +0 -0
  162. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/agent-skill.md +0 -0
  163. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/columns-from-paths.md +0 -0
  164. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/define-tables.md +0 -0
  165. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/embed.md +0 -0
  166. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/extract-from-contents.md +0 -0
  167. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/load-extension.md +0 -0
  168. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  169. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/persist.md +0 -0
  170. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/query-json.md +0 -0
  171. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/query-without-config.md +0 -0
  172. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/react-to-changes.md +0 -0
  173. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/search-by-meaning.md +0 -0
  174. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/search-indexes.md +0 -0
  175. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/skip-files.md +0 -0
  176. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/howto/write-a-plugin.md +0 -0
  177. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/index.md +0 -0
  178. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/package.json +0 -0
  179. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/plugins.md +0 -0
  180. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/pnpm-lock.yaml +0 -0
  181. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/pnpm-workspace.yaml +0 -0
  182. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/reference/columns.md +0 -0
  183. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/reference/glob.md +0 -0
  184. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/reference/hooks.md +0 -0
  185. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/reference/http-api.md +0 -0
  186. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/reference/path-tables.md +0 -0
  187. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/speed.json +0 -0
  188. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/docs/speed.md +0 -0
  189. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1334-listing-exemptions.json +0 -0
  190. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1397-watch-broadcast.json +0 -0
  191. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1398-inotify-error.json +0 -0
  192. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1399-server-cors.json +0 -0
  193. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1401-watch-scope.json +0 -0
  194. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1406-watch-no-tables-1008.json +0 -0
  195. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1407-child-place-depth.json +0 -0
  196. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1414-stat-speed.json +0 -0
  197. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1415-server-speed.json +0 -0
  198. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1416-watch-speed-1008.json +0 -0
  199. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1418-node-modules.json +0 -0
  200. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1419-dotdot-slashes.json +0 -0
  201. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1420-bracket-slash.json +0 -0
  202. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1421-posix-unicode.json +0 -0
  203. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1422-literal-gitignore.json +0 -0
  204. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1426-walk-spawn-threshold-1008.json +0 -0
  205. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1428-ndjson-hooks.json +0 -0
  206. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1429-parallel-hooks.json +0 -0
  207. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1430-dotdot-as-written.json +0 -0
  208. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1431-persist-cache-dir.json +0 -0
  209. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1437-test-cache-isolation.json +0 -0
  210. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1440-fix-main-build.json +0 -0
  211. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1448-wildcard-ignored-prefix.json +0 -0
  212. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1451-named-dir-never-hidden.json +0 -0
  213. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1454-split-few-files.json +0 -0
  214. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1457-incremental-refresh-1008.json +0 -0
  215. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1459-rescan-on-overflow-1008.json +0 -0
  216. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1461-chunked-read-1016.json +0 -0
  217. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1461-row-footprint-1008.json +0 -0
  218. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1461-step3-1020.json +0 -0
  219. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1461-wide-read-1015.json +0 -0
  220. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1462-paired-speed-harness-1008.json +0 -0
  221. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1464-fastest-native-1008.json +0 -0
  222. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1469-ci-speed-sanity-1009.json +0 -0
  223. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1470-perf-suite-1009.json +0 -0
  224. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1470-speed-page-1011.json +0 -0
  225. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1471-one-level-restore-1102.json +0 -0
  226. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1474-vtab-speed-1014.json +0 -0
  227. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1475-prefix-speed-1016.json +0 -0
  228. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1475-rowpath-1016.json +0 -0
  229. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1477-onelevel-speed-1015.json +0 -0
  230. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1477-onelevel-speed-1016.json +0 -0
  231. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1478-perdir-walk-1020.json +0 -0
  232. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1480-matcher-speed-1015.json +0 -0
  233. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1481-gitrepo-speed-1015.json +0 -0
  234. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1482-docs-fastest-bar-1102.json +0 -0
  235. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1483-fm-speed-1015.json +0 -0
  236. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1484-jsonl-speed-1015.json +0 -0
  237. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1487-frontend-query-1011.json +0 -0
  238. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1488-frontend-metadata-1011.json +0 -0
  239. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1489-frontend-browse-1013.json +0 -0
  240. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1503-bench-watch-1011.json +0 -0
  241. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1504-bench-names-1011.json +0 -0
  242. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1514-watch-rerun-inline-1015.json +0 -0
  243. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1524-path-eq-1016.json +0 -0
  244. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1526-main-red-1016.json +0 -0
  245. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1532-gitignore-fastest-bar-1016.json +0 -0
  246. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1534-fm-native-1016.json +0 -0
  247. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1534-fm-native-1102.json +0 -0
  248. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1538-onfile-order-1019.json +0 -0
  249. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1540-update-in-place-1019.json +0 -0
  250. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1541-defer-fk-1019.json +0 -0
  251. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1544-perfile-update-1020.json +0 -0
  252. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1552-watch-gap-1102.json +0 -0
  253. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1557-watch-rename-1102.json +0 -0
  254. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1560-scan-insert-1102.json +0 -0
  255. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1562-hook-savepoint-1102.json +0 -0
  256. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1562-watch-speed-1102.json +0 -0
  257. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-1575-bind-params-1104.json +0 -0
  258. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-fix-main-place-depth-20261007.json +0 -0
  259. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-issue-1486-frontend-20261009.json +0 -0
  260. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-gap-childpath-1011.json +0 -0
  261. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-parse-overlap-1011.json +0 -0
  262. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-parse-paths-1009.json +0 -0
  263. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-parse-pragmas-1011.json +0 -0
  264. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-sql-valueref-1013.json +0 -0
  265. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-walk-components-1011.json +0 -0
  266. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-walk-rowpath-1011.json +0 -0
  267. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-walk-rows-1013.json +0 -0
  268. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/bot-speed-walk-sched-1009.json +0 -0
  269. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  270. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  271. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  272. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  273. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  274. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  275. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
  276. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  277. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  278. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
  279. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
  280. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
  281. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
  282. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
  283. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
  284. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
  285. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
  286. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
  287. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
  288. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
  289. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
  290. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
  291. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
  292. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
  293. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1196-join-index.json +0 -0
  294. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1199-glob-prefix.json +0 -0
  295. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1201-single-walk.json +0 -0
  296. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1204-embed-batch.json +0 -0
  297. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +0 -0
  298. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +0 -0
  299. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1215-on-file-batch.json +0 -0
  300. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1216-on-file-cleanup.json +0 -0
  301. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1223-config-glob-rule.json +0 -0
  302. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1223-dir-one-level.json +0 -0
  303. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1225-plugin-no-default.json +0 -0
  304. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1226-stale-docs.json +0 -0
  305. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1232-anchor-plumbing.json +0 -0
  306. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1232-config-anchor.json +0 -0
  307. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1232-multi-watch.json +0 -0
  308. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1232-zero-match.json +0 -0
  309. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1238-parallel-walk.json +0 -0
  310. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1241-content-prefetch.json +0 -0
  311. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1243-ingest-perf.json +0 -0
  312. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1249-dotfiles.json +0 -0
  313. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1256-symlinks.json +0 -0
  314. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1257-trailing-slash.json +0 -0
  315. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1258-config-braces.json +0 -0
  316. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1258-config-directory.json +0 -0
  317. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1258-config-dotnames.json +0 -0
  318. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1258-config-gitignore.json +0 -0
  319. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1258-config-links.json +0 -0
  320. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1258-one-pattern.json +0 -0
  321. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1262-brace-literal.json +0 -0
  322. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1264-context.json +0 -0
  323. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1267-posix-classes.json +0 -0
  324. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1268-unbalanced-brackets.json +0 -0
  325. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1269-utf8-chars.json +0 -0
  326. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1271-node-modules-named.json +0 -0
  327. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1272-ancestor-gitignore.json +0 -0
  328. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1273-walk-prune.json +0 -0
  329. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1285-ingest-fast.json +0 -0
  330. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1286-nested-repo.json +0 -0
  331. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1305-glob-speed.json +0 -0
  332. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1308-glob-speed.json +0 -0
  333. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1310-glob-speed.json +0 -0
  334. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1311-glob-speed.json +0 -0
  335. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1312-glob-speed.json +0 -0
  336. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1318-lazy-stat.json +0 -0
  337. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1320-walk-speed.json +0 -0
  338. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1322-walk-readdir.json +0 -0
  339. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1325-parallel-walk.json +0 -0
  340. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1326-stream-rows.json +0 -0
  341. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1330-python-dirsql-patch.json +0 -0
  342. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1330-python-extensions-patch.json +0 -0
  343. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1330-scanner-effects.json +0 -0
  344. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1330-scanner-gitignore.json +0 -0
  345. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1330-vtab-prefetch-flag.json +0 -0
  346. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1330-vtab-read.json +0 -0
  347. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-1351-dot-dirs.json +0 -0
  348. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  349. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  350. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  351. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  352. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  353. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  354. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  355. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  356. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  357. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  358. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  359. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  360. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  361. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  362. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  363. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  364. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  365. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  366. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  367. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  368. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  369. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  370. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  371. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  372. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  373. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  374. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  375. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-02-readme-docs-host.md +0 -0
  376. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  377. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-03-path-table-directory-one-level.md +0 -0
  378. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  379. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-brace-groups-follow-bash.md +0 -0
  380. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-cli-context.md +0 -0
  381. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-gitignore-requires-repo.md +0 -0
  382. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-path-table-node-modules-named.md +0 -0
  383. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-path-table-symlinks.md +0 -0
  384. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-path-table-trailing-slash.md +0 -0
  385. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-05-unbalanced-brackets-literal.md +0 -0
  386. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-config-braces.md +0 -0
  387. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-config-directory.md +0 -0
  388. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-config-dotnames.md +0 -0
  389. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-config-gitignore.md +0 -0
  390. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-config-globs-anchor-at-the-config.md +0 -0
  391. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-config-links.md +0 -0
  392. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-one-glob-compiler.md +0 -0
  393. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-plugin-no-default-records.md +0 -0
  394. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-watch-broadcast.md +0 -0
  395. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-06-zero-match-config-table-warning.md +0 -0
  396. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-bracket-slash-literal.md +0 -0
  397. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-dotdot-and-double-slashes.md +0 -0
  398. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-dotdot-as-written.md +0 -0
  399. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-glob-walks-node-modules.md +0 -0
  400. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-literal-gitignore.md +0 -0
  401. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-on-file-ndjson.md +0 -0
  402. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-parallel-hooks.md +0 -0
  403. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-persist-cache-outside-root.md +0 -0
  404. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-posix-classes-unicode.md +0 -0
  405. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-07-spread-few-large-files.md +0 -0
  406. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-08-small-walks-stay-on-one-thread.md +0 -0
  407. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-10-08-watch-requires-named-table.md +0 -0
  408. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-11-02-watch-dirs-created-mid-registration.md +0 -0
  409. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-11-02-watch-live-on-return.md +0 -0
  410. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/2026-11-04-query-bind-params.md +0 -0
  411. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/migrations.d/README.md +0 -0
  412. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/src/lib.rs +0 -0
  413. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/testing-conventions.toml +0 -0
  414. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/__init__.py +0 -0
  415. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/conftest.py +0 -0
  416. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/e2e/__init__.py +0 -0
  417. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/e2e/bench_report.py +0 -0
  418. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/e2e/bench_suite.py +0 -0
  419. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  420. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  421. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/e2e/speed_of_light.py +0 -0
  422. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/integration/__init__.py +0 -0
  423. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/integration/binding/__init__.py +0 -0
  424. {dirsql-0.4.133 → dirsql-0.4.135}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  425. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/CHANGELOG.md +0 -0
  426. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/MIGRATIONS.md +0 -0
  427. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/README.md +0 -0
  428. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/benches/db_bench.rs +0 -0
  429. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/benches/differ_bench.rs +0 -0
  430. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/benches/matcher_bench.rs +0 -0
  431. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/benches/scanner_bench.rs +0 -0
  432. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/context/.gitattributes +0 -0
  433. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/context/recipes.md +0 -0
  434. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/context/troubleshooting.md +0 -0
  435. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/context/usage.md +0 -0
  436. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/explanation.md +0 -0
  437. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/getting-started.md +0 -0
  438. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/agent-skill.md +0 -0
  439. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  440. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/define-tables.md +0 -0
  441. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/embed.md +0 -0
  442. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  443. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/load-extension.md +0 -0
  444. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  445. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/persist.md +0 -0
  446. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/query-json.md +0 -0
  447. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/query-without-config.md +0 -0
  448. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/react-to-changes.md +0 -0
  449. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  450. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/search-indexes.md +0 -0
  451. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/skip-files.md +0 -0
  452. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/howto/write-a-plugin.md +0 -0
  453. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/index.md +0 -0
  454. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/plugins.md +0 -0
  455. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/reference/columns.md +0 -0
  456. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/reference/glob.md +0 -0
  457. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/reference/hooks.md +0 -0
  458. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/reference/http-api.md +0 -0
  459. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/reference/path-tables.md +0 -0
  460. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/speed.json +0 -0
  461. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/docs/speed.md +0 -0
  462. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/bin/dirsql.rs +0 -0
  463. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/brace.rs +0 -0
  464. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/context.rs +0 -0
  465. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/execute.rs +0 -0
  466. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/frontend.html +0 -0
  467. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/frontend.js +0 -0
  468. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/init.rs +0 -0
  469. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/mod.rs +0 -0
  470. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/repl.rs +0 -0
  471. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/router.rs +0 -0
  472. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/run.rs +0 -0
  473. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/serialize.rs +0 -0
  474. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/server.rs +0 -0
  475. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/cli/table.rs +0 -0
  476. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/command.rs +0 -0
  477. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/config.rs +0 -0
  478. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/default_config.toml +0 -0
  479. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/differ.rs +0 -0
  480. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/extension_resolution.rs +0 -0
  481. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/foreign_key.rs +0 -0
  482. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/functions.rs +0 -0
  483. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/gitignore_frame.rs +0 -0
  484. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/infer.rs +0 -0
  485. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/launcher.rs +0 -0
  486. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/lib.rs +0 -0
  487. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/listing.rs +0 -0
  488. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/matcher.rs +0 -0
  489. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/on_file.rs +0 -0
  490. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/params.rs +0 -0
  491. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/parsed_vtab.rs +0 -0
  492. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/per_thread.rs +0 -0
  493. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/persist.rs +0 -0
  494. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/posix_class.rs +0 -0
  495. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/progress.rs +0 -0
  496. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/row_event_flat.rs +0 -0
  497. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/row_stream.rs +0 -0
  498. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/scanner.rs +0 -0
  499. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/sql_literal.rs +0 -0
  500. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/tree_walk.rs +0 -0
  501. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/vtab.rs +0 -0
  502. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/vtab_scaffold.rs +0 -0
  503. {dirsql-0.4.133 → dirsql-0.4.135}/packages/rust/src/watcher.rs +0 -0
  504. {dirsql-0.4.133 → dirsql-0.4.135}/pyproject.toml +0 -0
@@ -573,7 +573,7 @@ dependencies = [
573
573
 
574
574
  [[package]]
575
575
  name = "dirsql"
576
- version = "0.4.133"
576
+ version = "0.4.135"
577
577
  dependencies = [
578
578
  "assert_cmd",
579
579
  "axum",
@@ -625,7 +625,7 @@ dependencies = [
625
625
 
626
626
  [[package]]
627
627
  name = "dirsql-py-ext"
628
- version = "0.4.133"
628
+ version = "0.4.135"
629
629
  dependencies = [
630
630
  "dirsql",
631
631
  "pyo3",
@@ -18,7 +18,7 @@ dirsql = { path = "packages/rust" }
18
18
  serde = { version = "1", features = ["derive"] }
19
19
  # `preserve_order`: a serialized row must keep the projection order the SELECT
20
20
  # list asked for, which the default BTreeMap-backed object discards.
21
- serde_json = { version = "1", features = ["preserve_order"] }
21
+ serde_json = { version = "1", features = ["preserve_order", "float_roundtrip"] }
22
22
  regex = "1"
23
23
  thiserror = "2"
24
24
  tokio = { version = "1", features = ["rt", "sync"] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.133
3
+ Version: 0.4.135
4
4
  Requires-Dist: bin-shim>=0.1
5
5
  Summary: Ephemeral SQL index over a local directory
6
6
  Keywords: sql,filesystem,directory,sqlite,index
@@ -128,6 +128,11 @@ several lines, and **anything longer than 60 characters is truncated with
128
128
  `…`** so one column cannot set the width of every row. `--format json`
129
129
  returns the values unaltered.
130
130
 
131
+ A text value that is not valid UTF-8 (`CAST(x'ff' AS TEXT)`, `char(55296)`)
132
+ fails the query in every format, with an error naming its column, rather
133
+ than printing replacement characters. Wrap it in `hex()` or `CAST(… AS BLOB)`
134
+ to read its bytes.
135
+
131
136
  Laying the table out to the terminal's width, and paging a long result, are
132
137
  both out of scope; pipe to `less` for the latter.
133
138
 
@@ -224,10 +224,10 @@ what its required `on-file` command emits — dirsql injects nothing (see
224
224
  | Key | Required | Description |
225
225
  |---|---|---|
226
226
  | `name` | yes | The table's SQL name — the name you query it by. Declared, never derived from `ddl`: dirsql does not read the DDL text. The `ddl` must create a table by this name; if it doesn't, loading fails. |
227
- | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (keys the `on-file` command emits that are not declared are dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
227
+ | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (a key the `on-file` command emits fills the declared column it names, compared case-insensitively for ASCII letters as SQL identifiers are; a key naming no declared column is dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
228
228
  | `glob` | yes | Glob pattern, [anchored](#glob-anchor) at the config file's directory, under the [one glob rule](#glob-rule): `*` matches one level, `**` any depth. Every table whose glob matches a file receives that file's rows — a file can populate multiple tables. A `{name}` is a literal, as in bash and a path-table; the [glob page](./glob.md) has the syntax. |
229
229
  | `on-file` | **yes** | A command run once per table, with every matched file's absolute path appended as a trailing argument; its stdout (one JSON object per line) is the table's rows. Must be non-empty. A `[[table]]` with no `on-file` is a load error (see [parse errors](#parse-errors)). See [Command hooks](./hooks.md#on-file). |
230
- | `strict` | no (default `false`) | When `true`, rows whose keys do not exactly match the declared columns are rejected with an error: extra keys error, and every declared column must be supplied by the `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
230
+ | `strict` | no (default `false`) | When `true`, rows whose keys do not match the declared columns (compared case-insensitively, as above) are rejected with an error: extra keys error, and every declared column must be supplied by the `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
231
231
 
232
232
  `on-file` is required because a table's rows come from nowhere else. dirsql
233
233
  does not read file contents or merge filesystem facts on your behalf: the
@@ -223,7 +223,9 @@ db.query_with(sql: &str, params: &Params) -> Result<Vec<Row>>
223
223
 
224
224
  :::
225
225
 
226
- Executes a SQL query and returns rows keyed by column name.
226
+ Executes a SQL query and returns rows keyed by column name. If a result repeats
227
+ a column name, later occurrences receive `:N` suffixes (for example, `a` and
228
+ `a:1`); suffixes avoid names already present in the result.
227
229
 
228
230
  **Bind parameters.** `params` reaches SQLite's own parameter binding; the
229
231
  values never enter the SQL text, so text holding quotes needs no escaping.
@@ -4,7 +4,7 @@ name = "dirsql-py-ext"
4
4
  # pypi/maturin handler can rewrite it via `write-version` before
5
5
  # `maturin build`. `pyproject.toml` declares `dynamic = ["version"]`
6
6
  # and maturin reads this field. Mirrors `packages/rust/Cargo.toml`.
7
- version = "0.4.133"
7
+ version = "0.4.135"
8
8
  # abi3 note: the `abi3-py310` pyo3 feature (below) builds ONE stable-ABI
9
9
  # `cp310-abi3` wheel per platform that loads on every CPython >= 3.10
10
10
  # (matching `pyproject.toml`'s `requires-python = ">=3.10"`), instead of
@@ -0,0 +1 @@
1
+ **Fixed** JSON numbers from `on-file` hooks and worker functions now parse to the exact double: `123456789.12345679` was stored one ULP off as `123456789.1234568`.
@@ -128,6 +128,11 @@ several lines, and **anything longer than 60 characters is truncated with
128
128
  `…`** so one column cannot set the width of every row. `--format json`
129
129
  returns the values unaltered.
130
130
 
131
+ A text value that is not valid UTF-8 (`CAST(x'ff' AS TEXT)`, `char(55296)`)
132
+ fails the query in every format, with an error naming its column, rather
133
+ than printing replacement characters. Wrap it in `hex()` or `CAST(… AS BLOB)`
134
+ to read its bytes.
135
+
131
136
  Laying the table out to the terminal's width, and paging a long result, are
132
137
  both out of scope; pipe to `less` for the latter.
133
138
 
@@ -224,10 +224,10 @@ what its required `on-file` command emits — dirsql injects nothing (see
224
224
  | Key | Required | Description |
225
225
  |---|---|---|
226
226
  | `name` | yes | The table's SQL name — the name you query it by. Declared, never derived from `ddl`: dirsql does not read the DDL text. The `ddl` must create a table by this name; if it doesn't, loading fails. |
227
- | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (keys the `on-file` command emits that are not declared are dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
227
+ | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (a key the `on-file` command emits fills the declared column it names, compared case-insensitively for ASCII letters as SQL identifiers are; a key naming no declared column is dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
228
228
  | `glob` | yes | Glob pattern, [anchored](#glob-anchor) at the config file's directory, under the [one glob rule](#glob-rule): `*` matches one level, `**` any depth. Every table whose glob matches a file receives that file's rows — a file can populate multiple tables. A `{name}` is a literal, as in bash and a path-table; the [glob page](./glob.md) has the syntax. |
229
229
  | `on-file` | **yes** | A command run once per table, with every matched file's absolute path appended as a trailing argument; its stdout (one JSON object per line) is the table's rows. Must be non-empty. A `[[table]]` with no `on-file` is a load error (see [parse errors](#parse-errors)). See [Command hooks](./hooks.md#on-file). |
230
- | `strict` | no (default `false`) | When `true`, rows whose keys do not exactly match the declared columns are rejected with an error: extra keys error, and every declared column must be supplied by the `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
230
+ | `strict` | no (default `false`) | When `true`, rows whose keys do not match the declared columns (compared case-insensitively, as above) are rejected with an error: extra keys error, and every declared column must be supplied by the `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
231
231
 
232
232
  `on-file` is required because a table's rows come from nowhere else. dirsql
233
233
  does not read file contents or merge filesystem facts on your behalf: the
@@ -223,7 +223,9 @@ db.query_with(sql: &str, params: &Params) -> Result<Vec<Row>>
223
223
 
224
224
  :::
225
225
 
226
- Executes a SQL query and returns rows keyed by column name.
226
+ Executes a SQL query and returns rows keyed by column name. If a result repeats
227
+ a column name, later occurrences receive `:N` suffixes (for example, `a` and
228
+ `a:1`); suffixes avoid names already present in the result.
227
229
 
228
230
  **Bind parameters.** `params` reaches SQLite's own parameter binding; the
229
231
  values never enter the SQL text, so text holding quotes needs no escaping.
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/query_contract_test.py tests/e2e/on_file_query_test.py tests/e2e/use_case_join_test.py tests/e2e/glob_speed_symlink_test.py tests/e2e/fanout_test.py -x -q",
3
+ "ran_at": 1793815070,
4
+ "exit_code": 0,
5
+ "commit": "5fe39f0e3b4c76172402aa69ecb8b992265d9994",
6
+ "branch": "bot/1580-abs-dotdot-1104"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/on_file_query_test.py tests/e2e/jsonl_parse_speed_test.py tests/e2e/use_case_transcripts_test.py tests/e2e/query_contract_test.py -x -q",
3
+ "ran_at": 1793814794,
4
+ "exit_code": 0,
5
+ "commit": "4696a76d81c3080df86c416ef418d3d9b2dc0062",
6
+ "branch": "bot/1586-float-roundtrip-1104"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/on_file_query_test.py tests/e2e/declared_table_name_test.py tests/e2e/query_contract_test.py tests/e2e/jsonl_parse_speed_test.py tests/e2e/frontmatter_parse_speed_test.py -x -q",
3
+ "ran_at": 1793815109,
4
+ "exit_code": 0,
5
+ "commit": "e75ae037dafecb3da6314c6367f2a3618fa0586a",
6
+ "branch": "bot/1587-hook-key-case-1104"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/query_contract_test.py tests/e2e/on_file_query_test.py tests/e2e/cli_version_test.py -x -q",
3
+ "ran_at": 1793814772,
4
+ "exit_code": 0,
5
+ "commit": "43d8e7c1ebd157e80090a466348da1b9d5aafcda",
6
+ "branch": "bot/1593-non-utf8-text-1104"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/query_contract_test.py tests/e2e/use_case_join_test.py tests/e2e/on_file_query_test.py -q",
3
+ "ran_at": 1793815574,
4
+ "exit_code": 0,
5
+ "commit": "cc34ffac114dcf2d3dda9dca56e0a09353455ab3",
6
+ "branch": "bot/1594-duplicate-columns-1104"
7
+ }
@@ -0,0 +1,21 @@
1
+ ### Exact JSON float parsing
2
+
3
+ #### Summary
4
+
5
+ A JSON float an `on-file` hook or worker function prints is stored as the correctly rounded REAL, where some were one ULP off before. No API or configuration changes are required.
6
+
7
+ #### Required changes
8
+
9
+ _None._
10
+
11
+ #### Deprecations removed
12
+
13
+ _None._
14
+
15
+ #### Behavior changes without code changes
16
+
17
+ A REAL column fed from JSON can hold a value one ULP different from before the upgrade: the correct one.
18
+
19
+ #### Verification
20
+
21
+ In an empty directory, write `{"v":123456789.12345679}` to `a.ndjson` and run `dirsql query "SELECT v = 123456789.12345679 AS eq FROM './a.ndjson'" --on-file cat --format json`; the output is `[{"eq":1}]`.
@@ -6,7 +6,7 @@ description = "Ephemeral SQL index over a local directory"
6
6
  # version stays for the binding crates (publish = false). The literal
7
7
  # intentionally lags the published version: putitoutthere rewrites it to
8
8
  # the planned release version at build time and never commits it back.
9
- version = "0.4.133"
9
+ version = "0.4.135"
10
10
  edition.workspace = true
11
11
  # Literal `license` (not `license.workspace = true`) because
12
12
  # putitoutthere's preflight check reads `[package].license` directly
@@ -128,6 +128,11 @@ several lines, and **anything longer than 60 characters is truncated with
128
128
  `…`** so one column cannot set the width of every row. `--format json`
129
129
  returns the values unaltered.
130
130
 
131
+ A text value that is not valid UTF-8 (`CAST(x'ff' AS TEXT)`, `char(55296)`)
132
+ fails the query in every format, with an error naming its column, rather
133
+ than printing replacement characters. Wrap it in `hex()` or `CAST(… AS BLOB)`
134
+ to read its bytes.
135
+
131
136
  Laying the table out to the terminal's width, and paging a long result, are
132
137
  both out of scope; pipe to `less` for the latter.
133
138
 
@@ -224,10 +224,10 @@ what its required `on-file` command emits — dirsql injects nothing (see
224
224
  | Key | Required | Description |
225
225
  |---|---|---|
226
226
  | `name` | yes | The table's SQL name — the name you query it by. Declared, never derived from `ddl`: dirsql does not read the DDL text. The `ddl` must create a table by this name; if it doesn't, loading fails. |
227
- | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (keys the `on-file` command emits that are not declared are dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
227
+ | `ddl` | yes | A SQL batch, run verbatim — any number of statements. It must create a table called `name`; that table holds the file rows, and only the columns it declares are kept (a key the `on-file` command emits fills the declared column it names, compared case-insensitively for ASCII letters as SQL identifiers are; a key naming no declared column is dropped). The rest of the batch is yours: indexes, virtual tables, triggers. See [Batch `ddl`](#batch-ddl). |
228
228
  | `glob` | yes | Glob pattern, [anchored](#glob-anchor) at the config file's directory, under the [one glob rule](#glob-rule): `*` matches one level, `**` any depth. Every table whose glob matches a file receives that file's rows — a file can populate multiple tables. A `{name}` is a literal, as in bash and a path-table; the [glob page](./glob.md) has the syntax. |
229
229
  | `on-file` | **yes** | A command run once per table, with every matched file's absolute path appended as a trailing argument; its stdout (one JSON object per line) is the table's rows. Must be non-empty. A `[[table]]` with no `on-file` is a load error (see [parse errors](#parse-errors)). See [Command hooks](./hooks.md#on-file). |
230
- | `strict` | no (default `false`) | When `true`, rows whose keys do not exactly match the declared columns are rejected with an error: extra keys error, and every declared column must be supplied by the `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
230
+ | `strict` | no (default `false`) | When `true`, rows whose keys do not match the declared columns (compared case-insensitively, as above) are rejected with an error: extra keys error, and every declared column must be supplied by the `on-file` output. When `false`, extra keys are dropped and missing columns become `NULL`. |
231
231
 
232
232
  `on-file` is required because a table's rows come from nowhere else. dirsql
233
233
  does not read file contents or merge filesystem facts on your behalf: the
@@ -223,7 +223,9 @@ db.query_with(sql: &str, params: &Params) -> Result<Vec<Row>>
223
223
 
224
224
  :::
225
225
 
226
- Executes a SQL query and returns rows keyed by column name.
226
+ Executes a SQL query and returns rows keyed by column name. If a result repeats
227
+ a column name, later occurrences receive `:N` suffixes (for example, `a` and
228
+ `a:1`); suffixes avoid names already present in the result.
227
229
 
228
230
  **Bind parameters.** `params` reaches SQLite's own parameter binding; the
229
231
  values never enter the SQL text, so text holding quotes needs no escaping.
@@ -49,6 +49,9 @@ pub enum DbError {
49
49
 
50
50
  #[error("{0}")]
51
51
  Params(String),
52
+
53
+ #[error("column {0:?} holds text that is not valid UTF-8")]
54
+ NotUtf8(String),
52
55
  }
53
56
 
54
57
  /// The reserved namespace for dirsql's internal bookkeeping tables. Every
@@ -363,7 +366,7 @@ pub(crate) fn shape_json_rows(
363
366
  .0
364
367
  .iter()
365
368
  .map(|(key, _)| &**key)
366
- .filter(|key| !columns.iter().any(|c| c == key))
369
+ .filter(|key| !columns.iter().any(|c| c.eq_ignore_ascii_case(key)))
367
370
  .collect();
368
371
  if !extra.is_empty() {
369
372
  return Err(DbError::SchemaMismatch(format!(
@@ -375,7 +378,7 @@ pub(crate) fn shape_json_rows(
375
378
  let missing: Vec<&str> = columns
376
379
  .iter()
377
380
  .map(String::as_str)
378
- .filter(|c| !row.0.iter().any(|(key, _)| **key == **c))
381
+ .filter(|c| !row.0.iter().any(|(key, _)| key.eq_ignore_ascii_case(c)))
379
382
  .collect();
380
383
  if !missing.is_empty() {
381
384
  return Err(DbError::SchemaMismatch(format!(
@@ -394,7 +397,7 @@ pub(crate) fn shape_json_rows(
394
397
  }
395
398
  let mut shaped = vec![Value::Null; columns.len()];
396
399
  for (key, value) in row.0 {
397
- if let Some(i) = columns.iter().position(|c| **c == *key) {
400
+ if let Some(i) = columns.iter().position(|c| c.eq_ignore_ascii_case(&key)) {
398
401
  emitted[i] = true;
399
402
  shaped[i] = json_into_value(value);
400
403
  }
@@ -755,7 +758,7 @@ impl Db {
755
758
  let mut cells = Vec::with_capacity(rows.len());
756
759
  for row in rows {
757
760
  for (i, column) in columns.iter().enumerate() {
758
- emitted[i] |= row.contains_key(column);
761
+ emitted[i] |= row.keys().any(|key| key.eq_ignore_ascii_case(column));
759
762
  }
760
763
  let mut row = Self::normalize_against(table, &columns, row, strict)?;
761
764
  cells.push(
@@ -782,11 +785,11 @@ impl Db {
782
785
  for key in row.keys() {
783
786
  validate_identifier(key)?;
784
787
  }
785
- let column_set: std::collections::HashSet<&str> =
786
- columns.iter().map(|s| s.as_str()).collect();
787
- let row_keys: std::collections::HashSet<&str> =
788
- row.keys().map(|s| s.as_str()).collect();
789
- let extra: Vec<&str> = row_keys.difference(&column_set).copied().collect();
788
+ let extra: Vec<&str> = row
789
+ .keys()
790
+ .map(String::as_str)
791
+ .filter(|key| !columns.iter().any(|c| c.eq_ignore_ascii_case(key)))
792
+ .collect();
790
793
  if !extra.is_empty() {
791
794
  return Err(DbError::SchemaMismatch(format!(
792
795
  "extra columns not in table {}: {}",
@@ -794,7 +797,11 @@ impl Db {
794
797
  extra.join(", ")
795
798
  )));
796
799
  }
797
- let missing: Vec<&str> = column_set.difference(&row_keys).copied().collect();
800
+ let missing: Vec<&str> = columns
801
+ .iter()
802
+ .map(String::as_str)
803
+ .filter(|c| !row.keys().any(|key| key.eq_ignore_ascii_case(c)))
804
+ .collect();
798
805
  if !missing.is_empty() {
799
806
  return Err(DbError::SchemaMismatch(format!(
800
807
  "missing columns for table {}: {}",
@@ -802,15 +809,19 @@ impl Db {
802
809
  missing.join(", ")
803
810
  )));
804
811
  }
805
- Ok(row)
806
- } else {
807
- let mut normalized = HashMap::with_capacity(columns.len());
808
- for col in columns {
809
- let value = row.remove(col).unwrap_or(Value::Null);
810
- normalized.insert(col.clone(), value);
811
- }
812
- Ok(normalized)
813
812
  }
813
+ let mut normalized = HashMap::with_capacity(columns.len());
814
+ for col in columns {
815
+ let value = row.remove(col).or_else(|| {
816
+ let key = row
817
+ .keys()
818
+ .find(|key| key.eq_ignore_ascii_case(col))?
819
+ .clone();
820
+ row.remove(&key)
821
+ });
822
+ normalized.insert(col.clone(), value.unwrap_or(Value::Null));
823
+ }
824
+ Ok(normalized)
814
825
  }
815
826
 
816
827
  /// Insert a row into a table.
@@ -1565,8 +1576,8 @@ impl Db {
1565
1576
  let mut spare: Vec<ValueRef<'_>> = Vec::with_capacity(column_names.len());
1566
1577
  while let Some(row) = rows.next()? {
1567
1578
  let mut cells = recycled(spare);
1568
- for i in 0..column_names.len() {
1569
- cells.push(row.get_ref(i)?);
1579
+ for (i, column) in column_names.iter().enumerate() {
1580
+ cells.push(utf8_checked(row.get_ref(i)?, column)?);
1570
1581
  }
1571
1582
  on_row(&mut state, &cells);
1572
1583
  spare = recycled(cells);
@@ -1584,7 +1595,7 @@ impl Db {
1584
1595
  while let Some(row) = rows.next()? {
1585
1596
  let mut map = HashMap::new();
1586
1597
  for (i, name) in column_names.iter().enumerate() {
1587
- let val: rusqlite::types::Value = row.get(i)?;
1598
+ let val = rusqlite::types::Value::from(utf8_checked(row.get_ref(i)?, name)?);
1588
1599
  map.insert(name.clone(), Value::from(val));
1589
1600
  }
1590
1601
  results.push(map);
@@ -1631,7 +1642,8 @@ impl Db {
1631
1642
  return Err(DbError::WriteForbidden);
1632
1643
  }
1633
1644
  self.scope.warm(&reads);
1634
- let column_names: Vec<String> = stmt.column_names().iter().map(|s| s.to_string()).collect();
1645
+ let column_names =
1646
+ unique_column_names(stmt.column_names().iter().map(|s| s.to_string()).collect());
1635
1647
  Ok((stmt, column_names, reads))
1636
1648
  }
1637
1649
 
@@ -1900,6 +1912,17 @@ fn fill_cell(cell: &mut Value, source: ValueRef<'_>) {
1900
1912
  }
1901
1913
  }
1902
1914
 
1915
+ /// `cell`, unless it is text that is not valid UTF-8: rusqlite panics
1916
+ /// converting such text to an owned value.
1917
+ fn utf8_checked<'a>(cell: ValueRef<'a>, column: &str) -> Result<ValueRef<'a>> {
1918
+ match cell {
1919
+ ValueRef::Text(bytes) if std::str::from_utf8(bytes).is_err() => {
1920
+ Err(DbError::NotUtf8(column.to_string()))
1921
+ }
1922
+ _ => Ok(cell),
1923
+ }
1924
+ }
1925
+
1903
1926
  fn value_ref(value: &Value) -> ValueRef<'_> {
1904
1927
  match value {
1905
1928
  Value::Null => ValueRef::Null,
@@ -1991,6 +2014,27 @@ fn bind(stmt: &mut rusqlite::Statement<'_>, params: &Params) -> Result<()> {
1991
2014
  Ok(())
1992
2015
  }
1993
2016
 
2017
+ fn unique_column_names(names: Vec<String>) -> Vec<String> {
2018
+ let reserved: HashSet<String> = names.iter().cloned().collect();
2019
+ let mut used = HashSet::new();
2020
+ names
2021
+ .into_iter()
2022
+ .map(|name| {
2023
+ if used.insert(name.clone()) {
2024
+ return name;
2025
+ }
2026
+ let mut suffix = 1;
2027
+ loop {
2028
+ let candidate = format!("{name}:{suffix}");
2029
+ if !reserved.contains(&candidate) && used.insert(candidate.clone()) {
2030
+ return candidate;
2031
+ }
2032
+ suffix += 1;
2033
+ }
2034
+ })
2035
+ .collect()
2036
+ }
2037
+
1994
2038
  /// `cells` emptied and free to borrow anew, keeping its allocation.
1995
2039
  fn recycled<'a>(mut cells: Vec<ValueRef<'_>>) -> Vec<ValueRef<'a>> {
1996
2040
  cells.clear();
@@ -2013,6 +2057,15 @@ impl From<rusqlite::types::Value> for Value {
2013
2057
  mod tests {
2014
2058
  use super::*;
2015
2059
 
2060
+ #[test]
2061
+ fn duplicate_result_names_avoid_existing_projection_names() {
2062
+ let names = ["a", "a", "a:1", "a"]
2063
+ .into_iter()
2064
+ .map(str::to_string)
2065
+ .collect();
2066
+ assert_eq!(unique_column_names(names), ["a", "a:2", "a:1", "a:3"]);
2067
+ }
2068
+
2016
2069
  #[test]
2017
2070
  fn text_and_blob_values_bind_without_copying() {
2018
2071
  let text = Value::Text("abc".into());
@@ -2322,12 +2375,31 @@ mod tests {
2322
2375
  }
2323
2376
 
2324
2377
  #[test]
2325
- fn query_each_reads_text_that_is_not_utf8_lossily() {
2378
+ fn query_each_fails_on_text_that_is_not_utf8() {
2326
2379
  let db = titled_docs(&["a"]);
2327
2380
 
2328
- let (_, rows) = folded(&db, "SELECT CAST(x'ff41' AS TEXT) AS t");
2381
+ let error = db
2382
+ .query_each("SELECT CAST(x'ff41' AS TEXT) AS t", |_| (), |_, _| {})
2383
+ .unwrap_err();
2329
2384
 
2330
- assert_eq!(rows, [[Value::Text("\u{FFFD}A".into())]]);
2385
+ assert_eq!(
2386
+ error.to_string(),
2387
+ "column \"t\" holds text that is not valid UTF-8"
2388
+ );
2389
+ }
2390
+
2391
+ #[test]
2392
+ fn query_ordered_fails_on_text_that_is_not_utf8() {
2393
+ let db = titled_docs(&["a"]);
2394
+
2395
+ let error = db
2396
+ .query_ordered("SELECT CAST(x'ff41' AS TEXT) AS t")
2397
+ .unwrap_err();
2398
+
2399
+ assert_eq!(
2400
+ error.to_string(),
2401
+ "column \"t\" holds text that is not valid UTF-8"
2402
+ );
2331
2403
  }
2332
2404
 
2333
2405
  fn owned(cell: &ValueRef<'_>) -> Value {
@@ -2376,7 +2448,7 @@ mod tests {
2376
2448
 
2377
2449
  let held = db
2378
2450
  .query_each_ref(
2379
- "SELECT CAST(x'ff41' AS TEXT) AS t",
2451
+ "SELECT CAST(x'c3a941' AS TEXT) AS t",
2380
2452
  &Params::default(),
2381
2453
  |_| Vec::new(),
2382
2454
  |held: &mut Vec<Vec<u8>>, cells| match cells {
@@ -2386,7 +2458,7 @@ mod tests {
2386
2458
  )
2387
2459
  .unwrap();
2388
2460
 
2389
- assert_eq!(held, [b"\xffA".to_vec()]);
2461
+ assert_eq!(held, [b"\xc3\xa9A".to_vec()]);
2390
2462
  }
2391
2463
 
2392
2464
  #[test]
@@ -2963,6 +3035,64 @@ mod tests {
2963
3035
  assert_eq!(normalized.len(), 2);
2964
3036
  }
2965
3037
 
3038
+ #[test]
3039
+ fn normalize_row_keys_a_case_differing_key_by_its_column() {
3040
+ let db = Db::new().unwrap();
3041
+ db.create_table("t", "CREATE TABLE t (Name TEXT)").unwrap();
3042
+ let row = HashMap::from([("nAME".into(), Value::Text("apple".into()))]);
3043
+ for strict in [false, true] {
3044
+ let normalized = db.normalize_row("t", &row, strict).unwrap();
3045
+ assert_eq!(
3046
+ normalized,
3047
+ HashMap::from([("Name".into(), Value::Text("apple".into()))])
3048
+ );
3049
+ }
3050
+ }
3051
+
3052
+ #[test]
3053
+ fn normalize_row_prefers_the_exact_key_over_a_case_variant() {
3054
+ let db = Db::new().unwrap();
3055
+ db.create_table("t", "CREATE TABLE t (name TEXT)").unwrap();
3056
+ let row = HashMap::from([
3057
+ ("NAME".into(), Value::Text("variant".into())),
3058
+ ("name".into(), Value::Text("exact".into())),
3059
+ ]);
3060
+ let normalized = db.normalize_row("t", &row, false).unwrap();
3061
+ assert_eq!(normalized["name"], Value::Text("exact".into()));
3062
+ }
3063
+
3064
+ #[test]
3065
+ fn normalize_row_does_not_fold_non_ascii_case() {
3066
+ let db = Db::new().unwrap();
3067
+ db.create_table("t", "CREATE TABLE t (\"é\" TEXT)").unwrap();
3068
+ let row = HashMap::from([("É".into(), Value::Text("x".into()))]);
3069
+ let normalized = db.normalize_row("t", &row, false).unwrap();
3070
+ assert_eq!(normalized["é"], Value::Null);
3071
+ }
3072
+
3073
+ #[test]
3074
+ fn normalize_row_strict_names_exactly_the_unmatched_keys_and_columns() {
3075
+ let db = Db::new().unwrap();
3076
+ db.create_table("t", "CREATE TABLE t (Name TEXT, n INTEGER)")
3077
+ .unwrap();
3078
+ let extra = HashMap::from([
3079
+ ("NAME".into(), Value::Null),
3080
+ ("N".into(), Value::Null),
3081
+ ("color".into(), Value::Null),
3082
+ ]);
3083
+ let err = db.normalize_row("t", &extra, true).unwrap_err();
3084
+ assert!(
3085
+ matches!(&err, DbError::SchemaMismatch(m) if m == "extra columns not in table t: color"),
3086
+ "got: {err}"
3087
+ );
3088
+ let missing = HashMap::from([("NAME".into(), Value::Null)]);
3089
+ let err = db.normalize_row("t", &missing, true).unwrap_err();
3090
+ assert!(
3091
+ matches!(&err, DbError::SchemaMismatch(m) if m == "missing columns for table t: n"),
3092
+ "got: {err}"
3093
+ );
3094
+ }
3095
+
2966
3096
  #[test]
2967
3097
  fn insert_and_query_real_value() {
2968
3098
  let db = Db::new().unwrap();
@@ -3988,6 +4118,53 @@ mod tests {
3988
4118
  );
3989
4119
  }
3990
4120
 
4121
+ #[test]
4122
+ fn shape_rows_fills_a_column_from_a_key_differing_only_by_case() {
4123
+ let db = Db::new().unwrap();
4124
+ db.create_table("t", "CREATE TABLE t (Id TEXT, n INTEGER)")
4125
+ .unwrap();
4126
+ for strict in [false, true] {
4127
+ let out = db
4128
+ .shape_rows(
4129
+ "t",
4130
+ json_rows(serde_json::json!([{"N":1,"iD":"a"}])),
4131
+ strict,
4132
+ )
4133
+ .unwrap();
4134
+ assert_eq!(out.columns, vec!["Id", "n"]);
4135
+ assert_eq!(out.emitted, vec![true, true]);
4136
+ assert_eq!(
4137
+ out.cells,
4138
+ vec![vec![Value::Text("a".into()), Value::Integer(1)]]
4139
+ );
4140
+ }
4141
+ }
4142
+
4143
+ #[test]
4144
+ fn shape_rows_strict_names_exactly_the_unmatched_keys_and_columns() {
4145
+ let db = Db::new().unwrap();
4146
+ db.create_table("t", "CREATE TABLE t (Name TEXT, n INTEGER)")
4147
+ .unwrap();
4148
+ let err = db
4149
+ .shape_rows(
4150
+ "t",
4151
+ json_rows(serde_json::json!([{"NAME":"x","N":1,"color":"red"}])),
4152
+ true,
4153
+ )
4154
+ .unwrap_err();
4155
+ assert!(
4156
+ matches!(&err, DbError::SchemaMismatch(m) if m == "extra columns not in table t: color"),
4157
+ "got: {err}"
4158
+ );
4159
+ let err = db
4160
+ .shape_rows("t", json_rows(serde_json::json!([{"NAME":"x"}])), true)
4161
+ .unwrap_err();
4162
+ assert!(
4163
+ matches!(&err, DbError::SchemaMismatch(m) if m == "missing columns for table t: n"),
4164
+ "got: {err}"
4165
+ );
4166
+ }
4167
+
3991
4168
  #[test]
3992
4169
  fn shape_rows_strict_names_the_extra_columns() {
3993
4170
  let db = Db::new().unwrap();
@@ -5504,6 +5681,22 @@ mod tests {
5504
5681
  );
5505
5682
  }
5506
5683
 
5684
+ #[test]
5685
+ fn shape_value_rows_marks_a_column_emitted_by_a_case_differing_key() {
5686
+ let db = Db::new().unwrap();
5687
+ db.create_table("t", "CREATE TABLE t (id INTEGER, Name TEXT)")
5688
+ .unwrap();
5689
+ let rows = vec![HashMap::from([(
5690
+ "nAME".to_string(),
5691
+ Value::Text("a".into()),
5692
+ )])];
5693
+
5694
+ let out = db.shape_value_rows("t", rows, false).unwrap();
5695
+
5696
+ assert_eq!(out.emitted, vec![false, true]);
5697
+ assert_eq!(out.cells, vec![vec![Value::Null, Value::Text("a".into())]]);
5698
+ }
5699
+
5507
5700
  #[test]
5508
5701
  fn shape_value_rows_strict_rejects_a_missing_column() {
5509
5702
  let db = Db::new().unwrap();