Skip to content

Commit abea032

Browse files
committed
add tests
1 parent 7469b15 commit abea032

1 file changed

Lines changed: 144 additions & 4 deletions

File tree

tests/integ/test_reduce_describe_query.py

Lines changed: 144 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
# Copyright (c) 2012-2025 Snowflake Computing Inc. All rights reserved.
33
#
44

5-
from typing import List
5+
from typing import Dict, List
66
import copy
77
import pytest
88

99
import snowflake.snowpark._internal.analyzer.snowflake_plan as snowflake_plan
1010

1111
from unittest.mock import patch
1212

13-
from snowflake.snowpark import DataFrame
13+
from snowflake.snowpark import DataFrame, Row
1414
from snowflake.snowpark._internal.analyzer.expression import Attribute, Star
1515
from snowflake.snowpark._internal.analyzer.unary_expression import UnresolvedAlias
1616
from snowflake.snowpark._internal.analyzer.unary_plan_node import Project
@@ -34,9 +34,9 @@
3434
_PYTHON_SNOWPARK_REDUCE_DESCRIBE_QUERY_ENABLED,
3535
Session,
3636
)
37-
from snowflake.snowpark.types import LongType, StructField, StructType
37+
from snowflake.snowpark.types import LongType, StringType, StructField, StructType
3838
from tests.integ.utils.sql_counter import SqlCounter
39-
from tests.utils import IS_IN_STORED_PROC, TestData
39+
from tests.utils import IS_IN_STORED_PROC, TestData, Utils
4040

4141
pytestmark = [
4242
pytest.mark.skipif(
@@ -228,6 +228,10 @@ def check_attributes_equality(attrs1: List[Attribute], attrs2: List[Attribute])
228228
assert attr1.nullable == attr2.nullable
229229

230230

231+
def _attrs_by_name(parent_attributes: List[Attribute]) -> Dict[str, Attribute]:
232+
return {attr.name: attr for attr in parent_attributes}
233+
234+
231235
def has_star_in_projection(df: DataFrame) -> bool:
232236
plan = df._plan.source_plan
233237
return isinstance(plan, Project) and any(
@@ -441,6 +445,142 @@ def test_project_alias_infers_attributes_from_parent_metadata(session):
441445
check_attributes_equality(df2._plan.attributes, expected_attributes)
442446

443447

448+
def test_swap_column_aliases_infers_types_from_source_names(session):
449+
"""n-1: columns a, b; n: b AS a, a AS b — metadata follows source column types."""
450+
df = session.create_dataframe([[1, 2]], schema=["a", "b"])
451+
_ = df.schema
452+
453+
parent_attributes = df._plan._metadata.attributes
454+
assert parent_attributes is not None
455+
by_name = _attrs_by_name(parent_attributes)
456+
expected_attributes = [
457+
by_name['"B"'].with_name("a"),
458+
by_name['"A"'].with_name("b"),
459+
]
460+
461+
df2 = df.select(col("b").alias("a"), col("a").alias("b"))
462+
Utils.check_answer(df2, [Row(A=2, B=1)], sort=False)
463+
464+
if session.reduce_describe_query_enabled:
465+
check_attributes_equality(df2._plan._metadata.attributes, expected_attributes)
466+
expected_describe_count = 0
467+
else:
468+
assert df2._plan._metadata.attributes is None
469+
expected_describe_count = 1
470+
471+
with SqlCounter(query_count=0, describe_count=expected_describe_count):
472+
check_attributes_equality(df2._plan.attributes, expected_attributes)
473+
474+
475+
def test_swap_mixed_column_types_inference_follows_source(session):
476+
"""Swapped output names must take datatypes from the referenced source column."""
477+
schema = StructType(
478+
[
479+
StructField("a", LongType(), nullable=True),
480+
StructField("b", StringType(), nullable=True),
481+
]
482+
)
483+
df = session.create_dataframe([(1, "x")], schema=schema)
484+
_ = df.schema
485+
486+
parent_attributes = df._plan._metadata.attributes
487+
assert parent_attributes is not None
488+
by_name = _attrs_by_name(parent_attributes)
489+
expected_attributes = [
490+
by_name['"B"'].with_name("a"),
491+
by_name['"A"'].with_name("b"),
492+
]
493+
494+
df2 = df.select(col("b").alias("a"), col("a").alias("b"))
495+
Utils.check_answer(df2, [Row(A="x", B=1)], sort=False)
496+
497+
if session.reduce_describe_query_enabled:
498+
check_attributes_equality(df2._plan._metadata.attributes, expected_attributes)
499+
expected_describe_count = 0
500+
else:
501+
assert df2._plan._metadata.attributes is None
502+
expected_describe_count = 1
503+
504+
with SqlCounter(query_count=0, describe_count=expected_describe_count):
505+
check_attributes_equality(df2._plan.attributes, expected_attributes)
506+
507+
508+
def test_column_permutation_inference_name_keyed_lookup(session):
509+
"""Non-swap rename: c->a, a->x, b->y — each output type matches its source column."""
510+
df = session.create_dataframe([[1, 2, 3]], schema=["a", "b", "c"])
511+
_ = df.schema
512+
513+
parent_attributes = df._plan._metadata.attributes
514+
assert parent_attributes is not None
515+
by_name = _attrs_by_name(parent_attributes)
516+
expected_attributes = [
517+
by_name['"C"'].with_name("a"),
518+
by_name['"A"'].with_name("x"),
519+
by_name['"B"'].with_name("y"),
520+
]
521+
522+
df2 = df.select(col("c").alias("a"), col("a").alias("x"), col("b").alias("y"))
523+
Utils.check_answer(df2, [Row(A=3, X=1, Y=2)], sort=False)
524+
525+
if session.reduce_describe_query_enabled:
526+
check_attributes_equality(df2._plan._metadata.attributes, expected_attributes)
527+
expected_describe_count = 0
528+
else:
529+
assert df2._plan._metadata.attributes is None
530+
expected_describe_count = 1
531+
532+
with SqlCounter(query_count=0, describe_count=expected_describe_count):
533+
check_attributes_equality(df2._plan.attributes, expected_attributes)
534+
535+
536+
def test_chained_simple_renames_infer_from_previous_metadata(session):
537+
"""Second select's parent already has inferred attributes from the first rename."""
538+
df = session.create_dataframe([[10, 20]], schema=["a", "b"])
539+
_ = df.schema
540+
541+
df1 = df.select(col("a").alias("p"), col("b").alias("q"))
542+
if session.reduce_describe_query_enabled:
543+
assert df1._plan._metadata.attributes is not None
544+
mid_attrs = df1._plan._metadata.attributes
545+
assert mid_attrs is not None or not session.reduce_describe_query_enabled
546+
547+
df2 = df1.select(col("p").alias("x"), col("q").alias("y"))
548+
_ = df1.schema
549+
550+
if session.reduce_describe_query_enabled:
551+
assert df2._plan._metadata.attributes is not None
552+
by_mid = _attrs_by_name(df1._plan._metadata.attributes or [])
553+
expected_attributes = [
554+
by_mid['"P"'].with_name("x"),
555+
by_mid['"Q"'].with_name("y"),
556+
]
557+
check_attributes_equality(df2._plan._metadata.attributes, expected_attributes)
558+
with SqlCounter(query_count=0, describe_count=0):
559+
check_attributes_equality(df2._plan.attributes, expected_attributes)
560+
else:
561+
assert df2._plan._metadata.attributes is None
562+
with SqlCounter(query_count=0, describe_count=1):
563+
_ = df2._plan.attributes
564+
565+
566+
def test_non_simple_projection_skips_metadata_inference(session):
567+
"""Expressions other than plain column or simple alias(column) do not infer attributes."""
568+
df = session.create_dataframe([[1, 2]], schema=["a", "b"])
569+
_ = df.schema
570+
571+
df2 = df.select((col("a") + lit(1)).alias("ap1"), "b")
572+
573+
assert df2._plan._metadata.attributes is None
574+
575+
with SqlCounter(query_count=0, describe_count=1):
576+
_ = df2._plan.attributes
577+
578+
df3 = df.select(col("a"), (col("b") + lit(1)).alias("b"))
579+
assert df3._plan._metadata.attributes is None
580+
with SqlCounter(query_count=0, describe_count=1):
581+
_ = df3._plan.attributes
582+
583+
444584
@pytest.mark.skipif(IS_IN_STORED_PROC, reason="Can't create a session in SP")
445585
def test_reduce_describe_query_enabled_on_session(db_parameters):
446586
with Session.builder.configs(db_parameters).create() as new_session:

0 commit comments

Comments
 (0)