sqlrite 0.2.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. {sqlrite-0.2.0 → sqlrite-0.4.0}/.github/workflows/release.yml +17 -12
  2. {sqlrite-0.2.0 → sqlrite-0.4.0}/Cargo.lock +7 -7
  3. {sqlrite-0.2.0 → sqlrite-0.4.0}/Cargo.toml +2 -2
  4. {sqlrite-0.2.0 → sqlrite-0.4.0}/PKG-INFO +1 -1
  5. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/package.json +1 -1
  6. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/file-format.md +7 -4
  7. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/pager.md +14 -1
  8. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/supported-sql.md +95 -7
  9. {sqlrite-0.2.0 → sqlrite-0.4.0}/pyproject.toml +1 -1
  10. {sqlrite-0.2.0 → sqlrite-0.4.0}/scripts/bump-version.sh +38 -3
  11. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/python/Cargo.toml +1 -1
  12. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/Cargo.toml +1 -1
  13. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/db/table.rs +282 -5
  14. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/executor.rs +300 -3
  15. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/mod.rs +730 -4
  16. sqlrite-0.4.0/src/sql/pager/allocator.rs +222 -0
  17. sqlrite-0.4.0/src/sql/pager/freelist.rs +258 -0
  18. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/header.rs +26 -7
  19. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/mod.rs +918 -113
  20. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/overflow.rs +39 -22
  21. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/pager.rs +21 -0
  22. sqlrite-0.4.0/src/sql/parser/create.rs +368 -0
  23. sqlrite-0.2.0/src/sql/parser/create.rs +0 -241
  24. {sqlrite-0.2.0 → sqlrite-0.4.0}/.github/workflows/ci.yml +0 -0
  25. {sqlrite-0.2.0 → sqlrite-0.4.0}/.github/workflows/release-pr.yml +0 -0
  26. {sqlrite-0.2.0 → sqlrite-0.4.0}/.github/workflows/rust.yml +0 -0
  27. {sqlrite-0.2.0 → sqlrite-0.4.0}/.gitignore +0 -0
  28. {sqlrite-0.2.0 → sqlrite-0.4.0}/CLAUDE.md +0 -0
  29. {sqlrite-0.2.0 → sqlrite-0.4.0}/CODE_OF_CONDUCT.md +0 -0
  30. {sqlrite-0.2.0 → sqlrite-0.4.0}/LICENSE +0 -0
  31. {sqlrite-0.2.0 → sqlrite-0.4.0}/MAINTAINERS +0 -0
  32. {sqlrite-0.2.0 → sqlrite-0.4.0}/Makefile +0 -0
  33. {sqlrite-0.2.0 → sqlrite-0.4.0}/README.md +0 -0
  34. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/index.html +0 -0
  35. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/package-lock.json +0 -0
  36. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/src/App.svelte +0 -0
  37. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/src/app.css +0 -0
  38. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/src/main.ts +0 -0
  39. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/src/vite-env.d.ts +0 -0
  40. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/svelte.config.js +0 -0
  41. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/tsconfig.json +0 -0
  42. {sqlrite-0.2.0 → sqlrite-0.4.0}/desktop/vite.config.ts +0 -0
  43. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/_index.md +0 -0
  44. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/architecture.md +0 -0
  45. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/ask-backend-examples.md +0 -0
  46. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/ask.md +0 -0
  47. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/design-decisions.md +0 -0
  48. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/desktop.md +0 -0
  49. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/embedding.md +0 -0
  50. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/fts.md +0 -0
  51. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/getting-started.md +0 -0
  52. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/mcp.md +0 -0
  53. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/phase-7-plan.md +0 -0
  54. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/phase-8-plan.md +0 -0
  55. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/release-plan.md +0 -0
  56. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/release-secrets.md +0 -0
  57. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/roadmap.md +0 -0
  58. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/smoke-test.md +0 -0
  59. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/sql-engine.md +0 -0
  60. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/storage-model.md +0 -0
  61. {sqlrite-0.2.0 → sqlrite-0.4.0}/docs/usage.md +0 -0
  62. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/README.md +0 -0
  63. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/c/Makefile +0 -0
  64. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/c/hello.c +0 -0
  65. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/go/go.mod +0 -0
  66. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/go/hello.go +0 -0
  67. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/hybrid-retrieval/README.md +0 -0
  68. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/hybrid-retrieval/hybrid_retrieval.rs +0 -0
  69. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/nodejs/hello.mjs +0 -0
  70. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/python/hello.py +0 -0
  71. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/rust/quickstart.rs +0 -0
  72. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/wasm/Makefile +0 -0
  73. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/wasm/index.html +0 -0
  74. {sqlrite-0.2.0 → sqlrite-0.4.0}/examples/wasm/server.mjs +0 -0
  75. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/SQLRite - Desktop.png +0 -0
  76. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/SQLRite Data Structures.png +0 -0
  77. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/SQLRite Simple SQL Execution High Level Diagram.png +0 -0
  78. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/SQLRite Simple SQL INSERT Execution High Level Diagram (Insert Row).png +0 -0
  79. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/SQLRite Simple SQL INSERT Execution High Level Diagram.png +0 -0
  80. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/SQLRite_logo.png +0 -0
  81. {sqlrite-0.2.0 → sqlrite-0.4.0}/images/architecture.png +0 -0
  82. {sqlrite-0.2.0 → sqlrite-0.4.0}/rust-toolchain.toml +0 -0
  83. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/AST.delete.example +0 -0
  84. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/AST.insert.exemple +0 -0
  85. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/AST.select.example +0 -0
  86. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/AST.update.example +0 -0
  87. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/CREATE TABLE sqlrite_schema.sql +0 -0
  88. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/CREATE_TABLE with duplicate.sql +0 -0
  89. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/CREATE_TABLE.sql +0 -0
  90. {sqlrite-0.2.0 → sqlrite-0.4.0}/samples/INSERT.sql +0 -0
  91. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/README.md +0 -0
  92. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/ask.go +0 -0
  93. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/ask_test.go +0 -0
  94. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/conn.go +0 -0
  95. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/go.mod +0 -0
  96. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/rows.go +0 -0
  97. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/sqlrite.go +0 -0
  98. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/sqlrite_test.go +0 -0
  99. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/go/stmt.go +0 -0
  100. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/python/README.md +0 -0
  101. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/python/src/lib.rs +0 -0
  102. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/python/tests/test_ask.py +0 -0
  103. {sqlrite-0.2.0 → sqlrite-0.4.0}/sdk/python/tests/test_sqlrite.py +0 -0
  104. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/README.md +0 -0
  105. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/src/lib.rs +0 -0
  106. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/src/prompt.rs +0 -0
  107. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/src/provider/anthropic.rs +0 -0
  108. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/src/provider/mock.rs +0 -0
  109. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/src/provider/mod.rs +0 -0
  110. {sqlrite-0.2.0 → sqlrite-0.4.0}/sqlrite-ask/tests/anthropic_http.rs +0 -0
  111. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/ask/mod.rs +0 -0
  112. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/ask/schema.rs +0 -0
  113. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/connection.rs +0 -0
  114. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/error.rs +0 -0
  115. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/lib.rs +0 -0
  116. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/main.rs +0 -0
  117. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/meta_command/mod.rs +0 -0
  118. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/repl/mod.rs +0 -0
  119. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/db/database.rs +0 -0
  120. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/db/mod.rs +0 -0
  121. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/db/secondary_index.rs +0 -0
  122. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/fts/bm25.rs +0 -0
  123. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/fts/mod.rs +0 -0
  124. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/fts/posting_list.rs +0 -0
  125. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/fts/tokenizer.rs +0 -0
  126. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/hnsw.rs +0 -0
  127. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/cell.rs +0 -0
  128. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/file.rs +0 -0
  129. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/fts_cell.rs +0 -0
  130. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/hnsw_cell.rs +0 -0
  131. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/index_cell.rs +0 -0
  132. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/interior_page.rs +0 -0
  133. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/page.rs +0 -0
  134. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/table_page.rs +0 -0
  135. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/varint.rs +0 -0
  136. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/pager/wal.rs +0 -0
  137. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/parser/insert.rs +0 -0
  138. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/parser/mod.rs +0 -0
  139. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/parser/select.rs +0 -0
  140. {sqlrite-0.2.0 → sqlrite-0.4.0}/src/sql/tokenizer.rs +0 -0
@@ -152,7 +152,14 @@ jobs:
152
152
  # click Approve before this job actually runs).
153
153
  publish-crate:
154
154
  name: Publish sqlrite crate to crates.io
155
- needs: [detect, tag-all]
155
+ # Engine depends on sqlrite-ask (post-v0.1.19 dep-direction flip), so
156
+ # publish-ask must complete first — otherwise crates.io rejects the
157
+ # engine publish with "failed to select a version for the requirement
158
+ # `sqlrite-ask = "^X.Y"`". This was masked through 0.1.x because old
159
+ # sqlrite-ask versions were already on crates.io and the engine's
160
+ # version requirement (`^0.1`) matched them; the v0.2.0 cut surfaced
161
+ # the latent bug.
162
+ needs: [detect, tag-all, publish-ask]
156
163
  if: needs.detect.outputs.should_release == 'true'
157
164
  runs-on: ubuntu-latest
158
165
  environment: release
@@ -203,9 +210,11 @@ jobs:
203
210
 
204
211
  # ---------------------------------------------------------------------------
205
212
  # Step 3a': publish the `sqlrite-ask` crate (Phase 7g.1) — natural-
206
- # language → SQL adapter built on top of the engine. Same shape as
207
- # `publish-crate` above; separate job so a registry hiccup on one
208
- # doesn't block the other and re-runs are surgical.
213
+ # language → SQL adapter. Since the v0.1.19 dep-direction flip,
214
+ # sqlrite-ask is dep-free of sqlrite-engine it's a pure-string-in /
215
+ # string-out adapter. The engine depends on IT, not the other way
216
+ # around. So this job runs FIRST in the publish chain; publish-crate
217
+ # waits on it.
209
218
  #
210
219
  # Crate name on crates.io: `sqlrite-ask`. Library name (the `use`
211
220
  # path): `sqlrite_ask`. No alias-renaming this time — the short
@@ -213,7 +222,7 @@ jobs:
213
222
  # for why the engine had to rename).
214
223
  publish-ask:
215
224
  name: Publish sqlrite-ask crate to crates.io
216
- needs: [detect, tag-all, publish-crate]
225
+ needs: [detect, tag-all]
217
226
  if: needs.detect.outputs.should_release == 'true'
218
227
  runs-on: ubuntu-latest
219
228
  environment: release
@@ -230,13 +239,9 @@ jobs:
230
239
  env:
231
240
  CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
232
241
  # `--no-verify` mirrors `publish-crate` — Release-PR CI
233
- # already validated this commit.
234
- #
235
- # `needs: [..., publish-crate]` is load-bearing: sqlrite-ask
236
- # depends on sqlrite-engine, and crates.io rejects publishes
237
- # whose path-deps haven't yet resolved to a published version
238
- # at the same number. Sequencing makes the dep visible by the
239
- # time we publish.
242
+ # already validated this commit. sqlrite-ask has no
243
+ # SQLRite-internal path-deps after the v0.1.19 dep-direction
244
+ # flip, so this job is unblocked the moment `tag-all` lands.
240
245
  run: cargo publish -p sqlrite-ask --no-verify
241
246
 
242
247
  - name: GitHub Release
@@ -3817,7 +3817,7 @@ dependencies = [
3817
3817
 
3818
3818
  [[package]]
3819
3819
  name = "sqlrite-ask"
3820
- version = "0.2.0"
3820
+ version = "0.4.0"
3821
3821
  dependencies = [
3822
3822
  "serde",
3823
3823
  "serde_json",
@@ -3828,7 +3828,7 @@ dependencies = [
3828
3828
 
3829
3829
  [[package]]
3830
3830
  name = "sqlrite-desktop"
3831
- version = "0.2.0"
3831
+ version = "0.4.0"
3832
3832
  dependencies = [
3833
3833
  "serde",
3834
3834
  "serde_json",
@@ -3840,7 +3840,7 @@ dependencies = [
3840
3840
 
3841
3841
  [[package]]
3842
3842
  name = "sqlrite-engine"
3843
- version = "0.2.0"
3843
+ version = "0.4.0"
3844
3844
  dependencies = [
3845
3845
  "clap",
3846
3846
  "env_logger",
@@ -3857,7 +3857,7 @@ dependencies = [
3857
3857
 
3858
3858
  [[package]]
3859
3859
  name = "sqlrite-ffi"
3860
- version = "0.2.0"
3860
+ version = "0.4.0"
3861
3861
  dependencies = [
3862
3862
  "cbindgen",
3863
3863
  "serde",
@@ -3867,7 +3867,7 @@ dependencies = [
3867
3867
 
3868
3868
  [[package]]
3869
3869
  name = "sqlrite-mcp"
3870
- version = "0.2.0"
3870
+ version = "0.4.0"
3871
3871
  dependencies = [
3872
3872
  "clap",
3873
3873
  "libc",
@@ -3878,7 +3878,7 @@ dependencies = [
3878
3878
 
3879
3879
  [[package]]
3880
3880
  name = "sqlrite-nodejs"
3881
- version = "0.2.0"
3881
+ version = "0.4.0"
3882
3882
  dependencies = [
3883
3883
  "napi",
3884
3884
  "napi-build",
@@ -3888,7 +3888,7 @@ dependencies = [
3888
3888
 
3889
3889
  [[package]]
3890
3890
  name = "sqlrite-python"
3891
- version = "0.2.0"
3891
+ version = "0.4.0"
3892
3892
  dependencies = [
3893
3893
  "pyo3",
3894
3894
  "sqlrite-engine",
@@ -27,7 +27,7 @@ resolver = "3"
27
27
  # `package =` key so the import name stays `sqlrite` internally:
28
28
  # sqlrite = { package = "sqlrite-engine", path = "…" }
29
29
  name = "sqlrite-engine"
30
- version = "0.2.0"
30
+ version = "0.4.0"
31
31
  authors = ["Joao Henrique Machado Silva <joaoh82@gmail.com>"]
32
32
  edition = "2024"
33
33
  rust-version = "1.85"
@@ -138,4 +138,4 @@ fs2 = { version = "0.4", optional = true }
138
138
  # crate publishes to crates.io, and a path-only dep without a
139
139
  # version field fails the manifest verification step. See PR #58
140
140
  # retrospective in docs/roadmap.md.
141
- sqlrite-ask = { version = "0.2", path = "sqlrite-ask", optional = true }
141
+ sqlrite-ask = { version = "0.4.0", path = "sqlrite-ask", optional = true }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlrite
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sqlrite-desktop-frontend",
3
3
  "private": true,
4
- "version": "0.2.0",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -4,7 +4,7 @@ A SQLRite database is a single file, by convention named `*.sqlrite`. The file i
4
4
 
5
5
  All multi-byte integers in this format are **little-endian**.
6
6
 
7
- The current on-disk format is **version 4** (Phase 7) by default, with **version 5** written on demand whenever an FTS index is attached to the database (Phase 8c). Decoders accept both v4 and v5; writers preserve the existing version on no-op resaves so a v4 database without FTS stays v4. Files produced by versions 1 – 3 are rejected on open.
7
+ The current on-disk format is **version 4** (Phase 7) by default, with **version 5** written on demand whenever an FTS index is attached to the database (Phase 8c) and **version 6** written on demand whenever a save produces a non-empty freelist (SQLR-6). Decoders accept v4, v5, and v6; writers preserve the existing version on no-op resaves so a v4 database without FTS or freelist stays v4. Files produced by versions 1 – 3 are rejected on open.
8
8
 
9
9
  ## Page 0 — the database header
10
10
 
@@ -15,17 +15,18 @@ The first 4096 bytes of every file are the header page. Only the first 28 bytes
15
15
  │ offset │ length │ content │
16
16
  ├────────┼────────┼─────────────────────────────────────────────────┤
17
17
  │ 0 │ 16 │ magic: "SQLRiteFormat\0\0\0" │
18
- │ 16 │ 2 │ format version (u16 LE) = 4 or 5
18
+ │ 16 │ 2 │ format version (u16 LE) = 4, 5, or 6
19
19
  │ 18 │ 2 │ page size (u16 LE) = 4096 │
20
20
  │ 20 │ 4 │ total page count (u32 LE), includes page 0 │
21
21
  │ 24 │ 4 │ root page of sqlrite_master (u32 LE) │
22
- │ 28 │ 4068reserved / zero
22
+ │ 28 │ 4freelist head (u32 LE; 0 = empty) — v6 only
23
+ │ 32 │ 4064 │ reserved / zero │
23
24
  └────────┴────────┴─────────────────────────────────────────────────┘
24
25
  ```
25
26
 
26
27
  The magic string is 14 ASCII bytes (`SQLRiteFormat`) padded with two NUL bytes to fill 16 bytes. It's deliberately different from SQLite's `"SQLite format 3\0"` so the two formats can't be confused on inspection.
27
28
 
28
- `decode_header` in [`src/sql/pager/header.rs`](../src/sql/pager/header.rs) validates all three of (magic, format version, page size) on open. A wrong magic produces `not a SQLRite database`; a wrong version or page size produces `unsupported ...` errors. The decoder accepts both v4 and v5 (anything else is rejected); the parsed `format_version` is propagated through the in-memory `DbHeader` so the writer can preserve it on resave when no version-bumping feature has been added.
29
+ `decode_header` in [`src/sql/pager/header.rs`](../src/sql/pager/header.rs) validates all three of (magic, format version, page size) on open. A wrong magic produces `not a SQLRite database`; a wrong version or page size produces `unsupported ...` errors. The decoder accepts v4, v5, and v6 (anything else is rejected); the parsed `format_version` is propagated through the in-memory `DbHeader` so the writer can preserve it on resave when no version-bumping feature has been added. `freelist_head` is read from bytes [28..32]: v4/v5 files leave that region zero so it always decodes as `0` (an empty freelist), and v6 files store the page number of the first freelist trunk there.
29
30
 
30
31
  ## Pages 1..page_count — payload pages
31
32
 
@@ -53,6 +54,7 @@ Every non-header page starts with a 7-byte header:
53
54
  | `2` | `TableLeaf` | Holds a slot directory and a set of cells representing rows of a table. Leaves for one table are linked by sibling `next_page` pointers. |
54
55
  | `3` | `Overflow` | Continuation page carrying the spilled body of a single oversized cell. |
55
56
  | `4` | `InteriorNode` | Interior B-Tree node. Holds a slot directory of divider cells routing to child pages plus a rightmost-child pointer in the payload header. |
57
+ | `5` | `FreelistTrunk` | One link of the persisted free-page list (SQLR-6). Payload carries `count: u16` followed by `count × u32` free leaf-page numbers; `next_page` chains to the next trunk (0 = end). |
56
58
 
57
59
  Tag `1` is reserved (it was `SchemaRoot` in format v1; unused in v2). Any other tag on open is a corruption error.
58
60
 
@@ -307,6 +309,7 @@ These are not all enforced on open — we validate the header strictly and rely
307
309
  - **v3** (Phase 3e) — `sqlrite_master` gains a `type` column; secondary indexes persist as their own cell-based B-Trees whose leaves carry `KIND_INDEX` cells.
308
310
  - **v4** (Phase 7a) — value block dispatch gains the `0x04 Vector` tag for the new `VECTOR(N)` column type. Per the [Phase 7 plan's Q8](phase-7-plan.md#q8-file-format-version-bump), later Phase 7 sub-phases (JSON storage, HNSW indexes) added their own value/cell tags inside this same v4 envelope. The `CREATE TABLE` SQL stored in `sqlrite_master` carries vector columns as `VECTOR(N)` in the type position; on open, the engine re-parses that SQL and reconstructs `DataType::Vector(N)` from the `Custom` AST node sqlparser produces.
309
311
  - **v5** (Phase 8c, current for FTS-bearing files) — adds the `KIND_FTS_POSTING` cell tag for persisted FTS posting lists. Bumped **on demand** per the [Phase 8 plan's Q10](phase-8-plan.md#q10-file-format-version-bump-strategy): existing v4 databases without FTS keep writing v4 across non-FTS saves; the first save with at least one FTS index attached promotes the file to v5. Decoders accept both v4 and v5; opening a v4 file with a build that supports v5 is a no-op until the user creates an FTS index.
312
+ - **v6** (SQLR-6, current for files with persisted free-page lists) — adds the `freelist_head` field at header bytes [28..32] and the `FreelistTrunk` page tag (`5`). Bumped **on demand**: a save that ends with an empty freelist preserves the existing version; the first save that produces a non-empty freelist promotes the file to v6. Decoders accept v4, v5, and v6; v6 is a strict superset, so opening a v4/v5 file with a v6-aware build is a no-op until the user creates a freelist (e.g., by dropping a table or index). VACUUM clears the freelist but doesn't downgrade.
310
313
 
311
314
  The page header (7 bytes) and chaining mechanism are stable across future phases. Phase 4's WAL introduces a sibling file (`.sqlrite-wal`) rather than changing the main file format.
312
315
 
@@ -187,10 +187,23 @@ Without the diff, step 3's "re-serialize every table" would trigger a full file
187
187
 
188
188
  This only works because `save_database` iterates tables in sorted order — if the order were random, a table that didn't change might land at a different page number, appearing dirty. See [Design decisions §7](design-decisions.md#7-deterministic-page-number-ordering-when-saving).
189
189
 
190
+ ## Free-page list and VACUUM (SQLR-6)
191
+
192
+ Save now uses a [`PageAllocator`](../src/sql/pager/allocator.rs) instead of a bare `next_free_page` counter. The allocator pulls pages from three sources, in preference order:
193
+
194
+ 1. **Per-table preferred pool** — every table/index/master is given the page numbers it occupied last save (collected by walking from its old `rootpage`). An unchanged table re-stages byte-identical pages at the same numbers, so the diff pager skips every write for it.
195
+ 2. **Global freelist** — pages from dropped tables/indexes that are recorded in the persisted freelist (rooted at `header.freelist_head`).
196
+ 3. **Extend** — `next_extend++`, monotonic past the high-water mark.
197
+
198
+ After staging, pages that were live before this save but didn't get restaged this round (e.g., the leaves of a dropped table) move onto the new freelist. The freelist itself is encoded into a chain of `FreelistTrunk` pages — each trunk holds up to 1021 free leaf-page numbers plus a `next_page` pointer to the following trunk. Trunks consume some of the free pages they describe (a trunk page IS a free page borrowed for metadata), so a freelist of N pages takes `ceil(N / 1022)` trunks and persists `N − T` leaf entries.
199
+
200
+ `VACUUM;` (a SQL statement) calls [`vacuum_database`](../src/sql/pager/mod.rs), which is `save_database` with empty per-table preferred pools and an empty initial freelist. Allocation falls through to extend on every page → contiguous layout from page 1, no freelist trunks, file truncates to the new high-water mark on the next checkpoint.
201
+
202
+ Format-version side effect: a save that produces a non-empty freelist promotes the file from v4/v5 to v6 (mirrors Phase 8c's v4→v5 FTS rule). VACUUM clears the freelist but doesn't downgrade — v6 is a strict superset.
203
+
190
204
  ## What it doesn't do (yet)
191
205
 
192
206
  - **No LRU eviction.** `on_disk` + `wal_cache` together grow with the page count. For a 1 GiB database, that's ~1 GiB of page cache. Bounded cache is future work.
193
- - **No free-page management.** When a table shrinks, the main file's tail pages are truncated at checkpoint, but there's no free-list to reuse pages inside a grown file.
194
207
  - **No per-statement granularity.** The whole database is re-serialized on every commit; the diff keeps the *written* set small but the CPU cost of reserialization is unchanged.
195
208
  - **No concurrent reader-and-writer.** Phase 4e graduated to shared/exclusive lock modes (multi-reader *or* single-writer), but POSIX flock can't give us both at once. True concurrent access would need a shared-memory coordination file with read marks — not on the roadmap.
196
209
  - **Savepoints / nested transactions.** Phase 4f added top-level `BEGIN` / `COMMIT` / `ROLLBACK` (snapshot-based rollback, auto-save suppressed inside a transaction), but nested `BEGIN` is rejected — real savepoints aren't on the roadmap.
@@ -8,13 +8,16 @@ If you're looking for _how_ to use SQLRite (REPL flow, meta-commands, history, e
8
8
 
9
9
  | Statement | Supported today |
10
10
  |---|---|
11
- | [`CREATE TABLE`](#create-table) | Columns with `PRIMARY KEY` / `UNIQUE` / `NOT NULL`; typed columns; auto-indexes on constrained columns |
11
+ | [`CREATE TABLE`](#create-table) | Columns with `PRIMARY KEY` / `UNIQUE` / `NOT NULL` / `DEFAULT <literal>`; typed columns; auto-indexes on constrained columns |
12
12
  | [`CREATE [UNIQUE] INDEX`](#create-index) | Single-column named indexes, `IF NOT EXISTS`, persisted as cell-based B-Trees |
13
- | [`INSERT INTO`](#insert-into) | Auto-ROWID, UNIQUE/PK enforcement, clean type errors, NULL padding |
13
+ | [`INSERT INTO`](#insert-into) | Auto-ROWID, UNIQUE/PK enforcement, clean type errors, NULL/DEFAULT padding |
14
14
  | [`SELECT`](#select) | `*` or column list, `WHERE`, single-column `ORDER BY`, `LIMIT`; index probing on `col = literal` |
15
15
  | [`UPDATE`](#update) | Multi-column `SET`, `WHERE`, arithmetic RHS, type + UNIQUE enforcement |
16
16
  | [`DELETE`](#delete) | `WHERE` predicate or whole-table |
17
+ | [`ALTER TABLE`](#alter-table) | `RENAME TO`, `RENAME COLUMN`, `ADD COLUMN`, `DROP COLUMN` (one operation per statement) |
18
+ | [`DROP TABLE`](#drop-table) / [`DROP INDEX`](#drop-index) | `IF EXISTS`; single target; auto-indexes refused for `DROP INDEX` |
17
19
  | [`BEGIN`](#transactions) / [`COMMIT`](#transactions) / [`ROLLBACK`](#transactions) | Snapshot-based; single-level; WAL-backed commit; auto-rollback on COMMIT disk failure |
20
+ | [`VACUUM`](#vacuum) | Compacts the file: rewrites every live B-Tree contiguously from page 1 and clears the freelist. Bare `VACUUM;` only — no modifiers. |
18
21
 
19
22
  Statements the parser accepts (because sqlparser understands them in the SQLite dialect) but SQLRite doesn't execute yet return `SQL Statement not supported yet`. The [Not yet supported](#not-yet-supported) section below enumerates the common ones.
20
23
 
@@ -41,12 +44,12 @@ CREATE TABLE <name> (<col> <type> [column_constraint]* [, ...]);
41
44
 
42
45
  - `PRIMARY KEY` — one column per table; the column **must** be `INTEGER` and gets auto-ROWID behavior (omitted on INSERT → auto-assigned). Auto-creates an index named `sqlrite_autoindex_<table>_<column>`.
43
46
  - `UNIQUE` — enforced at INSERT/UPDATE time. Auto-creates an index with the same naming scheme.
44
- - `NOT NULL` — rejects NULL at INSERT/UPDATE. Omitted columns on INSERT are NULL by default, so a `NOT NULL` without an INSERT-time value is an error.
47
+ - `NOT NULL` — rejects NULL at INSERT/UPDATE. Omitted columns on INSERT are NULL by default (or pick up the column's `DEFAULT`, if any), so a `NOT NULL` without an INSERT-time value or DEFAULT is an error.
48
+ - `DEFAULT <literal>` — value substituted when the column is omitted from an INSERT. Accepts integer / real / text / boolean / NULL literals (and unary `+` / `-` on numerics). Function-call defaults like `CURRENT_TIMESTAMP` and other non-literal expressions are rejected at CREATE TABLE time. Explicit `INSERT ... VALUES (..., NULL, ...)` is preserved as NULL — the default only fires for omitted columns (matches SQLite).
45
49
 
46
50
  ### What's **not** enforced at CREATE TABLE time
47
51
 
48
52
  - **Table-level constraints** (`PRIMARY KEY (col1, col2)`, `FOREIGN KEY`, `CHECK`, `UNIQUE (col1, col2)`) are parsed but ignored.
49
- - **`DEFAULT` values** are parsed but ignored.
50
53
  - **Multi-column `PRIMARY KEY`** — only single-column PKs work; a composite PK is accepted by the parser but treated as no PK.
51
54
 
52
55
  ### Errors returned
@@ -208,6 +211,72 @@ DELETE FROM <table> [WHERE <expr>];
208
211
 
209
212
  ---
210
213
 
214
+ ## `ALTER TABLE`
215
+
216
+ ```sql
217
+ ALTER TABLE [IF EXISTS] <table> RENAME TO <new_table>;
218
+ ALTER TABLE [IF EXISTS] <table> RENAME COLUMN <old_col> TO <new_col>;
219
+ ALTER TABLE [IF EXISTS] <table> ADD COLUMN <col_def>;
220
+ ALTER TABLE [IF EXISTS] <table> DROP COLUMN <col>;
221
+ ```
222
+
223
+ One operation per statement (SQLite-style). `ALTER TABLE foo RENAME TO bar, ADD COLUMN x ...` is rejected — issue separate statements instead.
224
+
225
+ ### `RENAME TO`
226
+
227
+ - Reserved-name rejection: cannot rename to `sqlrite_master`.
228
+ - Errors if the target name is already a table.
229
+ - Auto-indexes whose names embed the old table name (`sqlrite_autoindex_<old>_<col>`) are renamed in lockstep so the schema catalog stays consistent. Explicit indexes carry their user-given name unchanged.
230
+
231
+ ### `RENAME COLUMN`
232
+
233
+ - Errors if the old column doesn't exist or the new name already exists in the table.
234
+ - Re-keys the row storage and updates every dependent index (auto + explicit, secondary / HNSW / FTS) — including auto-index name regeneration.
235
+ - Renaming the PRIMARY KEY column is allowed; the table's `primary_key` pointer follows the new name.
236
+
237
+ ### `ADD COLUMN`
238
+
239
+ - The column definition reuses the same parser that handles CREATE TABLE columns: same types, same `NOT NULL` / `DEFAULT` semantics.
240
+ - **Rejected:** `PRIMARY KEY` and `UNIQUE` constraints on the added column. Both would require backfilling the column under uniqueness constraints against existing rows; that path will land alongside multi-column UNIQUE.
241
+ - **`NOT NULL` on a non-empty table requires `DEFAULT`.** Without one there's no value to backfill existing rowids with. Same rule SQLite applies.
242
+ - With a `DEFAULT`, every existing rowid is backfilled with the default value at ADD COLUMN time. Without a `DEFAULT`, existing rowids read as NULL for the new column.
243
+
244
+ ### `DROP COLUMN`
245
+
246
+ - **Rejected:** dropping the PRIMARY KEY column.
247
+ - **Rejected:** dropping the only remaining column (degenerate table).
248
+ - Cascades to every dependent index (auto + explicit, secondary / HNSW / FTS) on the dropped column.
249
+ - `CASCADE` / `RESTRICT` modifiers are accepted by the parser and ignored — SQLite has no real distinction here either.
250
+
251
+ ---
252
+
253
+ ## `DROP TABLE`
254
+
255
+ ```sql
256
+ DROP TABLE [IF EXISTS] <table>;
257
+ ```
258
+
259
+ - Single target per statement. `DROP TABLE a, b, c;` is parsed but rejected with a NotImplemented error.
260
+ - Reserved-name rejection: `DROP TABLE sqlrite_master` errors with the same message `CREATE TABLE` uses.
261
+ - All indexes attached to the table (auto, explicit, HNSW, FTS) disappear with the table — they live inside the `Table` struct and ride along.
262
+ - Without `IF EXISTS`, dropping a table that doesn't exist errors. With it, that's a benign 0-tables-dropped no-op.
263
+ - **Disk pages move onto the freelist.** Pages the dropped table occupied are pushed onto a persisted free-page list (SQLR-6) so subsequent `CREATE TABLE` or inserts can reuse them. The file doesn't shrink until [`VACUUM;`](#vacuum) compacts it.
264
+
265
+ ---
266
+
267
+ ## `DROP INDEX`
268
+
269
+ ```sql
270
+ DROP INDEX [IF EXISTS] <index_name>;
271
+ ```
272
+
273
+ - Single target per statement.
274
+ - Walks every table searching for an index with the given name across the secondary B-Tree, HNSW, and FTS index families.
275
+ - **Refuses to drop auto-indexes.** `sqlrite_autoindex_*` names are constraint-bound to the column they index — the only way to remove them is to drop the underlying column or table. Same rule SQLite enforces for its `sqlite_autoindex_*` indexes.
276
+ - `IF EXISTS` makes a missing index a benign no-op.
277
+
278
+ ---
279
+
211
280
  ## Expressions
212
281
 
213
282
  Expressions work inside `WHERE` (both in `SELECT`, `UPDATE`, `DELETE`) and on the right-hand side of `UPDATE`'s `SET`.
@@ -360,6 +429,24 @@ ROLLBACK; -- nothing was actually deleted
360
429
 
361
430
  ---
362
431
 
432
+ ## `VACUUM`
433
+
434
+ ```sql
435
+ VACUUM;
436
+ ```
437
+
438
+ Compacts the database file: rewrites every live table, index, HNSW graph, FTS posting tree, and `sqlrite_master` itself contiguously from page 1, drops the freelist, and lets the next checkpoint truncate the tail.
439
+
440
+ - **Bare `VACUUM;` only.** Modifiers — `VACUUM FULL`, `VACUUM REINDEX`, table targets, `TO ... PERCENT`, `BOOST` — are parsed (sqlparser supports them) but rejected at execution with `VACUUM modifiers (FULL, REINDEX, table targets, etc.) are not supported`.
441
+ - **Refused inside a transaction.** `BEGIN; VACUUM;` errors with `VACUUM cannot run inside a transaction`. Use `COMMIT;` first, then `VACUUM;`.
442
+ - **No-op on in-memory databases.** Returns a `VACUUM is a no-op for in-memory databases` status string and does nothing — there's no file to compact.
443
+ - **Status string** carries pages and bytes reclaimed: `VACUUM completed. <N> pages reclaimed (<B> bytes).`
444
+ - **Format-version side effect.** A v4/v5 file that has been promoted to v6 by an earlier drop stays at v6 after VACUUM (v6 is a strict superset; we don't downgrade). A file that's already at v4/v5 because no drop ever happened on it doesn't get bumped by VACUUM.
445
+
446
+ When to run it: any time after a string of `DROP TABLE` / `DROP INDEX` / `ALTER TABLE DROP COLUMN` operations if you care about file size. SQLRite reuses freelist pages on subsequent inserts, so a write-heavy workload may not need VACUUM at all — its main use is reclaiming space when you don't expect to grow back.
447
+
448
+ ---
449
+
363
450
  ## Read-only databases
364
451
 
365
452
  A REPL launched with `sqlrite --readonly foo.sqlrite` (or `sqlrite::open_database_read_only(path, name)` programmatically) takes a shared POSIX advisory lock instead of an exclusive one. In that mode:
@@ -405,11 +492,12 @@ For context when you hit `NotImplemented`. See [Roadmap](roadmap.md) for when th
405
492
  - Built-in functions (`LENGTH`, `UPPER`, `LOWER`, `COALESCE`, `IFNULL`, date/time, `printf`, …)
406
493
 
407
494
  ### DDL
408
- - `ALTER TABLE` (add column, rename column, rename table)
409
- - `DROP TABLE`, `DROP INDEX`
495
+ - `ALTER TABLE` extras: multi-operation (`ALTER TABLE foo RENAME TO bar, ADD COLUMN x ...`), `ALTER COLUMN ... SET / DROP DEFAULT`, `ALTER COLUMN ... TYPE`
496
+ - `ADD COLUMN` constraint extras: `PRIMARY KEY` and `UNIQUE` on the added column (would need backfill + uniqueness against existing rows)
497
+ - `DROP TABLE` / `DROP INDEX` extras: multi-target (`DROP TABLE a, b, c;`)
410
498
  - `CREATE VIEW`, `CREATE TRIGGER`
411
499
  - Table-level constraints (composite PK, composite UNIQUE, `FOREIGN KEY`, `CHECK`)
412
- - Column defaults (`DEFAULT <value>`)
500
+ - Non-literal `DEFAULT` expressions (`CURRENT_TIMESTAMP`, function calls, column references)
413
501
  - Composite / multi-column indexes
414
502
 
415
503
  ### Transactions
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "sqlrite"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "Python bindings for SQLRite — a small, embeddable SQLite clone written in Rust."
9
9
  authors = [{ name = "Joao Henrique Machado Silva", email = "joaoh82@gmail.com" }]
10
10
  license = { text = "MIT" }
@@ -6,8 +6,8 @@
6
6
  # scripts/bump-version.sh 0.2.0
7
7
  #
8
8
  # Rewrites the version field in every manifest that carries one
9
- # (eight Cargo.toml / pyproject.toml files, plus three JSON manifests
10
- # — eleven files total). Then you run `cargo build` yourself to
9
+ # (nine Cargo.toml / pyproject.toml files, plus three JSON manifests
10
+ # — twelve files total). Then you run `cargo build` yourself to
11
11
  # refresh Cargo.lock. Idempotent: running twice with the same version
12
12
  # is a no-op; running twice with different versions lands on the
13
13
  # second.
@@ -84,6 +84,25 @@ for file in "${TOML_FILES[@]}"; do
84
84
  fi
85
85
  sed "s/^version = \"[^\"]*\"/version = \"${VERSION}\"/" "$file" > "$file.tmp"
86
86
  mv "$file.tmp" "$file"
87
+
88
+ # Inter-workspace dep pins — lines like:
89
+ # sqlrite-ask = { version = "0.3", path = "sqlrite-ask", ... }
90
+ # sqlrite = { package = "sqlrite-engine", path = "..", version = "0.3", ... }
91
+ #
92
+ # These carry BOTH version and path because crates.io publishing
93
+ # rejects path-only deps (see PR #58 retrospective). The version
94
+ # field has to track the workspace bump or `cargo build` fails to
95
+ # resolve a candidate (SQLR-9; failed run for v0.3.0 hit exactly
96
+ # this — `failed to select a version for the requirement
97
+ # sqlrite-ask = "^0.2"`).
98
+ #
99
+ # Detection: any line containing both `version = "..."` and
100
+ # `path = "..."`. The package-level `^version = "..."` line at
101
+ # the top of each manifest has no `path` on it and can't match.
102
+ # Both inline-table orderings (version-first and path-first) work
103
+ # because sed acts per-line, not per-token-order.
104
+ sed -E '/path *= *"[^"]*"/ s/version *= *"[^"]*"/version = "'"${VERSION}"'"/' "$file" > "$file.tmp"
105
+ mv "$file.tmp" "$file"
87
106
  done
88
107
 
89
108
  # ---------------------------------------------------------------------------
@@ -147,6 +166,22 @@ for file in "${JSON_FILES[@]}"; do
147
166
  fi
148
167
  done
149
168
 
169
+ # Inter-workspace pin sweep — any surviving `version = "X"` on a TOML
170
+ # line that also has `path = "..."` and isn't already at $VERSION is a
171
+ # pin we missed. Catches future refactors that change pin shape (e.g.
172
+ # someone splits a long dep line across multiple TOML lines, where the
173
+ # single-line address would no longer match).
174
+ for file in "${TOML_FILES[@]}"; do
175
+ bad="$(grep -nE 'path *= *"[^"]*"' "$file" \
176
+ | grep -E 'version *= *"[^"]*"' \
177
+ | grep -vE "version *= *\"${VERSION}\"" || true)"
178
+ if [[ -n "$bad" ]]; then
179
+ echo " ✗ $file — inter-workspace pin not at ${VERSION}:" >&2
180
+ echo "$bad" | sed 's/^/ /' >&2
181
+ FAILURES=$((FAILURES + 1))
182
+ fi
183
+ done
184
+
150
185
  if [[ $FAILURES -gt 0 ]]; then
151
186
  echo
152
187
  echo "error: $FAILURES file(s) did not update as expected." >&2
@@ -157,5 +192,5 @@ fi
157
192
  echo
158
193
  echo "Done. Next steps:"
159
194
  echo " cargo build # refresh Cargo.lock with the new versions"
160
- echo " git diff # inspect the ten-file bump"
195
+ echo " git diff # inspect the twelve-file bump"
161
196
  echo " git checkout . # or back out if it looks wrong"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sqlrite-python"
3
- version = "0.2.0"
3
+ version = "0.4.0"
4
4
  authors = ["Joao Henrique Machado Silva <joaoh82@gmail.com>"]
5
5
  edition = "2024"
6
6
  rust-version = "1.85"
@@ -10,7 +10,7 @@
10
10
  # Published to crates.io as `sqlrite-ask`. Joins the lockstep release
11
11
  # wave (`sqlrite-ask-vX.Y.Z` tag) — see `docs/release-plan.md`.
12
12
  name = "sqlrite-ask"
13
- version = "0.2.0"
13
+ version = "0.4.0"
14
14
  authors = ["Joao Henrique Machado Silva <joaoh82@gmail.com>"]
15
15
  edition = "2024"
16
16
  rust-version = "1.85"