Repository: letsql/letsql (language: python)

Issue:
`read_parquet` does not register https `object_store`
To `read_parquet` with an https source, we must first `read_csv` with an https source
```
In [1]: import letsql as ls

In [2]: url = "https://nasa-avionics-data-ml.s3.us-east-2.amazonaws.com/Tail_652_1_parquet/652200101120916.16p0.parquet"

In [3]: con = ls.connect()

In [4]: con.read_parquet(url)
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 con.read_parquet(url)

File ~/repos/github/letsql/python/letsql/backends/let/__init__.py:94, in Backend.read_parquet(self, path, table_name, **kwargs)
     91 def read_parquet(
     92     self, path: str | Path, table_name: str | None = None, **kwargs: Any
     93 ) -> ir.Table:
---> 94     registered_table = super().read_parquet(path, table_name=table_name, **kwargs)
     95     self._sources[registered_table.op()] = registered_table.op()
     96     return registered_table

File ~/repos/github/letsql/python/letsql/backends/datafusion/__init__.py:627, in Backend.read_parquet(self, path, table_name, **kwargs)
    625 # Our other backends support overwriting views / tables when reregistering
    626 self.con.deregister_table(table_name)
--> 627 self.con.register_parquet(table_name, path, **kwargs)
    628 return self.table(table_name)

Exception: DataFusion error: Internal("No suitable object store found for https://nasa-avionics-data-ml.s3.us-east-2.amazonaws.com/Tail_652_1_parquet/652200101120916.16p0.parquet. See `RuntimeEnv::register_object_store`")

In [5]: con.read_csv(url)
Out[5]: 
DatabaseTable: ibis_read_csv_ureidhkgozcypmoykwda62euoy
  <empty schema>

In [6]: con.read_parquet(url)
Out[6]: 
DatabaseTable: ibis_read_parquet_gndzle6nu5a5riumpupygacyhm
  time float64
  FPAC float64
  BLAC float64
  CTAC float64
  IVV  float64
```


Discussion / hints:

Fix patch:
diff --git a/src/context.rs b/src/context.rs
index 9b8fc4eb..7efa1753 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -202,11 +202,15 @@ impl PySessionContext {
     }
 
     #[allow(clippy::too_many_arguments)]
-    #[pyo3(signature = (name, path, table_partition_cols=vec![],
+    #[pyo3(signature = (name,
+                        path,
+                        table_partition_cols=vec![],
                         parquet_pruning=true,
                         file_extension=".parquet",
                         skip_metadata=true,
-                        schema=None))]
+                        schema=None,
+                        storage_options=None
+    ))]
     fn register_parquet(
         &mut self,
         name: &str,
@@ -216,6 +220,7 @@ impl PySessionContext {
         file_extension: &str,
         skip_metadata: bool,
         schema: Option<PyArrowType<Schema>>,
+        storage_options: Option<HashMap<String, String>>,
         py: Python,
     ) -> PyResult<()> {
         let mut options = ParquetReadOptions::default()
@@ -224,8 +229,19 @@ impl PySessionContext {
             .skip_metadata(skip_metadata);
         options.file_extension = file_extension;
         options.schema = schema.as_ref().map(|x| &x.0);
+        let storage_options = storage_options.unwrap_or_default();
+
+        let result = async {
+            register_object_store_and_config_extensions(
+                &self.ctx,
+                &path.to_string(),
+                Some(ConfigFileType::PARQUET),
+                &storage_options,
+            )
+            .await?;
+            self.ctx.register_parquet(name, path, options).await
+        };
 
-        let result = self.ctx.register_parquet(name, path, options);
         wait_for_future(py, result).map_err(DataFusionError::from)?;
         Ok(())
     }
diff --git a/src/object_storage.rs b/src/object_storage.rs
index 54136f85..18e6d95a 100644
--- a/src/object_storage.rs
+++ b/src/object_storage.rs
@@ -28,6 +28,7 @@ use datafusion::error::{DataFusionError, Result};
 use datafusion::execution::context::SessionState;
 
 use async_trait::async_trait;
+use aws_config::BehaviorVersion;
 use aws_credential_types::provider::ProvideCredentials;
 use datafusion::datasource::listing::ListingTableUrl;
 use datafusion::prelude::SessionContext;
@@ -63,7 +64,9 @@ pub async fn get_s3_object_store_builder(
             builder = builder.with_token(session_token);
         }
     } else {
-        let config = aws_config::from_env().load().await;
+        let config = aws_config::defaults(BehaviorVersion::v2023_11_09())
+            .load()
+            .await;
         if let Some(region) = config.region() {
             builder = builder.with_region(region.to_string());
         }


Test patch:
diff --git a/python/letsql/tests/test_register.py b/python/letsql/tests/test_register.py
index 31ccbfe1..bfa52a55 100644
--- a/python/letsql/tests/test_register.py
+++ b/python/letsql/tests/test_register.py
@@ -78,6 +78,17 @@ def test_read_parquet(con, data_dir):
     assert t.count().execute()
 
 
+def test_read_parquet_from_url(con):
+    t = con.read_parquet(
+        "https://nasa-avionics-data-ml.s3.us-east-2.amazonaws.com/Tail_652_1_parquet/652200101120916.16p0.parquet",
+        table_name="nasa_table",
+    )
+
+    assert "nasa_table" in con.list_tables()
+    assert t.op().name == "nasa_table"
+    assert t.head().execute() is not None
+
+
 def test_register_table(con):
     tab = pa.table({"x": [1, 2, 3]})
     con.register(tab, "my_table")