uqa-sql 0.4.0

PostgreSQL-compatible SQL compiler built on libpg_query
Documentation
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

use super::*;

#[test]
fn generated_constraint_names_clip_utf8_components_with_the_collision_suffix() {
    for (table_letter, column_letter, length, plain, collision) in [
        (
            "t",
            "c",
            60,
            [(27, 26), (28, 28), (29, 29)],
            [(26, 26), (28, 27), (29, 28)],
        ),
        (
            "한",
            "글",
            20,
            [(9, 8), (9, 9), (9, 9)],
            [(8, 8), (9, 9), (9, 9)],
        ),
    ] {
        let table_name = table_letter.repeat(length);
        let column_name = column_letter.repeat(length);
        let expected = |parts: [(usize, usize); 3], suffix: &str| {
            parts
                .into_iter()
                .zip(["not_null", "check", "key"])
                .map(|((table, column), label)| {
                    format!(
                        "{}_{}_{label}{suffix}",
                        table_letter.repeat(table),
                        column_letter.repeat(column)
                    )
                })
                .collect::<Vec<_>>()
        };
        let plain_names = expected(plain, "");
        for occupied in [false, true] {
            let crate::Statement::CreateTable(table) = crate::compile(&format!(
                "CREATE TABLE \"{table_name}\"(\"{column_name}\" int NOT NULL CHECK(\"{column_name}\">0) UNIQUE)"
            )).unwrap().remove(0) else { panic!("table declaration") };
            let mut columns = table.columns;
            let mut constraints = TableConstraintSet {
                checks: table.checks,
                key_constraints: table.key_constraints,
                ..Default::default()
            };
            let mut next = 0_u8;
            crate::schema::constraint_metadata::materialize_constraint_metadata_with_names(
                &uqa_core::RelationIdentity::new("public", &table_name),
                &mut columns,
                &mut constraints,
                &mut |_: &str| {
                    next += 1;
                    Ok([next; 16])
                },
                &crate::schema::constraint_metadata::ConstraintNameScope {
                    schema: if occupied {
                        plain_names.iter().cloned().collect()
                    } else {
                        std::collections::BTreeSet::new()
                    },
                    ..Default::default()
                },
            )
            .unwrap();
            let names = ConstraintNames::from_definition(&columns, &constraints)
                .entries()
                .map(|entry| entry.name.to_owned())
                .collect::<Vec<_>>();
            assert_eq!(
                names,
                if occupied {
                    expected(collision, "1")
                } else {
                    plain_names.clone()
                }
            );
            assert!(names.iter().all(|name| name.len() <= 63));
        }
    }
}

#[test]
fn names_retain_each_catalog_row_identity_and_location() {
    let crate::Statement::CreateTable(table) = crate::compile("CREATE TABLE t(a int CONSTRAINT nn NOT NULL CONSTRAINT cc CHECK(a>0) CONSTRAINT cf REFERENCES parent(v), b int, CONSTRAINT tc CHECK(b>0), CONSTRAINT tf FOREIGN KEY(b) REFERENCES parent(v), CONSTRAINT uk UNIQUE(a))").unwrap().remove(0) else { panic!("table declaration") };
    let mut columns = table.columns;
    let mut constraints = TableConstraintSet {
        checks: table.checks,
        foreign_keys: table.foreign_keys,
        key_constraints: table.key_constraints,
        ..Default::default()
    };
    let mut next = 0_u8;
    crate::schema::constraint_metadata::materialize_constraint_metadata(
        &uqa_core::RelationIdentity::new("public", "t"),
        &mut columns,
        &mut constraints,
        &mut |_: &str| {
            next += 1;
            Ok([next; 16])
        },
    )
    .unwrap();
    let entries = ConstraintNames::from_definition(&columns, &constraints)
        .entries()
        .map(|entry| (entry.name, entry.object_id, entry.location))
        .collect::<Vec<_>>();
    let reference = columns[0].references.as_ref().unwrap();
    assert_eq!(
        entries,
        vec![
            (
                "nn",
                columns[0].not_null_identity.map(|id| id.object_id),
                ConstraintLocation::NotNull(0)
            ),
            (
                "cc",
                columns[0].check_object_id,
                ConstraintLocation::ColumnCheck(0)
            ),
            (
                "cf",
                reference.catalog_identity.map(|id| id.object_id),
                ConstraintLocation::ColumnForeignKey(0)
            ),
            (
                "tc",
                constraints.checks[0].object_id,
                ConstraintLocation::TableCheck(0)
            ),
            (
                "tf",
                constraints.foreign_keys[0]
                    .catalog_identity
                    .map(|id| id.object_id),
                ConstraintLocation::TableForeignKey(0)
            ),
            (
                "uk",
                constraints.key_constraints[0]
                    .catalog_identity
                    .map(|id| id.object_id),
                ConstraintLocation::Key(0)
            ),
        ]
    );
    assert!(entries.iter().all(|entry| entry.1.is_some()));
    assert_ne!(entries[2].1, reference.object_id);
    assert_ne!(entries[4].1, constraints.foreign_keys[0].object_id);
    let identities = entries
        .iter()
        .map(|entry| entry.1)
        .collect::<std::collections::BTreeSet<_>>();
    assert_eq!(identities.len(), entries.len());
}

#[test]
fn metadata_names_share_the_event_constraint_namespace() {
    for explicit in [false, true] {
        let sql = if explicit {
            "CREATE TABLE t(a int CONSTRAINT t_a_check CHECK(a>0))"
        } else {
            "CREATE TABLE t(a int NOT NULL CHECK(a>0) UNIQUE)"
        };
        let crate::Statement::CreateTable(table) = crate::compile(sql).unwrap().remove(0) else {
            panic!("table declaration")
        };
        let mut columns = table.columns;
        let mut constraints = TableConstraintSet {
            key_constraints: table.key_constraints,
            ..Default::default()
        };
        let occupied = ["t_a_not_null", "t_a_check", "t_a_key"]
            .map(str::to_owned)
            .into();
        let mut next = 0_u8;
        let result = crate::schema::constraint_metadata::materialize_constraint_metadata_with_names(
            &uqa_core::RelationIdentity::new("public", "t"),
            &mut columns,
            &mut constraints,
            &mut |_: &str| {
                next += 1;
                Ok([next; 16])
            },
            &crate::schema::constraint_metadata::ConstraintNameScope {
                events: occupied,
                ..Default::default()
            },
        );
        if explicit {
            let crate::schema::constraint_metadata::ConstraintMetadataError::Execution(error) =
                result.unwrap_err()
            else {
                panic!("expected SQL collision")
            };
            assert_eq!(error.sqlstate(), Some("42710"));
        } else {
            result.unwrap();
            let names = ConstraintNames::from_definition(&columns, &constraints)
                .entries()
                .map(|entry| entry.name)
                .collect::<Vec<_>>();
            assert_eq!(names, ["t_a_not_null1", "t_a_check1", "t_a_key1"]);
        }
    }
}

#[test]
fn schema_constraint_names_exclude_only_automatic_declarations() {
    for explicit in [false, true] {
        let statement = if explicit {
            "CREATE TABLE t(a int CONSTRAINT t_a_check CHECK(a>0))"
        } else {
            "CREATE TABLE t(a int NOT NULL CHECK(a>0) UNIQUE REFERENCES parent(v))"
        };
        let crate::Statement::CreateTable(table) = crate::compile(statement).unwrap().remove(0)
        else {
            panic!("table declaration")
        };
        let mut columns = table.columns;
        let mut constraints = TableConstraintSet {
            key_constraints: table.key_constraints,
            ..Default::default()
        };
        let names = crate::schema::constraint_metadata::ConstraintNameScope {
            schema: ["t_a_check", "t_a_fkey", "t_a_key", "t_a_not_null"]
                .map(str::to_owned)
                .into(),
            ..Default::default()
        };
        let mut next = 0_u8;
        crate::schema::constraint_metadata::materialize_constraint_metadata_with_names(
            &uqa_core::RelationIdentity::new("public", "t"),
            &mut columns,
            &mut constraints,
            &mut |_: &str| {
                next += 1;
                Ok([next; 16])
            },
            &names,
        )
        .unwrap();
        let mut actual = ConstraintNames::from_definition(&columns, &constraints)
            .entries()
            .map(|entry| entry.name)
            .collect::<Vec<_>>();
        actual.sort_unstable();
        assert_eq!(
            actual,
            if explicit {
                vec!["t_a_check"]
            } else {
                vec!["t_a_check1", "t_a_fkey1", "t_a_key1", "t_a_not_null1"]
            }
        );
    }
}