import pytest from gti_discovery.read_only import validate_read_only def test_accepts_and_normalizes_select_query(): assert validate_read_only(" SELECT * FROM RDB$DATABASE ; ") == ( "SELECT * FROM RDB$DATABASE" ) def test_accepts_with_query(): sql = "WITH x AS (SELECT 1 AS n FROM RDB$DATABASE) SELECT n FROM x" assert validate_read_only(sql) == sql @pytest.mark.parametrize( "sql", [ "INSERT INTO X VALUES (1)", "UPDATE X SET A=1", "DELETE FROM X", "CREATE TABLE X(A INT)", "ALTER TABLE X ADD A INT", "DROP TABLE X", "TRUNCATE TABLE X", "EXECUTE PROCEDURE P", "WITH x AS (SELECT 1 FROM RDB$DATABASE) INSERT INTO X VALUES (1)", ], ) def test_rejects_write_or_side_effect_sql(sql): with pytest.raises(ValueError): validate_read_only(sql) @pytest.mark.parametrize( "sql", [ "SELECT 1 FROM RDB$DATABASE; SELECT 2 FROM RDB$DATABASE", "SELECT 1 FROM RDB$DATABASE -- comment", "SELECT 1 /* comment */ FROM RDB$DATABASE", "", "VACUUM TABLE X", ], ) def test_rejects_multiple_statements_comments_and_non_queries(sql): with pytest.raises(ValueError): validate_read_only(sql) @pytest.mark.parametrize( "sql", [ "SELECT * FROM SIDE_EFFECTING_PROC(1)", "SELECT GEN_ID(MY_GENERATOR, 1) FROM RDB$DATABASE", "SELECT NEXT VALUE FOR MY_GENERATOR FROM RDB$DATABASE", "SELECT * FROM X WITH LOCK", "SELECT * FROM X FOR UPDATE", "SELECT * FROM PKG . SELECTABLE_PROC (1)", ], ) def test_rejects_known_side_effecting_select_constructs(sql): with pytest.raises(ValueError): validate_read_only(sql) @pytest.mark.parametrize( "sql", [ "SELECT IIF(RDB$SYSTEM_FLAG = 1, 'SYSTEM', 'USER') FROM RDB$RELATIONS", "SELECT EXTRACT(YEAR FROM CURRENT_DATE) FROM RDB$DATABASE", "SELECT COALESCE(RDB$RELATION_NAME, '') FROM RDB$RELATIONS", ], ) def test_accepts_legitimate_firebird_read_only_expressions(sql): assert validate_read_only(sql) == sql @pytest.mark.parametrize( "sql", [ "SELECT EXECUTE(1) FROM RDB$DATABASE", "SELECT GEN_ID(MY_GENERATOR, 1) FROM RDB$DATABASE", "SELECT * FROM SELECTABLE_PROC(1)", "SELECT * FROM PKG . SELECTABLE_PROC (1)", "SELECT * FROM PKG\t.\tSELECTABLE_PROC\t(1)", ], ) def test_rejects_explicit_unsafe_callable_forms(sql): with pytest.raises(ValueError): validate_read_only(sql) @pytest.mark.parametrize( "sql", [ "SELECT SIDE_EFFECTING_UDF(ID) FROM X", "SELECT PKG . SIDE_EFFECTING_UDF(ID) FROM X", ], ) def test_rejects_unknown_callable_in_select_projection(sql): with pytest.raises(ValueError, match="callable"): validate_read_only(sql) def test_normalization_does_not_change_quoted_literal_contents(): sql = " SELECT ' keep spaces ; -- text ' FROM RDB$DATABASE ; " assert validate_read_only(sql) == ( "SELECT ' keep spaces ; -- text ' FROM RDB$DATABASE" )