Skip to content

Commit 9c39365

Browse files
committed
feat(c): construct table from resolved schema
1 parent 4a933ec commit 9c39365

5 files changed

Lines changed: 385 additions & 5 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings/c/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ futures = "0.3"
3737
arrow = { workspace = true }
3838
arrow-array = { workspace = true }
3939
arrow-schema = { workspace = true }
40+
serde_json = "1.0.120"
4041

4142
[dev-dependencies]
4243
# Test-only: the vector-search integration tests build a real primary-key vindex

bindings/c/src/table.rs

Lines changed: 167 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@
1616
// under the License.
1717

1818
use std::collections::HashMap;
19-
use std::ffi::c_void;
19+
use std::ffi::{c_char, c_void};
2020

2121
use arrow_array::ffi::{FFI_ArrowArray, FFI_ArrowSchema};
2222
use arrow_array::{Array, StructArray};
2323
use futures::StreamExt;
24-
use paimon::spec::{DataField, DataType, Datum, Predicate, PredicateBuilder};
24+
use paimon::catalog::Identifier;
25+
use paimon::io::FileIO;
26+
use paimon::spec::{DataField, DataType, Datum, Predicate, PredicateBuilder, TableSchema};
2527
use paimon::table::{ArrowRecordBatchStream, DataSplit, Table};
2628
use paimon::Plan;
2729

2830
use crate::error::{check_non_null, paimon_error, validate_cstr, PaimonErrorCode};
2931
use crate::result::{
30-
paimon_result_new_read, paimon_result_next_batch, paimon_result_plan, paimon_result_predicate,
31-
paimon_result_read_builder, paimon_result_record_batch_reader, paimon_result_table_scan,
32+
paimon_result_get_table, paimon_result_new_read, paimon_result_next_batch, paimon_result_plan,
33+
paimon_result_predicate, paimon_result_read_builder, paimon_result_record_batch_reader,
34+
paimon_result_table_scan,
3235
};
3336
use crate::runtime;
3437
use crate::types::*;
@@ -58,10 +61,160 @@ unsafe fn box_table_read_state(state: TableReadState) -> *mut paimon_table_read
5861

5962
// ======================= Table ===============================
6063

64+
/// Create a table directly from a resolved Paimon table schema JSON.
65+
///
66+
/// This constructor does not create a catalog or derive a warehouse. Storage
67+
/// options are used only to build FileIO; they are not merged into the supplied
68+
/// table schema. `branch` selects the branch-scoped managers while preserving
69+
/// the supplied schema.
70+
///
71+
/// # Safety
72+
/// All string pointers must be valid null-terminated C strings. `storage_options`
73+
/// must point to `storage_options_len` valid `paimon_option` values, or be null
74+
/// when `storage_options_len` is 0.
75+
#[no_mangle]
76+
pub unsafe extern "C" fn paimon_table_from_schema_json(
77+
table_path: *const c_char,
78+
table_schema_json: *const c_char,
79+
database: *const c_char,
80+
table_name: *const c_char,
81+
branch: *const c_char,
82+
storage_options: *const paimon_option,
83+
storage_options_len: usize,
84+
) -> paimon_result_get_table {
85+
let table_path = match validate_cstr(table_path, "table_path") {
86+
Ok(value) => value,
87+
Err(error) => {
88+
return paimon_result_get_table {
89+
table: std::ptr::null_mut(),
90+
error,
91+
}
92+
}
93+
};
94+
let table_schema_json = match validate_cstr(table_schema_json, "table_schema_json") {
95+
Ok(value) => value,
96+
Err(error) => {
97+
return paimon_result_get_table {
98+
table: std::ptr::null_mut(),
99+
error,
100+
}
101+
}
102+
};
103+
let database = match validate_cstr(database, "database") {
104+
Ok(value) => value,
105+
Err(error) => {
106+
return paimon_result_get_table {
107+
table: std::ptr::null_mut(),
108+
error,
109+
}
110+
}
111+
};
112+
let table_name = match validate_cstr(table_name, "table_name") {
113+
Ok(value) => value,
114+
Err(error) => {
115+
return paimon_result_get_table {
116+
table: std::ptr::null_mut(),
117+
error,
118+
}
119+
}
120+
};
121+
let branch = match validate_cstr(branch, "branch") {
122+
Ok(value) => value,
123+
Err(error) => {
124+
return paimon_result_get_table {
125+
table: std::ptr::null_mut(),
126+
error,
127+
}
128+
}
129+
};
130+
if storage_options.is_null() && storage_options_len > 0 {
131+
return paimon_result_get_table {
132+
table: std::ptr::null_mut(),
133+
error: paimon_error::new(
134+
PaimonErrorCode::InvalidInput,
135+
"null storage_options pointer with non-zero length".to_string(),
136+
),
137+
};
138+
}
139+
140+
let schema = match serde_json::from_str::<TableSchema>(&table_schema_json) {
141+
Ok(schema) => schema,
142+
Err(error) => {
143+
return paimon_result_get_table {
144+
table: std::ptr::null_mut(),
145+
error: paimon_error::new(
146+
PaimonErrorCode::InvalidInput,
147+
format!("Failed to parse table schema JSON: {error}"),
148+
),
149+
}
150+
}
151+
};
152+
153+
let mut options = HashMap::with_capacity(storage_options_len);
154+
if storage_options_len > 0 {
155+
for option in std::slice::from_raw_parts(storage_options, storage_options_len) {
156+
let key = match validate_cstr(option.key, "storage option key") {
157+
Ok(value) => value,
158+
Err(error) => {
159+
return paimon_result_get_table {
160+
table: std::ptr::null_mut(),
161+
error,
162+
}
163+
}
164+
};
165+
let value = match validate_cstr(option.value, "storage option value") {
166+
Ok(value) => value,
167+
Err(error) => {
168+
return paimon_result_get_table {
169+
table: std::ptr::null_mut(),
170+
error,
171+
}
172+
}
173+
};
174+
options.insert(key, value);
175+
}
176+
}
177+
178+
let file_io = match FileIO::from_path(&table_path)
179+
.and_then(|builder| builder.with_props(options.iter()).build())
180+
{
181+
Ok(file_io) => file_io,
182+
Err(error) => {
183+
return paimon_result_get_table {
184+
table: std::ptr::null_mut(),
185+
error: paimon_error::from_paimon(error),
186+
}
187+
}
188+
};
189+
let table = match Table::from_resolved_schema(
190+
file_io,
191+
Identifier::new(database, table_name),
192+
table_path,
193+
schema,
194+
branch,
195+
) {
196+
Ok(table) => table,
197+
Err(error) => {
198+
return paimon_result_get_table {
199+
table: std::ptr::null_mut(),
200+
error: paimon_error::from_paimon(error),
201+
}
202+
}
203+
};
204+
let wrapper = Box::new(paimon_table {
205+
inner: Box::into_raw(Box::new(table)) as *mut c_void,
206+
});
207+
paimon_result_get_table {
208+
table: Box::into_raw(wrapper),
209+
error: std::ptr::null_mut(),
210+
}
211+
}
212+
61213
/// Free a paimon_table.
62214
///
63215
/// # Safety
64-
/// Only call with a table returned from `paimon_catalog_get_table`.
216+
/// Only call with a table returned from `paimon_catalog_get_table` or
217+
/// `paimon_table_from_schema_json`.
65218
#[no_mangle]
66219
pub unsafe extern "C" fn paimon_table_free(table: *mut paimon_table) {
67220
free_table_wrapper(table, |t| t.inner);
@@ -1773,6 +1926,15 @@ const _: unsafe extern "C" fn(
17731926
// constructors so an accidental signature change fails to compile rather than
17741927
// silently breaking header consumers. To add behavior, introduce a new
17751928
// `paimon_table_new_read_builder_*` symbol instead of changing one of these.
1929+
const _: unsafe extern "C" fn(
1930+
*const c_char,
1931+
*const c_char,
1932+
*const c_char,
1933+
*const c_char,
1934+
*const c_char,
1935+
*const paimon_option,
1936+
usize,
1937+
) -> paimon_result_get_table = paimon_table_from_schema_json;
17761938
const _: unsafe extern "C" fn(*const paimon_table) -> paimon_result_read_builder =
17771939
paimon_table_new_read_builder;
17781940
const _: unsafe extern "C" fn(

0 commit comments

Comments
 (0)