dirsql 0.4.107__tar.gz → 0.4.109__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 (427) hide show
  1. {dirsql-0.4.107 → dirsql-0.4.109}/Cargo.lock +2 -2
  2. {dirsql-0.4.107 → dirsql-0.4.109}/PKG-INFO +1 -1
  3. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/Cargo.toml +1 -1
  4. dirsql-0.4.109/packages/python/e2e-attestations/bot-1416-watch-speed-1008.json +7 -0
  5. dirsql-0.4.109/packages/python/e2e-attestations/bot-1461-row-footprint-1008.json +7 -0
  6. dirsql-0.4.109/packages/python/e2e-attestations/bot-1462-paired-speed-harness-1008.json +7 -0
  7. dirsql-0.4.109/packages/python/tests/e2e/speed_of_light.py +205 -0
  8. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/Cargo.toml +1 -1
  9. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/db.rs +1 -1
  10. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/lib.rs +69 -6
  11. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/on_file.rs +43 -5
  12. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/vtab.rs +56 -25
  13. dirsql-0.4.107/packages/python/tests/e2e/speed_of_light.py +0 -124
  14. {dirsql-0.4.107 → dirsql-0.4.109}/Cargo.toml +0 -0
  15. {dirsql-0.4.107 → dirsql-0.4.109}/README.md +0 -0
  16. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/__init__.py +0 -0
  17. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/_async.py +0 -0
  18. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/_dirsql.pyi +0 -0
  19. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/__init__.py +0 -0
  20. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/__init__.py +0 -0
  21. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
  22. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
  23. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
  24. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
  25. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/main.py +0 -0
  26. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/resolve_config_extensions.py +0 -0
  27. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/plan_entry_path.py +0 -0
  28. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/py.typed +0 -0
  29. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/read_config.py +0 -0
  30. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/resolve_configs_extension_specs.py +0 -0
  31. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/resolve_extension.py +0 -0
  32. {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/resolve_package.py +0 -0
  33. {dirsql-0.4.107 → dirsql-0.4.109}/docs/.claude/CLAUDE.md +0 -0
  34. {dirsql-0.4.107 → dirsql-0.4.109}/docs/.vitepress/config.ts +0 -0
  35. {dirsql-0.4.107 → dirsql-0.4.109}/docs/.vitepress/theme/index.ts +0 -0
  36. {dirsql-0.4.107 → dirsql-0.4.109}/docs/.vitepress/theme/lang.ts +0 -0
  37. {dirsql-0.4.107 → dirsql-0.4.109}/docs/AGENTS.md +0 -0
  38. {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/.gitattributes +0 -0
  39. {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/recipes.md +0 -0
  40. {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/troubleshooting.md +0 -0
  41. {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/usage.md +0 -0
  42. {dirsql-0.4.107 → dirsql-0.4.109}/docs/explanation.md +0 -0
  43. {dirsql-0.4.107 → dirsql-0.4.109}/docs/getting-started.md +0 -0
  44. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/agent-skill.md +0 -0
  45. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/columns-from-paths.md +0 -0
  46. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/define-tables.md +0 -0
  47. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/embed.md +0 -0
  48. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/extract-from-contents.md +0 -0
  49. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/load-extension.md +0 -0
  50. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/parse-files-into-columns.md +0 -0
  51. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/persist.md +0 -0
  52. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/query-json.md +0 -0
  53. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/query-without-config.md +0 -0
  54. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/react-to-changes.md +0 -0
  55. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/search-by-meaning.md +0 -0
  56. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/search-indexes.md +0 -0
  57. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/skip-files.md +0 -0
  58. {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/write-a-plugin.md +0 -0
  59. {dirsql-0.4.107 → dirsql-0.4.109}/docs/index.md +0 -0
  60. {dirsql-0.4.107 → dirsql-0.4.109}/docs/package.json +0 -0
  61. {dirsql-0.4.107 → dirsql-0.4.109}/docs/plugins.md +0 -0
  62. {dirsql-0.4.107 → dirsql-0.4.109}/docs/pnpm-lock.yaml +0 -0
  63. {dirsql-0.4.107 → dirsql-0.4.109}/docs/pnpm-workspace.yaml +0 -0
  64. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/cli.md +0 -0
  65. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/columns.md +0 -0
  66. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/config.md +0 -0
  67. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/glob.md +0 -0
  68. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/hooks.md +0 -0
  69. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/http-api.md +0 -0
  70. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/path-tables.md +0 -0
  71. {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/sdk.md +0 -0
  72. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/CHANGELOG.md +0 -0
  73. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/MIGRATIONS.md +0 -0
  74. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/README.md +0 -0
  75. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  76. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
  77. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  78. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
  79. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  80. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
  81. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
  82. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
  83. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  84. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
  85. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
  86. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
  87. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
  88. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
  89. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
  90. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
  91. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
  92. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
  93. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
  94. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
  95. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
  96. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
  97. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
  98. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
  99. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
  100. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
  101. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
  102. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
  103. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  104. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  105. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-02-readme-docs-host.md +0 -0
  106. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  107. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-03-path-table-directory-one-level.md +0 -0
  108. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  109. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-brace-groups-follow-bash.md +0 -0
  110. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-cli-context.md +0 -0
  111. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-gitignore-requires-repo.md +0 -0
  112. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-path-table-node-modules-named.md +0 -0
  113. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-path-table-symlinks.md +0 -0
  114. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-path-table-trailing-slash.md +0 -0
  115. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-unbalanced-brackets-literal.md +0 -0
  116. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-braces.md +0 -0
  117. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-directory.md +0 -0
  118. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-dotnames.md +0 -0
  119. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-gitignore.md +0 -0
  120. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-globs-anchor-at-the-config.md +0 -0
  121. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-links.md +0 -0
  122. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-one-glob-compiler.md +0 -0
  123. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-plugin-no-default-records.md +0 -0
  124. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-watch-broadcast.md +0 -0
  125. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-zero-match-config-table-warning.md +0 -0
  126. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-bracket-slash-literal.md +0 -0
  127. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-dotdot-and-double-slashes.md +0 -0
  128. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-dotdot-as-written.md +0 -0
  129. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-glob-walks-node-modules.md +0 -0
  130. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-literal-gitignore.md +0 -0
  131. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-on-file-ndjson.md +0 -0
  132. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-parallel-hooks.md +0 -0
  133. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-persist-cache-outside-root.md +0 -0
  134. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-posix-classes-unicode.md +0 -0
  135. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-spread-few-large-files.md +0 -0
  136. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-08-small-walks-stay-on-one-thread.md +0 -0
  137. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-08-watch-requires-named-table.md +0 -0
  138. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/README.md +0 -0
  139. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/conftest.py +0 -0
  140. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.claude/CLAUDE.md +0 -0
  141. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.vitepress/config.ts +0 -0
  142. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.vitepress/theme/index.ts +0 -0
  143. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
  144. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/AGENTS.md +0 -0
  145. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/.gitattributes +0 -0
  146. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/recipes.md +0 -0
  147. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/troubleshooting.md +0 -0
  148. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/usage.md +0 -0
  149. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/explanation.md +0 -0
  150. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/getting-started.md +0 -0
  151. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/agent-skill.md +0 -0
  152. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/columns-from-paths.md +0 -0
  153. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/define-tables.md +0 -0
  154. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/embed.md +0 -0
  155. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/extract-from-contents.md +0 -0
  156. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/load-extension.md +0 -0
  157. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
  158. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/persist.md +0 -0
  159. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/query-json.md +0 -0
  160. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/query-without-config.md +0 -0
  161. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/react-to-changes.md +0 -0
  162. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/search-by-meaning.md +0 -0
  163. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/search-indexes.md +0 -0
  164. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/skip-files.md +0 -0
  165. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/write-a-plugin.md +0 -0
  166. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/index.md +0 -0
  167. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/package.json +0 -0
  168. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/plugins.md +0 -0
  169. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/pnpm-lock.yaml +0 -0
  170. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/pnpm-workspace.yaml +0 -0
  171. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/cli.md +0 -0
  172. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/columns.md +0 -0
  173. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/config.md +0 -0
  174. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/glob.md +0 -0
  175. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/hooks.md +0 -0
  176. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/http-api.md +0 -0
  177. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/path-tables.md +0 -0
  178. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/sdk.md +0 -0
  179. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1334-listing-exemptions.json +0 -0
  180. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1397-watch-broadcast.json +0 -0
  181. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1398-inotify-error.json +0 -0
  182. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1399-server-cors.json +0 -0
  183. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1401-watch-scope.json +0 -0
  184. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1406-watch-no-tables-1008.json +0 -0
  185. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1407-child-place-depth.json +0 -0
  186. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1414-stat-speed.json +0 -0
  187. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1415-server-speed.json +0 -0
  188. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1418-node-modules.json +0 -0
  189. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1419-dotdot-slashes.json +0 -0
  190. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1420-bracket-slash.json +0 -0
  191. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1421-posix-unicode.json +0 -0
  192. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1422-literal-gitignore.json +0 -0
  193. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1426-walk-spawn-threshold-1008.json +0 -0
  194. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1428-ndjson-hooks.json +0 -0
  195. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1429-parallel-hooks.json +0 -0
  196. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1430-dotdot-as-written.json +0 -0
  197. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1431-persist-cache-dir.json +0 -0
  198. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1437-test-cache-isolation.json +0 -0
  199. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1440-fix-main-build.json +0 -0
  200. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1448-wildcard-ignored-prefix.json +0 -0
  201. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1451-named-dir-never-hidden.json +0 -0
  202. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1454-split-few-files.json +0 -0
  203. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1457-incremental-refresh-1008.json +0 -0
  204. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1459-rescan-on-overflow-1008.json +0 -0
  205. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-fix-main-place-depth-20261007.json +0 -0
  206. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
  207. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
  208. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
  209. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
  210. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
  211. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
  212. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
  213. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
  214. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
  215. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
  216. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
  217. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
  218. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
  219. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
  220. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
  221. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
  222. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
  223. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
  224. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
  225. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
  226. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
  227. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
  228. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
  229. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
  230. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1196-join-index.json +0 -0
  231. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1199-glob-prefix.json +0 -0
  232. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1201-single-walk.json +0 -0
  233. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1204-embed-batch.json +0 -0
  234. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +0 -0
  235. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +0 -0
  236. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1215-on-file-batch.json +0 -0
  237. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1216-on-file-cleanup.json +0 -0
  238. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1223-config-glob-rule.json +0 -0
  239. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1223-dir-one-level.json +0 -0
  240. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1225-plugin-no-default.json +0 -0
  241. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1226-stale-docs.json +0 -0
  242. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-anchor-plumbing.json +0 -0
  243. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-config-anchor.json +0 -0
  244. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-multi-watch.json +0 -0
  245. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-zero-match.json +0 -0
  246. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1238-parallel-walk.json +0 -0
  247. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1241-content-prefetch.json +0 -0
  248. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1243-ingest-perf.json +0 -0
  249. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1249-dotfiles.json +0 -0
  250. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1256-symlinks.json +0 -0
  251. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1257-trailing-slash.json +0 -0
  252. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-braces.json +0 -0
  253. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-directory.json +0 -0
  254. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-dotnames.json +0 -0
  255. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-gitignore.json +0 -0
  256. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-links.json +0 -0
  257. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-one-pattern.json +0 -0
  258. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1262-brace-literal.json +0 -0
  259. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1264-context.json +0 -0
  260. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1267-posix-classes.json +0 -0
  261. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1268-unbalanced-brackets.json +0 -0
  262. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1269-utf8-chars.json +0 -0
  263. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1271-node-modules-named.json +0 -0
  264. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1272-ancestor-gitignore.json +0 -0
  265. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1273-walk-prune.json +0 -0
  266. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1285-ingest-fast.json +0 -0
  267. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1286-nested-repo.json +0 -0
  268. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1305-glob-speed.json +0 -0
  269. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1308-glob-speed.json +0 -0
  270. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1310-glob-speed.json +0 -0
  271. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1311-glob-speed.json +0 -0
  272. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1312-glob-speed.json +0 -0
  273. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1318-lazy-stat.json +0 -0
  274. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1320-walk-speed.json +0 -0
  275. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1322-walk-readdir.json +0 -0
  276. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1325-parallel-walk.json +0 -0
  277. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1326-stream-rows.json +0 -0
  278. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-python-dirsql-patch.json +0 -0
  279. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-python-extensions-patch.json +0 -0
  280. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-scanner-effects.json +0 -0
  281. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-scanner-gitignore.json +0 -0
  282. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-vtab-prefetch-flag.json +0 -0
  283. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-vtab-read.json +0 -0
  284. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1351-dot-dirs.json +0 -0
  285. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
  286. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
  287. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
  288. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
  289. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
  290. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
  291. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
  292. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
  293. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
  294. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
  295. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
  296. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
  297. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
  298. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
  299. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
  300. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
  301. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
  302. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
  303. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
  304. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
  305. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
  306. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
  307. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
  308. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
  309. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
  310. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
  311. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
  312. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-02-readme-docs-host.md +0 -0
  313. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
  314. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-03-path-table-directory-one-level.md +0 -0
  315. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
  316. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-brace-groups-follow-bash.md +0 -0
  317. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-cli-context.md +0 -0
  318. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-gitignore-requires-repo.md +0 -0
  319. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-path-table-node-modules-named.md +0 -0
  320. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-path-table-symlinks.md +0 -0
  321. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-path-table-trailing-slash.md +0 -0
  322. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-unbalanced-brackets-literal.md +0 -0
  323. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-braces.md +0 -0
  324. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-directory.md +0 -0
  325. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-dotnames.md +0 -0
  326. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-gitignore.md +0 -0
  327. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-globs-anchor-at-the-config.md +0 -0
  328. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-links.md +0 -0
  329. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-one-glob-compiler.md +0 -0
  330. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-plugin-no-default-records.md +0 -0
  331. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-watch-broadcast.md +0 -0
  332. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-zero-match-config-table-warning.md +0 -0
  333. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-bracket-slash-literal.md +0 -0
  334. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-dotdot-and-double-slashes.md +0 -0
  335. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-dotdot-as-written.md +0 -0
  336. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-glob-walks-node-modules.md +0 -0
  337. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-literal-gitignore.md +0 -0
  338. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-on-file-ndjson.md +0 -0
  339. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-parallel-hooks.md +0 -0
  340. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-persist-cache-outside-root.md +0 -0
  341. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-posix-classes-unicode.md +0 -0
  342. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-spread-few-large-files.md +0 -0
  343. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-08-small-walks-stay-on-one-thread.md +0 -0
  344. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-08-watch-requires-named-table.md +0 -0
  345. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/README.md +0 -0
  346. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/src/lib.rs +0 -0
  347. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/testing-conventions.toml +0 -0
  348. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/__init__.py +0 -0
  349. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/conftest.py +0 -0
  350. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/e2e/__init__.py +0 -0
  351. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
  352. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
  353. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/integration/__init__.py +0 -0
  354. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/integration/binding/__init__.py +0 -0
  355. {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/integration/hermetic/__init__.py +0 -0
  356. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/CHANGELOG.md +0 -0
  357. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/MIGRATIONS.md +0 -0
  358. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/README.md +0 -0
  359. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/db_bench.rs +0 -0
  360. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/differ_bench.rs +0 -0
  361. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/matcher_bench.rs +0 -0
  362. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/scanner_bench.rs +0 -0
  363. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/.gitattributes +0 -0
  364. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/recipes.md +0 -0
  365. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/troubleshooting.md +0 -0
  366. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/usage.md +0 -0
  367. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/explanation.md +0 -0
  368. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/getting-started.md +0 -0
  369. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/agent-skill.md +0 -0
  370. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/columns-from-paths.md +0 -0
  371. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/define-tables.md +0 -0
  372. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/embed.md +0 -0
  373. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/extract-from-contents.md +0 -0
  374. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/load-extension.md +0 -0
  375. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
  376. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/persist.md +0 -0
  377. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/query-json.md +0 -0
  378. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/query-without-config.md +0 -0
  379. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/react-to-changes.md +0 -0
  380. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/search-by-meaning.md +0 -0
  381. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/search-indexes.md +0 -0
  382. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/skip-files.md +0 -0
  383. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/write-a-plugin.md +0 -0
  384. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/index.md +0 -0
  385. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/plugins.md +0 -0
  386. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/cli.md +0 -0
  387. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/columns.md +0 -0
  388. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/config.md +0 -0
  389. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/glob.md +0 -0
  390. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/hooks.md +0 -0
  391. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/http-api.md +0 -0
  392. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/path-tables.md +0 -0
  393. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/sdk.md +0 -0
  394. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/bin/dirsql.rs +0 -0
  395. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/brace.rs +0 -0
  396. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/context.rs +0 -0
  397. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/execute.rs +0 -0
  398. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/init.rs +0 -0
  399. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/mod.rs +0 -0
  400. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/repl.rs +0 -0
  401. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/router.rs +0 -0
  402. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/run.rs +0 -0
  403. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/serialize.rs +0 -0
  404. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/server.rs +0 -0
  405. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/table.rs +0 -0
  406. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/command.rs +0 -0
  407. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/config.rs +0 -0
  408. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/default_config.toml +0 -0
  409. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/differ.rs +0 -0
  410. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/extension_resolution.rs +0 -0
  411. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/functions.rs +0 -0
  412. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/infer.rs +0 -0
  413. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/launcher.rs +0 -0
  414. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/listing.rs +0 -0
  415. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/matcher.rs +0 -0
  416. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/parsed_vtab.rs +0 -0
  417. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/path_table.rs +0 -0
  418. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/persist.rs +0 -0
  419. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/posix_class.rs +0 -0
  420. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/progress.rs +0 -0
  421. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/row_event_flat.rs +0 -0
  422. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/scanner.rs +0 -0
  423. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/sql_literal.rs +0 -0
  424. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/tree_walk.rs +0 -0
  425. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/vtab_scaffold.rs +0 -0
  426. {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/watcher.rs +0 -0
  427. {dirsql-0.4.107 → dirsql-0.4.109}/pyproject.toml +0 -0
@@ -573,7 +573,7 @@ dependencies = [
573
573
 
574
574
  [[package]]
575
575
  name = "dirsql"
576
- version = "0.4.107"
576
+ version = "0.4.109"
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.107"
628
+ version = "0.4.109"
629
629
  dependencies = [
630
630
  "dirsql",
631
631
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirsql
3
- Version: 0.4.107
3
+ Version: 0.4.109
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
@@ -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.107"
7
+ version = "0.4.109"
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,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/watch_speed_test.py tests/e2e/on_file_query_test.py tests/e2e/server_speed_test.py -q -p no:cacheprovider",
3
+ "ran_at": 1791548609,
4
+ "exit_code": 0,
5
+ "commit": "ea38a739b990bc5135cc32ba9b61193fd0a2c1e1",
6
+ "branch": "bot/1416-watch-speed-1008"
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/stat_speed_test.py tests/e2e/glob_speed_one_level_test.py tests/e2e/glob_speed_recursive_test.py tests/e2e/use_case_dossier_test.py -x -q",
3
+ "ran_at": 1791489188,
4
+ "exit_code": 0,
5
+ "commit": "fe75eb79358f5324a5af3395d1e2383e6233c98f",
6
+ "branch": "bot/1461-row-footprint-1008"
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "command": "uv run python -m pytest tests/e2e/glob_speed_one_level_test.py -x -q -rA",
3
+ "ran_at": 1791492173,
4
+ "exit_code": 0,
5
+ "commit": "ce22059009267d7759e47aa47cba10e28fd6543c",
6
+ "branch": "bot/1462-paired-speed-harness-1008"
7
+ }
@@ -0,0 +1,205 @@
1
+ """Harness for the use-case speed-of-light tests.
2
+
3
+ Each use-case test asks one question of a generated corpus twice: once through
4
+ the fastest native pipeline (find, sort, join, awk, a single parser process)
5
+ and once through the real `dirsql` launcher. The rows must be identical and
6
+ dirsql's wall time, after subtracting the launcher's measured startup, must be
7
+ within ten percent of native. The bar is a ratio, never a millisecond number,
8
+ so it holds on any machine.
9
+
10
+ Native and dirsql run as alternating pairs (native first, then dirsql first,
11
+ and so on) after one discarded warm-up pair, so drift in machine speed hits
12
+ both sides alike. The verdict is the median of the per-pair ratios. Startup is
13
+ the median of `dirsql query 'SELECT 1'` runs with the first dropped, and
14
+ native's own is the median of `bash -c true` the same way; each is subtracted
15
+ from its side. A corpus too small to time is grown by doubling until the
16
+ native pipeline takes a second, unless the corpus ceiling stops it sooner.
17
+ Output is captured as bytes and decoded after the clock stops.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import os
24
+ import shutil
25
+ import statistics
26
+ import subprocess
27
+ import tempfile
28
+ import time
29
+ from dataclasses import dataclass
30
+ from typing import NamedTuple
31
+
32
+ ONE_SECOND = 1.0
33
+ TOLERANCE = 1.1
34
+ STARTUP_PROBES = 11
35
+ PAIRS = 100
36
+ HOPELESS_STREAK = 3
37
+ NATIVE_ENV = {**os.environ, "LC_ALL": "C"}
38
+
39
+
40
+ class Startup(NamedTuple):
41
+ dirsql: float
42
+ native: float
43
+
44
+
45
+ @dataclass
46
+ class Paired:
47
+ native_rows: object
48
+ dirsql_rows: object
49
+ native_seconds: list
50
+ dirsql_seconds: list
51
+
52
+
53
+ def cli() -> str:
54
+ dirsql = shutil.which("dirsql")
55
+ assert dirsql is not None, (
56
+ "`dirsql` console script not on PATH -- run `uv run maturin develop`"
57
+ )
58
+ return dirsql
59
+
60
+
61
+ def timed(argv, cwd, timeout, env=None):
62
+ started = time.perf_counter()
63
+ proc = subprocess.run(
64
+ argv,
65
+ cwd=str(cwd),
66
+ stdin=subprocess.DEVNULL,
67
+ capture_output=True,
68
+ timeout=timeout,
69
+ env=env,
70
+ check=False,
71
+ )
72
+ seconds = time.perf_counter() - started
73
+ proc.stdout = proc.stdout.decode()
74
+ proc.stderr = proc.stderr.decode()
75
+ return proc, seconds
76
+
77
+
78
+ def timed_native(argv, cwd, timeout=600):
79
+ return timed(argv, cwd, timeout, env=NATIVE_ENV)
80
+
81
+
82
+ def median_after_first(samples):
83
+ return statistics.median(samples[1:])
84
+
85
+
86
+ def probe_seconds(argv, cwd, env=None) -> float:
87
+ samples = []
88
+ for _ in range(STARTUP_PROBES):
89
+ proc, seconds = timed(argv, cwd, timeout=60, env=env)
90
+ assert proc.returncode == 0, proc.stderr
91
+ samples.append(seconds)
92
+ return median_after_first(samples)
93
+
94
+
95
+ def startup_seconds(argv=None, cwd=None) -> Startup:
96
+ argv = [cli(), "query", "SELECT 1"] if argv is None else argv
97
+ with tempfile.TemporaryDirectory() as empty:
98
+ dirsql = probe_seconds(argv, cwd or empty)
99
+ native = probe_seconds(["bash", "-c", "true"], empty, NATIVE_ENV)
100
+ return Startup(dirsql, native)
101
+
102
+
103
+ def dirsql_rows(proc, columns):
104
+ assert proc.returncode == 0, (
105
+ f"dirsql failed: stdout={proc.stdout!r} stderr={proc.stderr!r}"
106
+ )
107
+ return [tuple(row[column] for column in columns) for row in json.loads(proc.stdout)]
108
+
109
+
110
+ def grow_until_native_takes_a_second(build, native, start, ceiling):
111
+ """Grow the corpus by doubling until `native` runs for a second.
112
+
113
+ `build(lo, hi)` adds items `lo` through `hi - 1` to the corpus already
114
+ holding `lo` items, so each doubling writes only the new half.
115
+ """
116
+ built, size = 0, start
117
+ while True:
118
+ build(built, size)
119
+ os.sync()
120
+ built = size
121
+ _, seconds = native()
122
+ if seconds >= ONE_SECOND or size >= ceiling:
123
+ return size
124
+ size *= 2
125
+
126
+
127
+ def dirsql_timeout(native_seconds: float) -> float:
128
+ return max(60.0, 30 * native_seconds)
129
+
130
+
131
+ def pair_ratios(native_seconds, dirsql_seconds, startup):
132
+ ratios = []
133
+ for native, dirsql in zip(native_seconds, dirsql_seconds):
134
+ net = native - startup.native
135
+ assert net > 0, (
136
+ f"native {native:.3f}s is under its startup {startup.native:.3f}s"
137
+ )
138
+ ratios.append((dirsql - startup.dirsql) / net)
139
+ return ratios
140
+
141
+
142
+ def is_hopeless(native, dirsql, startup):
143
+ return dirsql - startup.dirsql > 10 * TOLERANCE * (native - startup.native)
144
+
145
+
146
+ def run_or_fail(run, name):
147
+ try:
148
+ return run()
149
+ except subprocess.TimeoutExpired as expired:
150
+ raise AssertionError(
151
+ f"{name} did not finish within {expired.timeout:.0f}s"
152
+ ) from None
153
+
154
+
155
+ def paired(native, dirsql, startup, pairs=PAIRS):
156
+ """Alternate `native()` and `dirsql(timeout)`, native first on even pairs.
157
+
158
+ Both return `(rows, seconds)`. The first pair is a discarded warm-up that
159
+ also sets dirsql's timeout. A run that raises `TimeoutExpired` fails the
160
+ test at once. Three hopeless pairs in a row end the series early: noise
161
+ does not account for an order of magnitude, so more pairs cannot bring
162
+ the median under the bar.
163
+ """
164
+ native_rows, native_warm = run_or_fail(native, "native")
165
+ timeout = dirsql_timeout(native_warm)
166
+
167
+ def run_dirsql():
168
+ return run_or_fail(lambda: dirsql(timeout), "dirsql")
169
+
170
+ dirsql_rows_, _ = run_dirsql()
171
+ native_times, dirsql_times, streak = [], [], 0
172
+ for index in range(pairs):
173
+ if index % 2 == 0:
174
+ native_rows, native_seconds = run_or_fail(native, "native")
175
+ dirsql_rows_, dirsql_seconds = run_dirsql()
176
+ else:
177
+ dirsql_rows_, dirsql_seconds = run_dirsql()
178
+ native_rows, native_seconds = run_or_fail(native, "native")
179
+ native_times.append(native_seconds)
180
+ dirsql_times.append(dirsql_seconds)
181
+ streak = (
182
+ streak + 1 if is_hopeless(native_seconds, dirsql_seconds, startup) else 0
183
+ )
184
+ if streak == HOPELESS_STREAK:
185
+ break
186
+ return Paired(native_rows, dirsql_rows_, native_times, dirsql_times)
187
+
188
+
189
+ def summarize(ratios):
190
+ low, high = min(ratios), max(ratios)
191
+ median = statistics.median(ratios)
192
+ return low, median, high, (high - low) / median
193
+
194
+
195
+ def assert_speed_of_light(name, result, startup):
196
+ ratios = pair_ratios(result.native_seconds, result.dirsql_seconds, startup)
197
+ low, median, high, spread = summarize(ratios)
198
+ line = (
199
+ f"{name}: {len(ratios)} pairs, ratio min {low:.3f} median {median:.3f}"
200
+ f" max {high:.3f} spread {spread:.0%}, native median"
201
+ f" {statistics.median(result.native_seconds):.3f}s, startup"
202
+ f" dirsql {startup.dirsql:.3f}s native {startup.native:.3f}s"
203
+ )
204
+ print(f"SPEED {line}")
205
+ assert median <= TOLERANCE, f"{line}; median is over {TOLERANCE}x"
@@ -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.107"
9
+ version = "0.4.109"
10
10
  edition.workspace = true
11
11
  # Literal `license` (not `license.workspace = true`) because
12
12
  # putitoutthere's preflight check reads `[package].license` directly
@@ -779,7 +779,7 @@ impl Db {
779
779
  };
780
780
  let mut stored: Vec<(i64, Vec<Value>)> = Vec::new();
781
781
  {
782
- let mut stmt = self.conn.prepare(&select)?;
782
+ let mut stmt = self.conn.prepare_cached(&select)?;
783
783
  let mut cursor = stmt.query([])?;
784
784
  while let Some(row) = cursor.next()? {
785
785
  let cells = (0..columns.len())
@@ -655,15 +655,31 @@ impl DirSQL {
655
655
  /// the TypeScript binding).
656
656
  #[doc(hidden)]
657
657
  pub fn apply_file_events(&self, events: Vec<FileEvent>) -> Vec<RowEvent> {
658
+ self.apply_file_events_draining(events, Vec::new)
659
+ }
660
+
661
+ /// [`apply_file_events`](Self::apply_file_events), asking `late` for events
662
+ /// that arrived while the batch was being routed, until it returns none,
663
+ /// before any per-table hook runs. One write reaches the watcher as
664
+ /// several events; a hook run for the first would run again for the rest.
665
+ fn apply_file_events_draining(
666
+ &self,
667
+ events: Vec<FileEvent>,
668
+ mut late: impl FnMut() -> Vec<FileEvent>,
669
+ ) -> Vec<RowEvent> {
658
670
  let mut pending = PendingRefresh::default();
659
671
  let mut out = Vec::new();
660
672
  let mut rescan = false;
661
- for fe in events {
662
- if fe == FileEvent::Rescan {
663
- rescan = true;
664
- continue;
673
+ let mut batch = events;
674
+ while !batch.is_empty() {
675
+ for fe in batch {
676
+ if fe == FileEvent::Rescan {
677
+ rescan = true;
678
+ continue;
679
+ }
680
+ out.extend(self.route_file_event(fe, &mut pending));
665
681
  }
666
- out.extend(self.route_file_event(fe, &mut pending));
682
+ batch = late();
667
683
  }
668
684
  if rescan {
669
685
  out.extend(self.rescan(&mut pending));
@@ -710,7 +726,17 @@ impl DirSQL {
710
726
  events
711
727
  };
712
728
 
713
- Ok(self.apply_file_events(file_events))
729
+ Ok(self.apply_file_events_draining(file_events, || self.queued_file_events()))
730
+ }
731
+
732
+ /// The events the watcher already holds, without waiting for more.
733
+ fn queued_file_events(&self) -> Vec<FileEvent> {
734
+ self.inner
735
+ .watcher
736
+ .lock()
737
+ .ok()
738
+ .and_then(|guard| guard.as_ref().map(Watcher::try_recv_all))
739
+ .unwrap_or_default()
714
740
  }
715
741
 
716
742
  /// Process a single [`FileEvent`], mutating the DB and cache as needed.
@@ -4135,6 +4161,18 @@ mod internal_tests {
4135
4161
  assert!(e2.to_string().contains("watch() is active"), "got: {e2}");
4136
4162
  }
4137
4163
 
4164
+ #[test]
4165
+ fn queued_file_events_hands_over_what_the_watcher_holds_without_waiting() {
4166
+ let (dir, db) = simple_db();
4167
+ assert!(db.queued_file_events().is_empty(), "no watcher yet");
4168
+ db.start_watching().unwrap();
4169
+ std::fs::write(dir.path().join("a.none"), "x").unwrap();
4170
+ let mut held = Vec::new();
4171
+ while !held.iter().any(|e| matches!(e, FileEvent::Created(_))) {
4172
+ held.extend(db.queued_file_events());
4173
+ }
4174
+ }
4175
+
4138
4176
  #[test]
4139
4177
  fn poll_events_locks_out_watch() {
4140
4178
  let (_dir, db) = simple_db();
@@ -5503,6 +5541,31 @@ mod internal_tests {
5503
5541
  assert_eq!(row_names(&db), vec!["a.txt", "b.txt"]);
5504
5542
  }
5505
5543
 
5544
+ #[test]
5545
+ fn events_arriving_while_a_batch_is_routed_share_its_per_table_rerun() {
5546
+ let dir = TempDir::new().unwrap();
5547
+ let root = dir.path().to_path_buf();
5548
+ let a = root.join("a.txt");
5549
+ let b = root.join("b.txt");
5550
+ let fake = FakeFs::default().with_subtree(
5551
+ root.clone(),
5552
+ vec![(a.clone(), "items".into()), (b.clone(), "items".into())],
5553
+ );
5554
+ let (items, calls) = recording_per_table("items", "**/*.txt");
5555
+ let db =
5556
+ DirSQL::with_ignore_and_fs(&root, vec![items], Vec::<String>::new(), Arc::new(fake))
5557
+ .unwrap();
5558
+
5559
+ let mut arrivals = vec![vec![FileEvent::Modified(b.clone())]];
5560
+ let events = db.apply_file_events_draining(vec![FileEvent::Created(a.clone())], || {
5561
+ arrivals.pop().unwrap_or_default()
5562
+ });
5563
+
5564
+ assert_eq!(*calls.lock().unwrap(), vec![vec![a, b]]);
5565
+ assert_eq!(events.len(), 2);
5566
+ assert_eq!(row_names(&db), vec!["a.txt", "b.txt"]);
5567
+ }
5568
+
5506
5569
  #[test]
5507
5570
  fn apply_file_events_refreshes_a_per_table_hook_without_walking_the_tree() {
5508
5571
  let dir = TempDir::new().unwrap();
@@ -73,12 +73,16 @@ pub(crate) fn run_streaming(
73
73
  .iter()
74
74
  .map(|path| command::non_verbatim(&path.to_string_lossy()))
75
75
  .collect();
76
- let cpus = std::thread::available_parallelism().map_or(1, std::num::NonZero::get);
77
76
  let mut runs = chunks(&args, ARG_BUDGET.saturating_sub(argv_bytes(&argv)));
78
77
  if runs.len() == 1 {
79
- runs = spread(&args, &file_sizes(paths), cpus);
78
+ runs = spread_runs(&args, &file_sizes(paths), cpus);
80
79
  }
81
- let workers = cpus.min(runs.len());
80
+ if let [only] = runs.as_slice() {
81
+ let mut full = argv;
82
+ full.extend(only.iter().cloned());
83
+ return run_invocation(command, &full, cwd, &mut |rows| sink(rows));
84
+ }
85
+ let workers = cpus().min(runs.len());
82
86
 
83
87
  let next = AtomicUsize::new(0);
84
88
  let aborted = AtomicBool::new(false);
@@ -97,7 +101,7 @@ pub(crate) fn run_streaming(
97
101
  }
98
102
  let mut full = argv.clone();
99
103
  full.extend(runs[index].iter().cloned());
100
- let outcome = run_invocation(command, &full, cwd, &|rows| {
104
+ let outcome = run_invocation(command, &full, cwd, &mut |rows| {
101
105
  let _ = rows_tx.send(rows);
102
106
  });
103
107
  if let Err(message) = outcome {
@@ -134,7 +138,7 @@ fn run_invocation(
134
138
  command: &str,
135
139
  full: &[String],
136
140
  cwd: &Path,
137
- send: &dyn Fn(Vec<JsonRow>),
141
+ send: &mut dyn FnMut(Vec<JsonRow>),
138
142
  ) -> Result<(), String> {
139
143
  let mut bad_output: Option<String> = None;
140
144
  let mut lines_seen = 0usize;
@@ -208,6 +212,24 @@ fn chunks(args: &[String], budget: usize) -> Vec<&[String]> {
208
212
  /// another invocation costs more than it saves.
209
213
  const MIN_RUN_BYTES: u64 = 4 * 1024 * 1024;
210
214
 
215
+ fn cpus() -> usize {
216
+ std::thread::available_parallelism().map_or(1, std::num::NonZero::get)
217
+ }
218
+
219
+ /// [`spread`] over the machine's CPUs, asking for the count only when the
220
+ /// data could fill more than one run: the query reads cgroup files each time.
221
+ fn spread_runs<'a>(
222
+ args: &'a [String],
223
+ sizes: &[u64],
224
+ cpus: impl FnOnce() -> usize,
225
+ ) -> Vec<&'a [String]> {
226
+ let by_data = usize::try_from(sizes.iter().sum::<u64>() / MIN_RUN_BYTES).unwrap_or(usize::MAX);
227
+ if by_data.min(args.len()) <= 1 {
228
+ return vec![args];
229
+ }
230
+ spread(args, sizes, cpus())
231
+ }
232
+
211
233
  /// Each path's size in bytes; a path that cannot be read counts as empty.
212
234
  fn file_sizes(paths: &[PathBuf]) -> Vec<u64> {
213
235
  paths
@@ -423,6 +445,22 @@ mod tests {
423
445
  assert_eq!(lens(&spread(&args, &[mib; 4], 16)), [4]);
424
446
  }
425
447
 
448
+ #[test]
449
+ fn spread_runs_does_not_count_cpus_for_data_that_fits_one_run() {
450
+ let args = args(&["a", "b", "c", "d"]);
451
+ let mib = 1024 * 1024;
452
+ let runs = spread_runs(&args, &[mib; 4], || panic!("counted the cpus"));
453
+ assert_eq!(lens(&runs), [4]);
454
+ }
455
+
456
+ #[test]
457
+ fn spread_runs_counts_cpus_once_the_data_fills_more_than_one_run() {
458
+ let args = args(&["a", "b", "c", "d"]);
459
+ let mib = 1024 * 1024;
460
+ let runs = spread_runs(&args, &[8 * mib; 4], || 2);
461
+ assert_eq!(lens(&runs), [2, 2]);
462
+ }
463
+
426
464
  #[test]
427
465
  fn spread_isolates_a_dominant_file() {
428
466
  let args = args(&["a", "b", "c"]);
@@ -130,7 +130,7 @@ fn read_contents(rows: &[&FileRow], read: &(dyn Fn(&FileRow) -> Option<String> +
130
130
  .filter(|row| row.content.get().is_none())
131
131
  .collect();
132
132
  share_out(&unread, READERS, &|row| {
133
- row.content.get_or_init(|| read(row));
133
+ row.content.get_or_init(|| Box::new(read(row)));
134
134
  });
135
135
  }
136
136
 
@@ -146,7 +146,7 @@ fn stat_rows(rows: &[&FileRow], stat: &(dyn Fn(&FileRow) -> StatFacts + Sync)) {
146
146
  .collect();
147
147
  let workers = thread::available_parallelism().map_or(1, usize::from);
148
148
  share_out(&unstatted, workers, &|row| {
149
- row.facts.get_or_init(|| stat(row));
149
+ row.facts.get_or_init(|| Box::new(stat(row)));
150
150
  });
151
151
  }
152
152
 
@@ -196,11 +196,11 @@ struct FileRow {
196
196
  spans: PathSpans,
197
197
  /// Stat'ed at most once per statement, and only when the statement reads
198
198
  /// a stat column or the content.
199
- facts: OnceLock<StatFacts>,
199
+ facts: OnceLock<Box<StatFacts>>,
200
200
  /// The file's text once a statement has asked for it, read at most once
201
201
  /// per statement: ahead for every row when the statement names the
202
202
  /// column, or on demand for the row being emitted.
203
- content: OnceLock<Option<String>>,
203
+ content: OnceLock<Box<Option<String>>>,
204
204
  }
205
205
 
206
206
  impl FileRow {
@@ -217,15 +217,15 @@ impl FileRow {
217
217
  }
218
218
 
219
219
  fn basename(&self) -> Option<&str> {
220
- self.spans.name_at.map(|at| &self.path[at..])
220
+ self.spans.name_at.map(|at| &self.path[at as usize..])
221
221
  }
222
222
 
223
223
  fn dir(&self) -> Option<&str> {
224
- self.spans.dir_len.map(|len| &self.path[..len])
224
+ self.spans.dir_len.map(|len| &self.path[..len as usize])
225
225
  }
226
226
 
227
227
  fn ext(&self) -> Option<&str> {
228
- self.spans.ext_at.map(|at| &self.path[at..])
228
+ self.spans.ext_at.map(|at| &self.path[at as usize..])
229
229
  }
230
230
  }
231
231
 
@@ -236,19 +236,20 @@ impl FileRow {
236
236
  /// one length each locates them.
237
237
  #[derive(Debug, PartialEq, Eq)]
238
238
  struct PathSpans {
239
- name_at: Option<usize>,
240
- dir_len: Option<usize>,
241
- ext_at: Option<usize>,
239
+ name_at: Option<u32>,
240
+ dir_len: Option<u32>,
241
+ ext_at: Option<u32>,
242
242
  }
243
243
 
244
244
  impl PathSpans {
245
245
  fn of(path: &str) -> Self {
246
246
  let p = Path::new(path);
247
- let suffix_at = |s: &std::ffi::OsStr| path.len() - s.len();
247
+ let span = |at: usize| u32::try_from(at).ok();
248
+ let suffix_at = |s: &std::ffi::OsStr| span(path.len() - s.len());
248
249
  Self {
249
- name_at: p.file_name().map(suffix_at),
250
- dir_len: p.parent().map(|d| d.as_os_str().len()),
251
- ext_at: p.extension().map(suffix_at),
250
+ name_at: p.file_name().and_then(suffix_at),
251
+ dir_len: p.parent().and_then(|d| span(d.as_os_str().len())),
252
+ ext_at: p.extension().and_then(suffix_at),
252
253
  }
253
254
  }
254
255
  }
@@ -374,7 +375,7 @@ impl ScanSpec {
374
375
 
375
376
  fn facts(&self, row: &FileRow) -> StatFacts {
376
377
  if let Some(facts) = row.facts.get() {
377
- return *facts;
378
+ return **facts;
378
379
  }
379
380
  let current = self
380
381
  .current
@@ -383,7 +384,7 @@ impl ScanSpec {
383
384
  if let Some(rows) = current.upgrade() {
384
385
  stat_rows(&rows.iter().collect::<Vec<_>>(), &|row| self.stat(row));
385
386
  }
386
- *row.facts.get_or_init(|| self.stat(row))
387
+ **row.facts.get_or_init(|| Box::new(self.stat(row)))
387
388
  }
388
389
 
389
390
  fn read(&self, row: &FileRow) -> Option<String> {
@@ -438,7 +439,7 @@ impl TableSource for ScanSpec {
438
439
  Some(value) => ctx.set_result(&ToSqlOutput::Borrowed(value)),
439
440
  // The one effectful read, reached only when a query names the
440
441
  // column: this is where laziness actually lives.
441
- None => ctx.set_result(row.content.get_or_init(|| self.read(row))),
442
+ None => ctx.set_result(&**row.content.get_or_init(|| Box::new(self.read(row)))),
442
443
  }
443
444
  }
444
445
 
@@ -674,7 +675,7 @@ mod tests {
674
675
 
675
676
  fn sized_row(rel: &str, size: i64) -> FileRow {
676
677
  let row = FileRow::new(Path::new(""), PathBuf::from(rel));
677
- row.facts.set(facts(size)).unwrap();
678
+ row.facts.set(Box::new(facts(size))).unwrap();
678
679
  row
679
680
  }
680
681
 
@@ -721,7 +722,10 @@ mod tests {
721
722
 
722
723
  spec.prefetch(&rows);
723
724
 
724
- assert_eq!(row.content.get(), Some(&Some("contents".to_owned())));
725
+ assert_eq!(
726
+ row.content.get().map(|c| &**c),
727
+ Some(&Some("contents".to_owned()))
728
+ );
725
729
  }
726
730
 
727
731
  fn facts(size: i64) -> StatFacts {
@@ -873,7 +877,7 @@ mod tests {
873
877
  #[test]
874
878
  fn stat_rows_stats_each_unstatted_row_once() {
875
879
  let rows: Vec<FileRow> = (0..40).map(|n| row_for("", &format!("f{n}.md"))).collect();
876
- rows[3].facts.set(facts(1)).unwrap();
880
+ rows[3].facts.set(Box::new(facts(1))).unwrap();
877
881
  let stats = AtomicUsize::new(0);
878
882
  let stat = |row: &FileRow| {
879
883
  stats.fetch_add(1, Ordering::Relaxed);
@@ -883,8 +887,8 @@ mod tests {
883
887
  stat_rows(&rows.iter().collect::<Vec<_>>(), &stat);
884
888
 
885
889
  assert_eq!(stats.load(Ordering::Relaxed), 39);
886
- assert_eq!(rows[3].facts.get(), Some(&facts(1)));
887
- assert_eq!(rows[7].facts.get(), Some(&facts(7)));
890
+ assert_eq!(rows[3].facts.get().map(|f| **f), Some(facts(1)));
891
+ assert_eq!(rows[7].facts.get().map(|f| **f), Some(facts(7)));
888
892
  stat_rows(&[], &|_| panic!("nothing to stat"));
889
893
  }
890
894
 
@@ -1002,7 +1006,10 @@ mod tests {
1002
1006
  #[test]
1003
1007
  fn read_contents_reads_each_unread_row_once() {
1004
1008
  let rows: Vec<FileRow> = (0..40).map(|n| row_for("", &format!("f{n}.md"))).collect();
1005
- rows[3].content.set(Some("already".to_string())).unwrap();
1009
+ rows[3]
1010
+ .content
1011
+ .set(Box::new(Some("already".to_string())))
1012
+ .unwrap();
1006
1013
  let reads = std::sync::Mutex::new(Vec::new());
1007
1014
  let read = |row: &FileRow| {
1008
1015
  reads.lock().unwrap().push(row.path.clone());
@@ -1033,13 +1040,13 @@ mod tests {
1033
1040
  fn read_contents_keeps_a_null_read() {
1034
1041
  let rows = [row_for("", "a.md")];
1035
1042
  read_contents(&[&rows[0]], &|_| None);
1036
- assert_eq!(rows[0].content.get(), Some(&None));
1043
+ assert_eq!(rows[0].content.get().map(|c| &**c), Some(&None));
1037
1044
  }
1038
1045
 
1039
1046
  #[test]
1040
1047
  fn read_contents_reads_nothing_when_every_row_is_read() {
1041
1048
  let rows = [row_for("", "a.md")];
1042
- rows[0].content.set(None).unwrap();
1049
+ rows[0].content.set(Box::new(None)).unwrap();
1043
1050
  read_contents(&[&rows[0]], &|_| {
1044
1051
  panic!("a row read already is not read again")
1045
1052
  });
@@ -1142,4 +1149,28 @@ mod tests {
1142
1149
  assert_eq!(epoch_secs(Some(t)), None);
1143
1150
  assert_eq!(epoch_secs(None), None);
1144
1151
  }
1152
+
1153
+ #[test]
1154
+ fn a_row_not_yet_stat_or_read_stays_compact() {
1155
+ let size = std::mem::size_of::<FileRow>();
1156
+ assert!(size <= 112, "a FileRow is {size} bytes");
1157
+ }
1158
+ #[test]
1159
+ fn a_content_column_reads_the_file_through_the_module() {
1160
+ let dir = tempfile::tempdir().unwrap();
1161
+ std::fs::write(dir.path().join("a.md"), "hi").unwrap();
1162
+ let conn = Connection::open_in_memory().unwrap();
1163
+ load_module(&conn, StatementScope::new()).unwrap();
1164
+ conn.execute_batch(&format!(
1165
+ "CREATE VIRTUAL TABLE t USING dirsql_path('{}', '*.md', '', 'no-gitignore')",
1166
+ dir.path().display()
1167
+ ))
1168
+ .unwrap();
1169
+
1170
+ let content: String = conn
1171
+ .query_row("SELECT content FROM t", [], |row| row.get(0))
1172
+ .unwrap();
1173
+
1174
+ assert_eq!(content, "hi");
1175
+ }
1145
1176
  }