synalog 2.0.0__tar.gz → 2.1.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.
- {synalog-2.0.0 → synalog-2.1.0}/Cargo.lock +1 -1
- {synalog-2.0.0 → synalog-2.1.0}/Cargo.toml +1 -1
- {synalog-2.0.0 → synalog-2.1.0}/PKG-INFO +10 -10
- {synalog-2.0.0 → synalog-2.1.0}/README.md +9 -9
- {synalog-2.0.0 → synalog-2.1.0}/docs/assertions.md +1 -1
- {synalog-2.0.0 → synalog-2.1.0}/docs/cli.md +23 -9
- {synalog-2.0.0 → synalog-2.1.0}/docs/development.md +1 -1
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/axioms.log +2092 -2327
- synalog-2.1.0/docs/examples/functions.log +103 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/recursion.log +20 -20
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/run.py +2 -2
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/syntax.log +7 -7
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal.log +3 -3
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal_graph.log +11 -11
- {synalog-2.0.0 → synalog-2.1.0}/docs/getting-started.md +1 -1
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/index.md +4 -4
- {synalog-2.0.0 → synalog-2.1.0}/docs/python-api.md +4 -4
- {synalog-2.0.0 → synalog-2.1.0}/pyproject.toml +1 -1
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/checking.py +37 -36
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/cli.py +83 -114
- synalog-2.1.0/python/synalog/config.py +54 -0
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/execution.py +11 -30
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/introspect.py +23 -33
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/project.py +32 -49
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/runners.py +82 -91
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/SKILL.md +10 -10
- synalog-2.0.0/docs/examples/functions.log +0 -103
- synalog-2.0.0/python/synalog/config.py +0 -113
- {synalog-2.0.0 → synalog-2.1.0}/.gitignore +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/LICENSE +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/NOTICE +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/assets/synalog-dark.svg +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/assets/synalog-light.svg +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/assets/synalog_favicon.png +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/results.json +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/speedup_by_engine.png +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/speedup_distribution.png +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/summary.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/summary.png +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/time_comparison.png +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/verify_by_engine.png +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/differences-datalog.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/differences.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/engines.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/aggregation.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/aggregation.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/axioms.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/axioms.ttl +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/directives.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/directives.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/functions.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/functors.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/functors.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/getting_started.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/getting_started.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/knowledge_graphs.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/knowledge_graphs.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/recursion.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/concepts/Customer.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/concepts/OrderStatus.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/data/orders.csv +0 -0
- /synalog-2.0.0/docs/examples/shop/synalog.toml → /synalog-2.1.0/docs/examples/shop/layer.toml +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/rules/CustomerRevenue.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/rules/OrdersByStatus.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/rules/TopCustomers.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/tables/Orders.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/concepts/Device.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/concepts/TestStatus.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/data/smoke_tests.csv +0 -0
- /synalog-2.0.0/docs/examples/smoke/synalog.toml → /synalog-2.1.0/docs/examples/smoke/layer.toml +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/rules/FailuresByDevice.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/rules/RunsPerDay.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/tables/SmokeTests.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/syntax.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal_graph.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/verification.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/examples/verification.log +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/index.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/javascripts/playground.js +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/javascripts/summarize.js +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/knowledge-graphs.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/aggregation.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/directives.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/functions.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/functors.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/recursion.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/syntax.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/language/temporal.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/playground/vendor/codemirror.js +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/playground.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/stylesheets/extra.css +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/verification.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/docs/why.md +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/img/synalog-dark.svg +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/img/synalog-light.svg +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/__init__.py +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/python/synalog/__main__.py +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/concepts/Department.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/concepts/Person.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/concepts/WorksIn.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/data/departments.csv +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/data/employees.csv +0 -0
- /synalog-2.0.0/skills/synalog/examples/company/synalog.toml → /synalog-2.1.0/skills/synalog/examples/company/layer.toml +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/rules/TeamMate.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/tables/Departments.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/tables/Employees.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/org/concepts/AllManagers.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/org/data/employees.csv +0 -0
- /synalog-2.0.0/skills/synalog/examples/org/synalog.toml → /synalog-2.1.0/skills/synalog/examples/org/layer.toml +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/org/tables/Employees.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/concepts/Region.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/data/sales.csv +0 -0
- /synalog-2.0.0/skills/synalog/examples/sales/synalog.toml → /synalog-2.1.0/skills/synalog/examples/sales/layer.toml +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/rules/MonthlySales.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/rules/TopRegion.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/rules/TotalByRegion.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/tables/Sales.l +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/assertion/mod.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/assertion/parse.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/assertion/report.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/assertion/translate.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/annotations.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/annotations_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/concertina.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/dialects.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/dialects_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/expr_translate.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/expr_translate_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/functors.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/functors_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/mod.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/program.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/program_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/rule_translate.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/rule_translate_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/builder.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/built_in.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/edge.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/expression.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/graph.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/inference.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/intersection.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/mod.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/types.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/universe.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/compiler/universe_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/errors.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/lib.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/json.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/json_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/mod.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/names.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/parse.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/parse_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/rewrite.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/rewrite_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/span.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/span_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/traverse.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/parser/traverse_test.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/python.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/arity.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/assertions.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/contradiction.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/directives.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/front_matter.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/functors.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/mod.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/orderby.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/positional.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/recursion.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/reserved.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/safety.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/sqlexpr.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/stratification.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/undefined.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/verifier/vars.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/src/wasm.rs +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/uv.lock +0 -0
- {synalog-2.0.0 → synalog-2.1.0}/zensical.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synalog
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -162,13 +162,13 @@ $ synalog program.l run EngineeringTeam
|
|
|
162
162
|
- `--limit` / `--offset` paginate the result.
|
|
163
163
|
- `--csv` (with `run`) prints results as CSV instead of the rendered table.
|
|
164
164
|
- `--search REGEX` (with `print`/`run`) keeps only rows where some column matches the regular expression `REGEX`, e.g. `synalog program.l run Customers --search "(?i)acme"`. In the interactive session the same is `.search Customers (?i)acme`.
|
|
165
|
-
- `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (
|
|
165
|
+
- `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (in a [project](#projects-layertoml) connected to it) out of the box: synalog depends on duckdb and on psycopg with its bundled libpq, on macOS and Linux alike. For other engines, install their driver (`pip install 'synalog[trino]'`, ...) or use `print` and run the SQL with your own client.
|
|
166
166
|
- `import path.to.file.Pred;` statements resolve `path/to/file.l` against the program file's directory, then the current directory; pass `--import-root DIR` (repeatable) to search elsewhere.
|
|
167
167
|
- `--load TABLE=PATH` (repeatable) loads a csv/tsv/json/jsonl/parquet file as a table before running, e.g. `synalog senior.l run Senior --load employees=employees.csv`.
|
|
168
168
|
|
|
169
|
-
### Projects: `
|
|
169
|
+
### Projects: `layer.toml`
|
|
170
170
|
|
|
171
|
-
A folder with a `
|
|
171
|
+
A folder with a `layer.toml` is a project. Its `[connection]` says which database its programs run on, as plain fields — commit it:
|
|
172
172
|
|
|
173
173
|
```toml
|
|
174
174
|
[connection]
|
|
@@ -180,7 +180,7 @@ user = "analyst"
|
|
|
180
180
|
schema = "public"
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database; `--engine
|
|
183
|
+
Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database. The project's file is the only place a connection comes from; `--engine` and an `@Engine` annotation choose the dialect, and a remote engine runs only through the project's connection to it. `synalog connect <engine> key=value ...` writes it — the non-secret fields to `layer.toml`, the secret ones to `.env` — `synalog connect` shows it (secrets hidden), `synalog connect clear` removes it. The fields of every engine are in `synalog.project.ENGINES`.
|
|
184
184
|
|
|
185
185
|
Running `synalog` with no arguments starts an interactive session, in the spirit of `python` (the options above, e.g. `--engine` or `--load`, apply to it too):
|
|
186
186
|
|
|
@@ -262,7 +262,7 @@ for name, sql in sqls.items():
|
|
|
262
262
|
print(name, sql)
|
|
263
263
|
```
|
|
264
264
|
|
|
265
|
-
### `execute(source, predicate, engine=None,
|
|
265
|
+
### `execute(source, predicate, engine=None, project=None, import_root=None, limit=None, offset=None, pattern=None, assertion=None, loads=()) -> tuple[list[str], list[tuple]]`
|
|
266
266
|
|
|
267
267
|
Run a predicate on its database (the program's or project's engine and connection) and return `(columns, rows)`. Each recursion stops as soon as a step changes nothing, so it costs the steps its data needs, and `@Recursive(P, -1)` (until nothing changes) runs on every engine. `plan()` returns the same steps for a host that runs them itself.
|
|
268
268
|
|
|
@@ -270,9 +270,9 @@ Run a predicate on its database (the program's or project's engine and connectio
|
|
|
270
270
|
columns, rows = synalog.execute(source, "TopCustomers", limit=20)
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
### `check(source, engine=None, import_root=None, assertions=True,
|
|
273
|
+
### `check(source, engine=None, import_root=None, assertions=True, project=None) -> tuple[list[str], list[str]]`
|
|
274
274
|
|
|
275
|
-
Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `
|
|
275
|
+
Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `layer.toml` has a `[connection]` — the current directory's, or the folder `project` names — it also runs the program's `@Assert` statements against the database and reports each violated one as an error; `assertions=False` keeps it offline.
|
|
276
276
|
|
|
277
277
|
```python
|
|
278
278
|
errors, warnings = synalog.check(source)
|
|
@@ -285,7 +285,7 @@ All of these functions accept an optional `engine` keyword that overrides the pr
|
|
|
285
285
|
|
|
286
286
|
## Language overview
|
|
287
287
|
|
|
288
|
-
A Synalog program is a **project**: a folder with a `
|
|
288
|
+
A Synalog program is a **project**: a folder with a `layer.toml`, holding one predicate per file in three folders, the layout of [semantic layers](https://github.com/SynaLinks/semantic-layers):
|
|
289
289
|
|
|
290
290
|
```
|
|
291
291
|
shop/
|
|
@@ -293,7 +293,7 @@ shop/
|
|
|
293
293
|
├── concepts/Customer.l # entities and relationships extracted from tables
|
|
294
294
|
├── rules/CustomerRevenue.l # insights derived from concepts
|
|
295
295
|
├── rules/TopCustomers.l
|
|
296
|
-
└──
|
|
296
|
+
└── layer.toml # the project's name, description and database
|
|
297
297
|
```
|
|
298
298
|
|
|
299
299
|
Each file is named after its predicate and opens with front matter (`name`, the predicate; `description`, what its rows are), then imports what it builds on by folder, file and predicate, then orders its predicate with `@OrderBy`. A database table is referenced by its lowercase database name in `tables/`, and everything else builds on the PascalCase predicate that maps it:
|
|
@@ -120,13 +120,13 @@ $ synalog program.l run EngineeringTeam
|
|
|
120
120
|
- `--limit` / `--offset` paginate the result.
|
|
121
121
|
- `--csv` (with `run`) prints results as CSV instead of the rendered table.
|
|
122
122
|
- `--search REGEX` (with `print`/`run`) keeps only rows where some column matches the regular expression `REGEX`, e.g. `synalog program.l run Customers --search "(?i)acme"`. In the interactive session the same is `.search Customers (?i)acme`.
|
|
123
|
-
- `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (
|
|
123
|
+
- `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (in a [project](#projects-layertoml) connected to it) out of the box: synalog depends on duckdb and on psycopg with its bundled libpq, on macOS and Linux alike. For other engines, install their driver (`pip install 'synalog[trino]'`, ...) or use `print` and run the SQL with your own client.
|
|
124
124
|
- `import path.to.file.Pred;` statements resolve `path/to/file.l` against the program file's directory, then the current directory; pass `--import-root DIR` (repeatable) to search elsewhere.
|
|
125
125
|
- `--load TABLE=PATH` (repeatable) loads a csv/tsv/json/jsonl/parquet file as a table before running, e.g. `synalog senior.l run Senior --load employees=employees.csv`.
|
|
126
126
|
|
|
127
|
-
### Projects: `
|
|
127
|
+
### Projects: `layer.toml`
|
|
128
128
|
|
|
129
|
-
A folder with a `
|
|
129
|
+
A folder with a `layer.toml` is a project. Its `[connection]` says which database its programs run on, as plain fields — commit it:
|
|
130
130
|
|
|
131
131
|
```toml
|
|
132
132
|
[connection]
|
|
@@ -138,7 +138,7 @@ user = "analyst"
|
|
|
138
138
|
schema = "public"
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database; `--engine
|
|
141
|
+
Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database. The project's file is the only place a connection comes from; `--engine` and an `@Engine` annotation choose the dialect, and a remote engine runs only through the project's connection to it. `synalog connect <engine> key=value ...` writes it — the non-secret fields to `layer.toml`, the secret ones to `.env` — `synalog connect` shows it (secrets hidden), `synalog connect clear` removes it. The fields of every engine are in `synalog.project.ENGINES`.
|
|
142
142
|
|
|
143
143
|
Running `synalog` with no arguments starts an interactive session, in the spirit of `python` (the options above, e.g. `--engine` or `--load`, apply to it too):
|
|
144
144
|
|
|
@@ -220,7 +220,7 @@ for name, sql in sqls.items():
|
|
|
220
220
|
print(name, sql)
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
-
### `execute(source, predicate, engine=None,
|
|
223
|
+
### `execute(source, predicate, engine=None, project=None, import_root=None, limit=None, offset=None, pattern=None, assertion=None, loads=()) -> tuple[list[str], list[tuple]]`
|
|
224
224
|
|
|
225
225
|
Run a predicate on its database (the program's or project's engine and connection) and return `(columns, rows)`. Each recursion stops as soon as a step changes nothing, so it costs the steps its data needs, and `@Recursive(P, -1)` (until nothing changes) runs on every engine. `plan()` returns the same steps for a host that runs them itself.
|
|
226
226
|
|
|
@@ -228,9 +228,9 @@ Run a predicate on its database (the program's or project's engine and connectio
|
|
|
228
228
|
columns, rows = synalog.execute(source, "TopCustomers", limit=20)
|
|
229
229
|
```
|
|
230
230
|
|
|
231
|
-
### `check(source, engine=None, import_root=None, assertions=True,
|
|
231
|
+
### `check(source, engine=None, import_root=None, assertions=True, project=None) -> tuple[list[str], list[str]]`
|
|
232
232
|
|
|
233
|
-
Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `
|
|
233
|
+
Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `layer.toml` has a `[connection]` — the current directory's, or the folder `project` names — it also runs the program's `@Assert` statements against the database and reports each violated one as an error; `assertions=False` keeps it offline.
|
|
234
234
|
|
|
235
235
|
```python
|
|
236
236
|
errors, warnings = synalog.check(source)
|
|
@@ -243,7 +243,7 @@ All of these functions accept an optional `engine` keyword that overrides the pr
|
|
|
243
243
|
|
|
244
244
|
## Language overview
|
|
245
245
|
|
|
246
|
-
A Synalog program is a **project**: a folder with a `
|
|
246
|
+
A Synalog program is a **project**: a folder with a `layer.toml`, holding one predicate per file in three folders, the layout of [semantic layers](https://github.com/SynaLinks/semantic-layers):
|
|
247
247
|
|
|
248
248
|
```
|
|
249
249
|
shop/
|
|
@@ -251,7 +251,7 @@ shop/
|
|
|
251
251
|
├── concepts/Customer.l # entities and relationships extracted from tables
|
|
252
252
|
├── rules/CustomerRevenue.l # insights derived from concepts
|
|
253
253
|
├── rules/TopCustomers.l
|
|
254
|
-
└──
|
|
254
|
+
└── layer.toml # the project's name, description and database
|
|
255
255
|
```
|
|
256
256
|
|
|
257
257
|
Each file is named after its predicate and opens with front matter (`name`, the predicate; `description`, what its rows are), then imports what it builds on by folder, file and predicate, then orders its predicate with `@OrderBy`. A database table is referenced by its lowercase database name in `tables/`, and everything else builds on the PascalCase predicate that maps it:
|
|
@@ -143,7 +143,7 @@ An assertion is checked by looking for its counterexamples: Synalog compiles tha
|
|
|
143
143
|
| `synalog program.l verify [Predicate ...]` | Runs every assertion, or those of the given predicates, and prints the counterexamples of each violated one (5 by default, `--limit` to change). Exits 1 if one is violated. |
|
|
144
144
|
| `synalog program.l run Predicate` | Checks the program's assertions before it prints anything, and exits 1 with a few counterexamples if one is violated. |
|
|
145
145
|
| `synalog program.l print Predicate` | Never touches the database: assertions are not run. |
|
|
146
|
-
| [`check()`](python-api.md#check) | Runs the assertions when a database is known: inside a project whose `
|
|
146
|
+
| [`check()`](python-api.md#check) | Runs the assertions when a database is known: inside a project whose `layer.toml` has a `[connection]` (the current directory's, or the folder `project` names). Each violated assertion is an error. `assertions=False` keeps it offline. |
|
|
147
147
|
|
|
148
148
|
```text
|
|
149
149
|
$ synalog family.l run Grandparent --load parents=parents.csv
|
|
@@ -55,13 +55,12 @@ $ synalog program.l run EngineeringTeam
|
|
|
55
55
|
| Option | Meaning |
|
|
56
56
|
| --- | --- |
|
|
57
57
|
| `-c PROGRAM` | Pass the program text inline instead of `FILE`, like `python -c`. |
|
|
58
|
-
| `--engine <name>` | Target SQL dialect. Resolution order: this flag, then the program's `@Engine` annotation, then `duckdb`. |
|
|
58
|
+
| `--engine <name>` | Target SQL dialect. Resolution order: this flag, then the program's `@Engine` annotation, then the project's engine, then `duckdb`. |
|
|
59
59
|
| `--limit N` / `--offset N` | Paginate the result. |
|
|
60
60
|
| `--csv` | With `run`: print results as CSV instead of a rendered table. |
|
|
61
61
|
| `--search REGEX` | With `print`/`run`: keep only rows where some column matches the regular expression `REGEX` (engine-native regex, not a SQL `LIKE` pattern). Applies to the filtered rows before pagination. |
|
|
62
62
|
| `--import-root DIR` | Directory where `import` statements look up `.l` files (repeatable). |
|
|
63
63
|
| `--load TABLE=PATH` | Load a csv/tsv/json/jsonl/parquet file as a table before running (repeatable). |
|
|
64
|
-
| `--dsn <conninfo>` | PostgreSQL connection string for `--engine psql` (or set `SYNALOG_PSQL_DSN`). |
|
|
65
64
|
|
|
66
65
|
Passing `-` as the file reads the program from stdin, and `-c` takes the program text directly, and both compose with the other options:
|
|
67
66
|
|
|
@@ -79,7 +78,7 @@ With `-c` there is no `FILE` argument: the positionals are the command and its p
|
|
|
79
78
|
|
|
80
79
|
- **duckdb**: the default engine, bundled with synalog; nothing extra to install.
|
|
81
80
|
- **sqlite**: Python's stdlib driver; Logica's runtime UDFs (ArgMin/ArgMax, ARRAY_CONCAT, ...) are registered when the `logica` package is installed.
|
|
82
|
-
- **psql**: bundled with synalog (psycopg with its own libpq);
|
|
81
|
+
- **psql**: bundled with synalog (psycopg with its own libpq); runs in a [project](#projects-layertoml) connected to PostgreSQL.
|
|
83
82
|
|
|
84
83
|
The [`Today` and `Now`](language/temporal.md) built-in concepts need no runner support, since the compiler inlines them per dialect, so they work on every engine.
|
|
85
84
|
|
|
@@ -100,7 +99,7 @@ duckdb and the PostgreSQL driver ship with synalog. For the other engines (`bigq
|
|
|
100
99
|
|
|
101
100
|
### Imports
|
|
102
101
|
|
|
103
|
-
In a project, `import <folder>.<Name>.<Name>;` statements resolve from the project's folder (the one holding `
|
|
102
|
+
In a project, `import <folder>.<Name>.<Name>;` statements resolve from the project's folder (the one holding `layer.toml`), whichever file runs and wherever from: `import tables.Orders.Orders;` reads the project's `tables/Orders.l`. The program file's directory and the current directory come next. Pass `--import-root DIR` (repeatable) to search elsewhere; explicit roots replace the defaults.
|
|
104
103
|
|
|
105
104
|
In the [`shop` project](language/index.md), `rules/TopCustomers.l` builds on `rules/CustomerRevenue.l`:
|
|
106
105
|
|
|
@@ -155,9 +154,9 @@ Compile error: No rules are defining 'Missing', but compilation was requested.
|
|
|
155
154
|
|
|
156
155
|
A failing program never produces partial output: `run` either prints the table or the error.
|
|
157
156
|
|
|
158
|
-
## Projects: `
|
|
157
|
+
## Projects: `layer.toml`
|
|
159
158
|
|
|
160
|
-
A Synalog program is a [project](language/index.md): a folder with a `
|
|
159
|
+
A Synalog program is a [project](language/index.md): a folder with a `layer.toml`, its predicates in `tables/`, `concepts/` and `rules/`, one per file. The file names and describes the project, and its `[connection]` says which database it runs on, as plain fields — commit it:
|
|
161
160
|
|
|
162
161
|
```toml
|
|
163
162
|
[project]
|
|
@@ -173,7 +172,22 @@ user = "analyst"
|
|
|
173
172
|
schema = "public"
|
|
174
173
|
```
|
|
175
174
|
|
|
176
|
-
Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Anywhere in the project, `run`, `print`, `verify` and `introspect` use that engine and connection, and [imports](#imports) resolve from the project's folder
|
|
175
|
+
Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Anywhere in the project, `run`, `print`, `verify` and `introspect` use that engine and connection, and [imports](#imports) resolve from the project's folder. The project's file is the only place a connection comes from: `--engine` and an `@Engine` annotation choose the dialect, and a remote engine runs only through the project's connection to it. The fields of every engine are in `synalog.project.ENGINES`.
|
|
176
|
+
|
|
177
|
+
### Connect
|
|
178
|
+
|
|
179
|
+
```console
|
|
180
|
+
$ synalog connect psql host=db.example.com database=sales user=analyst password=...
|
|
181
|
+
Connected /path/to/sales/layer.toml to psql (secrets in .env)
|
|
182
|
+
$ synalog connect
|
|
183
|
+
/path/to/sales/layer.toml
|
|
184
|
+
engine = psql
|
|
185
|
+
host = db.example.com
|
|
186
|
+
...
|
|
187
|
+
password: SYNALOG_PSQL_PASSWORD set
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`synalog connect <engine> key=value ...` writes it — the non-secret fields to `layer.toml`, the secret ones to `.env` — `synalog connect` shows it (secrets hidden), `synalog connect clear` removes it. It runs in the project's folder — the nearest `layer.toml`'s, else the current directory — keeps the file's other tables, adds `.env` to `.gitignore`, and writes nothing when a required field is missing.
|
|
177
191
|
|
|
178
192
|
### Introspect
|
|
179
193
|
|
|
@@ -193,7 +207,7 @@ description: Orders.
|
|
|
193
207
|
PublicOrders(order_id:, customer_id:, amount:, status:) :- public.orders(order_id:, customer_id:, amount:, status:);
|
|
194
208
|
```
|
|
195
209
|
|
|
196
|
-
Run it again when the schema changes: the declarations are regenerated, and a file's front matter (its description, keywords, ...) is kept as written. A table the database no longer has keeps its file, since concepts may import it, and is listed.
|
|
210
|
+
Run it again when the schema changes: the declarations are regenerated, and a file's front matter (its description, keywords, ...) is kept as written. A table the database no longer has keeps its file, since concepts may import it, and is listed. It needs a project connected to its database. PostgreSQL, Trino, Presto, Databricks and BigQuery can be introspected.
|
|
197
211
|
|
|
198
212
|
## Add the skill to your coding agent
|
|
199
213
|
|
|
@@ -243,7 +257,7 @@ Bad(x) :- x ==<EMPTY>
|
|
|
243
257
|
Greeting("hi");
|
|
244
258
|
```
|
|
245
259
|
|
|
246
|
-
The `--engine`, `--
|
|
260
|
+
The `--engine`, `--import-root` and `--load` options also apply to the session, and the project's connection too:
|
|
247
261
|
|
|
248
262
|
```bash
|
|
249
263
|
synalog --engine sqlite --load employees=employees.csv
|
|
@@ -131,7 +131,7 @@ It deploys to GitHub Pages automatically on push via `.github/workflows/docs.yml
|
|
|
131
131
|
|
|
132
132
|
### Documentation examples
|
|
133
133
|
|
|
134
|
-
The example projects (`docs/examples/shop/`, `docs/examples/smoke/`) are laid out as every Synalog project is: `tables/`, `concepts/`, `rules/`, a `
|
|
134
|
+
The example projects (`docs/examples/shop/`, `docs/examples/smoke/`) are laid out as every Synalog project is: `tables/`, `concepts/`, `rules/`, a `layer.toml`, and their data in `data/<table>.csv`. `run.py` loads the data into an in-memory DuckDB, checks every file, runs the predicate its front matter names, and writes the results to `<project>.log`.
|
|
135
135
|
|
|
136
136
|
The feature examples (aggregation, functors, recursion, ...) are single `.l` programs with their data as inline facts. For those, the `run.py` harness validates each program with `synalog.check()`, compiles every predicate listed in its `# run:` header, executes the SQL on an in-memory DuckDB, and writes the combined output to the matching `.log` file. A `# load: <file>.csv as <table>` header loads a CSV from `docs/examples/` into a DuckDB table before the predicates run. The `Today`/`Now` built-in concepts need no special handling, since the compiler inlines them per dialect:
|
|
137
137
|
|