dirsql 0.4.69 → 0.4.70
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.
- package/docs/howto/extract-from-contents.md +24 -14
- package/docs/howto/parse-files-into-columns.md +73 -6
- package/docs/howto/search-by-meaning.md +19 -0
- package/docs/reference/cli.md +17 -12
- package/docs/reference/hooks.md +47 -9
- package/docs/reference/path-tables.md +43 -11
- package/package.json +6 -6
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Paths and stat metadata only get you so far — when the columns you want live
|
|
4
4
|
*inside* the files (JSON fields, frontmatter, log lines), add an
|
|
5
|
-
[`on-file`](../reference/config.md#table) command: it runs once
|
|
6
|
-
file
|
|
5
|
+
[`on-file`](../reference/config.md#table) command: it runs once for the
|
|
6
|
+
table, with every matched file as an argument, and the JSON array it prints
|
|
7
|
+
becomes the table's rows.
|
|
7
8
|
|
|
8
9
|
## 1. Point a command at the files
|
|
9
10
|
|
|
@@ -13,8 +14,8 @@ Suppose each book is a JSON file:
|
|
|
13
14
|
{"title": "Middlemarch", "author": "George Eliot", "year": 1871}
|
|
14
15
|
```
|
|
15
16
|
|
|
16
|
-
Any program that reads
|
|
17
|
-
stdout works. With [`jq`](https://jqlang.org/):
|
|
17
|
+
Any program that reads the files named by its arguments and prints a **JSON
|
|
18
|
+
array of row objects** on stdout works. With [`jq`](https://jqlang.org/):
|
|
18
19
|
|
|
19
20
|
```toml
|
|
20
21
|
[[table]]
|
|
@@ -27,8 +28,8 @@ on-file = "jq -c -n '[inputs | {title, author, year}]'"
|
|
|
27
28
|
The command runs once for the table, with every matched file's absolute path
|
|
28
29
|
appended as an argument, and prints one array for all of them — the
|
|
29
30
|
[command hook contract](../reference/hooks.md#on-file), which also covers
|
|
30
|
-
the argv splitting, working directory, stdout protocol
|
|
31
|
-
|
|
31
|
+
the argv splitting, working directory, and stdout protocol shared by every
|
|
32
|
+
hook.
|
|
32
33
|
|
|
33
34
|
## 2. Query the extracted columns
|
|
34
35
|
|
|
@@ -68,13 +69,21 @@ dirsql query "SELECT event, user FROM events" -c ./.dirsql.toml
|
|
|
68
69
|
[{"event":"login","user":"alice"},{"event":"logout","user":"alice"},{"event":"login","user":"bob"}]
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
## When
|
|
72
|
+
## When the command fails
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
The command runs once for the whole table, so its failure is the table's: a
|
|
75
|
+
non-zero exit, no output, or output that is not a JSON array of objects fails
|
|
76
|
+
the build, and `dirsql query` exits `1` with the command's stderr tail on
|
|
77
|
+
stderr and nothing on stdout.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
dirsql query: failed to load config: table `books`: on-file command failed: command `jq -c -n '[inputs | {title, author, year}]'` failed (exit 5): jq: error (at /home/me/library/books/broken.json:1): Cannot index string with string "title"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
A file the command cannot parse is the command's to handle — skip it, or
|
|
84
|
+
emit a row with `NULL`s — because dirsql has no per-file view of the rows.
|
|
85
|
+
Details in [failure semantics](../reference/hooks.md#failure-semantics); the
|
|
86
|
+
JSON to SQLite value mapping is under
|
|
78
87
|
[`on-file` row mapping](../reference/config.md#on-file-row-mapping).
|
|
79
88
|
|
|
80
89
|
## Going further
|
|
@@ -82,8 +91,9 @@ SQLite value mapping is under
|
|
|
82
91
|
- Just want to read a field or two out of some JSON files? SQLite's JSON
|
|
83
92
|
operators work directly on a path-table's `content` column, with no config
|
|
84
93
|
and no command — [Query JSON file contents](./query-json.md).
|
|
85
|
-
- The command re-runs on every startup and
|
|
86
|
-
|
|
94
|
+
- The command re-runs on every startup and, over all of the table's files, on
|
|
95
|
+
every change under the table's glob. If it is expensive,
|
|
96
|
+
[keep the index across restarts](./persist.md).
|
|
87
97
|
- Embedding `dirsql` in a program instead? The SDK's `on_file` callback
|
|
88
98
|
fills the same role in-process — see
|
|
89
99
|
[Embed `dirsql` in your application](./embed.md).
|
|
@@ -66,12 +66,17 @@ dirsql query "SELECT title, author FROM './posts/*.md' ORDER BY title" \
|
|
|
66
66
|
Now the parser's output *is* the table. `--on-file` runs the command once,
|
|
67
67
|
with every matched file's absolute path appended as an argument, under the
|
|
68
68
|
shared [`on-file` hook contract](../reference/hooks.md#on-file) (argv
|
|
69
|
-
splitting
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
splitting and failure semantics come from there). It runs in the directory
|
|
70
|
+
you ran `dirsql` from, which is why `python3 extract.py` finds the script. The
|
|
71
|
+
stat columns are no longer reachable — a parser that wants the path emits it,
|
|
72
|
+
since it already has the path. See
|
|
72
73
|
[Parsing rows with `--on-file`](../reference/path-tables.md#parsing-rows-with-on-file)
|
|
73
74
|
for the full behavior.
|
|
74
75
|
|
|
76
|
+
One run, one process: on a thousand files the parser starts once, not a
|
|
77
|
+
thousand times, so a Python or Node parser costs its startup once per query.
|
|
78
|
+
That is the shape to write — loop over `sys.argv[1:]`, collect, print once.
|
|
79
|
+
|
|
75
80
|
`--on-file` applies to every path-table in the query and may be given at most
|
|
76
81
|
once. It is a `query`-only flag: there is no config file involved yet, so it is
|
|
77
82
|
the fastest way to see whether a parser produces the rows you expect.
|
|
@@ -114,12 +119,74 @@ gives every path-table one parser. In both spellings the table's columns are
|
|
|
114
119
|
exactly what the parser emits: `dirsql` merges no filesystem facts back on. A
|
|
115
120
|
row that needs the file's `path` emits it (the parser has the path).
|
|
116
121
|
|
|
122
|
+
## The two shapes
|
|
123
|
+
|
|
124
|
+
Every parser is one of two shapes. The array length is the row count either
|
|
125
|
+
way; what differs is how many files feed it.
|
|
126
|
+
|
|
127
|
+
**One file, many rows.** A lab notebook is a single Markdown file of dated
|
|
128
|
+
`## YYYY-MM-DD` sections, and the question is about the sections. Name the one
|
|
129
|
+
file as the path-table; the parser receives one path and prints one row per
|
|
130
|
+
section. `notebook.py`:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import json, re, sys
|
|
134
|
+
|
|
135
|
+
rows = []
|
|
136
|
+
for path in sys.argv[1:]:
|
|
137
|
+
text = open(path, encoding="utf-8").read()
|
|
138
|
+
for date, body in re.findall(r"^## (\d{4}-\d\d-\d\d)\n(.*?)(?=^## |\Z)", text, re.S | re.M):
|
|
139
|
+
rows.append({"date": date, "words": len(body.split())})
|
|
140
|
+
print(json.dumps(rows))
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
dirsql query "SELECT date, words FROM './notebook.md' ORDER BY date DESC LIMIT 2" \
|
|
145
|
+
--on-file 'python3 notebook.py'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
[{"date":"2026-09-23","words":10},{"date":"2026-09-22","words":3}]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Many files, many rows.** A tree of JSONL session logs under
|
|
153
|
+
`projects/<name>/`, and the question is how many messages each project has per
|
|
154
|
+
role. The parser receives every log's path and prints one row per message,
|
|
155
|
+
carrying the project name it derived from the path, because dirsql will not
|
|
156
|
+
attach it. `messages.py`:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
import json, os, sys
|
|
160
|
+
|
|
161
|
+
rows = []
|
|
162
|
+
for path in sys.argv[1:]:
|
|
163
|
+
project = os.path.relpath(path, "projects").split(os.sep)[0]
|
|
164
|
+
for line in open(path, encoding="utf-8"):
|
|
165
|
+
entry = json.loads(line)
|
|
166
|
+
if entry["type"] in ("user", "assistant"):
|
|
167
|
+
rows.append({"project": project, "role": entry["type"], "text": entry["message"]["content"]})
|
|
168
|
+
print(json.dumps(rows))
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
dirsql query "SELECT project, role, count(*) AS n FROM './projects/**/*.jsonl' GROUP BY project, role ORDER BY project, role" \
|
|
173
|
+
--on-file 'python3 messages.py'
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
[{"project":"alpha","role":"assistant","n":2},{"project":"alpha","role":"user","n":1},{"project":"beta","role":"user","n":2}]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The same two scripts work unchanged as a `[[table]]`'s `on-file` key. The
|
|
181
|
+
loop is the same in both shapes, so a parser written for one file already
|
|
182
|
+
handles a glob.
|
|
183
|
+
|
|
117
184
|
## Going further
|
|
118
185
|
|
|
119
|
-
- The full stat-vs-parsed behavior, failure
|
|
186
|
+
- The full stat-vs-parsed behavior, failure semantics, and skip rules:
|
|
120
187
|
[Parsing rows with `--on-file`](../reference/path-tables.md#parsing-rows-with-on-file).
|
|
121
188
|
- Starting from a declared table instead of a path-table?
|
|
122
189
|
[Extract rows from file contents](./extract-from-contents.md) covers the
|
|
123
190
|
config-first path.
|
|
124
|
-
-
|
|
125
|
-
|
|
191
|
+
- A very large table may reach the parser in several invocations; see
|
|
192
|
+
[Argument-list limits](../reference/hooks.md#argument-list-limits).
|
|
@@ -120,6 +120,25 @@ ORDER BY vec_distance_cosine(emb, embed('local private models'))
|
|
|
120
120
|
LIMIT 10
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
A field that needs real parsing — frontmatter, a log line — comes out with a
|
|
124
|
+
[parser attached](../reference/path-tables.md#parsing-rows-with-on-file) by
|
|
125
|
+
`--on-file`; the plugin's `embed()` then works on the parser's columns, since a
|
|
126
|
+
discovered plugin and an inline parser compose. With `title.py` printing one
|
|
127
|
+
`{"path", "title"}` row per note from its frontmatter:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
uvx --with dirsql-plugin-embeddings dirsql "
|
|
131
|
+
SELECT title,
|
|
132
|
+
vec_distance_cosine(embed(title), embed('how do I cook pasta?')) AS distance
|
|
133
|
+
FROM './notes/*.md'
|
|
134
|
+
ORDER BY distance
|
|
135
|
+
LIMIT 3" --on-file 'python3 title.py'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
[{"title":"Boiling spaghetti","distance":0.491},{"title":"Planting tomatoes","distance":0.836},{"title":"Feature branches","distance":1.064}]
|
|
140
|
+
```
|
|
141
|
+
|
|
123
142
|
The same projection works as an `on-file` hook feeding the indexed shape:
|
|
124
143
|
parse the field in the hook, store it as a column, and the trigger embeds it
|
|
125
144
|
once instead of on every query.
|
package/docs/reference/cli.md
CHANGED
|
@@ -361,8 +361,9 @@ uses**, so behavior is identical to `POST /query` by construction:
|
|
|
361
361
|
#### `--on-file <command>`
|
|
362
362
|
|
|
363
363
|
Attach a parser to every [path-table](./path-tables.md#parsing-rows-with-on-file)
|
|
364
|
-
in the query
|
|
365
|
-
array of row objects
|
|
364
|
+
in the query: the command runs once per path-table, with every matched file's
|
|
365
|
+
path as a trailing argument, and the JSON array of row objects it prints
|
|
366
|
+
replaces the stat columns:
|
|
366
367
|
|
|
367
368
|
```sh
|
|
368
369
|
dirsql query "SELECT title, author FROM './posts/*.md'" \
|
|
@@ -371,17 +372,21 @@ dirsql query "SELECT title, author FROM './posts/*.md'" \
|
|
|
371
372
|
|
|
372
373
|
The command follows the [`on-file` hook contract](./hooks.md#on-file) — argv
|
|
373
374
|
splitting, the `{root}` placeholder, every matched path as a trailing
|
|
374
|
-
argument, the failure semantics
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
argument, and the failure semantics — with one difference from the config
|
|
376
|
+
key: there is no config file, so the command runs in the index root (the
|
|
377
|
+
directory `dirsql query` was run in). The parser's output is the whole
|
|
378
|
+
schema; the stat columns are not reachable on a parsed path-table.
|
|
379
|
+
`--on-file` may be given **at most once** (a repeat is an error pointing at
|
|
380
|
+
config files) and never touches config-declared tables. It is a `query`-only flag — server mode rejects it as an unknown
|
|
378
381
|
argument. The command string is copy-paste identical to a `[[table]]`
|
|
379
382
|
`on-file` key, so an inline parser graduates to a config file unchanged — see
|
|
380
383
|
[Parse your files into columns](../howto/parse-files-into-columns.md).
|
|
381
384
|
|
|
382
385
|
Errors print the same diagnostic the HTTP `{"error": …}` body carries —
|
|
383
|
-
config failures, SQL errors, rejected reads, hook failures
|
|
384
|
-
|
|
386
|
+
config failures, SQL errors, rejected reads, hook failures — to stderr, with
|
|
387
|
+
exit code `1`. A failed `on-file` command is a hook failure: nothing is
|
|
388
|
+
printed on stdout, and the diagnostic carries the command's exit status and
|
|
389
|
+
the tail of its stderr.
|
|
385
390
|
|
|
386
391
|
#### `--format {auto,table,json}`
|
|
387
392
|
|
|
@@ -394,7 +399,7 @@ takes, with the same `auto` default. A one-shot query is usually piped, so
|
|
|
394
399
|
| Code | Meaning |
|
|
395
400
|
|---|---|
|
|
396
401
|
| `0` | Query succeeded; rows printed on stdout. |
|
|
397
|
-
| `1` | Any failure: config, SQL, rejected read,
|
|
402
|
+
| `1` | Any failure: config, SQL, rejected read, or a failed `on-file` command. The diagnostic is on stderr. |
|
|
398
403
|
|
|
399
404
|
## `dirsql init`
|
|
400
405
|
|
|
@@ -463,9 +468,9 @@ fragment is a launcher error naming the package — never a silent skip.
|
|
|
463
468
|
## Progress reporting
|
|
464
469
|
|
|
465
470
|
Building the index over a large tree is not instant: the walk visits every
|
|
466
|
-
file, then each
|
|
467
|
-
table's `ddl` fires on insert. On a big corpus that is minutes.
|
|
468
|
-
the two phases on **stderr** while they run:
|
|
471
|
+
file, then each table runs its `on-file` command over all of its files, plus
|
|
472
|
+
whatever the table's `ddl` fires on insert. On a big corpus that is minutes.
|
|
473
|
+
dirsql reports the two phases on **stderr** while they run:
|
|
469
474
|
|
|
470
475
|
```
|
|
471
476
|
dirsql: scanning 128413 files
|
package/docs/reference/hooks.md
CHANGED
|
@@ -37,10 +37,13 @@ The available placeholders are listed under the
|
|
|
37
37
|
|
|
38
38
|
### Working directory and environment
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
the command resolve predictably regardless of where
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
A command named by a `[[table]]` runs in the **config file's directory**, so
|
|
41
|
+
relative paths in the command resolve predictably regardless of where
|
|
42
|
+
`dirsql` was launched. A command named by the `--on-file` flag has no config
|
|
43
|
+
file: it runs in the **index root**, the directory `dirsql query` was run in,
|
|
44
|
+
which is also the directory a `./` path-table is relative to. In both forms
|
|
45
|
+
`{root}` is the index root. The command inherits `dirsql`'s environment, so
|
|
46
|
+
tools like `uvx --with …` / `npx …` resolve their dependencies as usual.
|
|
44
47
|
|
|
45
48
|
### stdout protocol
|
|
46
49
|
|
|
@@ -102,11 +105,31 @@ counts as the same kind of failure.
|
|
|
102
105
|
|
|
103
106
|
## `on-file` contract
|
|
104
107
|
|
|
105
|
-
Runs once per table
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
Runs **once per table**, with every file matched by the table's `glob`
|
|
109
|
+
appended to the command as a trailing argument, and prints **one JSON array
|
|
110
|
+
of row objects** for the whole table. That is the whole contract. The command
|
|
111
|
+
reads the files itself; see [`[[table]]`](./config.md#table) for the
|
|
108
112
|
row-mapping rules.
|
|
109
113
|
|
|
114
|
+
One process over all the files is the shape to write: a loop over the
|
|
115
|
+
arguments that collects rows and prints them once at the end. Parsing is the
|
|
116
|
+
command's cost, not dirsql's — dirsql spawns the command, waits, and reads
|
|
117
|
+
its stdout. A table over no matched files spawns nothing.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
import json, sys
|
|
121
|
+
|
|
122
|
+
rows = []
|
|
123
|
+
for path in sys.argv[1:]:
|
|
124
|
+
rows.extend(parse(path))
|
|
125
|
+
print(json.dumps(rows))
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
There is no row-to-file attribution: dirsql does not know which file a row
|
|
129
|
+
came from, and a row that needs the path carries it as a column the command
|
|
130
|
+
emitted. In watch mode a change to any file under the table's glob re-runs
|
|
131
|
+
the command over all of the table's files and replaces the table's rows.
|
|
132
|
+
|
|
110
133
|
The same command is attachable two ways, over the same contract: the
|
|
111
134
|
`on-file` config key on a `[[table]]`, and the
|
|
112
135
|
[`--on-file <command>`](./cli.md#on-file-command) flag on `dirsql query`,
|
|
@@ -125,5 +148,20 @@ the path or stat metadata emits it (it has the paths).
|
|
|
125
148
|
The matched files' **absolute** paths are not placeholders: they are appended
|
|
126
149
|
to the command as trailing arguments, after everything written in the
|
|
127
150
|
command, so `on-file = "extract.py"` receives them as `sys.argv[1:]` — one run
|
|
128
|
-
per table, self-sufficient from any working directory. A
|
|
129
|
-
|
|
151
|
+
per table, self-sufficient from any working directory. A tool that needs a
|
|
152
|
+
path anywhere other than last gets a wrapper script. A command that spells
|
|
153
|
+
`{path}` is rejected at startup:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
on-file command `python3 extract.py {path}` uses `{path}`, but an on-file command now runs once per table with every matched path appended as trailing arguments and prints one JSON array of row objects. Remove `{path}` and read the paths from the command's arguments.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Argument-list limits
|
|
160
|
+
|
|
161
|
+
Every platform caps the bytes one process may receive as arguments. When a
|
|
162
|
+
table's paths outgrow that cap, dirsql splits them into the fewest
|
|
163
|
+
consecutive runs that fit, runs the command once per run, and concatenates
|
|
164
|
+
the arrays it prints, in order. The command cannot tell: each run is an
|
|
165
|
+
ordinary invocation with a subset of the paths. A command must therefore not
|
|
166
|
+
assume one invocation sees every path — a count, a cross-file join, or a
|
|
167
|
+
dedupe over `sys.argv[1:]` is per run, not per table. Do that work in SQL.
|
|
@@ -79,6 +79,25 @@ SELECT * FROM ./;
|
|
|
79
79
|
-- hint: paths used as table names must be quoted; did you mean "./"?
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
### Symlinks
|
|
83
|
+
|
|
84
|
+
Symlinks are followed the way `bash -O globstar` follows them. A symlinked
|
|
85
|
+
file is a file: `'./*'` lists it, and so does naming it. A symlinked directory
|
|
86
|
+
is entered by any component of the path except `**`, which never walks into
|
|
87
|
+
one. A broken link lists nothing.
|
|
88
|
+
|
|
89
|
+
With `linkdir -> real`:
|
|
90
|
+
|
|
91
|
+
| You write | dirsql scans |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| `'./*/*'` | `linkdir/r.md` and `real/r.md` |
|
|
94
|
+
| `'./**'` | everything under `real/`, nothing under `linkdir/` |
|
|
95
|
+
| `'./**/*.md'` | `linkdir/r.md` too: `**` may stop on `linkdir` for `*.md` to enter |
|
|
96
|
+
| `'./linkdir/**'` | everything under `linkdir/` |
|
|
97
|
+
|
|
98
|
+
Each link entered uses up one component of the path, so a symlink cycle is
|
|
99
|
+
followed at most once per component and never hangs a scan.
|
|
100
|
+
|
|
82
101
|
### Paths outside the index root
|
|
83
102
|
|
|
84
103
|
Three other prefixes resolve, with their usual shell meanings:
|
|
@@ -153,16 +172,27 @@ dirsql query "SELECT title, author FROM './posts/*.md'" \
|
|
|
153
172
|
The command runs once, with every matched file's absolute path appended as an
|
|
154
173
|
argument, and prints one JSON array of row objects, exactly like a declared
|
|
155
174
|
table's [`on-file` hook](/reference/hooks) — same argv splitting, same `{root}`
|
|
156
|
-
placeholder, same
|
|
175
|
+
placeholder, same [argument-list splitting](/reference/hooks#argument-list-limits)
|
|
176
|
+
over a very large table. Its output *is* the table:
|
|
157
177
|
|
|
158
178
|
- **The parser supplies the whole schema.** Columns are inferred from the keys
|
|
159
179
|
across the emitted rows. The stat columns (`path`, `size`, …) are **not**
|
|
160
180
|
reachable on a parsed path-table — a parser that wants the path emits it (it
|
|
161
181
|
has the paths). The two modes stay cleanly separate.
|
|
162
|
-
- **
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
182
|
+
- **The parser runs from the index root.** There is no config file to anchor
|
|
183
|
+
it, so the command's working directory is the directory `dirsql query` was
|
|
184
|
+
run in — the same directory the `./` path is relative to — and `{root}`
|
|
185
|
+
names it. `python3 extract.py` finds `extract.py` in that directory,
|
|
186
|
+
whatever directory the path-table scans.
|
|
187
|
+
- **A parser failure is the query's failure.** A run that fails (spawn,
|
|
188
|
+
non-zero exit, or no output) or whose output is not a JSON array of row
|
|
189
|
+
objects fails the query with the command's stderr tail; there is no partial
|
|
190
|
+
table, and the CLI exits `1`. A run that succeeds but prints an empty array
|
|
191
|
+
fails too, because there are no keys to infer columns from:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
dirsql_parsed: parser produced no rows for `posts/*.md`; cannot infer a schema
|
|
195
|
+
```
|
|
166
196
|
- **The skip rules still apply.** A parsed scan honors the same `node_modules`
|
|
167
197
|
/`.git`/`ignore` rules a stat scan does (see below).
|
|
168
198
|
- **The parser runs on every start.** A parsed path-table is not kept in the
|
|
@@ -180,12 +210,14 @@ A path-table is scanned when the statement runs, so it always reflects the
|
|
|
180
210
|
filesystem as it is *now* — unlike declared tables, which are indexed on build
|
|
181
211
|
and updated by the watcher. A file created a moment ago shows up immediately.
|
|
182
212
|
|
|
183
|
-
The scan is live all the way down to `content`: a file's body is read
|
|
184
|
-
query names the `content` column, not when the row is discovered. A
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
213
|
+
The scan is live all the way down to `content`: a file's body is read only
|
|
214
|
+
when the query names the `content` column, not when the row is discovered. A
|
|
215
|
+
query that names it has the bodies of the rows it selects read all at once,
|
|
216
|
+
several files at a time, before the first row comes back. A file deleted
|
|
217
|
+
*after* the scan finds it but *before* its `content` is read yields `NULL`
|
|
218
|
+
content — the same NULL an unreadable or non-UTF-8 file gives — rather than
|
|
219
|
+
failing the query. This is an accepted consequence of reading live, not a bug
|
|
220
|
+
to design around.
|
|
189
221
|
|
|
190
222
|
The table itself is per-connection: it lives in `temp`, so it cannot leak into
|
|
191
223
|
`sqlite_master` or survive a restart. Under `--persist` a *parsed* table's rows
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dirsql",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.70",
|
|
4
4
|
"description": "Ephemeral SQL index over a local directory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/thekevinscott/dirsql",
|
|
@@ -221,10 +221,10 @@
|
|
|
221
221
|
]
|
|
222
222
|
},
|
|
223
223
|
"optionalDependencies": {
|
|
224
|
-
"@dirsql/lib-linux-x64-gnu": "0.4.
|
|
225
|
-
"@dirsql/lib-linux-arm64-gnu": "0.4.
|
|
226
|
-
"@dirsql/lib-darwin-x64": "0.4.
|
|
227
|
-
"@dirsql/lib-darwin-arm64": "0.4.
|
|
228
|
-
"@dirsql/lib-win32-x64-msvc": "0.4.
|
|
224
|
+
"@dirsql/lib-linux-x64-gnu": "0.4.70",
|
|
225
|
+
"@dirsql/lib-linux-arm64-gnu": "0.4.70",
|
|
226
|
+
"@dirsql/lib-darwin-x64": "0.4.70",
|
|
227
|
+
"@dirsql/lib-darwin-arm64": "0.4.70",
|
|
228
|
+
"@dirsql/lib-win32-x64-msvc": "0.4.70"
|
|
229
229
|
}
|
|
230
230
|
}
|