|
16 | 16 | // under the License. |
17 | 17 |
|
18 | 18 | use std::collections::HashMap; |
19 | | -use std::ffi::c_void; |
| 19 | +use std::ffi::{c_char, c_void}; |
20 | 20 |
|
21 | 21 | use arrow_array::ffi::{FFI_ArrowArray, FFI_ArrowSchema}; |
22 | 22 | use arrow_array::{Array, StructArray}; |
23 | 23 | 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}; |
25 | 27 | use paimon::table::{ArrowRecordBatchStream, DataSplit, Table}; |
26 | 28 | use paimon::Plan; |
27 | 29 |
|
28 | 30 | use crate::error::{check_non_null, paimon_error, validate_cstr, PaimonErrorCode}; |
29 | 31 | 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, |
32 | 35 | }; |
33 | 36 | use crate::runtime; |
34 | 37 | use crate::types::*; |
@@ -58,10 +61,160 @@ unsafe fn box_table_read_state(state: TableReadState) -> *mut paimon_table_read |
58 | 61 |
|
59 | 62 | // ======================= Table =============================== |
60 | 63 |
|
| 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 | + |
61 | 213 | /// Free a paimon_table. |
62 | 214 | /// |
63 | 215 | /// # 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`. |
65 | 218 | #[no_mangle] |
66 | 219 | pub unsafe extern "C" fn paimon_table_free(table: *mut paimon_table) { |
67 | 220 | free_table_wrapper(table, |t| t.inner); |
@@ -1773,6 +1926,15 @@ const _: unsafe extern "C" fn( |
1773 | 1926 | // constructors so an accidental signature change fails to compile rather than |
1774 | 1927 | // silently breaking header consumers. To add behavior, introduce a new |
1775 | 1928 | // `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; |
1776 | 1938 | const _: unsafe extern "C" fn(*const paimon_table) -> paimon_result_read_builder = |
1777 | 1939 | paimon_table_new_read_builder; |
1778 | 1940 | const _: unsafe extern "C" fn( |
|
0 commit comments