From 2c4e146a6cbb3d018e384189c91a4c0325ceb617 Mon Sep 17 00:00:00 2001 From: Dhruv Gupta Date: Fri, 23 Oct 2026 06:39:55 -0400 Subject: [PATCH] fix(audits): accept RFC 9562 UUID versions 6-8 in valid_uuid The valid_uuid audit only allowed version digits 1-5, so well-formed UUIDv6, UUIDv7 and UUIDv8 values (RFC 9562) were reported as invalid. Widen the version digit to 1-8 and update the documented pattern. Signed-off-by: Dhruv Gupta --- docs/concepts/audits.md | 2 +- sqlmesh/core/audit/builtin.py | 2 +- tests/core/test_audit.py | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/docs/concepts/audits.md b/docs/concepts/audits.md index c7c7cbd190..18368cd103 100644 --- a/docs/concepts/audits.md +++ b/docs/concepts/audits.md @@ -477,7 +477,7 @@ MODEL ( #### valid_uuid, valid_uuid_non_blocking Ensures that all non-NULL rows of a column contain a string with the UUID structure. -UUID structure determined by matching regular expression `'^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'`. +UUID structure determined by matching regular expression `'^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'`. This example asserts that all `uuid` values have the UUID structure: diff --git a/sqlmesh/core/audit/builtin.py b/sqlmesh/core/audit/builtin.py index b4eaab8d50..35453028fb 100644 --- a/sqlmesh/core/audit/builtin.py +++ b/sqlmesh/core/audit/builtin.py @@ -297,7 +297,7 @@ def create_non_blocking_copy(audit: Audit) -> Audit: SELECT * FROM @this_model WHERE @AND( - NOT REGEXP_LIKE(LOWER(@column), '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'), + NOT REGEXP_LIKE(LOWER(@column), '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'), @condition, ) diff --git a/tests/core/test_audit.py b/tests/core/test_audit.py index b5226563b5..1b043b79fa 100644 --- a/tests/core/test_audit.py +++ b/tests/core/test_audit.py @@ -852,6 +852,44 @@ def test_not_constant_audit(model: Model): ) +def test_valid_uuid_audit(model: Model): + import duckdb + + rendered_query = model.render_audit_query(builtin.valid_uuid_audit, column=exp.column("a")) + assert ( + rendered_query.sql() + == """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_0" WHERE NOT REGEXP_LIKE(LOWER("a"), '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$') AND TRUE""" + ) + + valid = [ + "6ba7b810-9dad-11d1-80b4-00c04fd430c8", # v1 + "000003e8-2363-21ef-b200-325096b39f47", # v2 + "5df41881-3aed-3515-88a7-2f4a814cf09e", # v3 + "919108F7-52D1-4320-9BAC-F847DB4148A8", # v4, upper case + "2ed6657d-e927-568b-95e1-2665a8aea6a2", # v5 + "1ec9414c-232a-6b00-b3c8-9f6bdeced846", # v6 + "017f22e2-79b0-7cc3-98c4-dc0c0c07398f", # v7 + "2489e9ad-2ee2-8e00-8ec9-32d5f69181c0", # v8 + ] + invalid = [ + "017f22e2-79b0-9cc3-98c4-dc0c0c07398f", # unknown version + "017f22e2-79b0-7cc3-c8c4-dc0c0c07398f", # non-RFC variant + "017f22e279b07cc398c4dc0c0c07398f", # no hyphens + "not-a-uuid", + ] + + connection = duckdb.connect() + connection.execute("CREATE SCHEMA db") + connection.execute("CREATE TABLE db.test_model (a TEXT, b INT, ds TEXT)") + connection.executemany( + "INSERT INTO db.test_model VALUES (?, 1, '1970-01-01')", + [[value] for value in valid + invalid + [None]], + ) + + failing_rows = connection.execute(rendered_query.sql(dialect="duckdb")).fetchall() + assert sorted(row[0] for row in failing_rows) == sorted(invalid) + + def test_condition_with_macro_var(model: Model): rendered_query = model.render_audit_query( builtin.not_null_audit,