Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6d65c59
build: require fensu 0.28.0
kvlonge Oct 27, 2026
dc484bb
fix(audit): store standalone audit history beside the models on prese…
kvlonge Oct 27, 2026
84521a0
feat(query)!: print query results as a table when they fit
kvlonge Oct 27, 2026
9741519
docs(skill): keep the default query format and reshape the question i…
kvlonge Oct 27, 2026
5e4470a
feat(recall): record the effective selection of each invocation
kvlonge Oct 27, 2026
26471e3
fix(recall): keep the SQL of failed and cancelled statements
kvlonge Oct 27, 2026
dbdfaf6
feat(recall): keep failing audits' evidence rows
kvlonge Oct 27, 2026
3a788d8
feat(recall): mark cost telemetry statements and record cost attribut…
kvlonge Oct 27, 2026
63bb682
fix(recall): record SIGTERM as a cancelled run with its real end
kvlonge Oct 27, 2026
41bc92a
fix(rules): judge SQL macro slot reads by how they are written
kvlonge Oct 27, 2026
3a1a9fb
feat(compile): write models' compiled SQL when Rules fail
kvlonge Oct 27, 2026
a984721
refactor: keep the batch within the architecture checks
kvlonge Oct 27, 2026
8f5d585
test: follow the batch's behaviour changes in existing tests
kvlonge Oct 27, 2026
4516bea
style(rules): pass the slot-read predicate directly
kvlonge Oct 27, 2026
97672e4
fix(recall): keep the interrupt as the result when recording an inter…
kvlonge Oct 27, 2026
29ad54c
fix(recall): stop on SIGTERM by cancelling running statements from a …
kvlonge Oct 28, 2026
467905e
fix(recall): exit 143 on SIGTERM wherever the command is, without a t…
kvlonge Oct 28, 2026
c300f52
fix(recall): record a cancelled run's exit code and its lifecycle eve…
kvlonge Oct 28, 2026
db4c7de
fix(rules): judge every column of a macro call's query as it was written
kvlonge Oct 28, 2026
e908c44
fix(recall): collect failing audits' rows only when recall.audit_evid…
kvlonge Oct 28, 2026
bcfd1b3
fix(query): leave the type cell empty when an adapter reports no type
kvlonge Oct 28, 2026
fc65431
fix(compile): remove a stale manifest when Rules fail
kvlonge Oct 28, 2026
096c6b6
refactor: lower the native crossing budgets the batch raised
kvlonge Oct 28, 2026
2452548
test(postgres): expect the column types the query result now reports
kvlonge Oct 28, 2026
7449859
test(compile): record compiled SQL in the Rules failure goldens
kvlonge Oct 28, 2026
2f666d7
test(query): expect the boxed default and JSON for exact target output
kvlonge Oct 28, 2026
34487c6
fix(compile): remove stale compiled files when Rules fail
kvlonge Oct 28, 2026
fe731ab
fix(rules): let a macro's reads through a passed relation fit either …
kvlonge Oct 28, 2026
a3895d7
perf(recall): read SQL references once in the ClickHouse statements view
kvlonge Oct 28, 2026
5282322
fix(compile): keep other artifacts when Rules fail and remove only re…
kvlonge Oct 28, 2026
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
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,13 @@ ruff_text_size = "=0.0.4"
# Unix only: `sqb` shares the compute log prune lock with Python's `filelock` through `flock`.
# Fast deterministic hasher for hot in-memory maps whose iteration order never reaches output.
rustc-hash = "2"
# The native SIGTERM handler: sets a flag the CLI watches, without unsafe code in the workspace.
signal-hook = { version = "0.3", default-features = false }
# Features signal-hook's registry enables, matched in the sqb build through sqlbuild-build-features.
errno = "0.3"
windows-sys = { version = "0.61", default-features = false, features = ["Win32_System_Diagnostics_Debug"] }
# Recall's local store: SQLite compiled in, so every build has the same engine version.
rusqlite = { version = "0.37", features = ["bundled"] }
rusqlite = { version = "0.37", features = ["bundled", "column_decltype"] }
rustix = { version = "1.1.4", default-features = false, features = ["fs", "std", "termios"] }
saphyr-parser = "0.1"
regex = "1"
Expand Down
7 changes: 7 additions & 0 deletions crates/sqlbuild-build-features/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ once_cell.workspace = true
regex-automata.workspace = true
thiserror.workspace = true

# signal-hook's registry, in the extension only, enables these on the platforms that use them.
[target.'cfg(unix)'.dependencies]
errno.workspace = true

[target.'cfg(windows)'.dependencies]
windows-sys.workspace = true

# Proc-macro dependencies resolve with build dependencies, so the syn features PyO3's macros use
# are enabled here. The empty build script makes Cargo resolve them.
[build-dependencies]
Expand Down
9 changes: 9 additions & 0 deletions crates/sqlbuild-cli/src/argv/_helpers/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ impl TableReader {
help: help(choice.get("help").unwrap_or(&Json::Null)),
})
.collect(),
removed_choices: items(json, "removed_choices")
.iter()
.map(|removed| {
(
text(removed, "value").unwrap_or_default(),
text(removed, "instead").unwrap_or_default(),
)
})
.collect(),
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions crates/sqlbuild-cli/src/argv/_helpers/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ impl Run<'_> {
if choices.iter().any(|choice| choice == text) {
return Ok(());
}
if let Some((_, instead)) = action
.removed_choices
.iter()
.find(|(removed, _)| removed == text)
{
return Err(argument_error(
action,
&format!("{} was removed; {instead}", self.host.repr(text)),
));
}
let listed: Vec<String> = choices
.iter()
.map(|choice| python_str_repr(choice))
Expand Down
54 changes: 38 additions & 16 deletions crates/sqlbuild-cli/src/argv/command_table.json
Original file line number Diff line number Diff line change
Expand Up @@ -4397,13 +4397,21 @@
"--format"
],
"dest": "query_format",
"default": "long",
"choices": [
"long",
"table",
"json",
"csv"
"json"
],
"removed_choices": [
{
"value": "table",
"instead": "the default output prints a table when the result fits; use --format long for one record per row"
},
{
"value": "csv",
"instead": "use --json for programs, or the warehouse's own export for bulk data"
}
],
"help": "by default a table when the result fits, else one record per row; long always prints records; json for programs",
"mutex_group": 0,
"group": 1
},
Expand Down Expand Up @@ -7183,14 +7191,21 @@
"--format"
],
"dest": "recall_format",
"default": "long",
"choices": [
"long",
"table",
"json",
"csv"
],
"help": "long (default) for reading, json for programs, csv for export; table is deprecated",
"json"
],
"removed_choices": [
{
"value": "table",
"instead": "the default output prints a table when the result fits; use --format long for one record per row"
},
{
"value": "csv",
"instead": "use --json for programs"
}
],
"help": "by default a table when the result fits, else one record per row; long always prints records; json for programs",
"mutex_group": 0,
"group": 1
},
Expand Down Expand Up @@ -7374,14 +7389,21 @@
"--format"
],
"dest": "recall_format",
"default": "long",
"choices": [
"long",
"table",
"json",
"csv"
],
"help": "long (default) for reading, json for programs, csv for export; table is deprecated",
"json"
],
"removed_choices": [
{
"value": "table",
"instead": "use the default long output"
},
{
"value": "csv",
"instead": "use --json for programs"
}
],
"help": "long (default) for reading, json for programs",
"mutex_group": 0,
"group": 1
},
Expand Down
4 changes: 2 additions & 2 deletions crates/sqlbuild-cli/src/argv/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub(crate) const CLI_NAMESPACE_DEFAULTS: [(&str, NamespaceDefault); 184] = [
("max_columns", NamespaceDefault::None),
("query_sql", NamespaceDefault::None),
("query_file", NamespaceDefault::None),
("query_format", NamespaceDefault::Text("long")),
("query_format", NamespaceDefault::None),
("query_limit", NamespaceDefault::Int(20)),
("query_no_limit", NamespaceDefault::Bool(false)),
("cost_selector", NamespaceDefault::Text("latest")),
Expand Down Expand Up @@ -228,7 +228,7 @@ pub(crate) const CLI_NAMESPACE_DEFAULTS: [(&str, NamespaceDefault); 184] = [
("recall_command", NamespaceDefault::None),
("recall_sql", NamespaceDefault::None),
("recall_backend", NamespaceDefault::Text("logs")),
("recall_format", NamespaceDefault::Text("long")),
("recall_format", NamespaceDefault::None),
("recall_limit", NamespaceDefault::Int(100)),
("recall_no_limit", NamespaceDefault::Bool(false)),
("recall_max_cell_bytes", NamespaceDefault::None),
Expand Down
2 changes: 2 additions & 0 deletions crates/sqlbuild-cli/src/argv/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ pub struct ActionSpec {
/// Sub-command names and their parsers, in order.
pub parsers: Vec<(String, ParserSpec)>,
pub choices_help: Vec<ChoiceHelp>,
/// Values no longer accepted, each with what to use instead; never listed in help.
pub removed_choices: Vec<(String, String)>,
}

/// One argument group's title and description.
Expand Down
2 changes: 2 additions & 0 deletions crates/sqlbuild-cli/src/dispatch/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub(crate) const DBT_INIT_SUBCOMMAND: &str = "init";
pub(crate) const DIFF_COMMAND_NAME: &str = "diff";
/// `sqb recall` reads history and is never recorded itself.
pub(crate) const RECALL_COMMAND_NAME: &str = "recall";
/// A `--select-file` line starting with this is a comment.
pub(crate) const SELECTOR_COMMENT_PREFIX: &str = "#";

const PROJECT_DIR: Field = field("project_dir", Source::ProjectDir);
const SELECT: Field = field("select", Source::Select);
Expand Down
1 change: 1 addition & 0 deletions crates/sqlbuild-cli/src/dispatch/main/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pub mod command_dispatch;
pub mod event_export_warning;
pub mod failure_report;
pub mod invocation_policy;
pub mod selector_file_selectors;
pub mod unavailable_handler;
17 changes: 17 additions & 0 deletions crates/sqlbuild-cli/src/dispatch/main/selector_file_selectors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! The selectors a `--select-file` lists.

use sqlbuild_core::text::main::python_splitlines::python_splitlines;
use sqlbuild_core::text::main::python_strip::python_strip;

use crate::dispatch::constants::SELECTOR_COMMENT_PREFIX;

/// One selector per stripped line of `text`, without blank lines and `#` comments.
#[must_use]
pub fn selector_file_selectors(text: &str) -> Vec<String> {
python_splitlines(text)
.into_iter()
.map(python_strip)
.filter(|line| !line.is_empty() && !line.starts_with(SELECTOR_COMMENT_PREFIX))
.map(str::to_owned)
.collect()
}
5 changes: 4 additions & 1 deletion crates/sqlbuild-config/src/recall/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

pub const RECALL_SECTION: &str = "recall";
/// Keys `[recall]` and `targets.<name>.recall` accept, sorted.
pub const RECALL_KEYS: [&str; 7] = [
pub const RECALL_KEYS: [&str; 8] = [
"audit_evidence",
"diagnostics_min_severity",
"enabled",
"max_output_bytes",
Expand All @@ -15,6 +16,8 @@ pub const RECALL_KEYS: [&str; 7] = [
pub const REMOVED_CONNECTION_KEY: &str = "connection";
/// The diagnostic severities a Recall store can keep from, least severe first.
pub const RECALL_SEVERITIES: [&str; 6] = ["trace", "debug", "info", "warning", "error", "critical"];
/// Which audit outcomes keep their evidence rows in Recall, the default first.
pub const AUDIT_EVIDENCE_MODES: [&str; 3] = ["warn-and-error", "error", "off"];
/// The longest shutdown flush budget, in milliseconds.
pub const MAX_SHUTDOWN_TIMEOUT_MS: u64 = 600_000;
/// The longest retention, in days.
Expand Down
13 changes: 12 additions & 1 deletion crates/sqlbuild-config/src/recall/main/recall_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
use crate::models::ConfigValue;
use crate::recall::_helpers::values::{non_empty, output_bytes, retention_days, timeout_ms};
use crate::recall::constants::{
MAX_RETENTION_DAYS, RECALL_KEYS, RECALL_SEVERITIES, REMOVED_CONNECTION_KEY,
AUDIT_EVIDENCE_MODES, MAX_RETENTION_DAYS, RECALL_KEYS, RECALL_SEVERITIES,
REMOVED_CONNECTION_KEY,
};
use crate::recall::models::RecallSection;

Expand Down Expand Up @@ -65,6 +66,16 @@ pub fn recall_section(value: &ConfigValue, label: &str) -> Result<RecallSection,
}
section.diagnostics_min_severity = Some(text.to_owned());
}
"audit_evidence" => {
let text = non_empty(value, label, key)?;
if !AUDIT_EVIDENCE_MODES.contains(&text) {
return Err(format!(
"{label}.audit_evidence must be one of: {}",
AUDIT_EVIDENCE_MODES.join(", ")
));
}
section.audit_evidence = Some(text.to_owned());
}
other => {
return Err(format!(
"{label} contains unknown key(s): {other}. Allowed keys: {}",
Expand Down
2 changes: 2 additions & 0 deletions crates/sqlbuild-config/src/recall/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ pub struct RecallSection {
pub diagnostics_min_severity: Option<String>,
/// Bytes of each output stream to record; `Some(0)` records everything.
pub max_output_bytes: Option<u64>,
/// Which audit outcomes keep their evidence rows: one of [`AUDIT_EVIDENCE_MODES`](crate::recall::constants::AUDIT_EVIDENCE_MODES).
pub audit_evidence: Option<String>,
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn given_valid_sections_when_validated_then_settings_are_typed() {
("shutdown_timeout", text("1500ms")),
("diagnostics_min_severity", text("debug")),
("max_output_bytes", ConfigValue::Integer(4_194_304)),
("audit_evidence", text("error")),
]),
expected_section: RecallSection {
enabled: Some(false),
Expand All @@ -26,6 +27,7 @@ fn given_valid_sections_when_validated_then_settings_are_typed() {
shutdown_timeout_ms: Some(1500),
diagnostics_min_severity: Some("debug".to_owned()),
max_output_bytes: Some(4_194_304),
audit_evidence: Some("error".to_owned()),
},
},
ValidSectionTestCase {
Expand Down Expand Up @@ -64,6 +66,13 @@ fn given_valid_sections_when_validated_then_settings_are_typed() {
#[test]
fn given_invalid_sections_when_validated_then_the_message_names_the_key() {
let test_cases = [
InvalidSectionTestCase {
description: "audit evidence names one of its modes",
value: table(&[("audit_evidence", text("always"))]),
label: "targets.prod.recall",
expected_message_prefix: "targets.prod.recall.audit_evidence must be one of: \
warn-and-error, error, off",
},
InvalidSectionTestCase {
description: "output bytes must not be negative",
value: table(&[("max_output_bytes", ConfigValue::Integer(-1))]),
Expand Down
8 changes: 6 additions & 2 deletions crates/sqlbuild-core/src/lifecycle_events/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub(crate) const FORBIDDEN_STATEMENT_PAYLOAD_FIELDS: [&str; 8] = [
"sql",
"statement_sql",
];
pub(crate) const STRING_PAYLOAD_FIELDS: [&str; 55] = [
pub(crate) const STRING_PAYLOAD_FIELDS: [&str; 56] = [
"adapter",
"attached_column_name",
"attached_target_kind",
Expand All @@ -92,6 +92,7 @@ pub(crate) const STRING_PAYLOAD_FIELDS: [&str; 55] = [
"binding_key",
"command",
"configured_batch_size",
"cost_status",
"cursor_end_exclusive",
"cursor_start",
"definition_fingerprint",
Expand Down Expand Up @@ -653,7 +654,7 @@ pub(crate) const LIFECYCLE_EVENT_CATALOG_V2: [EventDefinition; 23] = [
event_type: "invocation_completed",
required_correlations: &[],
required_payload: &[],
allowed_payload: &["command", "duration_ms", "exit_code"],
allowed_payload: &["command", "cost_status", "duration_ms", "exit_code"],
terminal: true,
},
EventDefinition {
Expand All @@ -662,11 +663,13 @@ pub(crate) const LIFECYCLE_EVENT_CATALOG_V2: [EventDefinition; 23] = [
required_payload: &[],
allowed_payload: &[
"command",
"cost_status",
"duration_ms",
"error_code",
"error_message",
"error_type",
"exit_code",
"signal_number",
],
terminal: true,
},
Expand Down Expand Up @@ -1010,6 +1013,7 @@ pub(crate) const LIFECYCLE_EVENT_CATALOG_V2: [EventDefinition; 23] = [
"intent",
"job_id",
"metadata",
"outcome",
"purpose",
"query_id",
"row_count",
Expand Down
1 change: 0 additions & 1 deletion crates/sqlbuild-core/src/progress/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ pub(crate) const QUERY_COMMAND: &str = "query";
pub(crate) const DBT_COMMAND: &str = "dbt";
pub(crate) const DBT_JSON_OPTION: &str = "--json";
pub(crate) const JSON_FORMAT: &str = "json";
pub(crate) const CSV_FORMAT: &str = "csv";
/// What replaces the cut tail of a transient status message.
pub(crate) const STATUS_ELLIPSIS: &str = "...";
/// The resource kind of audits and the prefix of their resource identifiers.
Expand Down
Loading
Loading