@@ -804,6 +804,48 @@ def mock_make_api_call(self, operation_name, kwarg):
804804 assert describe_table_calls == 1
805805
806806
807+ def test_create_iceberg_table_escapes_single_quotes_in_columns_comments () -> None :
808+ # Single quotes in caller-supplied columns_comments / additional_table_properties
809+ # values must be doubled so they cannot terminate the surrounding 'literal' and
810+ # change the structure of the generated DDL.
811+ from awswrangler .athena import _write_iceberg
812+
813+ captured : list [str ] = []
814+
815+ def fake_start (* , sql : str , ** _ ) -> str :
816+ captured .append (sql )
817+ return "qid"
818+
819+ df = pd .DataFrame ({"id" : pd .Series (dtype = "int64" ), "user_name" : pd .Series (dtype = "string" )})
820+ wg_config = mock .MagicMock ()
821+ wg_config .enforce_workgroup_location = False
822+
823+ with mock .patch .object (_write_iceberg , "_start_query_execution" , side_effect = fake_start ), mock .patch .object (
824+ _write_iceberg , "wait_query"
825+ ):
826+ _write_iceberg ._create_iceberg_table (
827+ df = df ,
828+ database = "db" ,
829+ table = "t" ,
830+ path = "s3://intended/output/" ,
831+ wg_config = wg_config ,
832+ partition_cols = None ,
833+ additional_table_properties = {"prop" : "val') LOCATION 's3://other/' --" },
834+ index = False ,
835+ boto3_session = mock .MagicMock (),
836+ columns_comments = {"user_name" : "') LOCATION 's3://other/' TBLPROPERTIES ('x'='y" },
837+ )
838+
839+ sql = captured [0 ]
840+ # Quotes were doubled in both splices, so unescaped caller content stays inside the
841+ # COMMENT / TBLPROPERTIES string literals and does not open a new DDL clause.
842+ assert "COMMENT ''') LOCATION ''s3://other/'' TBLPROPERTIES (''x''=''y'" in sql
843+ assert "'prop'='val'') LOCATION ''s3://other/'' --'" in sql
844+ # The intended LOCATION (un-doubled quotes) is the only top-level clause.
845+ assert "LOCATION 's3://intended/output/'" in sql
846+ assert "LOCATION 's3://other/'" not in sql
847+
848+
807849def test_csv_pandas_mode_append (moto_s3_client : "S3Client" ) -> None :
808850 path = "s3://bucket/test_append.csv"
809851 df1 = pd .DataFrame ({"col" : [1 , 2 , 3 ]})
0 commit comments