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.
- {dirsql-0.4.107 → dirsql-0.4.109}/Cargo.lock +2 -2
- {dirsql-0.4.107 → dirsql-0.4.109}/PKG-INFO +1 -1
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/Cargo.toml +1 -1
- dirsql-0.4.109/packages/python/e2e-attestations/bot-1416-watch-speed-1008.json +7 -0
- dirsql-0.4.109/packages/python/e2e-attestations/bot-1461-row-footprint-1008.json +7 -0
- dirsql-0.4.109/packages/python/e2e-attestations/bot-1462-paired-speed-harness-1008.json +7 -0
- dirsql-0.4.109/packages/python/tests/e2e/speed_of_light.py +205 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/Cargo.toml +1 -1
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/db.rs +1 -1
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/lib.rs +69 -6
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/on_file.rs +43 -5
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/vtab.rs +56 -25
- dirsql-0.4.107/packages/python/tests/e2e/speed_of_light.py +0 -124
- {dirsql-0.4.107 → dirsql-0.4.109}/Cargo.toml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/README.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/_async.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/main.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/cli/resolve_config_extensions.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/plan_entry_path.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/py.typed +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/read_config.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/resolve_configs_extension_specs.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/resolve_extension.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/dirsql/resolve_package.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/AGENTS.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/.gitattributes +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/recipes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/troubleshooting.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/context/usage.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/explanation.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/getting-started.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/agent-skill.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/embed.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/persist.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/query-json.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/search-indexes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/index.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/package.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/plugins.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/cli.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/columns.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/glob.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/hooks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/http-api.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/docs/reference/sdk.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/CHANGELOG.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/MIGRATIONS.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/README.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-20-curtaincall-dev-dependency.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-08-20-declared-table-name.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-04-config-paths-from-argv-module.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-05-config-extension-loader-split.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-05-resolve-config-extensions-split.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-05-resolve-extension-split.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-09-launcher-config-path-scan.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-17-extension-resolution-via-core.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-18-export-scan-failure.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-18-flatten-row-event-in-core.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-18-programmatic-extension-plan.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-09-24-query-rows-in-projection-order.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-02-readme-docs-host.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-03-path-table-directory-one-level.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-brace-groups-follow-bash.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-cli-context.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-gitignore-requires-repo.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-path-table-node-modules-named.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-path-table-symlinks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-path-table-trailing-slash.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-05-unbalanced-brackets-literal.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-braces.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-directory.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-dotnames.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-gitignore.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-globs-anchor-at-the-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-config-links.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-one-glob-compiler.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-plugin-no-default-records.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-watch-broadcast.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-06-zero-match-config-table-warning.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-bracket-slash-literal.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-dotdot-and-double-slashes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-dotdot-as-written.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-glob-walks-node-modules.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-literal-gitignore.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-on-file-ndjson.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-parallel-hooks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-persist-cache-outside-root.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-posix-classes-unicode.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-07-spread-few-large-files.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-08-small-walks-stay-on-one-thread.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/2026-10-08-watch-requires-named-table.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/changelog.d/README.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/conftest.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/.gitattributes +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/recipes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/troubleshooting.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/context/usage.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/agent-skill.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/persist.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/query-json.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/search-indexes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/index.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/package.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/plugins.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/cli.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/glob.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/docs/reference/sdk.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1334-listing-exemptions.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1397-watch-broadcast.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1398-inotify-error.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1399-server-cors.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1401-watch-scope.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1406-watch-no-tables-1008.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1407-child-place-depth.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1414-stat-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1415-server-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1418-node-modules.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1419-dotdot-slashes.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1420-bracket-slash.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1421-posix-unicode.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1422-literal-gitignore.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1426-walk-spawn-threshold-1008.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1428-ndjson-hooks.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1429-parallel-hooks.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1430-dotdot-as-written.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1431-persist-cache-dir.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1437-test-cache-isolation.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1440-fix-main-build.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1448-wildcard-ignored-prefix.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1451-named-dir-never-hidden.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1454-split-few-files.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1457-incremental-refresh-1008.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-1459-rescan-on-overflow-1008.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/bot-fix-main-place-depth-20261007.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/cc-keen-knuth-f11gyq.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1056-py-ofpf9.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1066-p1-resolve-extension.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1078-argv-config-paths-core.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1079-p2-resolve-config-extensions.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1080-share-vtab-scaffolding.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1081-indicatif-progress.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1096-watch-moved-dir.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1109-watch-moved-out.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1119-ext-plan.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1120-py-ext.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1122-ext-cleanup.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1130-codec-crates.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1131-flatten-row-event.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1139-sse-flatten.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1140-scan-failure-export.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1148-clippy-all-targets.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1164-sigint-fatal.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1170-projection-order.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1172-on-file-path.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1173-py-projection-order.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1184-win-rel-paths.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1185-win-abs-table-names.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1186-win-test-harness.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1196-join-index.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1199-glob-prefix.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1201-single-walk.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1204-embed-batch.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1207-win-argv-quote.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1208-win-nonverbatim-paths.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1215-on-file-batch.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1216-on-file-cleanup.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1223-config-glob-rule.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1223-dir-one-level.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1225-plugin-no-default.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1226-stale-docs.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-anchor-plumbing.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-config-anchor.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-multi-watch.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1232-zero-match.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1238-parallel-walk.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1241-content-prefetch.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1243-ingest-perf.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1249-dotfiles.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1256-symlinks.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1257-trailing-slash.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-braces.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-directory.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-dotnames.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-gitignore.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-config-links.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1258-one-pattern.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1262-brace-literal.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1264-context.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1267-posix-classes.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1268-unbalanced-brackets.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1269-utf8-chars.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1271-node-modules-named.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1272-ancestor-gitignore.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1273-walk-prune.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1285-ingest-fast.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1286-nested-repo.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1305-glob-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1308-glob-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1310-glob-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1311-glob-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1312-glob-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1318-lazy-stat.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1320-walk-speed.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1322-walk-readdir.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1325-parallel-walk.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1326-stream-rows.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-python-dirsql-patch.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-python-extensions-patch.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-scanner-effects.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-scanner-gitignore.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-vtab-prefetch-flag.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1330-vtab-read.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-1351-dot-dirs.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-epic-953-slice2-reedline.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-epic-953-slice3-format.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-epic-953-stacked-prs-x59wm3.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-issue-951-0eease.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-issue-962-0jjrfv.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-issue-986-red-test-e7sjrb.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-1034-cachesplit.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-1042-py12.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-956-xzen74.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6-worker-calls.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-957-lrm0z6.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/e2e-attestations/claude-tackle-975-n8mc2h.json +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-08-20-declared-table-name.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-09-20-sigint-ends-a-run-in-progress.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-09-24-query-rows-in-projection-order.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-02-readme-docs-host.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-03-one-glob-rule-for-config-globs.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-03-path-table-directory-one-level.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-04-path-table-dotfiles-hidden.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-brace-groups-follow-bash.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-cli-context.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-gitignore-requires-repo.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-path-table-node-modules-named.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-path-table-symlinks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-path-table-trailing-slash.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-05-unbalanced-brackets-literal.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-braces.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-directory.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-dotnames.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-gitignore.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-globs-anchor-at-the-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-config-links.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-one-glob-compiler.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-plugin-no-default-records.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-watch-broadcast.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-06-zero-match-config-table-warning.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-bracket-slash-literal.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-dotdot-and-double-slashes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-dotdot-as-written.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-glob-walks-node-modules.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-literal-gitignore.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-on-file-ndjson.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-parallel-hooks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-persist-cache-outside-root.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-posix-classes-unicode.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-07-spread-few-large-files.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-08-small-walks-stay-on-one-thread.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/2026-10-08-watch-requires-named-table.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/migrations.d/README.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/src/lib.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/testing-conventions.toml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/integration/binding/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/python/tests/integration/hermetic/__init__.py +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/CHANGELOG.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/MIGRATIONS.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/README.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/.gitattributes +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/recipes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/troubleshooting.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/context/usage.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/agent-skill.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/persist.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/query-json.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/search-indexes.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/index.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/plugins.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/cli.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/glob.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/docs/reference/sdk.md +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/bin/dirsql.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/brace.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/context.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/execute.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/repl.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/run.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/cli/table.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/command.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/config.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/default_config.toml +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/extension_resolution.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/functions.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/infer.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/launcher.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/listing.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/parsed_vtab.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/path_table.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/posix_class.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/progress.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/row_event_flat.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/sql_literal.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/tree_walk.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/vtab_scaffold.rs +0 -0
- {dirsql-0.4.107 → dirsql-0.4.109}/packages/rust/src/watcher.rs +0 -0
- {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.
|
|
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.
|
|
628
|
+
version = "0.4.109"
|
|
629
629
|
dependencies = [
|
|
630
630
|
"dirsql",
|
|
631
631
|
"pyo3",
|
|
@@ -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.
|
|
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,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.
|
|
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.
|
|
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
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
78
|
+
runs = spread_runs(&args, &file_sizes(paths), cpus);
|
|
80
79
|
}
|
|
81
|
-
let
|
|
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,
|
|
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
|
|
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<
|
|
240
|
-
dir_len: Option<
|
|
241
|
-
ext_at: Option<
|
|
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
|
|
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().
|
|
250
|
-
dir_len: p.parent().
|
|
251
|
-
ext_at: p.extension().
|
|
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
|
|
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
|
-
|
|
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!(
|
|
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(
|
|
887
|
-
assert_eq!(rows[7].facts.get(), Some(
|
|
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]
|
|
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
|
}
|