88#include < arrow/table.h>
99#include < arrow/type.h>
1010#include < arrow/util/compression.h>
11- #include < arrow/util/config.h>
1211#include < parquet/arrow/writer.h>
1312
1413#include < algorithm>
@@ -77,11 +76,10 @@ FileWriter makeParquetFileWriter( const std::string & path, const std::shared_pt
7776
7877 ::parquet::WriterProperties::Builder props;
7978 props.compression ( resolveCompression ( compression ) );
80- #if ARROW_VERSION_MAJOR >= 20
79+ // csp builds against libparquet >= 16.0.0, where PARQUET_2_6 (the modern default, available since
80+ // Arrow 6.0) is always present, so there is no need to gate on the Arrow version or fall back to
81+ // the deprecated PARQUET_2_0.
8182 props.version ( ::parquet::ParquetVersion::PARQUET_2_6 );
82- #else
83- props.version ( ::parquet::ParquetVersion::PARQUET_2_0 );
84- #endif
8583 ::parquet::ArrowWriterProperties::Builder arrowProps;
8684 arrowProps.store_schema (); // preserve arrow (file/column) schema metadata in the parquet file
8785
@@ -128,9 +126,13 @@ FileWriter makeIpcFileWriter( const std::string & path, const std::shared_ptr<::
128126 std::shared_ptr<::arrow::ipc::RecordBatchWriter> writer = result.MoveValueUnsafe ();
129127
130128 return FileWriter{
131- [writer]( const std::shared_ptr<::arrow::RecordBatch> & rb )
129+ [writer, compression ]( const std::shared_ptr<::arrow::RecordBatch> & rb )
132130 {
133- STATUS_OK_OR_THROW_RUNTIME ( writer -> WriteRecordBatch ( *rb ), " Failed to write arrow record batch" );
131+ // Arrow validates which codecs IPC allows here and appends its own reason (e.g. "Only
132+ // LZ4_FRAME and ZSTD compression allowed for IPC"); surface which codec was requested
133+ // rather than a bare "failed to write". The allowed set is Arrow's to define, not ours.
134+ STATUS_OK_OR_THROW_RUNTIME ( writer -> WriteRecordBatch ( *rb ),
135+ " Failed to write Arrow IPC record batch with compression '" << compression << " '" );
134136 },
135137 [writer, stream]()
136138 {
@@ -154,11 +156,26 @@ FileWriter makeSplitWriter( const std::string & dir, const std::shared_ptr<::arr
154156 subWriters -> reserve ( schema -> num_fields () );
155157 colSchemas -> reserve ( schema -> num_fields () );
156158
157- for ( int i = 0 ; i < schema -> num_fields (); ++i )
159+ try
160+ {
161+ for ( int i = 0 ; i < schema -> num_fields (); ++i )
162+ {
163+ auto colSchema = ::arrow::schema ( { schema -> field ( i ) }, schema -> metadata () );
164+ colSchemas -> push_back ( colSchema );
165+ subWriters -> push_back ( perColumn ( dir + " /" + schema -> field ( i ) -> name () + extension, colSchema ) );
166+ }
167+ }
168+ catch ( ... )
158169 {
159- auto colSchema = ::arrow::schema ( { schema -> field ( i ) }, schema -> metadata () );
160- colSchemas -> push_back ( colSchema );
161- subWriters -> push_back ( perColumn ( dir + " /" + schema -> field ( i ) -> name () + extension, colSchema ) );
170+ // A later column failed to open (e.g. allow_overwrite=false and that file already exists).
171+ // Close the sub-writers already opened so we don't leak file descriptors or leave half-open
172+ // files behind, then rethrow the original error.
173+ for ( auto & w : *subWriters )
174+ {
175+ try { w.close (); }
176+ catch ( ... ) {}
177+ }
178+ throw ;
162179 }
163180
164181 return FileWriter{
@@ -230,8 +247,12 @@ RecordBatchSink makeFileSink( bool writeArrowBinary, bool splitColumns,
230247 sink.onStart = [schemaHolder]( const std::shared_ptr<::arrow::Schema> & schema ) { *schemaHolder = schema; };
231248 sink.onBatch = [current]( const std::shared_ptr<::arrow::RecordBatch> & rb )
232249 {
233- if ( current -> has_value () )
234- ( *current ) -> writeBatch ( rb );
250+ // A batch must only ever arrive while a file is open (the writer guards this via isFileOpen()
251+ // before flushing). If the writer and sink ever desync, fail loudly instead of silently
252+ // dropping the batch's rows.
253+ CSP_TRUE_OR_THROW_RUNTIME ( current -> has_value (),
254+ " RecordBatchFileSink received a record batch with no open output file" );
255+ ( *current ) -> writeBatch ( rb );
235256 };
236257 sink.onFileChange = [factory, schemaHolder, current, currentPath, closeCurrent]( const std::string & path )
237258 {
0 commit comments