All, Historically, exception handling looked this way at the JRD level:
try { tdbb->tdbb_status_vector = user_status; } catch (const Exception& ex) { return error(user_status); } Errors were raised via ERR_post() which *appends* to tdbb_status_vector and then throws. Errors were reposted via ERR_punt() which just re-throws tdbb_status_vector. It allowed different errors (I don't mean primary/secondary error codes, but really independent errors) to be subsequently posted to the status vector and then returned to the user. This ability seems to be broken since v2.x though, because: - We have a tendency to replace ERR_* calls with direct exception::raise() calls. - Our commonly used stuff_exception() routine overwrites the status vector instead of appending there. This issue remains invisible mostly because it's quite rare in practice. I can think of two possible scenarios: 1) Lock manager error is returned from the LCK_* call via tdbb_status_vector and then a proper higher level exception is thrown. But now we treat this as a minor bug and clear tdbb_status_vector in such cases. Also, this is possible only with ERR_post(), which used less nowadays. 2) Some error is handled by the looper and another error is thrown while unwinding the request. This is a more common situation, but most unwinding exceptions are thrown from VIO_verb_cleanup() and we had a bugcheck reported in this case, which hides the real problem. This problem became better visible in v3 after I removed this bugcheck in favor of transaction invalidation + proper error reporting. Now we have the backout error reported but the original error (which initiated a backout) is lost. Here's a demonstration of the problem: catch (const Exception& ex) { ex.stuff_exception(tdbb->tdbb_status_vector); <some code>; throw; } - If <some code> is trivial, the logic is absolutely the same as ERR_punt() -- no problems here. - If <some code> throws, then we have exception A residing in tdbb_status_vector but exception B is thrown. Quite inconsistent, given that we still have some tdbb_status_vector[1] checks in the code. Finally, at the JRD level, user_status will be overwritten with exception B, thus losing the original exception. - If <some code> throws, catches and re-throws, then tdbb_status_vector will be overwritten inside <some code>. Again, the original exception is lost. At the first glance, one might think that all we need is to change stuff_exception() to append instead of overwriting. But it's not really so. Given how many times we may catch and re-throw within the call stack, I'd expect lots of duplicated items inside the status vector. And proper duplication checking (with strcmp for string args) looks like an overkill. There may be other unexpected side effects in this solution too. A long-term solution would include: - fixing all direct tdbb_status_vector checks - removing tdbb_status_vector completely - removing stuff_exception too But that's surely for another day. For v3, I can think only about the following approach: catch (const Exception& ex) { Arg::StatusVector error(ex); try { <some code> } catch (const Exception& ex2) { error.append(ex2); } error.raise(); } Of course, in many cases it's unnecessary and would just pollute the code. So we need to carefully analyze every catch() block for possible side effects like throwing exceptions from inside. Fortunately, most of them are not really important in practice, so it's not a showstopper. A more complex case is the looper which holds an active exception inside tdbb_status_vector for the whole duration of the unwinding process, but it also looks solvable. I have draftly implemented this approach for the looper and it seems to work, now I see both original and backout errors reported. This resolves the mostly visible reason of the problem. But maybe someone can suggest a better solution? Dmitry ------------------------------------------------------------------------------ Dive into the World of Parallel Programming. The Go Parallel Website, sponsored by Intel and developed in partnership with Slashdot Media, is your hub for all things parallel software development, from weekly thought leadership blogs to news, videos, case studies, tutorials and more. Take a look and join the conversation now. http://goparallel.sourceforge.net/ Firebird-Devel mailing list, web interface at https://lists.sourceforge.net/lists/listinfo/firebird-devel