WL#3679: Class hierarchy for non-intrusive auto-boxing of internal error codes

Affects: WorkLog-3.4   —   Status: Un-Assigned


Currently, there are several kinds of error number floating around in
the code. We have the following categories of classes:

- POSIX.1 error codes, mentioned in, e.g., errno(3)
- Handler error codes, starting with the prefix ``HA_ERR_``
- MySQL error codes, starting with the prefix ``ER_``
- Ad-hoc (internal) error codes
- Storage engine-specific error codes
- SQL state codes

The five first ones are represented as integers, while SQL state codes
are stored as an include file.


The SQL state mappings are not seen internally, so we disregard them
for now.

When writing code, it is easy to pass the integers around, but since
there is no kind of protocol for enforcing separation of the error
codes, it is easy to return, e.g., a handler error code from a
function that is supposed to return a MySQL error code.  The problem
can, of course, be eliminated with proper code reviews, but as the
development speed picks up, the current approach is not good for at
least three reasons:

1. New developers are not familiar with the different error codes,
   and will therefore make mistakes.

2. All developers run the risk of making a mistake in this
   regard. Since the patches are usually focused on more complicated
   issues, it is easy to forget about proper use of error codes.

3. Review takes time, and wherever we can enforce compile-time or
   run-time checks, we should use these to find errors before they are
   deployed or even submitted as a patch.


To prevent mistakes and get proper compile-time checks for error
codes, we have to introduce a C++ way of accomplishing autoboxing for
the error codes.  This proposal outlines how that should be

The proposal introduces a non-intrusive way of auto-boxing the error codes
(automatically wrapping a class around each error code) which imposes no
execution overhead as compared to the current approach and still allows extensions.

Error code type

The error codes are stored as an integral type, which we denote by
``error_code_type`` in this proposal.

Error class hierarchy

In order to simplify and encourage proper typing of error codes, we
introduce a hierarchy of error classes.  The properties of the classes
in the hierarchy is:

- No virtual functions in the classes
- All member functions are inline
- Constructors for all leaf classes are implicit and accept one
  parameter of type ``error_code_type``. These constructors are called
  *converting constructors*.
- All conversions between leaf classes are disallowed by default
  (since there is no way to automatically do conversions between
  distinct classes), but for those cases where conversion is possible, a
  converting constructor can be added to convert between leaf classes.

No virtual functions

The classes contain no virtual function in order to allow efficient
copying of the objects.  The default copy constructor should suffice
to copy the object.

Inline member functions

All member functions are inline in order to not introduce any overhead
as compared to passing integers around.  Almost all compilers are able
to handle simple objects that only contain a single field that is a
base type.

Converting constructors

In order to allow transparent usage of the error classes, the
constructors are non-explicit and accepts just one parameter: the
error code.

With converting constructors (i.e., single-argument implicit
constructors), transformation of a function from using error numbers
to error classes is straightforward: just change the return type.

Allowing conversions

To allow, and implement, conversion from one category of errors to
another, it is a simple matter to add a converting constructor.  For
example, to allow conversion from ``Handler_error`` to
``Mysql_error``, we just add the converting constructor::

    class Mysql_error : public Error {
      Mysql_error(Handler_error const&) {

Special considerations

This section should contain a description of special considerations, but there
are none right now.


The goal of this design is to provide a reasonable balance between
execution efficiency and safeness. This means that there are certain
cases that are not handled:

- Returning error codes from different categories from the same
  function will not be caught. This is not practical to do as an
  intended solution, so there is no need to consider this as a
  potential *deliberate* case.  It can, however, be done involontary.

- Adding new conversions require the addition of a new constructor to
  the class being converted to, so the design violates the `Open-Closed
  Principle `_.

- We are not using any virtual functions, which places restrictions on
  it's usage as a general class.  The base class only serves as a
  convenience class, and is not required to inherit from.  This has
  the advantage of allowing conversion from existing error classes
  (e.g., ``NdbError``), but has the disadvantage of breaking the 
  `Liskov Subsitution Principle
  i.e., it is not possible to pass the error classes to a function
  accepting a base class instance and expect it to work.

  It is, however, possible to extend the hierarchy with a class that
  is intended for this purpose, and inherit from that branch where such
  support is deemed necessary. This would allow adding a hierarchy of 
  dynamic error classes, with a more object-oriented approach but also
  with the "overhead" of using virtual functions.