dirsql 0.4.18__tar.gz → 0.4.20__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.18 → dirsql-0.4.20}/Cargo.lock +1 -1
- {dirsql-0.4.18 → dirsql-0.4.20}/PKG-INFO +1 -1
- {dirsql-0.4.18/packages/rust → dirsql-0.4.20}/docs/howto/persist.md +13 -0
- {dirsql-0.4.18/packages/rust → dirsql-0.4.20}/docs/reference/path-tables.md +14 -6
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/Cargo.toml +1 -1
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/persist.md +13 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/path-tables.md +14 -6
- dirsql-0.4.20/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +7 -0
- dirsql-0.4.20/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +7 -0
- dirsql-0.4.20/packages/rust/changelog.d/2026-08-11-persist-parsed-path-tables.md +4 -0
- dirsql-0.4.20/packages/rust/changelog.d/2026-08-12-unquote-inverts-quote-literal.md +3 -0
- {dirsql-0.4.18 → dirsql-0.4.20/packages/rust}/docs/howto/persist.md +13 -0
- {dirsql-0.4.18 → dirsql-0.4.20/packages/rust}/docs/reference/path-tables.md +14 -6
- dirsql-0.4.20/packages/rust/migrations.d/2026-08-11-persist-parsed-path-tables.md +58 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/db.rs +88 -26
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/lib.rs +52 -20
- dirsql-0.4.20/packages/rust/src/parsed_cache.rs +454 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/parsed_vtab.rs +548 -61
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/persist.rs +76 -31
- dirsql-0.4.20/packages/rust/src/sql_literal.rs +127 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/vtab.rs +1 -42
- {dirsql-0.4.18 → dirsql-0.4.20}/Cargo.toml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/_async.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/discover_plugins/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/main.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/cli/resolve_config_extensions.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/py.typed +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/resolve_config_extensions.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/dirsql/resolve_extension.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/AGENTS.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/explanation.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/getting-started.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/embed.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/index.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/migrations.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/package.json +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/plugins.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/reference/cli.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/reference/columns.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/reference/config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/reference/hooks.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/reference/http-api.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/docs/reference/sdk.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/CHANGELOG.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/MIGRATIONS.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/changelog.d/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/conftest.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/.vitepress/config.ts +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/AGENTS.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/index.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/migrations.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/package.json +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/plugins.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/cli.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/docs/reference/sdk.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/migrations.d/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/src/lib.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/integration/binding/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/python/tests/integration/hermetic/__init__.py +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/CHANGELOG.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/MIGRATIONS.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-batch-ingest-transaction.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-chained-pre-post-query-hooks.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-fan-out-file-table-matching.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-persist-wal.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-repeatable-builder-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-repeatable-cli-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-13-subcommand-local-config-flags.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-19-path-table-query-fallback.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-19-path-table-vtab.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-20-path-table-glob-semantics.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-20-schema-inference.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-21-on-file-flag.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-23-init-escalation-example.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-23-query-default-cli.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-07-29-on-file-failures-accumulate.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-02-drop-regex.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-02-per-file-scan-failures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-03-gitignore-by-default.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-03-nested-default-ignores.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-04-cli-run-shim.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-04-configless-extension.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-10-dirsql-function.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-10-remove-query-hooks.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-11-oneshot-query-no-timeout.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/2026-08-11-retire-hook-timeout.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/changelog.d/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/search-by-meaning.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/index.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/migrations.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/plugins.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/reference/cli.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/docs/reference/sdk.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-13-fan-out-file-table-matching.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-13-persist-wal.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-13-subcommand-local-config-flags.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-20-path-table-glob-semantics.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-23-init-escalation-example.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-23-query-default-cli.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-08-02-per-file-scan-failures.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-08-03-gitignore-by-default.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-08-03-nested-default-ignores.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-08-10-remove-query-hooks.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-08-11-oneshot-query-no-timeout.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/2026-08-11-retire-hook-timeout.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/migrations.d/README.md +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/bin/dirsql.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/execute.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/run.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/command.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/config.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/default_config.toml +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/functions.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/infer.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/path_table.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/packages/rust/src/watcher.rs +0 -0
- {dirsql-0.4.18 → dirsql-0.4.20}/pyproject.toml +0 -0
|
@@ -55,6 +55,19 @@ can't be trusted at all — the table/ignore configuration changed, or the
|
|
|
55
55
|
automatically. You never need to delete it by hand; a full rebuild costs
|
|
56
56
|
exactly what a non-persistent startup does.
|
|
57
57
|
|
|
58
|
+
A run that changes nothing writes nothing: the cache file is read and left
|
|
59
|
+
exactly as it was.
|
|
60
|
+
|
|
61
|
+
This covers both kinds of table. Declared tables keep their indexed rows, and
|
|
62
|
+
a [path-table](../reference/path-tables.md) parsed with `--on-file` keeps each
|
|
63
|
+
file's parser output — so the second run over an unchanged tree spawns no
|
|
64
|
+
parser process at all. A path-table's cached rows are keyed by the tree, the
|
|
65
|
+
glob, the parser command and the `dirsql` version together, so changing any of
|
|
66
|
+
them parses afresh rather than serving rows the current command never
|
|
67
|
+
produced. A *stat* path-table caches nothing, because there is nothing to
|
|
68
|
+
save: its columns are the metadata the scan already collected, and `content`
|
|
69
|
+
is read live by design.
|
|
70
|
+
|
|
58
71
|
Persistence is a startup-time optimization, not a change in meaning: the
|
|
59
72
|
database remains a derived view of your files, and queries return the same
|
|
60
73
|
rows either way ([how `dirsql` thinks](../explanation.md)).
|
|
@@ -140,6 +140,12 @@ the table:
|
|
|
140
140
|
scan continues. The schema is inferred from the files that did parse.
|
|
141
141
|
- **The skip rules still apply.** A parsed scan honors the same `node_modules`
|
|
142
142
|
/`.git`/`ignore` rules a stat scan does (see below).
|
|
143
|
+
- **`--persist` skips the parser for unchanged files.** With a
|
|
144
|
+
[persistent cache](/howto/persist), each file's parser output is stored
|
|
145
|
+
against its stat metadata, so a later run over an unchanged tree serves the
|
|
146
|
+
rows from the cache and spawns no process. Change the file, the glob, the
|
|
147
|
+
parser command, or the dirsql version and that file (or that whole table) is
|
|
148
|
+
parsed again.
|
|
143
149
|
|
|
144
150
|
`--on-file` applies to **every** path-table in the query and may be given **at
|
|
145
151
|
most once**. For different parsers per file set, define named tables in a
|
|
@@ -160,15 +166,17 @@ deleted *after* the scan finds it but *before* its `content` is read yields
|
|
|
160
166
|
than failing the query. This is an accepted consequence of reading live, not a
|
|
161
167
|
bug to design around.
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
The table itself is per-connection: it lives in `temp`, so it cannot leak into
|
|
170
|
+
`sqlite_master` or survive a restart. Under `--persist` a *parsed* table's rows
|
|
171
|
+
outlive the connection in the cache (above), but the table is still minted
|
|
172
|
+
fresh each run and the scan still decides what exists. The reserved top-level
|
|
173
|
+
`.dirsql/` directory is excluded from the scan, as everywhere else.
|
|
166
174
|
|
|
167
175
|
### When to promote to a declared table
|
|
168
176
|
|
|
169
|
-
Every query re-scans the filesystem: a path-table has no index
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
Every query re-scans the filesystem: a path-table has no index and no watcher.
|
|
178
|
+
That is the right trade for a hundreds-of-files, run-it-once question. When the
|
|
179
|
+
same tree is queried repeatedly, or is large, declare a
|
|
172
180
|
[table](/reference/config) for it instead — a declared table is indexed on
|
|
173
181
|
build, kept fresh by the watcher, and (with `--persist`) survives restarts, so
|
|
174
182
|
its rows are read from SQLite rather than re-walked each time.
|
|
@@ -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.20"
|
|
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
|
|
@@ -55,6 +55,19 @@ can't be trusted at all — the table/ignore configuration changed, or the
|
|
|
55
55
|
automatically. You never need to delete it by hand; a full rebuild costs
|
|
56
56
|
exactly what a non-persistent startup does.
|
|
57
57
|
|
|
58
|
+
A run that changes nothing writes nothing: the cache file is read and left
|
|
59
|
+
exactly as it was.
|
|
60
|
+
|
|
61
|
+
This covers both kinds of table. Declared tables keep their indexed rows, and
|
|
62
|
+
a [path-table](../reference/path-tables.md) parsed with `--on-file` keeps each
|
|
63
|
+
file's parser output — so the second run over an unchanged tree spawns no
|
|
64
|
+
parser process at all. A path-table's cached rows are keyed by the tree, the
|
|
65
|
+
glob, the parser command and the `dirsql` version together, so changing any of
|
|
66
|
+
them parses afresh rather than serving rows the current command never
|
|
67
|
+
produced. A *stat* path-table caches nothing, because there is nothing to
|
|
68
|
+
save: its columns are the metadata the scan already collected, and `content`
|
|
69
|
+
is read live by design.
|
|
70
|
+
|
|
58
71
|
Persistence is a startup-time optimization, not a change in meaning: the
|
|
59
72
|
database remains a derived view of your files, and queries return the same
|
|
60
73
|
rows either way ([how `dirsql` thinks](../explanation.md)).
|
|
@@ -140,6 +140,12 @@ the table:
|
|
|
140
140
|
scan continues. The schema is inferred from the files that did parse.
|
|
141
141
|
- **The skip rules still apply.** A parsed scan honors the same `node_modules`
|
|
142
142
|
/`.git`/`ignore` rules a stat scan does (see below).
|
|
143
|
+
- **`--persist` skips the parser for unchanged files.** With a
|
|
144
|
+
[persistent cache](/howto/persist), each file's parser output is stored
|
|
145
|
+
against its stat metadata, so a later run over an unchanged tree serves the
|
|
146
|
+
rows from the cache and spawns no process. Change the file, the glob, the
|
|
147
|
+
parser command, or the dirsql version and that file (or that whole table) is
|
|
148
|
+
parsed again.
|
|
143
149
|
|
|
144
150
|
`--on-file` applies to **every** path-table in the query and may be given **at
|
|
145
151
|
most once**. For different parsers per file set, define named tables in a
|
|
@@ -160,15 +166,17 @@ deleted *after* the scan finds it but *before* its `content` is read yields
|
|
|
160
166
|
than failing the query. This is an accepted consequence of reading live, not a
|
|
161
167
|
bug to design around.
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
The table itself is per-connection: it lives in `temp`, so it cannot leak into
|
|
170
|
+
`sqlite_master` or survive a restart. Under `--persist` a *parsed* table's rows
|
|
171
|
+
outlive the connection in the cache (above), but the table is still minted
|
|
172
|
+
fresh each run and the scan still decides what exists. The reserved top-level
|
|
173
|
+
`.dirsql/` directory is excluded from the scan, as everywhere else.
|
|
166
174
|
|
|
167
175
|
### When to promote to a declared table
|
|
168
176
|
|
|
169
|
-
Every query re-scans the filesystem: a path-table has no index
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
Every query re-scans the filesystem: a path-table has no index and no watcher.
|
|
178
|
+
That is the right trade for a hundreds-of-files, run-it-once question. When the
|
|
179
|
+
same tree is queried repeatedly, or is large, declare a
|
|
172
180
|
[table](/reference/config) for it instead — a declared table is indexed on
|
|
173
181
|
build, kept fresh by the watcher, and (with `--persist`) survives restarts, so
|
|
174
182
|
its rows are read from SQLite rather than re-walked each time.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
**Fixed**
|
|
2
|
+
|
|
3
|
+
- **`--persist` now caches a parsed path-table's rows, so a second run over an unchanged tree re-runs the `on-file` parser for no file.** Previously the persistent cache only covered declared `[[table]]` entries: a path-table (`SELECT ... FROM './**/*.json' --on-file '<command>'`) re-scanned, re-read and re-parsed every file on every run, which made `--persist` pure overhead on the surface a configless `dirsql --persist` actually queries — measured 2.7s cold and 2.7s warm over a 2,000-file tree, versus 2.7s → 0.04s now. Each file's parser output is stored in the cache against its stat tuple (`_dirsql_parsed_rows`, reconciled by the same racy-stat rule declared tables use), keyed by the tree, glob, parser command and `dirsql` version together, so changed files re-parse and a changed command or glob starts fresh. A cache that cannot be updated costs the run its speedup, not its rows: the failure is a one-line stderr warning and the next run re-parses. A *stat* path-table is unaffected either way: its columns are the scan's own metadata and `content` stays a live read. (#825)
|
|
4
|
+
- **A run that changes nothing no longer rewrites the cache file.** The `_dirsql_meta` block was rewritten on every startup, so `cache.db` changed even when the tree had not; it is now written only when the meta actually differs, leaving an unchanged warm start byte-for-byte identical. (#825)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
**Fixed**
|
|
2
|
+
|
|
3
|
+
- **A path-table argument containing a single quote now reaches the module as written.** `dirsql --on-file "sh -c 'jq . \"$1\"' sh {path}"` — `sh -c '…'` being the documented way to ask for a shell — arrived at the parser as `sh -c ''…''` and ran a different script. Minting a path-table writes its arguments as SQL literals, where an embedded quote is escaped by doubling; the modules stripped the surrounding quotes without undoing the doubling. The same corruption applied to any glob or ignore pattern carrying a quote. `quote_literal`, `quote_identifier` and `unquote` now live together in one module with a round-trip property test, so the encoder and decoder cannot drift apart again. (#827)
|
|
@@ -55,6 +55,19 @@ can't be trusted at all — the table/ignore configuration changed, or the
|
|
|
55
55
|
automatically. You never need to delete it by hand; a full rebuild costs
|
|
56
56
|
exactly what a non-persistent startup does.
|
|
57
57
|
|
|
58
|
+
A run that changes nothing writes nothing: the cache file is read and left
|
|
59
|
+
exactly as it was.
|
|
60
|
+
|
|
61
|
+
This covers both kinds of table. Declared tables keep their indexed rows, and
|
|
62
|
+
a [path-table](../reference/path-tables.md) parsed with `--on-file` keeps each
|
|
63
|
+
file's parser output — so the second run over an unchanged tree spawns no
|
|
64
|
+
parser process at all. A path-table's cached rows are keyed by the tree, the
|
|
65
|
+
glob, the parser command and the `dirsql` version together, so changing any of
|
|
66
|
+
them parses afresh rather than serving rows the current command never
|
|
67
|
+
produced. A *stat* path-table caches nothing, because there is nothing to
|
|
68
|
+
save: its columns are the metadata the scan already collected, and `content`
|
|
69
|
+
is read live by design.
|
|
70
|
+
|
|
58
71
|
Persistence is a startup-time optimization, not a change in meaning: the
|
|
59
72
|
database remains a derived view of your files, and queries return the same
|
|
60
73
|
rows either way ([how `dirsql` thinks](../explanation.md)).
|
|
@@ -140,6 +140,12 @@ the table:
|
|
|
140
140
|
scan continues. The schema is inferred from the files that did parse.
|
|
141
141
|
- **The skip rules still apply.** A parsed scan honors the same `node_modules`
|
|
142
142
|
/`.git`/`ignore` rules a stat scan does (see below).
|
|
143
|
+
- **`--persist` skips the parser for unchanged files.** With a
|
|
144
|
+
[persistent cache](/howto/persist), each file's parser output is stored
|
|
145
|
+
against its stat metadata, so a later run over an unchanged tree serves the
|
|
146
|
+
rows from the cache and spawns no process. Change the file, the glob, the
|
|
147
|
+
parser command, or the dirsql version and that file (or that whole table) is
|
|
148
|
+
parsed again.
|
|
143
149
|
|
|
144
150
|
`--on-file` applies to **every** path-table in the query and may be given **at
|
|
145
151
|
most once**. For different parsers per file set, define named tables in a
|
|
@@ -160,15 +166,17 @@ deleted *after* the scan finds it but *before* its `content` is read yields
|
|
|
160
166
|
than failing the query. This is an accepted consequence of reading live, not a
|
|
161
167
|
bug to design around.
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
The table itself is per-connection: it lives in `temp`, so it cannot leak into
|
|
170
|
+
`sqlite_master` or survive a restart. Under `--persist` a *parsed* table's rows
|
|
171
|
+
outlive the connection in the cache (above), but the table is still minted
|
|
172
|
+
fresh each run and the scan still decides what exists. The reserved top-level
|
|
173
|
+
`.dirsql/` directory is excluded from the scan, as everywhere else.
|
|
166
174
|
|
|
167
175
|
### When to promote to a declared table
|
|
168
176
|
|
|
169
|
-
Every query re-scans the filesystem: a path-table has no index
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
Every query re-scans the filesystem: a path-table has no index and no watcher.
|
|
178
|
+
That is the right trade for a hundreds-of-files, run-it-once question. When the
|
|
179
|
+
same tree is queried repeatedly, or is large, declare a
|
|
172
180
|
[table](/reference/config) for it instead — a declared table is indexed on
|
|
173
181
|
build, kept fresh by the watcher, and (with `--persist`) survives restarts, so
|
|
174
182
|
its rows are read from SQLite rather than re-walked each time.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
### Persisted path-tables reuse a parser's output across runs
|
|
2
|
+
|
|
3
|
+
#### Summary
|
|
4
|
+
|
|
5
|
+
Under `--persist`, a path-table parsed with `--on-file` now serves an unchanged
|
|
6
|
+
file's rows from the cache instead of re-running the parser (dirsql#825). No
|
|
7
|
+
API changed and no call site moves; what changes is how often your parser
|
|
8
|
+
command runs. A parser whose output is a function of the file it is handed —
|
|
9
|
+
the documented `on-file` contract, and what declared `[[table]]` hooks have
|
|
10
|
+
always assumed under `--persist` — sees no difference beyond speed. A parser
|
|
11
|
+
that returns something different for the same unchanged file (it reads the
|
|
12
|
+
clock, a network service, or an environment variable) will now be observed
|
|
13
|
+
returning the *previous* run's answer until the file itself changes.
|
|
14
|
+
|
|
15
|
+
#### Required changes
|
|
16
|
+
|
|
17
|
+
_None._ Nothing to update: the fix restores the behavior
|
|
18
|
+
`docs/howto/persist.md` already documented.
|
|
19
|
+
|
|
20
|
+
| Surface | Before | After |
|
|
21
|
+
| ------- | ------ | ----- |
|
|
22
|
+
| `dirsql query "… FROM './**/*.json'" --on-file '<cmd>' --persist` | the parser ran once per file on every run | the parser runs only for files whose stat metadata moved |
|
|
23
|
+
|
|
24
|
+
#### Deprecations removed
|
|
25
|
+
|
|
26
|
+
_None._
|
|
27
|
+
|
|
28
|
+
#### Behavior changes without code changes
|
|
29
|
+
|
|
30
|
+
- **Parsed path-tables under `--persist`**: previously the parser ran once per
|
|
31
|
+
matched file on every run; now a file whose size/mtime/ctime/inode/device
|
|
32
|
+
tuple matches the cache serves its cached rows and the process is not
|
|
33
|
+
spawned. Cached rows are keyed by the scan root, the glob, the parser command
|
|
34
|
+
and the `dirsql` version together, so changing any of them re-parses. A
|
|
35
|
+
parser that is not a pure function of its input file should either be run
|
|
36
|
+
without `--persist` or made to vary with the file.
|
|
37
|
+
- **The cache file on an unchanged run**: previously every startup rewrote the
|
|
38
|
+
`_dirsql_meta` block, so `cache.db` changed even when nothing about the tree
|
|
39
|
+
had; now a run that changes nothing writes nothing and the file is left
|
|
40
|
+
byte-for-byte identical. Tooling that watched the cache's mtime as a
|
|
41
|
+
"dirsql ran" signal needs a different signal.
|
|
42
|
+
- **The internal `dirsql_parsed` vtab module** takes a fifth fixed argument
|
|
43
|
+
(the cache path, empty for none) between the gitignore switch and the ignore
|
|
44
|
+
patterns. It is `#[doc(hidden)]` plumbing that `dirsql` mints for itself; a
|
|
45
|
+
hand-written `CREATE VIRTUAL TABLE … USING dirsql_parsed(…)` must add the
|
|
46
|
+
argument.
|
|
47
|
+
|
|
48
|
+
#### Verification
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
mkdir -p /tmp/dirsql-825 && cd /tmp/dirsql-825
|
|
52
|
+
printf '[{"id":1}]' > a.json
|
|
53
|
+
dirsql query "SELECT id FROM './*.json'" --on-file 'cat {path}' --persist
|
|
54
|
+
md5sum .dirsql/cache.db
|
|
55
|
+
time dirsql query "SELECT id FROM './*.json'" --on-file 'cat {path}' --persist
|
|
56
|
+
md5sum .dirsql/cache.db
|
|
57
|
+
# expected: the same rows, the second run near-instant, the same md5 both times
|
|
58
|
+
```
|
|
@@ -6,6 +6,7 @@ use thiserror::Error;
|
|
|
6
6
|
use crate::parsed_vtab;
|
|
7
7
|
use crate::path_table::{self, PathTable, Resolution};
|
|
8
8
|
use crate::scanner;
|
|
9
|
+
use crate::sql_literal::{quote_identifier, quote_literal};
|
|
9
10
|
use crate::vtab;
|
|
10
11
|
|
|
11
12
|
/// The user's home directory, if the platform reports one. Injected here so
|
|
@@ -94,15 +95,6 @@ fn no_home_path_table(name: &str) -> String {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
/// Wrap `s` as a SQL string literal.
|
|
97
|
-
fn quote_literal(s: &str) -> String {
|
|
98
|
-
format!("'{}'", s.replace('\'', "''"))
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/// Wrap `s` as a double-quoted SQL identifier.
|
|
102
|
-
fn quote_identifier(s: &str) -> String {
|
|
103
|
-
format!("\"{}\"", s.replace('"', "\"\""))
|
|
104
|
-
}
|
|
105
|
-
|
|
106
98
|
/// The DDL that mints a path-table.
|
|
107
99
|
///
|
|
108
100
|
/// It lands in `temp`, which is per-connection and never written to the
|
|
@@ -114,12 +106,18 @@ fn quote_identifier(s: &str) -> String {
|
|
|
114
106
|
/// the parsed module instead: its rows and schema come from the parser, not
|
|
115
107
|
/// the stat columns, and the `path_prefix` is irrelevant (a parser wanting a
|
|
116
108
|
/// path emits it). Both forms carry the same ignore rules.
|
|
109
|
+
///
|
|
110
|
+
/// A parsed table also carries `cache` — the persistent cache path when the
|
|
111
|
+
/// index has one — so it can serve an unchanged file's rows without re-running
|
|
112
|
+
/// the parser. The stat module takes no such argument: its columns are the
|
|
113
|
+
/// stat tuple the scan already has, so there is nothing to save.
|
|
117
114
|
fn path_table_ddl(
|
|
118
115
|
name: &str,
|
|
119
116
|
table: &PathTable,
|
|
120
117
|
ignore: &[String],
|
|
121
118
|
gitignore: bool,
|
|
122
119
|
parser: Option<&str>,
|
|
120
|
+
cache: Option<&Path>,
|
|
123
121
|
) -> String {
|
|
124
122
|
let (module, mut args) = match parser {
|
|
125
123
|
Some(command) => (
|
|
@@ -144,6 +142,13 @@ fn path_table_ddl(
|
|
|
144
142
|
} else {
|
|
145
143
|
scanner::NO_GITIGNORE_ARG
|
|
146
144
|
}));
|
|
145
|
+
if parser.is_some() {
|
|
146
|
+
args.push(quote_literal(
|
|
147
|
+
&cache
|
|
148
|
+
.map(|p| p.to_string_lossy().into_owned())
|
|
149
|
+
.unwrap_or_default(),
|
|
150
|
+
));
|
|
151
|
+
}
|
|
147
152
|
args.extend(ignore.iter().map(|p| quote_literal(p)));
|
|
148
153
|
|
|
149
154
|
format!(
|
|
@@ -222,6 +227,9 @@ pub struct Db {
|
|
|
222
227
|
/// parser instead of the stat columns: its rows and schema come from the
|
|
223
228
|
/// command's output. `None` keeps the stat path-table behavior.
|
|
224
229
|
path_table_parser: Option<String>,
|
|
230
|
+
/// The persistent cache a parsed path-table reuses across runs. `None` for
|
|
231
|
+
/// an ephemeral index, which has nowhere to cache to.
|
|
232
|
+
path_table_cache: Option<PathBuf>,
|
|
225
233
|
}
|
|
226
234
|
|
|
227
235
|
/// The skip rules a fresh `Db` starts with.
|
|
@@ -256,6 +264,7 @@ impl Db {
|
|
|
256
264
|
path_table_ignore: default_path_table_ignore(),
|
|
257
265
|
path_table_gitignore: true,
|
|
258
266
|
path_table_parser: None,
|
|
267
|
+
path_table_cache: None,
|
|
259
268
|
})
|
|
260
269
|
}
|
|
261
270
|
|
|
@@ -277,6 +286,7 @@ impl Db {
|
|
|
277
286
|
path_table_ignore: default_path_table_ignore(),
|
|
278
287
|
path_table_gitignore: true,
|
|
279
288
|
path_table_parser: None,
|
|
289
|
+
path_table_cache: None,
|
|
280
290
|
})
|
|
281
291
|
}
|
|
282
292
|
|
|
@@ -313,6 +323,14 @@ impl Db {
|
|
|
313
323
|
self.path_table_parser = Some(command);
|
|
314
324
|
}
|
|
315
325
|
|
|
326
|
+
/// Point every parsed path-table minted on this connection at the
|
|
327
|
+
/// persistent cache, so an unchanged file's rows are served from it
|
|
328
|
+
/// instead of re-running the parser. Set only when the index persists;
|
|
329
|
+
/// an ephemeral index has nowhere to cache to.
|
|
330
|
+
pub fn set_path_table_cache(&mut self, path: PathBuf) {
|
|
331
|
+
self.path_table_cache = Some(path);
|
|
332
|
+
}
|
|
333
|
+
|
|
316
334
|
/// Borrow the underlying SQLite connection. Internal use only — exposed
|
|
317
335
|
/// to the `persist` module so it can manage the sidecar tables.
|
|
318
336
|
#[doc(hidden)]
|
|
@@ -783,6 +801,7 @@ impl Db {
|
|
|
783
801
|
&self.path_table_ignore,
|
|
784
802
|
self.path_table_gitignore,
|
|
785
803
|
self.path_table_parser.as_deref(),
|
|
804
|
+
self.path_table_cache.as_deref(),
|
|
786
805
|
))?;
|
|
787
806
|
Ok(())
|
|
788
807
|
}
|
|
@@ -2147,16 +2166,6 @@ mod tests {
|
|
|
2147
2166
|
);
|
|
2148
2167
|
}
|
|
2149
2168
|
|
|
2150
|
-
#[test]
|
|
2151
|
-
fn quote_literal_doubles_embedded_quotes() {
|
|
2152
|
-
assert_eq!(quote_literal("it's"), "'it''s'");
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
#[test]
|
|
2156
|
-
fn quote_identifier_doubles_embedded_quotes() {
|
|
2157
|
-
assert_eq!(quote_identifier("a\"b"), "\"a\"\"b\"");
|
|
2158
|
-
}
|
|
2159
|
-
|
|
2160
2169
|
fn docs_path_table() -> PathTable {
|
|
2161
2170
|
PathTable {
|
|
2162
2171
|
root: PathBuf::from("/root"),
|
|
@@ -2167,7 +2176,7 @@ mod tests {
|
|
|
2167
2176
|
|
|
2168
2177
|
#[test]
|
|
2169
2178
|
fn path_table_ddl_creates_in_temp_if_not_exists() {
|
|
2170
|
-
let ddl = path_table_ddl("./docs/*.md", &docs_path_table(), &[], true, None);
|
|
2179
|
+
let ddl = path_table_ddl("./docs/*.md", &docs_path_table(), &[], true, None, None);
|
|
2171
2180
|
assert_eq!(
|
|
2172
2181
|
ddl,
|
|
2173
2182
|
"CREATE VIRTUAL TABLE IF NOT EXISTS temp.\"./docs/*.md\" \
|
|
@@ -2183,16 +2192,16 @@ mod tests {
|
|
|
2183
2192
|
path_prefix: "/var/log".to_string(),
|
|
2184
2193
|
};
|
|
2185
2194
|
assert!(
|
|
2186
|
-
path_table_ddl("/var/log/*.log", &table, &[], true, None)
|
|
2195
|
+
path_table_ddl("/var/log/*.log", &table, &[], true, None, None)
|
|
2187
2196
|
.contains("'/var/log', '*.log', '/var/log', 'gitignore')"),
|
|
2188
2197
|
"got: {}",
|
|
2189
|
-
path_table_ddl("/var/log/*.log", &table, &[], true, None)
|
|
2198
|
+
path_table_ddl("/var/log/*.log", &table, &[], true, None, None)
|
|
2190
2199
|
);
|
|
2191
2200
|
}
|
|
2192
2201
|
|
|
2193
2202
|
#[test]
|
|
2194
2203
|
fn path_table_ddl_carries_the_no_gitignore_switch() {
|
|
2195
|
-
let ddl = path_table_ddl("./", &docs_path_table(), &[], false, None);
|
|
2204
|
+
let ddl = path_table_ddl("./", &docs_path_table(), &[], false, None, None);
|
|
2196
2205
|
assert!(
|
|
2197
2206
|
ddl.ends_with("'', 'no-gitignore')"),
|
|
2198
2207
|
"gitignore off must emit the no-gitignore switch, got: {ddl}"
|
|
@@ -2207,6 +2216,7 @@ mod tests {
|
|
|
2207
2216
|
&["node_modules/**".to_string(), "*.tmp".to_string()],
|
|
2208
2217
|
true,
|
|
2209
2218
|
None,
|
|
2219
|
+
None,
|
|
2210
2220
|
);
|
|
2211
2221
|
assert!(
|
|
2212
2222
|
ddl.ends_with("'', 'gitignore', 'node_modules/**', '*.tmp')"),
|
|
@@ -2222,11 +2232,44 @@ mod tests {
|
|
|
2222
2232
|
&[],
|
|
2223
2233
|
true,
|
|
2224
2234
|
Some("cat {path}"),
|
|
2235
|
+
None,
|
|
2225
2236
|
);
|
|
2226
2237
|
assert_eq!(
|
|
2227
2238
|
ddl,
|
|
2228
2239
|
"CREATE VIRTUAL TABLE IF NOT EXISTS temp.\"./docs/*.md\" \
|
|
2229
|
-
USING dirsql_parsed('/root', 'docs/*.md', 'cat {path}', 'gitignore')"
|
|
2240
|
+
USING dirsql_parsed('/root', 'docs/*.md', 'cat {path}', 'gitignore', '')"
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
#[test]
|
|
2245
|
+
fn path_table_ddl_parser_form_carries_the_cache_path() {
|
|
2246
|
+
let ddl = path_table_ddl(
|
|
2247
|
+
"./docs/*.md",
|
|
2248
|
+
&docs_path_table(),
|
|
2249
|
+
&[],
|
|
2250
|
+
true,
|
|
2251
|
+
Some("cat {path}"),
|
|
2252
|
+
Some(Path::new("/cache/dirsql.db")),
|
|
2253
|
+
);
|
|
2254
|
+
assert!(
|
|
2255
|
+
ddl.ends_with("'cat {path}', 'gitignore', '/cache/dirsql.db')"),
|
|
2256
|
+
"a persisted index points the parsed module at its cache, got: {ddl}"
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
#[test]
|
|
2261
|
+
fn path_table_ddl_stat_form_takes_no_cache_path() {
|
|
2262
|
+
let ddl = path_table_ddl(
|
|
2263
|
+
"./docs/*.md",
|
|
2264
|
+
&docs_path_table(),
|
|
2265
|
+
&[],
|
|
2266
|
+
true,
|
|
2267
|
+
None,
|
|
2268
|
+
Some(Path::new("/cache/dirsql.db")),
|
|
2269
|
+
);
|
|
2270
|
+
assert!(
|
|
2271
|
+
ddl.ends_with("'', 'gitignore')"),
|
|
2272
|
+
"the stat form has nothing to cache, got: {ddl}"
|
|
2230
2273
|
);
|
|
2231
2274
|
}
|
|
2232
2275
|
|
|
@@ -2243,11 +2286,12 @@ mod tests {
|
|
|
2243
2286
|
&["node_modules/**".to_string()],
|
|
2244
2287
|
true,
|
|
2245
2288
|
Some("parse.py {path}"),
|
|
2289
|
+
None,
|
|
2246
2290
|
);
|
|
2247
2291
|
assert!(
|
|
2248
2292
|
ddl.ends_with(
|
|
2249
2293
|
"USING dirsql_parsed('/var/log', '*.log', 'parse.py {path}', 'gitignore', \
|
|
2250
|
-
'node_modules/**')"
|
|
2294
|
+
'', 'node_modules/**')"
|
|
2251
2295
|
),
|
|
2252
2296
|
"the parser form drops the path prefix and keeps ignore rules, got: {ddl}"
|
|
2253
2297
|
);
|
|
@@ -2255,7 +2299,14 @@ mod tests {
|
|
|
2255
2299
|
|
|
2256
2300
|
#[test]
|
|
2257
2301
|
fn path_table_ddl_parser_form_quotes_a_command_with_a_quote() {
|
|
2258
|
-
let ddl = path_table_ddl(
|
|
2302
|
+
let ddl = path_table_ddl(
|
|
2303
|
+
"./",
|
|
2304
|
+
&docs_path_table(),
|
|
2305
|
+
&[],
|
|
2306
|
+
true,
|
|
2307
|
+
Some("sh -c 'echo hi'"),
|
|
2308
|
+
None,
|
|
2309
|
+
);
|
|
2259
2310
|
assert!(
|
|
2260
2311
|
ddl.contains("'sh -c ''echo hi'''"),
|
|
2261
2312
|
"an embedded quote must be doubled, got: {ddl}"
|
|
@@ -2269,6 +2320,17 @@ mod tests {
|
|
|
2269
2320
|
assert_eq!(db.path_table_parser.as_deref(), Some("cat {path}"));
|
|
2270
2321
|
}
|
|
2271
2322
|
|
|
2323
|
+
#[test]
|
|
2324
|
+
fn set_path_table_cache_points_parsed_tables_at_the_cache() {
|
|
2325
|
+
let mut db = Db::new().unwrap();
|
|
2326
|
+
assert_eq!(db.path_table_cache, None, "an ephemeral index has no cache");
|
|
2327
|
+
db.set_path_table_cache(PathBuf::from("/cache/dirsql.db"));
|
|
2328
|
+
assert_eq!(
|
|
2329
|
+
db.path_table_cache.as_deref(),
|
|
2330
|
+
Some(Path::new("/cache/dirsql.db"))
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2272
2334
|
#[test]
|
|
2273
2335
|
fn path_table_gitignore_defaults_on() {
|
|
2274
2336
|
let db = Db::new().unwrap();
|