Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,13 @@ npm_link_package(
src = "@valdi//src/valdi_modules/src/valdi/valdi_core:valdi_core_dts",
visibility = ["//visibility:public"],
)

filegroup(
name = "clientsql_generator_test_data",
srcs = [
"MODULE.bazel",
"fossa-deps.yml",
],
testonly = True,
visibility = ["//compiler/clientsql:__pkg__"],
)
16 changes: 16 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,22 @@ http_archive(
url = "https://github.com/fmtlib/fmt/releases/download/7.1.3/fmt-7.1.3.zip",
)

http_archive(
name = "sqlite",
build_file = "@valdi//third-party/sqlite:sqlite.BUILD",
sha256 = "0e9483900e92cd5de8fd48d16bf9200145a61f7fd5be542a5ac81d8a9516eb9c",
strip_prefix = "sqlite-autoconf-3530400",
url = "https://www.sqlite.org/2026/sqlite-autoconf-3530400.tar.gz",
)

http_archive(
name = "sqlite_316",
build_file = "@valdi//third-party/sqlite:sqlite_316.BUILD",
sha256 = "3b5dfb65807e2b17e6463357df848e322badba01dc9a4a1de8fdbb72d448e3b0",
strip_prefix = "sqlite-amalgamation-3160000",
url = "https://www.sqlite.org/2017/sqlite-amalgamation-3160000.zip",
)

bazel_dep(name = "android_macros")
local_path_override(
module_name = "android_macros",
Expand Down
17 changes: 15 additions & 2 deletions bin/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ load(
"valdi_compiler_companion_files",
)

filegroup(
alias(
name = "sqldelight_compiler",
srcs = [],
actual = "@valdi//compiler/clientsql:clientsql",
visibility = ["//visibility:public"],
)

alias(
name = "clientsql_sqlite_validator",
actual = "@valdi//compiler/clientsql:sqlite_316_validator",
visibility = ["//visibility:public"],
)

Expand Down Expand Up @@ -52,3 +58,10 @@ js_binary(
visibility = ["//visibility:public"],
# log_level = "debug", # increase verbosity of logs if you need to debug anything
)

filegroup(
name = "clientsql_generator_test_data",
srcs = ["BUILD.bazel"],
testonly = True,
visibility = ["//compiler/clientsql:__pkg__"],
)
6 changes: 6 additions & 0 deletions bzl/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
filegroup(
name = "clientsql_generator_test_data",
srcs = ["dependencies.bzl"],
testonly = True,
visibility = ["//compiler/clientsql:__pkg__"],
)
16 changes: 16 additions & 0 deletions bzl/dependencies.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,22 @@ def setup_dependencies(workspace_root = None):
url = "https://github.com/fmtlib/fmt/releases/download/7.1.3/fmt-7.1.3.zip",
)

http_archive(
name = "sqlite",
build_file = "@valdi//third-party/sqlite:sqlite.BUILD",
sha256 = "0e9483900e92cd5de8fd48d16bf9200145a61f7fd5be542a5ac81d8a9516eb9c",
strip_prefix = "sqlite-autoconf-3530400",
url = "https://www.sqlite.org/2026/sqlite-autoconf-3530400.tar.gz",
)

http_archive(
name = "sqlite_316",
build_file = "@valdi//third-party/sqlite:sqlite_316.BUILD",
sha256 = "3b5dfb65807e2b17e6463357df848e322badba01dc9a4a1de8fdbb72d448e3b0",
strip_prefix = "sqlite-amalgamation-3160000",
url = "https://www.sqlite.org/2017/sqlite-amalgamation-3160000.zip",
)

local_or_nested_repository(
workspace_root = workspace_root,
name = "android_macros",
Expand Down
17 changes: 16 additions & 1 deletion bzl/valdi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,11 @@ valdi_toolchain(
compiler = ":compiler_source",
compiler_companion = ":compiler_companion_source",
compiler_toolbox = "@valdi_toolchain//:valdi_compiler_toolbox",
sqldelight_compiler = "@valdi_toolchain//:sqldelight_compiler",
# ClientSQL is built from the reviewed generator sources in this repository.
# Keeping it in the exec configuration gives local and remote actions the
# same hermetic executable and runfiles rather than an unpublished prebuilt.
sqldelight_compiler = "//compiler/clientsql:clientsql",
clientsql_sqlite_validator = "//compiler/clientsql:sqlite_316_validator",
)

# Cross-compilation setups whose first execution platform is a remote linux worker set this
Expand Down Expand Up @@ -469,3 +473,14 @@ toolchain(
toolchain = ":valdi",
toolchain_type = ":toolchain_type",
)

filegroup(
name = "clientsql_generator_test_data",
srcs = [
"BUILD.bazel",
"valdi_run_compiler.bzl",
"valdi_toolchain.bzl",
],
testonly = True,
visibility = ["//compiler/clientsql:__pkg__"],
)
4 changes: 4 additions & 0 deletions bzl/valdi/valdi_compiled.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,7 @@ def _prepare_hotreload_arguments(module_names, config_yaml_file, explicit_input_
compiler_toolbox = toolchain.compiler_toolbox.files.to_list()[0]
minify_config = toolchain.minify_config.files.to_list()[0]
client_sql = toolchain.sqldelight_compiler.files.to_list()
client_sql_validator = toolchain.clientsql_sqlite_validator.files.to_list()

args = []

Expand All @@ -2220,6 +2221,9 @@ def _prepare_hotreload_arguments(module_names, config_yaml_file, explicit_input_
if client_sql:
args.append("--direct-client-sql-path")
args.append(client_sql[0].path)
if client_sql_validator:
args.append("--direct-client-sql-validator-path")
args.append(client_sql_validator[0].path)

args.append("--module")
args.append(_VALDI_BASE_MODULE_NAME)
Expand Down
6 changes: 6 additions & 0 deletions bzl/valdi/valdi_run_compiler.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def resolve_compiler_executable(ctx, toolchain, include_tools):
* companion tool (see //src/valdi_internal/compiler/companion:bin_wrapper)
* minify config file
* sqldelight compiler binary
* ClientSQL SQLite 3.16 validator binary

Args:
ctx: The context of the current rule invocation
Expand All @@ -69,8 +70,10 @@ def resolve_compiler_executable(ctx, toolchain, include_tools):
inputs_depsets.append(toolchain.compiler_toolbox.files)
inputs_depsets.append(toolchain.minify_config.files)
inputs_depsets.append(toolchain.sqldelight_compiler.files)
inputs_depsets.append(toolchain.clientsql_sqlite_validator.files)

tools.append(toolchain.sqldelight_compiler[DefaultInfo].files_to_run)
tools.append(toolchain.clientsql_sqlite_validator[DefaultInfo].files_to_run)

return (toolchain.compiler.files.to_list()[0], depset(transitive = inputs_depsets), tools)

Expand All @@ -86,6 +89,7 @@ def run_valdi_compiler(ctx, args, outputs, inputs, mnemonic, progress_message, u
compiler_toolbox = toolchain.compiler_toolbox.files.to_list()[0]
minify_config = toolchain.minify_config.files.to_list()[0]
client_sql = toolchain.sqldelight_compiler.files.to_list()
client_sql_validator = toolchain.clientsql_sqlite_validator.files.to_list()

args.add("--bazel")
args.add("--direct-companion-path", companion_bin_wrapper)
Expand All @@ -94,6 +98,8 @@ def run_valdi_compiler(ctx, args, outputs, inputs, mnemonic, progress_message, u

if client_sql:
args.add("--direct-client-sql-path", client_sql[0])
if client_sql_validator:
args.add("--direct-client-sql-validator-path", client_sql_validator[0])

env = {
# required for the companion app execution under Bazel
Expand Down
10 changes: 9 additions & 1 deletion bzl/valdi/valdi_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ValdiCompilerInfo = provider(
"companion",
"minify_config",
"sqldelight_compiler",
"clientsql_sqlite_validator",
],
)

Expand All @@ -19,6 +20,7 @@ def _valdi_toolchain_impl(ctx):
companion = ctx.attr.compiler_companion,
minify_config = ctx.attr.minify_config,
sqldelight_compiler = ctx.attr.sqldelight_compiler,
clientsql_sqlite_validator = ctx.attr.clientsql_sqlite_validator,
),
)
return [info]
Expand Down Expand Up @@ -49,8 +51,14 @@ valdi_toolchain = rule(
default = "//modules:minify_config",
),
"sqldelight_compiler": attr.label(
executable = True,
cfg = "exec",
doc = "The ClientSQL generator executable to use.",
),
"clientsql_sqlite_validator": attr.label(
executable = True,
cfg = "exec",
doc = "The sqldelight compiler to use.",
doc = "The hermetic SQLite 3.16 validator used by the ClientSQL generator.",
),
},
)
19 changes: 19 additions & 0 deletions compiler/clientsql/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
load("@aspect_rules_js//js:defs.bzl", "js_binary")
load("@rules_python//python:defs.bzl", "py_test")

cc_binary(
name = "sqlite_316_validator",
srcs = ["sqlite_316_validator.cpp"],
visibility = ["//visibility:public"],
deps = ["@sqlite_316//:sqlite"],
)

py_library(
name = "clientsql_lib",
srcs = glob(["src/clientsql/*.py"]),
Expand Down Expand Up @@ -37,12 +44,24 @@ py_test(
":clientsql",
":clientsql_test_javascript_runner",
":package_clientsql",
":sqlite_316_validator",
"sqlite_316_validator.cpp",
"@npm_typescript//:tsc",
"//:clientsql_generator_test_data",
"//bin:clientsql_generator_test_data",
"//bzl:clientsql_generator_test_data",
"//bzl/valdi:clientsql_generator_test_data",
"//compiler/compiler:clientsql_generator_test_data",
"//src/valdi_modules/src/valdi/client_sql:clientsql_generator_test_data",
"//third-party/sqlite:clientsql_generator_test_data",
"//valdi:clientsql_generator_test_data",
"//valdi/testdata/resources/modules/client_sql_smoke:clientsql_generator_test_data",
],
env = {
"BAZEL_BINDIR": ".",
"CLIENTSQL_TEST_GENERATOR": "$(rootpath :clientsql)",
"CLIENTSQL_TEST_JAVASCRIPT_RUNNER": "$(rootpath :clientsql_test_javascript_runner)",
"CLIENTSQL_TEST_SQLITE_VALIDATOR": "$(rootpath :sqlite_316_validator)",
"CLIENTSQL_TEST_TYPESCRIPT_COMPILER": "$(rootpath @npm_typescript//:tsc)",
},
size = "medium",
Expand Down
58 changes: 53 additions & 5 deletions compiler/clientsql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ The source is divided by responsibility:
- `sql.py` parses and validates SQL, migrations, parameters, and result shapes.
- `typescript.py` emits generated TypeScript bindings and database classes.

The public Valdi toolchain continues to supply its ClientSQL executable through
the existing `sqldelight_compiler` target. This source package intentionally
does not replace that toolchain binary or check in a generated executable. Use
the Bazel `//compiler/clientsql:clientsql` target for source builds, or create a
deterministic standalone zipapp at an explicit local path:
The public Valdi toolchain uses the Bazel `//compiler/clientsql:clientsql`
`py_binary` as its canonical `sqldelight_compiler` executable. The reviewed
sources above are therefore the actual input to local and remote ClientSQL
generation; no Git LFS generator binary or unpublished toolchain release is
required. A deterministic standalone zipapp can still be created at an
explicit local path:

```bash
python3 compiler/clientsql/package_clientsql.py --output /tmp/clientsql
Expand All @@ -25,3 +26,50 @@ An explicitly supplied executable can be checked against the canonical source:
```bash
python3 compiler/clientsql/package_clientsql.py --output /tmp/clientsql --check
```

Both the source entry point and zipapp require the same explicit floor-validator
path:

```bash
python3 compiler/clientsql/src/clientsql_main.py \
--sqlite-validator /path/to/sqlite_316_validator -version
/tmp/clientsql --sqlite-validator /path/to/sqlite_316_validator -version
```

The Bazel Valdi toolchain builds `//compiler/clientsql:sqlite_316_validator` in
the execution configuration and passes its path through the compiler. Direct
invocations fail closed if the path is absent, cannot execute, or reports a
different SQLite version/source identity.

`clientsql -version` includes a SHA-256 digest over the canonical package
sources plus the exact validator executable's SHA-256, SQLite version, official
source ID, `sqlite3.c` SHA-1, and protocol version. `ClientSqlProcessor` stores
that full identity in its disk-cache metadata, so either source or validator
changes invalidate generated outputs even when the human release number remains
`0.2.0`.
Generator tests execute both the source entry point and packaged zipapp against
the same fixture and require identical version identities and generated bytes.
For a source-tree test run, first build or otherwise supply the verified target
and set `CLIENTSQL_TEST_SQLITE_VALIDATOR` to that executable; the tests do not
fall back to the host Python SQLite library.

SQL generation targets the SQLite 3.16.0 runtime floor supported by Apple system
SQLite. Schema statements are prepared and executed, generated queries are
prepared as `EXPLAIN` with their exact parameter counts, and every migration
statement is prepared by a validator actually linked to the verified 3.16.0
amalgamation. There is no host-Python SQLite dependency and no finite syntax
blacklist. Non-Apple runtime builds continue to use the separate pinned SQLite
3.53.4 release documented in `third-party/sqlite/README.md`.

Generated database classes keep the caller-facing database name for display,
but derive a canonical lowercase ASCII storage identity from the Valdi module
digest plus a collision-free UTF-16 encoding of that name. The compiler passes
the canonical Valdi bundle name separately from the database package/class, so
identically named databases in different bundles receive different module
digests. This prevents two
modules from sharing a file accidentally and prevents filesystem case,
normalization, separator, or trailing-character aliases. Names are limited to
48 UTF-16 code units and may not contain control characters. Schema versions
are positive 32-bit integers; migration filenames must be unique, contiguous
32-bit versions beginning at 2. Concurrent handles for one storage identity
must present the same length-delimited schema fingerprint.
Loading
Loading