1919import gzip
2020import pathlib
2121import shutil
22+ from concurrent .futures import ThreadPoolExecutor
2223
2324import pyarrow as pa
2425import pyarrow .dataset as ds
@@ -41,6 +42,91 @@ def test_create_context_no_args():
4142 SessionContext ()
4243
4344
45+ @pytest .mark .parametrize ("discard_returned" , [False , True ])
46+ def test_enable_url_table_shares_session (tmp_path , discard_returned ):
47+ """URL tables, configuration, and registrations belong to all aliases."""
48+ ctx = SessionContext ()
49+ original_config = (
50+ ctx .sql ("SHOW datafusion.catalog.create_default_catalog_and_schema" )
51+ .collect ()[0 ]
52+ .column (1 )
53+ .to_pylist ()
54+ )
55+ alias = ctx .with_python_udf_inlining (enabled = False )
56+ session_id = ctx .session_id ()
57+ ctx .sql ("CREATE SCHEMA existing" ).collect ()
58+ ctx .register_record_batches ("existing.numbers" , [[pa .record_batch ({"n" : [7 ]})]])
59+ ctx .register_udf (
60+ udf (lambda x : x , [pa .int64 ()], pa .int64 (), "immutable" , "identity" )
61+ )
62+ path = tmp_path / "numbers.csv"
63+ path .write_text ("n\n 7\n " )
64+
65+ if discard_returned :
66+ alias .enable_url_table ()
67+ returned = ctx
68+ else :
69+ returned = alias .enable_url_table ()
70+
71+ # Both directions must see subsequent SETs, not only the initial snapshot.
72+ for writer , reader , value in [(ctx , returned , 111 ), (returned , alias , 222 )]:
73+ writer .sql (f"SET datafusion.execution.batch_size = { value } " ).collect ()
74+ assert reader .sql ("SHOW datafusion.execution.batch_size" ).collect ()[0 ].column (
75+ 1
76+ ).to_pylist () == [str (value )]
77+
78+ returned .register_udf (
79+ udf (lambda x : x , [pa .int64 ()], pa .int64 (), "immutable" , "later_identity" )
80+ )
81+ for handle in (ctx , alias , returned ):
82+ assert handle .session_id () == session_id
83+ assert (
84+ handle .sql ("SHOW datafusion.catalog.create_default_catalog_and_schema" )
85+ .collect ()[0 ]
86+ .column (1 )
87+ .to_pylist ()
88+ == original_config
89+ )
90+ assert handle .sql ("SELECT identity(n) FROM existing.numbers" ).collect ()[
91+ 0
92+ ].column (0 ).to_pylist () == [7 ]
93+ assert handle .sql ("SELECT later_identity(9)" ).collect ()[0 ].column (
94+ 0
95+ ).to_pylist () == [9 ]
96+ assert handle .sql (f'SELECT n FROM "{ path } "' ).collect ()[0 ].column (
97+ 0
98+ ).to_pylist () == [7 ]
99+ handle .enable_url_table ()
100+ handle .enable_url_table ()
101+ assert handle .sql (f'SELECT n FROM "{ path } "' ).collect ()[0 ].column (
102+ 0
103+ ).to_pylist () == [7 ]
104+
105+
106+ def test_enable_url_table_from_multiple_aliases (tmp_path ):
107+ """Enabling through multiple handles preserves concurrent registrations."""
108+ ctx = SessionContext ()
109+ path = tmp_path / "numbers.csv"
110+ path .write_text ("n\n 7\n " )
111+ aliases = [ctx .with_python_udf_inlining (enabled = False ) for _ in range (4 )]
112+
113+ def enable_and_register (index ):
114+ alias = aliases [index ]
115+ for _ in range (4 ):
116+ alias .enable_url_table ()
117+ alias .sql (f'SELECT n FROM "{ path } "' ).collect ()
118+ alias .register_udf (
119+ udf (lambda x : x , [pa .int64 ()], pa .int64 (), "immutable" , f"identity_{ index } " )
120+ )
121+
122+ with ThreadPoolExecutor (max_workers = 4 ) as executor :
123+ list (executor .map (enable_and_register , range (4 )))
124+ for index in range (4 ):
125+ assert ctx .sql (f"SELECT identity_{ index } (7)" ).collect ()[0 ].column (
126+ 0
127+ ).to_pylist () == [7 ]
128+
129+
44130def test_create_context_session_config_only ():
45131 SessionContext (config = SessionConfig ())
46132
0 commit comments