Skip to content

Commit a4512c6

Browse files
authored
Allow passing auth properties (#11)
1 parent 9711e4c commit a4512c6

File tree

7 files changed

+122
-15
lines changed

7 files changed

+122
-15
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ jobs:
9999
${{ runner.os }}-
100100
101101
- uses: julia-actions/julia-buildpkg@v1
102+
- name: Force recompile with custom library
103+
if: inputs.build_rust
104+
run: julia --project=. -e 'Base.compilecache(Base.identify_package("RustyIceberg"))'
102105
- uses: julia-actions/julia-runtest@v1
103106
- uses: julia-actions/julia-processcoverage@v1
104107
- uses: codecov/codecov-action@v3

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ test: build
4949
fi
5050
@set -a && . ./.env && set +a && \
5151
export ICEBERG_RUST_LIB=$$(pwd)/$(TARGET_DIR) && \
52-
$(JULIA_THREADS_ENV) julia --project=. -e 'using Pkg; Pkg.test()'
52+
$(JULIA_THREADS_ENV) julia --project=. -e 'Base.compilecache(Base.identify_package("RustyIceberg")); using Pkg; Pkg.test()'
5353

5454
# Start Julia REPL with environment configured (requires .env file)
5555
repl: build

docker/docker-compose.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@ services:
7373
entrypoint: |
7474
/bin/sh -c "
7575
until (/usr/bin/mc alias set minio http://minio:9000 root password) do echo '...waiting...' && sleep 1; done;
76-
/usr/bin/mc rm -r --force minio/warehouse
76+
/usr/bin/mc rm -r --force minio/warehouse;
77+
/usr/bin/mc rb --force minio/warehouse;
7778
/usr/bin/mc mb minio/warehouse;
7879
/usr/bin/mc cp -r /input/tpch.sf01/ minio/warehouse/tpch.sf01/;
7980
/usr/bin/mc cp -r /input_incremental/ minio/warehouse/incremental/;
80-
tail -f /dev/null
81+
tail -f /dev/null;
8182
"

iceberg_rust_ffi/Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iceberg_rust_ffi/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "iceberg_rust_ffi"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
edition = "2021"
55

66
[lib]
@@ -12,7 +12,7 @@ default = ["julia"]
1212
julia = []
1313

1414
[dependencies]
15-
iceberg = { git = "https://github.com/RelationalAI/iceberg-rust.git", rev = "ccb1e0c9983a1bdcb5b70fa637759df526a9a75e" }
15+
iceberg = { git = "https://github.com/RelationalAI/iceberg-rust.git", rev = "37e79b805407a0340d08823373cafed9cdac0083" }
1616
object_store_ffi = { git = "https://github.com/RelationalAI/object_store_ffi", rev = "79b08071c7a1642532b5891253280861eca9e44e", default-features = false }
1717
tokio = { version = "1.0", features = ["full"] }
1818
futures = "0.3"

iceberg_rust_ffi/src/lib.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ impl Default for IcebergStaticConfig {
8282
}
8383
}
8484

85+
// FFI structure for passing key-value properties
86+
#[repr(C)]
87+
pub struct PropertyEntry {
88+
pub key: *const c_char,
89+
pub value: *const c_char,
90+
}
91+
92+
unsafe impl Send for PropertyEntry {}
93+
8594
// Direct structures - no opaque wrappers
8695
#[repr(C)]
8796
pub struct IcebergTable {
@@ -302,12 +311,42 @@ export_runtime_op!(
302311
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 in snapshot path: {}", e))?
303312
};
304313

305-
Ok(snapshot_path_str.to_string())
314+
let scheme_str = unsafe {
315+
CStr::from_ptr(scheme).to_str()
316+
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 in scheme: {}", e))?
317+
};
318+
319+
// Convert properties from FFI to Rust Vec
320+
let mut props = Vec::new();
321+
if !properties.is_null() && properties_len > 0 {
322+
let properties_slice = unsafe {
323+
std::slice::from_raw_parts(properties, properties_len)
324+
};
325+
326+
for prop in properties_slice {
327+
let key = unsafe {
328+
CStr::from_ptr(prop.key).to_str()
329+
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 in property key: {}", e))?
330+
};
331+
let value = unsafe {
332+
CStr::from_ptr(prop.value).to_str()
333+
.map_err(|e| anyhow::anyhow!("Invalid UTF-8 in property value: {}", e))?
334+
};
335+
props.push((key.to_string(), value.to_string()));
336+
}
337+
}
338+
339+
Ok((snapshot_path_str.to_string(), scheme_str.to_string(), props))
306340
},
307-
full_metadata_path,
341+
result_tuple,
308342
async {
309-
// Create file IO for S3
310-
let file_io = FileIOBuilder::new("s3").build()?;
343+
let (full_metadata_path, scheme_string, props) = result_tuple;
344+
345+
// Create file IO with the specified scheme
346+
// Default behavior (when props is empty) uses environment variables for credentials
347+
let file_io = FileIOBuilder::new(&scheme_string)
348+
.with_props(props)
349+
.build()?;
311350

312351
// Create table identifier
313352
let table_ident = TableIdent::from_strs(["default", "table"])?;
@@ -318,7 +357,10 @@ export_runtime_op!(
318357

319358
Ok::<IcebergTable, anyhow::Error>(IcebergTable { table: static_table.into_table() })
320359
},
321-
snapshot_path: *const c_char
360+
snapshot_path: *const c_char,
361+
scheme: *const c_char,
362+
properties: *const PropertyEntry,
363+
properties_len: usize
322364
);
323365

324366

src/RustyIceberg.jl

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,20 +260,80 @@ end
260260
# High-level functions using the async API pattern from RustyObjectStore.jl
261261

262262
"""
263-
table_open(snapshot_path::String)::IcebergTable
263+
PropertyEntry
264+
265+
FFI structure for passing key-value properties to Rust.
266+
267+
# Fields
268+
- `key::Ptr{Cchar}`: Pointer to the key string
269+
- `value::Ptr{Cchar}`: Pointer to the value string
270+
"""
271+
struct PropertyEntry
272+
key::Ptr{Cchar}
273+
value::Ptr{Cchar}
274+
end
275+
276+
"""
277+
table_open(snapshot_path::String; scheme::String="s3", properties::Dict{String,String}=Dict{String,String}())::Table
264278
265279
Open an Iceberg table from the given snapshot path.
280+
281+
# Arguments
282+
- `snapshot_path::String`: Path to the metadata.json file for the table snapshot
283+
- `scheme::String`: Storage scheme (e.g., "s3", "file"). Defaults to "s3"
284+
- `properties::Dict{String,String}`: Optional key-value properties for the FileIO configuration.
285+
By default (empty dict), credentials are read from environment variables (AWS_ACCESS_KEY_ID,
286+
AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_ENDPOINT_URL, etc.).
287+
288+
Common S3 properties include:
289+
- "s3.endpoint": Custom S3 endpoint URL
290+
- "s3.access-key-id": AWS access key ID
291+
- "s3.secret-access-key": AWS secret access key
292+
- "s3.session-token": AWS session token
293+
- "s3.region": AWS region
294+
- "s3.allow-anonymous": Set to "true" for anonymous access (no credentials)
295+
296+
# Example
297+
```julia
298+
# Open with credentials from environment variables (default)
299+
table = table_open("s3://bucket/path/metadata/metadata.json")
300+
301+
# Open with anonymous S3 access
302+
table = table_open(
303+
"s3://bucket/path/metadata/metadata.json",
304+
properties=Dict("s3.allow-anonymous" => "true")
305+
)
306+
307+
# Open with custom S3 credentials
308+
table = table_open(
309+
"s3://bucket/path/metadata/metadata.json",
310+
scheme="s3",
311+
properties=Dict(
312+
"s3.endpoint" => "http://localhost:9000",
313+
"s3.access-key-id" => "minioadmin",
314+
"s3.secret-access-key" => "minioadmin",
315+
"s3.region" => "us-east-1"
316+
)
317+
)
318+
```
266319
"""
267-
function table_open(snapshot_path::String)
320+
function table_open(snapshot_path::String; scheme::String="s3", properties::Dict{String,String}=Dict{String,String}())
268321
response = TableResponse()
269322
ct = current_task()
270323
event = Base.Event()
271324
handle = pointer_from_objref(event)
272325

326+
# Convert properties dict to array of PropertyEntry structs
327+
property_entries = [PropertyEntry(pointer(k), pointer(v)) for (k, v) in properties]
328+
properties_len = length(property_entries)
329+
273330
preserve_task(ct)
274-
result = GC.@preserve response event try
331+
result = GC.@preserve response event property_entries properties try
275332
result = @ccall rust_lib.iceberg_table_open(
276333
snapshot_path::Cstring,
334+
scheme::Cstring,
335+
(properties_len > 0 ? pointer(property_entries) : C_NULL)::Ptr{PropertyEntry},
336+
properties_len::Csize_t,
277337
response::Ref{TableResponse},
278338
handle::Ptr{Cvoid}
279339
)::Cint

0 commit comments

Comments
 (0)