///|
pub struct SQLCandidateCheckV1 {
priv record : Json
}
///|
pub fn sql_candidate_check_v1_json(result : SQLCandidateCheckV1) -> Json {
v4_json_copy(result.record)
}
///|
fn sql_input_candidate_v1(
config : SQLConfigV1,
source : String,
parameters : Json,
descriptor : Json,
bindings : Json,
) -> Json {
{
"format": "moontxnwitness-sql-binding-candidate/v1",
"descriptor": descriptor,
"bindings": bindings,
"configSha256": Json(config.digest),
"sqlSha256": Json(v3_dislock_sha256(source)),
"parametersSha256": Json(v3_dislock_sha256(sql_canonical_v1(parameters))),
}
}
///|
fn sql_check_work_v1(
config : SQLConfigV1,
source : String,
parameters : Json,
candidate : Json?,
options : SQLVerificationOptionsV1,
) -> SQLCandidateCheckV1 {
let work : SQLParseWorkV1 = {
config,
options,
tokens: [],
refs: [],
pos: 0,
checks: 0,
table: None,
table_alias: None,
resolution: "",
style: "",
stray_percent: false,
}
let mut descriptor : Json = Json::null()
let mut bindings : Json = Json::null()
let mut expected : Json = Json::null()
let mut reason = ""
let status = try {
if options.max_checks < 0 ||
options.max_checks > 1000000 ||
options.max_tokens < 0 ||
options.max_tokens > 32768 ||
options.max_sql_bytes < 0 ||
options.max_sql_bytes > 1048576 {
fail("invalid:caller-budget")
}
if !sql_well_formed_utf16_v1(source) {
fail("invalid:SQL-Unicode")
}
if orm_utf8_size(source) > options.max_sql_bytes ||
orm_utf8_size(source) > config.limits.at("max_sql_bytes") {
fail("budget:SQL-bytes")
}
sql_charge_v1(work)
sql_lex_v1(work, source)
descriptor = work.descriptor(source)
bindings = work.bindings(parameters)
expected = sql_input_candidate_v1(
config, source, parameters, descriptor, bindings,
)
match candidate {
None => "recognized"
Some(actual) =>
if sql_canonical_v1(actual) == sql_canonical_v1(expected) {
"matched"
} else {
reason = "candidate differs from independent grammar, configuration or original parameter binding"
"mismatch"
}
}
} catch {
error => {
reason = error.to_string()
if reason.contains("invalid:") {
"invalid"
} else if reason.contains("mismatch:") || reason.contains("parameter:") {
"mismatch"
} else {
"unknown"
}
}
}
{
record: {
"format": "moontxnwitness-sql-candidate-verification/v1",
"semanticContract": "moonbit-independent-configured-sql/v1",
"algorithm": "moonbit-configured-single-table-grammar-1",
"status": Json(status),
"valid": Json(status == "matched" || status == "recognized"),
"grammarVerified": Json(status == "matched" || status == "recognized"),
"candidateCompared": Json(candidate is Some(_)),
"captureFactsProduced": false,
"actualDriverResultsVerified": false,
"sourceWindowVerified": false,
"sourceAuthenticated": false,
"scope": "independent configured SQL grammar and supplied parameter-claim consistency only; not SQL execution, same-statement read-version provenance, complete fetch or isolation analysis",
"descriptor": descriptor,
"bindings": bindings,
"expectedCandidate": expected,
"gaps": if status == "matched" || status == "recognized" {
[]
} else {
[Json(reason)]
},
"execution": {
"counterContract": "configured-sql-lexer-parser-step-checks-1",
"checks": Json(work.checks),
"tokens": Json(
if !work.tokens.is_empty() &&
work.tokens[work.tokens.length() - 1].kind == "eof" {
work.tokens.length() - 1
} else {
work.tokens.length()
},
),
"limits": {
"maxChecks": Json(options.max_checks),
"maxTokens": Json(options.max_tokens),
"maxSqlBytes": Json(options.max_sql_bytes),
},
"astBudgetScope": "conservative two units per lexical token plus root accounting, not SQLGlot AST counter equivalence",
},
},
}
}
///|
pub fn normalize_sql_candidate_v1(
config : SQLConfigV1,
source : String,
parameters : Json,
options~ : SQLVerificationOptionsV1 = sql_verification_options_v1(),
) -> SQLCandidateCheckV1 {
sql_check_work_v1(config, source, parameters, None, options)
}
///|
pub fn verify_sql_candidate_v1(
config : SQLConfigV1,
source : String,
parameters : Json,
candidate : Json,
options~ : SQLVerificationOptionsV1 = sql_verification_options_v1(),
) -> SQLCandidateCheckV1 {
sql_check_work_v1(config, source, parameters, Some(candidate), options)
}
///|
fn sql_wire_options_v1(text : String) -> SQLVerificationOptionsV1 raise {
let fields = object_fields(
sql_json_v1(text, 8192),
"SQL verification options",
)
known_fields(
fields,
["maxChecks", "maxTokens", "maxSqlBytes"],
"SQL verification options",
)
fn integer(name : String, fallback : Int32, ceiling : Int32) -> Int32 raise {
match fields.get(name) {
None => fallback
Some(value) => {
let digits = sql_number_text_v1(value)
if !sql_digits_v1(digits) || digits.length() > 7 {
fail("invalid:caller-budget")
}
let mut number = 0
for c in digits.to_array() {
number = number * 10 + c.to_int() - 48
}
if number > ceiling {
fail("invalid:caller-budget")
}
number
}
}
}
sql_verification_options_v1(
max_checks=integer("maxChecks", 1000000, 1000000),
max_tokens=integer("maxTokens", 8192, 32768),
max_sql_bytes=integer("maxSqlBytes", 65536, 1048576),
)
}
///|
fn sql_wire_error_v1(reason : String) -> String {
(
{
"format": "moontxnwitness-sql-candidate-verification/v1",
"status": "invalid",
"valid": false,
"captureFactsProduced": false,
"sourceAuthenticated": false,
"gaps": [Json(reason)],
} : Json).stringify(indent=2)
}
///|
pub fn normalize_configured_sql_v1_json(
config_text : String,
source : String,
parameters_text : String,
options_text : String,
) -> String {
try {
let config = match parse_sql_config_v1(config_text) {
Ok(config) => config
Err(reason) => fail(reason)
}
let options = sql_wire_options_v1(options_text)
sql_candidate_check_v1_json(
normalize_sql_candidate_v1(
config,
source,
sql_json_v1(parameters_text, 1048576),
options~,
),
).stringify(indent=2)
} catch {
error => sql_wire_error_v1(error.to_string())
}
}
///|
pub fn verify_configured_sql_v1_json(
config_text : String,
source : String,
parameters_text : String,
candidate_text : String,
options_text : String,
) -> String {
try {
let config = match parse_sql_config_v1(config_text) {
Ok(config) => config
Err(reason) => fail(reason)
}
let options = sql_wire_options_v1(options_text)
sql_candidate_check_v1_json(
verify_sql_candidate_v1(
config,
source,
sql_json_v1(parameters_text, 1048576),
sql_json_v1(candidate_text, 8388608),
options~,
),
).stringify(indent=2)
} catch {
error => sql_wire_error_v1(error.to_string())
}
}
///|
/// Comparison helper only. PostgreSQL encoding/collation/catalog proof is external.
pub fn compare_configured_c_text_v1(a : String, b : String) -> Int32 {
relation_v3_text_compare(a, b)
}
///|
pub fn normalize_sql_config_v1_json(text : String) -> String {
match parse_sql_config_v1(text) {
Err(reason) => sql_wire_error_v1(reason)
Ok(config) =>
(
{
"valid": true,
"config": config.record,
"canonicalText": Json(sql_canonical_v1(config.record)),
"configSha256": Json(config.digest),
} : Json).stringify()
}
}