dirsql 0.4.19__tar.gz → 0.4.21__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.19 → dirsql-0.4.21}/Cargo.lock +1 -1
- {dirsql-0.4.19 → dirsql-0.4.21}/PKG-INFO +1 -1
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/.vitepress/config.ts +1 -2
- {dirsql-0.4.19/packages/python → dirsql-0.4.21}/docs/AGENTS.md +4 -6
- {dirsql-0.4.19/packages/python → dirsql-0.4.21}/docs/howto/search-by-meaning.md +8 -1
- {dirsql-0.4.19/packages/rust → dirsql-0.4.21}/docs/plugins.md +1 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/Cargo.toml +1 -1
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/MIGRATIONS.md +1 -2
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/.vitepress/config.ts +1 -2
- {dirsql-0.4.19 → dirsql-0.4.21/packages/python}/docs/AGENTS.md +4 -6
- {dirsql-0.4.19 → dirsql-0.4.21/packages/python}/docs/howto/search-by-meaning.md +8 -1
- {dirsql-0.4.19 → dirsql-0.4.21/packages/python}/docs/plugins.md +1 -0
- dirsql-0.4.21/packages/python/e2e-attestations/claude-827-unquote-doubled-quotes.json +7 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/MIGRATIONS.md +1 -2
- dirsql-0.4.21/packages/rust/changelog.d/2026-08-12-unquote-inverts-quote-literal.md +3 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/search-by-meaning.md +8 -1
- {dirsql-0.4.19/packages/python → dirsql-0.4.21/packages/rust}/docs/plugins.md +1 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/db.rs +1 -19
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/lib.rs +2 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/parsed_vtab.rs +1 -42
- dirsql-0.4.21/packages/rust/src/sql_literal.rs +127 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/vtab.rs +1 -42
- dirsql-0.4.19/docs/migrations.md +0 -5
- dirsql-0.4.19/packages/python/docs/migrations.md +0 -5
- dirsql-0.4.19/packages/rust/docs/migrations.md +0 -5
- {dirsql-0.4.19 → dirsql-0.4.21}/Cargo.toml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/_async.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/_dirsql.pyi +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/discover_plugins/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/discover_plugins/discovered_fragments.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/discover_plugins/discovery_disabled.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/discover_plugins/fragment_path.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/discover_plugins/user_passed_config.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/discover_plugins/with_discovered_plugins.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/main.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/cli/resolve_config_extensions.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/py.typed +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/resolve_config_extensions.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/dirsql/resolve_extension.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/explanation.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/getting-started.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/embed.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/persist.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/index.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/package.json +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/cli.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/columns.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/hooks.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/http-api.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/docs/reference/sdk.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/CHANGELOG.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-13-plugin-discovery.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-13-repeatable-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-02-scan-failures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-03-discovery-ext-resolution.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-03-manylinux-dynamic-binary.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-03-python-no-ignore.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/changelog.d/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/conftest.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/.claude/CLAUDE.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/.vitepress/theme/index.ts +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/.vitepress/theme/lang.ts +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/explanation.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/getting-started.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/embed.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/persist.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/index.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/package.json +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/pnpm-lock.yaml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/pnpm-workspace.yaml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/cli.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/columns.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/hooks.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/http-api.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/docs/reference/sdk.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/e2e-attestations/claude-819-oneshot-timeout.json +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/e2e-attestations/claude-github-issue-825-e4mhhp.json +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-07-29-restore-python-310.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/2026-08-04-cli-in-process.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/migrations.d/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/src/lib.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/conftest.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/e2e/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/e2e/fixtures/dirsql_plugin_fixture/dirsql.toml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/integration/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/integration/binding/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/python/tests/integration/hermetic/__init__.py +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/CHANGELOG.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/Cargo.toml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/benches/db_bench.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/benches/differ_bench.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/benches/matcher_bench.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/benches/scanner_bench.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-batch-ingest-transaction.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-chained-pre-post-query-hooks.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-fan-out-file-table-matching.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-persist-wal.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-repeatable-builder-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-repeatable-cli-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-13-subcommand-local-config-flags.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-19-path-table-query-fallback.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-19-path-table-vtab.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-20-path-table-glob-semantics.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-20-schema-inference.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-21-on-file-flag.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-23-init-escalation-example.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-23-query-default-cli.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-07-29-on-file-failures-accumulate.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-02-drop-regex.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-02-per-file-scan-failures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-02-release-profile.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-03-gitignore-by-default.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-03-nested-default-ignores.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-04-cast-lints.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-04-cli-run-shim.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-04-configless-extension.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-10-dirsql-function.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-10-remove-query-hooks.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-11-oneshot-query-no-timeout.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-11-persist-parsed-path-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/2026-08-11-retire-hook-timeout.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/changelog.d/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/explanation.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/getting-started.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/columns-from-paths.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/define-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/embed.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/extract-from-contents.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/load-extension.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/parse-files-into-columns.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/persist.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/query-without-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/react-to-changes.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/skip-files.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/howto/write-a-plugin.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/index.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/cli.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/columns.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/hooks.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/http-api.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/path-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/docs/reference/sdk.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-13-drop-cwd-config-auto-detect.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-13-fan-out-file-table-matching.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-13-persist-wal.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-13-rename-extract-to-on-file.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-13-sdk-default-baked-in-config.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-13-subcommand-local-config-flags.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-20-drop-implicit-files-table.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-20-path-table-glob-semantics.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-21-remove-glob-captures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-23-init-escalation-example.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-23-query-default-cli.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-24-hookless-table-config-error.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-07-24-remove-stat-fact-injection.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-02-per-file-scan-failures.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-03-gitignore-by-default.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-03-nested-default-ignores.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-10-remove-query-hooks.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-11-oneshot-query-no-timeout.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-11-persist-parsed-path-tables.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/2026-08-11-retire-hook-timeout.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/migrations.d/README.md +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/bin/dirsql.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/execute.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/init.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/mod.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/router.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/run.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/serialize.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/cli/server.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/command.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/config.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/default_config.toml +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/differ.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/functions.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/infer.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/matcher.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/parsed_cache.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/path_table.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/persist.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/scanner.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/packages/rust/src/watcher.rs +0 -0
- {dirsql-0.4.19 → dirsql-0.4.21}/pyproject.toml +0 -0
|
@@ -72,8 +72,7 @@ export default defineConfig({
|
|
|
72
72
|
{ text: 'Columns', link: '/reference/columns' },
|
|
73
73
|
{ text: 'Path-tables', link: '/reference/path-tables' },
|
|
74
74
|
{ text: 'HTTP API', link: '/reference/http-api' },
|
|
75
|
-
{ text: 'SDK', link: '/reference/sdk' }
|
|
76
|
-
{ text: 'Migrations', link: '/migrations' }
|
|
75
|
+
{ text: 'SDK', link: '/reference/sdk' }
|
|
77
76
|
]
|
|
78
77
|
},
|
|
79
78
|
{
|
|
@@ -60,15 +60,13 @@ migrated -- a page survives only if a slot wants its content):
|
|
|
60
60
|
protocol, exit codes, timeouts); the columns model (named tables get their
|
|
61
61
|
hook's output; path-tables get stat columns); the
|
|
62
62
|
HTTP API; the SDK page (`reference/sdk.md`, one page with
|
|
63
|
-
Python/TypeScript/Rust code-groups -- the sole SDK home)
|
|
64
|
-
Migrations include (`migrations.md`).
|
|
63
|
+
Python/TypeScript/Rust code-groups -- the sole SDK home).
|
|
65
64
|
- **Explanation** (`explanation.md`) -- one page: how `dirsql` thinks (the
|
|
66
65
|
filesystem is the source of truth; the database is a derived, ephemeral,
|
|
67
66
|
read-only view; reconcile and diffing). Its canonical home is the root
|
|
68
|
-
`ARCHITECTURE.md`;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
a rendered include.
|
|
67
|
+
`ARCHITECTURE.md`; #374 surfaces it via a VitePress include
|
|
68
|
+
(`<!--@include: ../ARCHITECTURE.md-->`). Edit the root file, never a
|
|
69
|
+
rendered include.
|
|
72
70
|
|
|
73
71
|
Working rules:
|
|
74
72
|
|
|
@@ -51,6 +51,7 @@ uvx --with dirsql-plugin-embeddings dirsql "
|
|
|
51
51
|
SELECT path,
|
|
52
52
|
vec_distance_cosine(emb, embed('how do I cook pasta?')) AS distance
|
|
53
53
|
FROM (SELECT path, embed(content) AS emb FROM './notes/*.md')
|
|
54
|
+
WHERE emb IS NOT NULL
|
|
54
55
|
ORDER BY distance
|
|
55
56
|
LIMIT 3"
|
|
56
57
|
```
|
|
@@ -72,7 +73,12 @@ Reading the query inside-out:
|
|
|
72
73
|
the one-liner normalizes a bare glob for you.
|
|
73
74
|
2. `embed('how do I cook pasta?')` embeds the question once (the function is
|
|
74
75
|
deterministic, so SQLite reuses the value across rows).
|
|
75
|
-
3. `
|
|
76
|
+
3. `WHERE emb IS NOT NULL` drops the files that could not be embedded. A
|
|
77
|
+
file that is unreadable or not valid UTF-8 has
|
|
78
|
+
[`NULL` content](../reference/path-tables.md#columns), so its embedding
|
|
79
|
+
and its distance are NULL too — and SQLite sorts NULLs *first* ascending,
|
|
80
|
+
so without this line the unrankable files take the top-k slots.
|
|
81
|
+
4. `vec_distance_cosine(...)` computes cosine distance between the two
|
|
76
82
|
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
|
|
77
83
|
|
|
78
84
|
Structured files compose with SQL's JSON operators — embed one field instead
|
|
@@ -82,6 +88,7 @@ of the whole file:
|
|
|
82
88
|
SELECT path
|
|
83
89
|
FROM (SELECT path, embed(content ->> 'abstract') AS emb
|
|
84
90
|
FROM './papers/**/metadata.json')
|
|
91
|
+
WHERE emb IS NOT NULL
|
|
85
92
|
ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
86
93
|
LIMIT 10
|
|
87
94
|
```
|
|
@@ -35,6 +35,7 @@ uvx --with dirsql-plugin-embeddings dirsql "
|
|
|
35
35
|
SELECT path
|
|
36
36
|
FROM (SELECT path, embed(content ->> 'abstract') AS emb
|
|
37
37
|
FROM './arxiv-firehose/data/**/metadata.json')
|
|
38
|
+
WHERE emb IS NOT NULL
|
|
38
39
|
ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
39
40
|
LIMIT 10"
|
|
40
41
|
```
|
|
@@ -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.21"
|
|
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
|
|
@@ -7,5 +7,4 @@ Migration entries for this package live as one file per breaking change under
|
|
|
7
7
|
Nothing is assembled back into this file.
|
|
8
8
|
|
|
9
9
|
Pre-fragment history (all packages) is frozen in the repository-root
|
|
10
|
-
[`MIGRATIONS.md`](../../MIGRATIONS.md)
|
|
11
|
-
[`/migrations`](https://thekevinscott.github.io/dirsql/migrations).
|
|
10
|
+
[`MIGRATIONS.md`](../../MIGRATIONS.md).
|
|
@@ -72,8 +72,7 @@ export default defineConfig({
|
|
|
72
72
|
{ text: 'Columns', link: '/reference/columns' },
|
|
73
73
|
{ text: 'Path-tables', link: '/reference/path-tables' },
|
|
74
74
|
{ text: 'HTTP API', link: '/reference/http-api' },
|
|
75
|
-
{ text: 'SDK', link: '/reference/sdk' }
|
|
76
|
-
{ text: 'Migrations', link: '/migrations' }
|
|
75
|
+
{ text: 'SDK', link: '/reference/sdk' }
|
|
77
76
|
]
|
|
78
77
|
},
|
|
79
78
|
{
|
|
@@ -60,15 +60,13 @@ migrated -- a page survives only if a slot wants its content):
|
|
|
60
60
|
protocol, exit codes, timeouts); the columns model (named tables get their
|
|
61
61
|
hook's output; path-tables get stat columns); the
|
|
62
62
|
HTTP API; the SDK page (`reference/sdk.md`, one page with
|
|
63
|
-
Python/TypeScript/Rust code-groups -- the sole SDK home)
|
|
64
|
-
Migrations include (`migrations.md`).
|
|
63
|
+
Python/TypeScript/Rust code-groups -- the sole SDK home).
|
|
65
64
|
- **Explanation** (`explanation.md`) -- one page: how `dirsql` thinks (the
|
|
66
65
|
filesystem is the source of truth; the database is a derived, ephemeral,
|
|
67
66
|
read-only view; reconcile and diffing). Its canonical home is the root
|
|
68
|
-
`ARCHITECTURE.md`;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
a rendered include.
|
|
67
|
+
`ARCHITECTURE.md`; #374 surfaces it via a VitePress include
|
|
68
|
+
(`<!--@include: ../ARCHITECTURE.md-->`). Edit the root file, never a
|
|
69
|
+
rendered include.
|
|
72
70
|
|
|
73
71
|
Working rules:
|
|
74
72
|
|
|
@@ -51,6 +51,7 @@ uvx --with dirsql-plugin-embeddings dirsql "
|
|
|
51
51
|
SELECT path,
|
|
52
52
|
vec_distance_cosine(emb, embed('how do I cook pasta?')) AS distance
|
|
53
53
|
FROM (SELECT path, embed(content) AS emb FROM './notes/*.md')
|
|
54
|
+
WHERE emb IS NOT NULL
|
|
54
55
|
ORDER BY distance
|
|
55
56
|
LIMIT 3"
|
|
56
57
|
```
|
|
@@ -72,7 +73,12 @@ Reading the query inside-out:
|
|
|
72
73
|
the one-liner normalizes a bare glob for you.
|
|
73
74
|
2. `embed('how do I cook pasta?')` embeds the question once (the function is
|
|
74
75
|
deterministic, so SQLite reuses the value across rows).
|
|
75
|
-
3. `
|
|
76
|
+
3. `WHERE emb IS NOT NULL` drops the files that could not be embedded. A
|
|
77
|
+
file that is unreadable or not valid UTF-8 has
|
|
78
|
+
[`NULL` content](../reference/path-tables.md#columns), so its embedding
|
|
79
|
+
and its distance are NULL too — and SQLite sorts NULLs *first* ascending,
|
|
80
|
+
so without this line the unrankable files take the top-k slots.
|
|
81
|
+
4. `vec_distance_cosine(...)` computes cosine distance between the two
|
|
76
82
|
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
|
|
77
83
|
|
|
78
84
|
Structured files compose with SQL's JSON operators — embed one field instead
|
|
@@ -82,6 +88,7 @@ of the whole file:
|
|
|
82
88
|
SELECT path
|
|
83
89
|
FROM (SELECT path, embed(content ->> 'abstract') AS emb
|
|
84
90
|
FROM './papers/**/metadata.json')
|
|
91
|
+
WHERE emb IS NOT NULL
|
|
85
92
|
ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
86
93
|
LIMIT 10
|
|
87
94
|
```
|
|
@@ -35,6 +35,7 @@ uvx --with dirsql-plugin-embeddings dirsql "
|
|
|
35
35
|
SELECT path
|
|
36
36
|
FROM (SELECT path, embed(content ->> 'abstract') AS emb
|
|
37
37
|
FROM './arxiv-firehose/data/**/metadata.json')
|
|
38
|
+
WHERE emb IS NOT NULL
|
|
38
39
|
ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
39
40
|
LIMIT 10"
|
|
40
41
|
```
|
|
@@ -7,5 +7,4 @@ Migration entries for this package live as one file per breaking change under
|
|
|
7
7
|
Nothing is assembled back into this file.
|
|
8
8
|
|
|
9
9
|
Pre-fragment history (all packages) is frozen in the repository-root
|
|
10
|
-
[`MIGRATIONS.md`](../../MIGRATIONS.md)
|
|
11
|
-
[`/migrations`](https://thekevinscott.github.io/dirsql/migrations).
|
|
10
|
+
[`MIGRATIONS.md`](../../MIGRATIONS.md).
|
|
@@ -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)
|
|
@@ -51,6 +51,7 @@ uvx --with dirsql-plugin-embeddings dirsql "
|
|
|
51
51
|
SELECT path,
|
|
52
52
|
vec_distance_cosine(emb, embed('how do I cook pasta?')) AS distance
|
|
53
53
|
FROM (SELECT path, embed(content) AS emb FROM './notes/*.md')
|
|
54
|
+
WHERE emb IS NOT NULL
|
|
54
55
|
ORDER BY distance
|
|
55
56
|
LIMIT 3"
|
|
56
57
|
```
|
|
@@ -72,7 +73,12 @@ Reading the query inside-out:
|
|
|
72
73
|
the one-liner normalizes a bare glob for you.
|
|
73
74
|
2. `embed('how do I cook pasta?')` embeds the question once (the function is
|
|
74
75
|
deterministic, so SQLite reuses the value across rows).
|
|
75
|
-
3. `
|
|
76
|
+
3. `WHERE emb IS NOT NULL` drops the files that could not be embedded. A
|
|
77
|
+
file that is unreadable or not valid UTF-8 has
|
|
78
|
+
[`NULL` content](../reference/path-tables.md#columns), so its embedding
|
|
79
|
+
and its distance are NULL too — and SQLite sorts NULLs *first* ascending,
|
|
80
|
+
so without this line the unrankable files take the top-k slots.
|
|
81
|
+
4. `vec_distance_cosine(...)` computes cosine distance between the two
|
|
76
82
|
vectors; `ORDER BY distance LIMIT 3` keeps the three nearest.
|
|
77
83
|
|
|
78
84
|
Structured files compose with SQL's JSON operators — embed one field instead
|
|
@@ -82,6 +88,7 @@ of the whole file:
|
|
|
82
88
|
SELECT path
|
|
83
89
|
FROM (SELECT path, embed(content ->> 'abstract') AS emb
|
|
84
90
|
FROM './papers/**/metadata.json')
|
|
91
|
+
WHERE emb IS NOT NULL
|
|
85
92
|
ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
86
93
|
LIMIT 10
|
|
87
94
|
```
|
|
@@ -35,6 +35,7 @@ uvx --with dirsql-plugin-embeddings dirsql "
|
|
|
35
35
|
SELECT path
|
|
36
36
|
FROM (SELECT path, embed(content ->> 'abstract') AS emb
|
|
37
37
|
FROM './arxiv-firehose/data/**/metadata.json')
|
|
38
|
+
WHERE emb IS NOT NULL
|
|
38
39
|
ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
39
40
|
LIMIT 10"
|
|
40
41
|
```
|
|
@@ -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
|
|
@@ -2174,16 +2166,6 @@ mod tests {
|
|
|
2174
2166
|
);
|
|
2175
2167
|
}
|
|
2176
2168
|
|
|
2177
|
-
#[test]
|
|
2178
|
-
fn quote_literal_doubles_embedded_quotes() {
|
|
2179
|
-
assert_eq!(quote_literal("it's"), "'it''s'");
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
#[test]
|
|
2183
|
-
fn quote_identifier_doubles_embedded_quotes() {
|
|
2184
|
-
assert_eq!(quote_identifier("a\"b"), "\"a\"\"b\"");
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
2169
|
fn docs_path_table() -> PathTable {
|
|
2188
2170
|
PathTable {
|
|
2189
2171
|
root: PathBuf::from("/root"),
|
|
@@ -39,6 +39,7 @@ use crate::parsed_cache::{self, CachedParse, Entry, RowCache, SqliteRowCache};
|
|
|
39
39
|
use crate::path_table;
|
|
40
40
|
use crate::persist::{FileStat, hash_file, now_ns};
|
|
41
41
|
use crate::scanner::{self, scan_glob};
|
|
42
|
+
use crate::sql_literal::unquote;
|
|
42
43
|
|
|
43
44
|
/// SQL module name a parsed path-table is created with.
|
|
44
45
|
pub const MODULE_NAME: &str = "dirsql_parsed";
|
|
@@ -49,18 +50,6 @@ pub fn load_module(conn: &Connection) -> Result<()> {
|
|
|
49
50
|
conn.create_module(MODULE_NAME, read_only_module::<ParsedTab>(), aux)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
/// Strip one layer of SQL string quoting from a `CREATE VIRTUAL TABLE`
|
|
53
|
-
/// argument. SQLite hands module arguments through verbatim, quotes included.
|
|
54
|
-
fn unquote(arg: &str) -> &str {
|
|
55
|
-
let trimmed = arg.trim();
|
|
56
|
-
for quote in ['\'', '"'] {
|
|
57
|
-
if trimmed.len() >= 2 && trimmed.starts_with(quote) && trimmed.ends_with(quote) {
|
|
58
|
-
return &trimmed[1..trimmed.len() - 1];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
trimmed
|
|
62
|
-
}
|
|
63
|
-
|
|
64
53
|
/// Number of module arguments that are not ignore patterns.
|
|
65
54
|
const FIXED_ARGS: usize = 5;
|
|
66
55
|
|
|
@@ -485,36 +474,6 @@ mod tests {
|
|
|
485
474
|
assert_eq!(MODULE_NAME, "dirsql_parsed");
|
|
486
475
|
}
|
|
487
476
|
|
|
488
|
-
#[test]
|
|
489
|
-
fn unquote_strips_single_quotes() {
|
|
490
|
-
assert_eq!(unquote("'./docs'"), "./docs");
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
#[test]
|
|
494
|
-
fn unquote_strips_double_quotes() {
|
|
495
|
-
assert_eq!(unquote("\"./docs\""), "./docs");
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
#[test]
|
|
499
|
-
fn unquote_trims_surrounding_whitespace() {
|
|
500
|
-
assert_eq!(unquote(" './docs' "), "./docs");
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
#[test]
|
|
504
|
-
fn unquote_leaves_unquoted_arguments_alone() {
|
|
505
|
-
assert_eq!(unquote("./docs"), "./docs");
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
#[test]
|
|
509
|
-
fn unquote_leaves_a_lone_quote_alone() {
|
|
510
|
-
assert_eq!(unquote("'"), "'");
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
#[test]
|
|
514
|
-
fn unquote_leaves_mismatched_quotes_alone() {
|
|
515
|
-
assert_eq!(unquote("'./docs\""), "'./docs\"");
|
|
516
|
-
}
|
|
517
|
-
|
|
518
477
|
/// The three fixed arguments SQLite prepends before the module's own.
|
|
519
478
|
fn args_with<'a>(user: &[&'a [u8]]) -> Vec<&'a [u8]> {
|
|
520
479
|
let mut all: Vec<&'a [u8]> = vec![
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//! The one encoding a path-table's arguments travel through.
|
|
2
|
+
//!
|
|
3
|
+
//! `dirsql` mints a path-table by writing a `CREATE VIRTUAL TABLE` statement
|
|
4
|
+
//! whose module arguments are SQL literals ([`quote_literal`]), and SQLite
|
|
5
|
+
//! hands those arguments to the module verbatim, quotes included, for
|
|
6
|
+
//! [`unquote`] to decode. The two are inverses, and they live together so they
|
|
7
|
+
//! stay that way: a value that survives one but not the other reaches the
|
|
8
|
+
//! module as something the user never wrote.
|
|
9
|
+
|
|
10
|
+
use std::borrow::Cow;
|
|
11
|
+
|
|
12
|
+
/// Wrap `s` as a single-quoted SQL string literal.
|
|
13
|
+
pub fn quote_literal(s: &str) -> String {
|
|
14
|
+
format!("'{}'", s.replace('\'', "''"))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Wrap `s` as a double-quoted SQL identifier.
|
|
18
|
+
pub fn quote_identifier(s: &str) -> String {
|
|
19
|
+
format!("\"{}\"", s.replace('"', "\"\""))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Decode one quoted `CREATE VIRTUAL TABLE` argument: strip the surrounding
|
|
23
|
+
/// quotes and collapse the doubling that escaped them. Borrowed when there is
|
|
24
|
+
/// nothing to collapse, which is the ordinary case.
|
|
25
|
+
pub fn unquote(arg: &str) -> Cow<'_, str> {
|
|
26
|
+
let trimmed = arg.trim();
|
|
27
|
+
for quote in ['\'', '"'] {
|
|
28
|
+
if trimmed.len() >= 2 && trimmed.starts_with(quote) && trimmed.ends_with(quote) {
|
|
29
|
+
let inner = &trimmed[1..trimmed.len() - 1];
|
|
30
|
+
let doubled = [quote, quote].iter().collect::<String>();
|
|
31
|
+
return if inner.contains(&doubled) {
|
|
32
|
+
Cow::Owned(inner.replace(&doubled, "e.to_string()))
|
|
33
|
+
} else {
|
|
34
|
+
Cow::Borrowed(inner)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
Cow::Borrowed(trimmed)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#[cfg(test)]
|
|
42
|
+
mod tests {
|
|
43
|
+
use super::*;
|
|
44
|
+
|
|
45
|
+
#[test]
|
|
46
|
+
fn quote_literal_doubles_embedded_quotes() {
|
|
47
|
+
assert_eq!(quote_literal("it's"), "'it''s'");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
fn quote_identifier_doubles_embedded_quotes() {
|
|
52
|
+
assert_eq!(quote_identifier("a\"b"), "\"a\"\"b\"");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[test]
|
|
56
|
+
fn unquote_strips_single_quotes() {
|
|
57
|
+
assert_eq!(unquote("'./docs'"), "./docs");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#[test]
|
|
61
|
+
fn unquote_strips_double_quotes() {
|
|
62
|
+
assert_eq!(unquote("\"./docs\""), "./docs");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[test]
|
|
66
|
+
fn unquote_trims_surrounding_whitespace() {
|
|
67
|
+
assert_eq!(unquote(" './docs' "), "./docs");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[test]
|
|
71
|
+
fn unquote_leaves_unquoted_arguments_alone() {
|
|
72
|
+
assert_eq!(unquote("./docs"), "./docs");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[test]
|
|
76
|
+
fn unquote_leaves_a_lone_quote_alone() {
|
|
77
|
+
assert_eq!(unquote("'"), "'");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#[test]
|
|
81
|
+
fn unquote_leaves_mismatched_quotes_alone() {
|
|
82
|
+
assert_eq!(unquote("'./docs\""), "'./docs\"");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[test]
|
|
86
|
+
fn unquote_collapses_the_doubling_that_escaped_a_quote() {
|
|
87
|
+
assert_eq!(unquote("'sh -c ''echo hi'''"), "sh -c 'echo hi'");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[test]
|
|
91
|
+
fn unquote_collapses_only_the_quote_character_it_stripped() {
|
|
92
|
+
// A double-quote inside a single-quoted literal was never doubled, so
|
|
93
|
+
// collapsing it would corrupt the value in the other direction.
|
|
94
|
+
assert_eq!(unquote("'say \"\"hi\"\"'"), "say \"\"hi\"\"");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#[test]
|
|
98
|
+
fn unquote_borrows_when_there_is_nothing_to_collapse() {
|
|
99
|
+
assert!(matches!(unquote("'plain'"), Cow::Borrowed("plain")));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#[test]
|
|
103
|
+
fn unquote_inverts_quote_literal() {
|
|
104
|
+
for value in [
|
|
105
|
+
"plain",
|
|
106
|
+
"it's",
|
|
107
|
+
"sh -c 'echo hi'",
|
|
108
|
+
"''",
|
|
109
|
+
"a''b",
|
|
110
|
+
"say \"hi\"",
|
|
111
|
+
"",
|
|
112
|
+
] {
|
|
113
|
+
assert_eq!(
|
|
114
|
+
unquote("e_literal(value)),
|
|
115
|
+
value,
|
|
116
|
+
"round trip must be lossless for {value:?}"
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#[test]
|
|
122
|
+
fn unquote_inverts_quote_identifier() {
|
|
123
|
+
for value in ["plain", "a\"b", "./docs/*.md"] {
|
|
124
|
+
assert_eq!(unquote("e_identifier(value)), value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -13,6 +13,7 @@ use crate::compute_stat_virtuals;
|
|
|
13
13
|
use crate::matcher::TableMatcher;
|
|
14
14
|
use crate::path_table;
|
|
15
15
|
use crate::scanner::{self, scan_glob};
|
|
16
|
+
use crate::sql_literal::unquote;
|
|
16
17
|
use crate::{Row, Value};
|
|
17
18
|
|
|
18
19
|
/// SQL module name a path-table is created with:
|
|
@@ -58,18 +59,6 @@ fn compile_glob(pattern: &str) -> Result<GlobSet> {
|
|
|
58
59
|
scanner::compile_glob(pattern).map_err(|e| Error::ModuleError(e.to_string()))
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
/// Strip one layer of SQL string quoting from a `CREATE VIRTUAL TABLE`
|
|
62
|
-
/// argument. SQLite hands module arguments through verbatim, quotes included.
|
|
63
|
-
fn unquote(arg: &str) -> &str {
|
|
64
|
-
let trimmed = arg.trim();
|
|
65
|
-
for quote in ['\'', '"'] {
|
|
66
|
-
if trimmed.len() >= 2 && trimmed.starts_with(quote) && trimmed.ends_with(quote) {
|
|
67
|
-
return &trimmed[1..trimmed.len() - 1];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
trimmed
|
|
71
|
-
}
|
|
72
|
-
|
|
73
62
|
/// Read `path` as text, yielding `None` when it is unreadable or not valid
|
|
74
63
|
/// UTF-8. A file that cannot be read is a NULL cell, never a failed row: the
|
|
75
64
|
/// filesystem is allowed to be messy and a query over it should still return.
|
|
@@ -376,36 +365,6 @@ mod tests {
|
|
|
376
365
|
assert_eq!(MODULE_NAME, "dirsql_path");
|
|
377
366
|
}
|
|
378
367
|
|
|
379
|
-
#[test]
|
|
380
|
-
fn unquote_strips_single_quotes() {
|
|
381
|
-
assert_eq!(unquote("'./docs'"), "./docs");
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
#[test]
|
|
385
|
-
fn unquote_strips_double_quotes() {
|
|
386
|
-
assert_eq!(unquote("\"./docs\""), "./docs");
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
#[test]
|
|
390
|
-
fn unquote_trims_surrounding_whitespace() {
|
|
391
|
-
assert_eq!(unquote(" './docs' "), "./docs");
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
#[test]
|
|
395
|
-
fn unquote_leaves_unquoted_arguments_alone() {
|
|
396
|
-
assert_eq!(unquote("./docs"), "./docs");
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
#[test]
|
|
400
|
-
fn unquote_leaves_a_lone_quote_alone() {
|
|
401
|
-
assert_eq!(unquote("'"), "'");
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
#[test]
|
|
405
|
-
fn unquote_leaves_mismatched_quotes_alone() {
|
|
406
|
-
assert_eq!(unquote("'./docs\""), "'./docs\"");
|
|
407
|
-
}
|
|
408
|
-
|
|
409
368
|
#[test]
|
|
410
369
|
fn compile_glob_accepts_a_valid_pattern() {
|
|
411
370
|
assert!(compile_glob("**/*.md").is_ok());
|
dirsql-0.4.19/docs/migrations.md
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|