Returning a result, vs Exceptions

That is what the documentation says.

Unfortunately, it would be wrong to only catch DatabaseExceptions as certain functions can also raise UnsupportedOperationException.

Without help from the IDE / compiler, it is difficult to write robust code using exceptions unless you wrap everything in try / catch blocks that catch all exceptions.

Unfortunately, it would be wrong to only catch DatabaseExceptions as certain functions can also raise UnsupportedOperationException.

Could you give an example how this could happen at runtime, in the context @Thom_McGrath described in his post?

1 Like

I don’t think it’s more robust at all. It’s a lot of boilerplate, which leads to errors and laziness. If you want to catch a broader range of exceptions, define the handler as RuntimeException instead of DatabaseException.

But all of this glosses over the fact that after an error, your code is in an untrusted state. The only sane thing to do is stop. You can either write a handler to tell your code how to stop gracefully, or you can let your app can shut down. If it’s safe to ignore the error, you can just write an empty catch block. No matter what you choose, you must deal with the error because your code is no longer running in a state you planned. Shutting down is a valid way to deal with the error. It’s not convenient, but it’s valid.

With error codes, error handling is optional. That’s bad. With exceptions, error handling is mandatory. It really isn’t more complicated of a topic than that.

6 Likes

To be honest, I wasn’t referring to Thom’s example when I said that other exceptions could be raised. It was more of a general statement that you cannot just rely on catching DatabaseException when working with the database classes.

According to the Xojo documentation for ExecuteSQL:

If the SQL passed is invalid, a DatabaseException will occur.

I imagine invalid SQL is not the only thing that can go wrong so do you always get a DatabaseException or could you get some other kind of exception?

Without help from the IDE / compiler, writing good exception handling code is much harder than writing good error code based code.

Raymond Chen says it much better than me:

Both of these articles ignore the correct solution, and that’s using the exception handler to clean up after yourself. Handlers are not for unplanned exceptions, they are used when you expect the possibility of an error. Part of that is planning your recovery.

5 Likes

Interesting read. Raymond Chen says what is really hard is writing good exception-based code, harder than writing good error code-based code. I have a different perspective:
During the last 15 years I developed solutions in an error code-based scripting language. From experience, this leads to all kind of troubles (omitting error-catching because of time/budget pressure), unwieldy code because the language does not really cater for catching errors (no access to any kind of stack).
I agree that all kinds of errors should be provided by the Catch statement. I did not yet try nesting Try-Catch statements for catching different exception types.

1 Like

You don’t nest them. You’d write something like

Try
  Fail()
Catch NilErr As NilObjectException
  DoSomething()
Catch BoundsErr As OutOfBoundsException
  DoSomethingElse()
Catch OtherErr As RuntimeException
  DoSomethingGeneric()
End Try

In my experience, this type of code is rarely necessary. It would be nice if Xojo would add properties to their exception subclasses. For example, DatabaseException should have an SQL property that tells you which statement failed.

7 Likes

What is an unplanned exception? How do you know to expect the possibility of an error?

The only solution is to read the documentation, try to remember it all and hope that new exceptions aren’t introduced in an update.

Without help from the IDE / compiler it is highly likely that you will write code that isn’t as robust as you think it is.

Correct. Reading the documentation is always a good thing. You either read the docs to learn about the error codes, or you read the docs to learn about the exceptions.

3 Likes

I take it that knowing error codes and exception codes is part of my job as a developer. Implementing a last resort catching routine that documents the unexpected - and unhandled (there is always the possibility that my code is not perfect) - is good practise.

Btw. how do we simulate i.e. database exceptions in Xojo for testing?

Thats a bit tricky when xojo doesn’t document all places exceptions occur.

https://tracker.xojo.com/xojoinc/xojo/-/issues/57720

At least when an error code was returned you knew about it at implementation time and could deal with it before moving on.

3 Likes

True, but that’s a documentation issue. It is equally likely that error properties can be forgotten to be set. That has absolutely happened in the past. There’s no perfect solution.

2 Likes

I would say its more an IDE / compiler issue. We should at least be getting warnings for any exceptions our code isn’t handling.

This is hard to answer, but generally speaking, don’t simulate. Write tests that intentionally do things wrong, as well as right. This helps you confirm both the success and error scenarios at the same time. TDD (Test Driven Design) is kind of complicated in Xojo though. I “get away” with a Tests module that runs during debug, but there’s always room for this to be more robust.

1 Like

I think that would be about as reliable as autocomplete…

I mean, I wouldn’t complain about it. But realistically, it’s not likely to happen.

Or the lazy one I’m used to:

Try
  Fail()
Catch NilErr As NilObjectException
  DoSomething()
Catch BoundsErr As OutOfBoundsException
  DoSomethingElse()
Catch err
  UnexpectedError(err) // Do you remember Microsoft's "Catastrophic Failure"? They found something that should not happen.
End Try

That could also happen with exceptions if it wasn’t passed along to your code, that would be a failure in the framework and you’d never know about it. But you are still in a position of more knowledge with error codes at coding time.

As it stands exceptions are sometimes only mentioned in examples on the docs page so unless you read the entire doc section including all the examples (which is obscenely bad) you might miss that something is raised e.g. https://documentation.xojo.com/api/databases/database.html#database-executesql. With the error codes its right there in your face at design time with the return.

The main advantage of exceptions is that you can encompass blocks of code to treat them as “unsafe” instead of micromanaging each line of code but if there’s no easy and obvious way to find out if something raises an exception or what type of exception then they are useless as you will need to catchall across the whole app. This moves the onus from xojo to the developer which, as I understand it, isn’t the ethos of xojo.

1 Like

Catching other exceptions is easy. Unfortunately, you have no way of knowing what exceptions to catch and which functions will raise them.

For example, the database classes can raise DatabaseException so you add code to catch those. However, some of the database functions can raise other exceptions. If you are lucky, you might just read that part of the documentation and see that. If not, you then you won’t know until someone reports the crash to you.

Alternatively, you could just write boiler plate code everywhere similar to what you have written above. That to me, is no better than using error codes.

They shouldn’t, and when asked you haven’t presented us a case that does. If you have a reproducible case of this, please report it as a bug – because it is.

As someone who relies on exceptions and designs for them, I would be very interested to know which database functions are misclassified when an exception is raised.

The only misclassed exceptions I know of come from URLConnection.

1 Like

According to the RowSet documentation, the following can raise UnsupportedOperationException.

  • If you try to use a RowSet after it has been closed.
  • RowCount

There are also methods such as MoveToFirstRow, MoveToPreviousRow and MoveToLastRow that don’t work with all ODBC drivers. Do they raise DatabaseException or maybe UnsupportedOperationException?