This library reports failures through two distinct mechanisms, neither of which can be caught or
recovered from once it fires. The reader, writer and schema surface has no status or ierr return
code anywhere, so check inputs (file existence, column names, array bounds) before calling into it
if you need to avoid aborting. The parquet_table layer is the exception: thirteen of its
type-bound generics — %get, %set, %col, %column, %get_slice, %set_slice, %get_element,
%set_element, %get_valid_mask, %set_null, %clear_null, %materialize and %prefetch — take
an optional found=, and passing it turns what would abort into a reported miss. See
Whole tables in memory.
Both are genuine, immediate process terminations, and the practical advice is the same either way: validate before calling, and do not rely on catching anything. The distinction is for reading a log — it tells you which half of the library detected the problem, and the two look nothing alike.
error stopPrints your compiler's ERROR STOP line carrying the library's message, usually followed by a
backtrace. Both the exact prefix and whether you get a backtrace are properties of your compiler and
build flags rather than guarantees this library makes — gfortran writes ERROR STOP <message>, NAG
ERROR STOP: <message>, flang Fortran ERROR STOP: <message>. What is the same everywhere is the
message after it, which is the library's.
This is the class used for precondition and validation failures this library's own Fortran code detects directly: a missing file, invalid MAML, an unknown column name, calling a reader/writer procedure before it's open, a declared vector column's size not matching what was requested at the schema level, and so on.
Messages from the parquet_table layer carry a second prefix of their own, so a real one reads
parquet_table: <procedure>: <message> after whatever your compiler puts in front. That is worth
recognising: it tells you the failure came from the table container rather than from the reader or
writer underneath it.
Prints a single line to stderr with no ERROR STOP prefix and no Fortran backtrace, always in the
form parquet-fortran: <procedure>: <message>:
parquet-fortran: parquet_read_column: type mismatch for column: d (expected int32/int64, got timestamp[us])
This is the class used where the failing check happens on the C++/Arrow side of the
parquet_wrapper.cpp boundary rather than in Fortran. It covers:
null_value=/is_valid= (see
Null values);col_size/table-column-count/chunk_size int32 ceilings (see
Limitations);reader/writer is called from two threads at once.Three things distinguish them, and the first is the one to reach for in a script:
Fortran error stop |
C++-level exit | |
|---|---|---|
| exit status | nonzero, and never 134 | always exactly 134 |
| first line | your compiler's ERROR STOP prefix, then <message> |
parquet-fortran: <procedure>: <message> |
| backtrace | usually | never |
Test for 134, not for the other one. The C++ status is exact because that path ends in an
explicit _Exit(134); the Fortran one is whatever your compiler chose for ERROR STOP — 1 with
gfortran and flang, 2 with NAG — and the Fortran standard leaves it processor-dependent, so a script
that keys on a particular value is testing its compiler rather than this library.
134 is also what a shell reports for a process killed by SIGABRT, so a wrapper that inspects
only the exit code cannot tell a deliberate C++-side failure from a genuine crash. Read the stderr
line to be sure: a deliberate one always begins parquet-fortran:.
If you're contributing to this library and need to add or test one of these failure paths, see CONTRIBUTING.md for how that's done out-of-process, and for this project's own conventions around C++-level error reporting.
Not everything the library disapproves of is fatal. A third outcome exists and is easy to mistake
for one of the two above when you meet it in a log: a line beginning WARNING:, and an exit status
of 0. Closing a writer that declared columns and wrote none is one — every declared column is
written with 0 rows and the program carries on:
WARNING: parquet_close_writer: no column was written; writing every declared column with 0 rows
Warnings behave differently from errors in both directions that matter. They are suppressed at
verbosity="errors_only" — note that "silent" is not enough, since it quiets informational and
solicited output while leaving warnings alone — and they follow message_stream, so a program
can move them to stderr. Nothing that terminates the process can be silenced or moved either way.
See Settings for the two knobs themselves.
Once a reader/writer/schema is far enough along to know it (i.e.
parquet_open_reader/parquet_open_writer has already recorded the filename, or
schema%init/parquet_schema(...) has already named the schema), most read/write/schema-building
error stop messages append that context — e.g. ... (file: output.parquet, maml: my_schema.maml)
— so a failure is identifiable even when a program has several readers/writers/schemas in play at
once. This wording isn't a fixed contract (exact phrasing may change between releases); only the
presence of file/schema context, where available, is intended to be relied on.
Some context arrives on a second stream instead, and this catches people out. Where naming the file would make the abort message unreasonably long, the library prints it as its own line before aborting — and those lines go to standard output, while the abort message itself goes to standard error. Closing a writer with a declared column left unwritten is the case you are most likely to meet:
stdout: parquet_close_writer: output file: catalogue.parquet
stdout: parquet_close_writer: schema: internal:demo
stderr: ERROR STOP parquet_close_writer: missing write for enabled column: col_c
(the ERROR STOP prefix there is gfortran's; see Telling them apart)
These lines are never suppressed and never redirected, deliberately: not even
verbosity="errors_only", the strictest setting, removes them, and message_stream does not move
them, because silencing them would leave an abort that names no file at all. The practical consequence is the one to remember — a program that
captures only stderr loses the filename. Capture both streams when you want a diagnosable failure.
Every procedure that takes a parquet_reader — all twenty-five of them, from parquet_read_column
and parquet_prefetch_columns through the metadata queries to parquet_reader_set_filter and
parquet_release_column — checks that the reader is open before doing anything else, and every
procedure taking a parquet_writer does the same. Calling one before
parquet_open_reader/parquet_open_writer fails with error stop, naming both the procedure you
called and the open call you missed:
ERROR STOP parquet_read_column: reader has not been opened (call parquet_open_reader first). The
same applies to parquet_close_reader/parquet_close_writer themselves: closing a reader/writer
that was never opened, or that was already closed, also fails with error stop rather than silently
doing nothing.
found=The reader, writer and schema surface has no way to say "tell me rather than stopping". The
parquet_table layer does: pass found= to any of the thirteen generics listed at the top of this
page and a miss is reported instead of aborting. It applies to a missing column name and to an
out-of-range column position alike.
program found_or_abort
use parquet
use iso_fortran_env, only: real64
implicit none
type(parquet_table) :: t
real(real64) :: mass(3)
real(real64), allocatable :: got(:)
logical :: ok
mass = [1.5_real64, 2.5_real64, 3.5_real64]
call parquet_new_table(t)
call t%add_column("mass", mass)
call t%get("flux", got, found=ok) ! there is no "flux" column
print *, "flux found? ", ok ! F -- and the program is still running
call t%get("mass", got, found=ok)
print *, "mass found? ", ok, " rows:", size(got)
end program found_or_abort
Omit found= from that first call and the same missing name is an immediate error stop. That is
the whole of the difference: the argument does not change what counts as a miss, only what happens
when there is one.
Re-opening an already-open reader/writer variable (calling
parquet_open_reader/parquet_open_writer again on one that's already in use, without closing it
first) does not error and does not leak the old handle: Fortran automatically finalizes the
previous handle first, since reader/writer are intent(out) arguments of a finalizable type.
This finalization is not the same as an explicit parquet_close_writer/parquet_close_reader
call, though: it skips the completeness checks those perform (e.g. "every enabled schema column was
written"), so the abandoned file is not guaranteed to be a complete or valid parquet file. Always
call parquet_close_writer/parquet_close_reader explicitly when you actually want the file that
was being written/read to end up valid; rely on the implicit finalizer only as a safety net for a
variable you're discarding or reusing anyway.
parquet_table inverts this advice, because there is no parquet_close_table to call. A table
is finalized when it goes out of scope or is re-opened, and that is the normal path rather than a
safety net — there is nothing you were supposed to have called instead. This is safe because a
table's finalizer frees its store without validating anything, which is the rule every finalizer in
this library follows: an implicit finalizer runs at points a caller cannot see or handle, so it must
always succeed silently rather than being able to fail.