Skip to content

Commit 23a40af

Browse files
SNOW-3665226: fix FQN DDL cache test to assert unchanged instead of None (#2920)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0d6bc48 commit 23a40af

2 files changed

Lines changed: 87 additions & 24 deletions

File tree

test/integ/test_connection.py

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,20 +2696,14 @@ def test_fqn_ddl_does_not_pollute_schema_cache(conn_cnx, db_parameters):
26962696
schema = db_parameters["schema"].upper()
26972697
view_name = f'"{database}"."{schema}"."SNOW_3665226_REPRO_VIEW"'
26982698

2699-
# Open a session with NO database and NO schema so both caches start as None.
2700-
# The FQN object below lives in `database`.`schema`; without the guard the
2701-
# connector would overwrite the None caches with the object's namespace. We
2702-
# start _database as None too (not just _schema) so its "None is not
2703-
# overridden" path is actually exercised rather than a tautology.
2704-
with conn_cnx(database=None, schema=None) as cnx:
2699+
# Open a session with NO schema so the cache starts as None.
2700+
with conn_cnx(schema=None) as cnx:
27052701
with cnx.cursor() as cur:
2706-
# Sanity-check: cache and server agree (both empty) before the test.
2707-
assert cnx._database is None
2702+
# Sanity-check: cache and server agree before the test.
27082703
assert cnx._schema is None
2709-
server_db_before, server_schema_before = cur.execute(
2710-
"SELECT CURRENT_DATABASE(), CURRENT_SCHEMA()"
2711-
).fetchone()
2712-
assert server_db_before is None
2704+
db_before = cnx._database
2705+
assert db_before is not None # database was set at connect time
2706+
server_schema_before = cur.execute("SELECT CURRENT_SCHEMA()").fetchone()[0]
27132707
assert server_schema_before is None
27142708

27152709
# Touch a fully-qualified object — this must not change the session context.
@@ -2718,23 +2712,18 @@ def test_fqn_ddl_does_not_pollute_schema_cache(conn_cnx, db_parameters):
27182712
finally:
27192713
cur.execute(f"DROP VIEW IF EXISTS {view_name}")
27202714

2721-
# Caches must still be None — the session context never changed.
2722-
assert cnx._database is None, (
2723-
f"connector cache was polluted: _database={cnx._database!r}, "
2724-
"but no USE DATABASE was issued"
2725-
)
2715+
# Cache must still be None / unchanged — the session context never changed.
27262716
assert cnx._schema is None, (
27272717
f"connector cache was polluted: _schema={cnx._schema!r}, "
27282718
"but no USE SCHEMA was issued"
27292719
)
2720+
assert cnx._database == db_before, (
2721+
f"connector _database was polluted: got {cnx._database!r}, "
2722+
f"expected {db_before!r}"
2723+
)
27302724

2731-
# Server must also still return NULL for the current namespace.
2732-
server_db_after, server_schema_after = cur.execute(
2733-
"SELECT CURRENT_DATABASE(), CURRENT_SCHEMA()"
2734-
).fetchone()
2735-
assert (
2736-
server_db_after is None
2737-
), f"unexpected server database: {server_db_after!r}"
2725+
# Server must also still return NULL for CURRENT_SCHEMA().
2726+
server_schema_after = cur.execute("SELECT CURRENT_SCHEMA()").fetchone()[0]
27382727
assert (
27392728
server_schema_after is None
27402729
), f"unexpected server schema: {server_schema_after!r}"

test/unit/test_connection.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
import stat
77
import sys
8+
import uuid
89
from pathlib import Path
910
from secrets import token_urlsafe
1011
from textwrap import dedent
@@ -1021,3 +1022,76 @@ def test_server_session_keep_alive_false_calls_async_check(mock_post_requests):
10211022

10221023
# Verify delete_session WAS called (since async queries are finished and keep_alive=False)
10231024
delete_session_mock.assert_called_once()
1025+
1026+
1027+
def _run_cmd_query_with_final_names(
1028+
conn: SnowflakeConnection, sql: str, final_db: str | None, final_schema: str | None
1029+
) -> None:
1030+
"""Drive ``cmd_query`` with a mocked server response carrying
1031+
finalDatabaseName / finalSchemaName so the cache-update guard runs against a
1032+
controlled response. Caches are mutated in place per the guard logic."""
1033+
conn._rest.request = mock.MagicMock(
1034+
return_value={
1035+
"success": True,
1036+
"data": {
1037+
"finalDatabaseName": final_db,
1038+
"finalSchemaName": final_schema,
1039+
},
1040+
}
1041+
)
1042+
# _no_results=True skips query-context plumbing; the cache-update logic under
1043+
# test runs regardless of that flag.
1044+
conn.cmd_query(sql, 0, uuid.uuid4(), _no_results=True)
1045+
1046+
1047+
@pytest.mark.skipolddriver
1048+
def test_fqn_ddl_does_not_pollute_none_database_cache(mock_post_requests):
1049+
"""SNOW-3665226: when the session has no current database (e.g. the user has
1050+
no default namespace), a fully-qualified DDL's finalDatabaseName /
1051+
finalSchemaName reflect the *referenced* object, not the session context, and
1052+
must NOT overwrite the connector's ``None`` cache.
1053+
1054+
This is the mocked counterpart to the integ test: the integ test exercises
1055+
the schema side on real accounts (which have no default schema), while this
1056+
unit test locks in the database side, which can't be driven end-to-end on
1057+
accounts that carry a default database.
1058+
"""
1059+
conn = fake_connector()
1060+
try:
1061+
# Simulate a session with no current namespace at all.
1062+
conn._database = None
1063+
conn._schema = None
1064+
1065+
# A fully-qualified DDL returns the referenced object's namespace in
1066+
# final*Name — this must be ignored while the cache is None.
1067+
_run_cmd_query_with_final_names(
1068+
conn,
1069+
'CREATE OR REPLACE VIEW "OTHER_DB"."OTHER_SCHEMA"."V" AS SELECT 1',
1070+
final_db="OTHER_DB",
1071+
final_schema="OTHER_SCHEMA",
1072+
)
1073+
assert conn._database is None
1074+
assert conn._schema is None
1075+
1076+
# A genuine context switch (USE ...) must still update the None caches,
1077+
# otherwise the guard would wrongly suppress real changes.
1078+
_run_cmd_query_with_final_names(
1079+
conn,
1080+
"USE SCHEMA OTHER_DB.OTHER_SCHEMA",
1081+
final_db="OTHER_DB",
1082+
final_schema="OTHER_SCHEMA",
1083+
)
1084+
assert conn._database == "OTHER_DB"
1085+
assert conn._schema == "OTHER_SCHEMA"
1086+
1087+
# Once a context is set, subsequent final*Name values refresh the cache
1088+
# as before (pre-existing behaviour is unchanged).
1089+
_run_cmd_query_with_final_names(
1090+
conn,
1091+
"SELECT 1",
1092+
final_db="OTHER_DB",
1093+
final_schema="NEW_SCHEMA",
1094+
)
1095+
assert conn._schema == "NEW_SCHEMA"
1096+
finally:
1097+
conn.close()

0 commit comments

Comments
 (0)