Skip to content

Commit 2421da8

Browse files
authored
docs: explain S3 object-store configuration for SQL (#1716)
* docs: explain S3 object-store configuration for SQL Signed-off-by: Yifan Chen <emecii23@gmail.com> * docs: demonstrate SQL reads through registered object stores * docs: simplify S3 SQL example wording --------- Signed-off-by: Yifan Chen <emecii23@gmail.com>
1 parent 1d629a9 commit 2421da8

2 files changed

Lines changed: 49 additions & 4 deletions

File tree

docs/source/user-guide/data-sources.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ ctx.create_dataframe([[batch]]).show()
8989
## Object Store
9090

9191
DataFusion has support for multiple storage options in addition to local files.
92-
The example below requires an appropriate S3 account with access credentials.
92+
The example below requires access to the S3 bucket. Set `AWS_ACCESS_KEY_ID` and
93+
`AWS_SECRET_ACCESS_KEY` in the environment before running it. For temporary
94+
credentials, also set `AWS_SESSION_TOKEN`.
9395

9496
Supported Object Stores are
9597

@@ -100,16 +102,17 @@ Supported Object Stores are
100102
- {py:class}`~datafusion.object_store.MicrosoftAzure`
101103

102104
```python
105+
from datafusion import SessionContext
103106
from datafusion.object_store import AmazonS3
104107

105108
region = "us-east-1"
106109
bucket_name = "yellow-trips"
107110

111+
ctx = SessionContext()
112+
108113
s3 = AmazonS3(
109114
bucket_name=bucket_name,
110115
region=region,
111-
access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
112-
secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
113116
)
114117

115118
path = f"s3://{bucket_name}/"
@@ -120,6 +123,27 @@ ctx.register_parquet("trips", path)
120123
ctx.table("trips").show()
121124
```
122125

126+
### Query S3 data with SQL
127+
128+
To reach the same data from SQL, give the S3 path a table name with
129+
`CREATE EXTERNAL TABLE`. The registered object store carries the credentials and
130+
the region, so the statement itself needs only the location.
131+
132+
Register the store for the bucket as shown above, then use that same
133+
{py:class}`~datafusion.context.SessionContext` to create and query the table:
134+
135+
```python
136+
ctx.sql(
137+
f"""
138+
CREATE EXTERNAL TABLE trips_sql
139+
STORED AS PARQUET
140+
LOCATION '{path}'
141+
"""
142+
).collect()
143+
144+
ctx.sql("SELECT count(passenger_count) FROM trips_sql").show()
145+
```
146+
123147
## Other DataFrame Libraries
124148

125149
DataFusion can import DataFrames directly from other libraries, such as

python/tests/test_object_store_param.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18-
"""Tests for the object_store parameter on register/read file methods."""
18+
"""Tests for object store registration and register/read file methods."""
1919

2020
import contextlib
2121
from pathlib import Path
@@ -138,3 +138,24 @@ def test_parquet_methods_with_local_object_store(ctx, tmp_path, method_name):
138138
dataframe = ctx.read_parquet(path, object_store=LocalFileSystem())
139139

140140
assert dataframe.collect()[0].column("value").to_pylist() == [10, 20, 30]
141+
142+
143+
def test_sql_external_table_uses_registered_object_store(ctx, tmp_path):
144+
"""Read a remote URL through a registered store without network access."""
145+
table = pa.table({"passenger_count": [1, None, 3]})
146+
pq.write_table(table, tmp_path / "trips.parquet")
147+
148+
# Back the S3 URL with local files to test SQL's registry lookup, not AWS.
149+
store = LocalFileSystem(prefix=str(tmp_path))
150+
ctx.register_object_store("s3://", store, host="test-bucket")
151+
ctx.sql(
152+
"""
153+
CREATE EXTERNAL TABLE trips_sql
154+
STORED AS PARQUET
155+
LOCATION 's3://test-bucket/'
156+
"""
157+
).collect()
158+
159+
assert ctx.table("trips_sql").to_pydict() == table.to_pydict()
160+
result = ctx.sql("SELECT count(passenger_count) AS count FROM trips_sql")
161+
assert result.to_pydict() == {"count": [2]}

0 commit comments

Comments
 (0)