
(********************************************************************)
(*                                                                  *)
(*  chkdb.sd7     Checks the database interface.                    *)
(*  Copyright (C) 2014, 2017 - 2019  Thomas Mertes                  *)
(*                                                                  *)
(*  This program is free software; you can redistribute it and/or   *)
(*  modify it under the terms of the GNU General Public License as  *)
(*  published by the Free Software Foundation; either version 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  This program is distributed in the hope that it will be useful, *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of  *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *)
(*  GNU General Public License for more details.                    *)
(*                                                                  *)
(*  You should have received a copy of the GNU General Public       *)
(*  License along with this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


$ include "seed7_05.s7i";
  include "sql_base.s7i";
  include "bstring.s7i";
  include "time.s7i";
  include "duration.s7i";
  include "console.s7i";


const func boolean: raisesRangeError (ref proc: expression) is func
  result
    var boolean: raisesRangeError is FALSE;
  begin
    block
      expression;
    exception
      catch RANGE_ERROR: raisesRangeError := TRUE;
    end block;
  end func;


const func boolean: raisesDatabaseError (ref proc: expression) is func
  result
    var boolean: raisesDatabaseError is FALSE;
  begin
    block
      expression;
    exception
      catch DATABASE_ERROR: raisesDatabaseError := TRUE;
    end block;
  end func;


const func string: int8Type (in dbCategory: databaseKind) is func
  result
    var string: int8Type is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      int8Type := "TINYINT";
      when {DB_SQLITE}:     int8Type := "TINYINT";
      when {DB_POSTGRESQL}: int8Type := "SMALLINT";
      when {DB_OCI}:        int8Type := "SMALLINT";
      when {DB_ODBC}:       int8Type := "TINYINT";
      when {DB_FIRE}:       int8Type := "SMALLINT";
    end case;
  end func;


const func integer: maxInt8Value (in dbCategory: databaseKind) is func
  result
    var integer: maxInt8Value is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      maxInt8Value := 127;
      when {DB_SQLITE}:     maxInt8Value := 127;
      when {DB_POSTGRESQL}: maxInt8Value := 127;
      when {DB_OCI}:        maxInt8Value := 127;
      when {DB_ODBC}:       maxInt8Value := 255;
      when {DB_FIRE}:       maxInt8Value := 127;
    end case;
  end func;


const func string: int64Type (in dbCategory: databaseKind) is func
  result
    var string: int64Type is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      int64Type := "BIGINT";
      when {DB_SQLITE}:     int64Type := "BIGINT";
      when {DB_POSTGRESQL}: int64Type := "BIGINT";
      when {DB_OCI}:        int64Type := "INTEGER";
      when {DB_ODBC}:       int64Type := "BIGINT";
      when {DB_FIRE}:       int64Type := "BIGINT";
    end case;
  end func;


const func boolean: withRangeCheck (in dbCategory: databaseKind) is func
  result
    var boolean: withRangeCheck is FALSE;
  begin
    case databaseKind of
      when {DB_MYSQL}:      withRangeCheck := FALSE;
      when {DB_SQLITE}:     withRangeCheck := FALSE;
      when {DB_POSTGRESQL}: withRangeCheck := TRUE;
      when {DB_OCI}:        withRangeCheck := FALSE;
      when {DB_ODBC}:       withRangeCheck := TRUE;
      when {DB_FIRE}:       withRangeCheck := TRUE;
    end case;
  end func;


const func string: bigIntType (in dbCategory: databaseKind) is func
  result
    var string: bigIntType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      bigIntType := "DECIMAL(65)";
      when {DB_SQLITE}:     bigIntType := "DECIMAL";
      when {DB_POSTGRESQL}: bigIntType := "DECIMAL";
      when {DB_OCI}:        bigIntType := "DECIMAL";
      when {DB_ODBC}:       bigIntType := "DECIMAL(38)";
      when {DB_FIRE}:       bigIntType := "DECIMAL(18)";
    end case;
  end func;


const func integer: sqlIntLiteralBits (in dbCategory: databaseKind) is func
  result
    var integer: sqlIntLiteralBits is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      sqlIntLiteralBits := 216;
      when {DB_SQLITE}:     sqlIntLiteralBits := 64;
      when {DB_POSTGRESQL}: sqlIntLiteralBits := 435411;
      when {DB_OCI}:        sqlIntLiteralBits := 127;
      when {DB_ODBC}:       sqlIntLiteralBits := 127;
      when {DB_FIRE}:       sqlIntLiteralBits := 64;
    end case;
  end func;


const func integer: bigIntTypeBits (in dbCategory: databaseKind) is func
  result
    var integer: bigIntTypeBits is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      bigIntTypeBits := 216;
      when {DB_SQLITE}:     bigIntTypeBits := integer.last;
      when {DB_POSTGRESQL}: bigIntTypeBits := 435411;
      when {DB_OCI}:        bigIntTypeBits := 127;
      when {DB_ODBC}:       bigIntTypeBits := 127;
      when {DB_FIRE}:       bigIntTypeBits := 64;
    end case;
  end func;


const func string: floatType (in dbCategory: databaseKind) is func
  result
    var string: floatType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      floatType := "FLOAT";
      when {DB_SQLITE}:     floatType := "REAL";
      when {DB_POSTGRESQL}: floatType := "REAL";
      when {DB_OCI}:        floatType := "FLOAT";
      when {DB_ODBC}:       floatType := "REAL";
      when {DB_FIRE}:       floatType := "FLOAT";
    end case;
  end func;


const func string: doubleType (in dbCategory: databaseKind) is func
  result
    var string: doubleType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      doubleType := "DOUBLE";
      when {DB_SQLITE}:     doubleType := "DOUBLE";
      when {DB_POSTGRESQL}: doubleType := "DOUBLE PRECISION";
      when {DB_OCI}:        doubleType := "DOUBLE PRECISION";
      when {DB_ODBC}:       doubleType := "DOUBLE PRECISION";
      when {DB_FIRE}:       doubleType := "DOUBLE PRECISION";
    end case;
  end func;


const func string: bigRatType (in dbCategory: databaseKind) is func
  result
    var string: bigRatType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      bigRatType := "DECIMAL(65,30)";
      when {DB_SQLITE}:     bigRatType := "NUMBER";
      when {DB_POSTGRESQL}: bigRatType := "DECIMAL";
      when {DB_OCI}:        bigRatType := "NUMBER";
      when {DB_ODBC}:       bigRatType := "NUMERIC(38,15)";
      when {DB_FIRE}:       bigRatType := "DECIMAL(18, 9)";
    end case;
  end func;


const func string: decimalIntType (in dbCategory: databaseKind) is func
  result
    var string: decimalType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      decimalType := "DECIMAL(65)";   # DECIMAL(p), 1 <= p <= 65
      when {DB_SQLITE}:     decimalType := "NUMBER";
      when {DB_POSTGRESQL}: decimalType := "DECIMAL(100)";  # DECIMAL(p), 1 <= p <= 1000
      when {DB_OCI}:        decimalType := "NUMBER(38)";    # NUMBER(p),  1 <= p <= 38
      when {DB_ODBC}:       decimalType := "DECIMAL(38)";   # DECIMAL(p), 1 <= p <= 38
      when {DB_FIRE}:       decimalType := "DECIMAL(18)";   # DECIMAL(p), 1 <= p <= 18
    end case;
  end func;


const func integer: maxDecimalPrecision (in dbCategory: databaseKind) is func
  result
    var integer: precision is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      precision := 65;
      when {DB_SQLITE}:     precision := 100;  # NUMBER works without scale.
      when {DB_POSTGRESQL}: precision := 100;  # PostgreSQL is tested with maximum precision of 100.
      when {DB_OCI}:        precision := 38;
      when {DB_ODBC}:       precision := 38;
      when {DB_FIRE}:       precision := 18;
    end case;
  end func;


const func integer: maxDecimalLiteralPrecision (in dbCategory: databaseKind) is func
  result
    var integer: precision is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      precision := 65;
      when {DB_SQLITE}:     precision := 13;
      when {DB_POSTGRESQL}: precision := 100;
      when {DB_OCI}:        precision := 38;
      when {DB_ODBC}:       precision := 38;
      when {DB_FIRE}:       precision := 18;
    end case;
  end func;


const func string: numericIntType (in dbCategory: databaseKind) is func
  result
    var string: numericType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      numericType := "NUMERIC(65)";   # NUMERIC(p), 1 <= p <= 65
      when {DB_SQLITE}:     numericType := "NUMBER";
      when {DB_POSTGRESQL}: numericType := "NUMERIC(100)";  # NUMERIC(p), 1 <= p <= 1000
      when {DB_OCI}:        numericType := "NUMBER(38)";    # NUMBER(p),  1 <= p <= 38
      when {DB_ODBC}:       numericType := "NUMERIC(38)";   # NUMERIC(p), 1 <= p <= 38
      when {DB_FIRE}:       numericType := "NUMERIC(18)";   # NUMERIC(p), 1 <= p <= 18
     end case;
  end func;


const func integer: maxNumericPrecision (in dbCategory: databaseKind) is func
  result
    var integer: precision is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      precision := 65;
      when {DB_SQLITE}:     precision := 100;  # NUMBER works without scale.
      when {DB_POSTGRESQL}: precision := 100;  # PostgreSQL is tested with maximum precision of 100.
      when {DB_OCI}:        precision := 38;
      when {DB_ODBC}:       precision := 38;
      when {DB_FIRE}:       precision := 18;
     end case;
  end func;


const func integer: maxNumericLiteralPrecision (in dbCategory: databaseKind) is func
  result
    var integer: precision is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      precision := 65;
      when {DB_SQLITE}:     precision := 13;
      when {DB_POSTGRESQL}: precision := 100;
      when {DB_OCI}:        precision := 38;
      when {DB_ODBC}:       precision := 38;
      when {DB_FIRE}:       precision := 18;
    end case;
  end func;


const func string: decimalType (in dbCategory: databaseKind, in integer: scale) is func
  result
    var string: decimalType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      decimalType := "DECIMAL(65," <& scale <& ")";   # DECIMAL(p,s), 1 <= p <= 65, 0 <= s <= 30
      when {DB_SQLITE}:     decimalType := "NUMBER";
      when {DB_POSTGRESQL}: decimalType := "DECIMAL(100," <& scale <& ")";  # DECIMAL(p,s), 1 <= p <= 1000, 0 <= s <= p
      when {DB_OCI}:        decimalType := "NUMBER(38," <& scale <& ")";    # NUMBER(p,s),  1 <= p <= 38, -84 <= s <= 127
      when {DB_ODBC}:       decimalType := "DECIMAL(38," <& scale <& ")";   # DECIMAL(p,s), 1 <= p <= 38, 0 <= s <= p
      when {DB_FIRE}:       decimalType := "DECIMAL(18," <& scale <& ")";   # DECIMAL(p,s), 1 <= p <= 18, 0 <= s <= p
    end case;
  end func;


const func integer: minDecimalScale (in dbCategory: databaseKind) is func
  result
    var integer: scale is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      scale := 0;
      when {DB_SQLITE}:     scale := -5;   # NUMBER works without scale.
      when {DB_POSTGRESQL}: scale := 0;
      when {DB_OCI}:        scale := -84;
      when {DB_ODBC}:       scale := 0;
      when {DB_FIRE}:       scale := 0;
    end case;
  end func;


const func integer: maxDecimalScale (in dbCategory: databaseKind) is func
  result
    var integer: scale is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      scale := 30;
      when {DB_SQLITE}:     scale := 10;   # NUMBER works without scale.
      when {DB_POSTGRESQL}: scale := 100;  # PostgreSQL is tested with maximum precision of 100.
      when {DB_OCI}:        scale := 127;  # The scale can be bigger than the precision.
      when {DB_ODBC}:       scale := 38;
      when {DB_FIRE}:       scale := 18;
    end case;
  end func;


const func string: numericType (in dbCategory: databaseKind, in integer: scale) is func
  result
    var string: numericType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      numericType := "NUMERIC(65," <& scale <& ")";   # NUMERIC(p,s), 1 <= p <= 65, 0 <= s <= 30
      when {DB_SQLITE}:     numericType := "NUMBER";
      when {DB_POSTGRESQL}: numericType := "NUMERIC(100," <& scale <& ")";  # NUMERIC(p,s), 1 <= p <= 1000, 0 <= s <= p
      when {DB_OCI}:        numericType := "NUMBER(38," <& scale <& ")";    # NUMBER(p,s),  1 <= p <= 38, -84 <= s <= 127
      when {DB_ODBC}:       numericType := "NUMERIC(38," <& scale <& ")";   # NUMERIC(p,s), 1 <= p <= 38, 0 <= s <= p
      when {DB_FIRE}:       numericType := "NUMERIC(18," <& scale <& ")";   # NUMERIC(p,s), 1 <= p <= 18, 0 <= s <= p
     end case;
  end func;


const func integer: minNumericScale (in dbCategory: databaseKind) is func
  result
    var integer: scale is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      scale := 0;
      when {DB_SQLITE}:     scale := -5;   # NUMBER works without scale.
      when {DB_POSTGRESQL}: scale := 0;
      when {DB_OCI}:        scale := -84;
      when {DB_ODBC}:       scale := 0;
      when {DB_FIRE}:       scale := 0;
    end case;
  end func;


const func integer: maxNumericScale (in dbCategory: databaseKind) is func
  result
    var integer: scale is 0;
  begin
    case databaseKind of
      when {DB_MYSQL}:      scale := 30;
      when {DB_SQLITE}:     scale := 10;   # NUMBER works without scale.
      when {DB_POSTGRESQL}: scale := 100;  # PostgreSQL is tested with maximum precision of 100.
      when {DB_OCI}:        scale := 127;  # The scale can be bigger than the precision.
      when {DB_ODBC}:       scale := 38;
      when {DB_FIRE}:       scale := 18;
    end case;
  end func;


const func char: maxChar1FieldCharacter (in dbCategory: databaseKind) is func
  result
    var char: maxChar1FieldCharacter is char.last;
  begin
    case databaseKind of
      when {DB_MYSQL}:      maxChar1FieldCharacter := '\8888;'; # Code page 1252
      when {DB_SQLITE}:     maxChar1FieldCharacter := char.last;
      when {DB_POSTGRESQL}: maxChar1FieldCharacter := char.last;
      when {DB_OCI}:        maxChar1FieldCharacter := char.last;
      when {DB_ODBC}:       maxChar1FieldCharacter := char.last;
      when {DB_FIRE}:       maxChar1FieldCharacter := '\127;';
    end case;
  end func;


const func boolean: charFieldPreservesTrailingSpaces (in dbCategory: databaseKind) is func
  result
    var boolean: charFieldPreservesTrailingSpaces is FALSE;
  begin
    case databaseKind of
      when {DB_MYSQL}:      charFieldPreservesTrailingSpaces := FALSE;
      when {DB_SQLITE}:     charFieldPreservesTrailingSpaces := TRUE;
      when {DB_POSTGRESQL}: charFieldPreservesTrailingSpaces := FALSE;
      when {DB_OCI}:        charFieldPreservesTrailingSpaces := FALSE;
      when {DB_ODBC}:       charFieldPreservesTrailingSpaces := FALSE;
      when {DB_FIRE}:       charFieldPreservesTrailingSpaces := FALSE;
    end case;
  end func;


const func boolean: nullAllowedInStringLiteral (in dbCategory: databaseKind) is func
  result
    var boolean: nullAllowedInStringLiteral is FALSE;
  begin
    case databaseKind of
      when {DB_MYSQL}:      nullAllowedInStringLiteral := TRUE;
      when {DB_SQLITE}:     nullAllowedInStringLiteral := FALSE;
      when {DB_POSTGRESQL}: nullAllowedInStringLiteral := FALSE;
      when {DB_OCI}:        nullAllowedInStringLiteral := TRUE;
      when {DB_ODBC}:       nullAllowedInStringLiteral := FALSE;
      when {DB_FIRE}:       nullAllowedInStringLiteral := TRUE;
    end case;
  end func;


const func boolean: nullAllowedInString (in dbCategory: databaseKind) is func
  result
    var boolean: nullAllowedInString is FALSE;
  begin
    case databaseKind of
      when {DB_MYSQL}:      nullAllowedInString := TRUE;
      when {DB_SQLITE}:     nullAllowedInString := TRUE;
      when {DB_POSTGRESQL}: nullAllowedInString := FALSE;
      when {DB_OCI}:        nullAllowedInString := TRUE;
      when {DB_ODBC}:       nullAllowedInString := FALSE;
      when {DB_FIRE}:       nullAllowedInString := TRUE;
    end case;
  end func;


const func string: varcharType (in dbCategory: databaseKind) is func
  result
    var string: varcharType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      varcharType := "VARCHAR";
      when {DB_SQLITE}:     varcharType := "VARCHAR";
      when {DB_POSTGRESQL}: varcharType := "VARCHAR";
      when {DB_OCI}:        varcharType := "VARCHAR2";
      when {DB_ODBC}:       varcharType := "VARCHAR";
      when {DB_FIRE}:       varcharType := "VARCHAR";
    end case;
  end func;


const func string: blobType (in dbCategory: databaseKind) is func
  result
    var string: blobType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      blobType := "LONGBLOB";
      when {DB_SQLITE}:     blobType := "BLOB";
      when {DB_POSTGRESQL}: blobType := "BYTEA";
      when {DB_OCI}:        blobType := "BLOB";
      when {DB_ODBC}:       blobType := "VARBINARY(MAX)";
      when {DB_FIRE}:       blobType := "BLOB";
    end case;
  end func;


const func string: clobType (in dbCategory: databaseKind) is func
  result
    var string: clobType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      clobType := "TEXT";
      when {DB_SQLITE}:     clobType := "BLOB";
      when {DB_POSTGRESQL}: clobType := "TEXT";
      when {DB_OCI}:        clobType := "CLOB";
      when {DB_ODBC}:       clobType := "VARCHAR(MAX)";
    end case;
  end func;


const func string: timeType (in dbCategory: databaseKind) is func
  result
    var string: timeType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      timeType := "DATETIME";
      when {DB_SQLITE}:     timeType := "VARCHAR";
      when {DB_POSTGRESQL}: timeType := "TIMESTAMP";
      when {DB_OCI}:        timeType := "TIMESTAMP WITH TIME ZONE";
      when {DB_ODBC}:       timeType := "DATETIME2";
      when {DB_FIRE}:       timeType := "TIMESTAMP";
    end case;
  end func;


const func string: durationType (in dbCategory: databaseKind) is func
  result
    var string: durationType is "";
  begin
    case databaseKind of
      when {DB_MYSQL}:      durationType := "DATETIME";
      when {DB_SQLITE}:     durationType := "VARCHAR(32)";
      when {DB_POSTGRESQL}: durationType := "INTERVAL";
      when {DB_OCI}:        durationType := "INTERVAL DAY TO SECOND";
      when {DB_ODBC}:       durationType := "DATETIMEOFFSET"; # "INTERVAL DAY(3) TO SECOND(6)";
      when {DB_FIRE}:       durationType := "TIMESTAMP";
    end case;
  end func;


const func boolean: supportsFloatingDecimals (in dbCategory: databaseKind) is func
  result
    var boolean: supportsFloatingDecimals is FALSE;
  begin
    case databaseKind of
      when {DB_MYSQL}:      supportsFloatingDecimals := FALSE;
      when {DB_SQLITE}:     supportsFloatingDecimals := TRUE;
      when {DB_POSTGRESQL}: supportsFloatingDecimals := TRUE;
      when {DB_OCI}:        supportsFloatingDecimals := TRUE;
      when {DB_ODBC}:       supportsFloatingDecimals := FALSE;
    end case;
  end func;


const type: testState is new struct
    var string: tableName is "";
    var string: fieldName is "";
    var sqlStatement: statement is sqlStatement.value;
    var boolean: okay is TRUE;
    var boolean: details is FALSE;
  end struct;


const func testState: testState (in string: tableName,
    in string: fieldName, in boolean: details) is func
  result
    var testState: state is testState.value;
  begin
    state.tableName := tableName;
    state.fieldName := fieldName;
    state.details := details;
  end func;


const boolean: infiniteAllowed is FALSE;
const boolean: nanAllowed is FALSE;


const proc: DECL_INSERT (in type: elemType) is func
  begin

    const proc: insert (inout sqlStatement: statement, in elemType: anElement,
        in proc: succeedAction) is func
      local
        var boolean: succeeded is TRUE;
      begin
        block
          bind(statement, 1, anElement);
          execute(statement);
        exception
          otherwise: succeeded := FALSE;
        end block;
        if succeeded then
          succeedAction;
        else
          writeln(" *** Cannot insert " <& literal(anElement));
        end if;
      end func;

    const proc: insert (inout testState: state, in elemType: anElement,
        in proc: succeedAction) is func
      local
        var boolean: succeeded is TRUE;
      begin
        block
          bind(state.statement, 1, anElement);
          execute(state.statement);
        exception
          otherwise: succeeded := FALSE;
        end block;
        if succeeded then
          succeedAction;
        else
          state.okay := FALSE;
          if state.details then
            writeln(" *** Cannot insert " <& literal(anElement));
          end if;
        end if;
      end func;

    const proc: insInf (inout testState: state, in elemType: num,
        in proc: succeedAction) is func
      local
        var boolean: succeeded is TRUE;
      begin
        block
          bind(state.statement, 1, num);
          execute(state.statement);
        exception
          otherwise: succeeded := FALSE;
        end block;
        if succeeded then
          succeedAction;
          if not infiniteAllowed then
            state.okay := FALSE;
            if state.details then
              writeln(" *** Insert " <& num <& " succeeded, but it is not allowed");
            end if;
          end if
        elsif infiniteAllowed then
          state.okay := FALSE;
          if state.details then
            writeln(" *** Cannot insert " <& num);
          end if;
        end if;
      end func;

    const proc: insNaN (inout testState: state, in elemType: num,
        in proc: succeedAction) is func
      local
        var boolean: succeeded is TRUE;
      begin
        block
          bind(state.statement, 1, num);
          execute(state.statement);
        exception
          otherwise: succeeded := FALSE;
        end block;
        if succeeded then
          succeedAction;
          if not nanAllowed then
            state.okay := FALSE;
            if state.details then
              writeln(" *** Insert " <& num <& " succeeded, but it is not allowed");
            end if;
          end if
        elsif nanAllowed then
          state.okay := FALSE;
          if state.details then
            writeln(" *** Cannot insert " <& num);
          end if;
        end if;
      end func;

    const proc: insert (inout testState: state, in elemType: anElement,
        in proc: succeedAction, RANGE_CHECK) is func
      local
        var boolean: succeeded is TRUE;
      begin
        block
          bind(state.statement, 1, anElement);
          execute(state.statement);
        exception
          otherwise: succeeded := FALSE;
        end block;
        if succeeded then
          succeedAction;
          state.okay := FALSE;
          if state.details then
            writeln(" *** Insert " <& anElement <& " did not trigger an exception.");
          end if;
        end if;
      end func;

   end func;


DECL_INSERT(boolean);
DECL_INSERT(integer);
DECL_INSERT(bigInteger);
DECL_INSERT(float);
DECL_INSERT(bigRational);
DECL_INSERT(string);
DECL_INSERT(bstring);
DECL_INSERT(time);
DECL_INSERT(duration);


const proc: insert (inout sqlStatement: statement, NULL,
    in proc: succeedAction) is func
  local
    var boolean: succeeded is TRUE;
  begin
    block
      bind(statement, 1, NULL);
      execute(statement);
    exception
      otherwise: succeeded := FALSE;
    end block;
    if succeeded then
      succeedAction;
    else
      writeln(" *** Cannot insert NULL");
    end if;
  end func;


const proc: insert (inout testState: state, NULL, in proc: succeedAction) is func
  local
    var boolean: succeeded is TRUE;
  begin
    block
      bind(state.statement, 1, NULL);
      execute(state.statement);
    exception
      otherwise: succeeded := FALSE;
    end block;
    if succeeded then
      succeedAction;
    else
      state.okay := FALSE;
      if state.details then
        writeln(" *** Cannot insert NULL");
      end if;
    end if;
  end func;


const proc: insert (in database: testDb, inout testState: state,
    in string: aValue, in proc: succeedAction) is func
  local
    var sqlStatement: statement is sqlStatement.value;
  begin
    if succeeds(statement :=
          prepare(testDb, "INSERT INTO " & state.tableName &
                  " (" & state.fieldName & ") VALUES (" & aValue & ")")) and
        succeeds(execute(statement)) then
      succeedAction;
    else
      state.okay := FALSE;
      if state.details then
        writeln(" *** Cannot insert " <& literal(aValue));
      end if;
    end if;
  end func;


const proc: testPrepareAndExecute (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("testTable", "charField", FALSE);
    var database: emptyDb is database.value;
  begin
    if state.details then
      writeln("testPrepareAndExecute");
    end if;
    block
      execute(state.statement); # Executing an empty statement does nothing.
      if  not raisesRangeError(bind(state.statement, 1, 1_)) or
          not raisesRangeError(bind(state.statement, 1, 1_ / 1_)) or
          not raisesRangeError(bind(state.statement, 1, TRUE)) or
          not raisesRangeError(bind(state.statement, 1, bstring("x"))) or
          not raisesRangeError(bind(state.statement, 1, duration("P1D"))) or
          not raisesRangeError(bind(state.statement, 1, 1.0)) or
          not raisesRangeError(bind(state.statement, 1, 1)) or
          not raisesRangeError(bind(state.statement, 1, NULL)) or
          not raisesRangeError(bind(state.statement, 1, "x")) or
          not raisesRangeError(bind(state.statement, 1, time("2010-11-12 13:14:15.16"))) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Binding an empty statement succeeds.");
        end if;
      end if;
      if not raisesRangeError(state.statement := prepare(emptyDb,
                                  "CREATE TABLE " & state.tableName &
                                  " (" & state.fieldName & " CHAR)")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Preparing with empty database succeeds.");
        end if;
      end if;

      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR)");
      execute(state.statement);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                 " WHERE " & state.fieldName & " = ?");
      if not raisesDatabaseError(execute(state.statement)) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Omitted binding did not raise DATABASE_ERROR.");
        end if;
      end if;
      if not raisesRangeError(bind(state.statement, 0, "x")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Binding of parameter 0 did not raise RANGE_ERROR.");
        end if;
      end if;
      if not raisesRangeError(bind(state.statement, 2, "x")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Binding of non-existing parameter did not raise RANGE_ERROR.");
        end if;
      end if;
      if raisesRangeError(bind(state.statement, 1, "x")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Binding of existing parameter raises RANGE_ERROR.");
        end if;
      end if;
      if raisesDatabaseError(execute(state.statement)) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Execute select statement with bind variable raises DATABASE_ERROR.");
        end if;
      elsif fetch(state.statement) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Fetching data from an empty table succeeds.");
        end if;
      end if;

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                 " WHERE " & state.fieldName & " = 'x'");
      execute(state.statement);
      # Prepare next statement before the result of executed statement is fetched.
      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName & " WHERE " & state.fieldName & " = ?")) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Preparing a statement before a fetch of a previous statement is done raises DATABASE_ERROR. (empty table)");
        end if;
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES ('x')");
      execute(state.statement);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                 " WHERE " & state.fieldName & " = ?");
      bind(state.statement, 1, "x");
      execute(state.statement);
      if not fetch(state.statement) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Cannot fetch data from table with one entry.");
        end if;
      elsif fetch(state.statement) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Can fetch two entries from table with one entry.)");
        end if;
      end if;

      execute(state.statement);
      # Execute a second time before fetching data from the first execute.
      execute(state.statement);
      if not fetch(state.statement) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Cannot fetch data from table with one entry, when executing again.");
        end if;
      elsif fetch(state.statement) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Can fetch two entries from table with one entry, when executing again.)");
        end if;
      end if;

      # Bind and execute again.
      bind(state.statement, 1, "x");
      execute(state.statement);
      if not fetch(state.statement) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Cannot fetch data from table with one entry.");
        end if;
      elsif fetch(state.statement) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Can fetch two entries from table with one entry.)");
        end if;
      end if;

      execute(state.statement);
      # Bind and execute again although a previous execute is not fetched.
      bind(state.statement, 1, "x");
      execute(state.statement);
      if not fetch(state.statement) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Cannot fetch data from table with one entry.");
        end if;
      elsif fetch(state.statement) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Can fetch two entries from table with one entry.)");
        end if;
      end if;

      # Prepare next statement before the result of executed statement is fetched.
      execute(state.statement);
      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                     " WHERE " & state.fieldName & " = ?")) then
        # Reset statement to make sure that the next prepare works:
        state.statement := sqlStatement.value;
        state.okay := FALSE;
        if state.details then
          writeln(" *** Preparing a statement before a fetch of a previous statement is done raises DATABASE_ERROR. (non-empty table)");
        end if;
      end if;

      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                     " WHERE " & state.fieldName & " = 'x' /* ? */")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Prepare: Question mark in comment interpreted as parameter placeholder.");
        end if;
      elsif raisesDatabaseError(execute(state.statement)) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Execute: Question mark in comment interpreted as parameter placeholder.");
        end if;
      end if;
      # Reset statement before a fetch is done to make sure that the next prepare works.
      state.statement := sqlStatement.value;

      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                     " WHERE " & state.fieldName & " = 'x' -- ? \n")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Prepare: Question mark in line comment interpreted as parameter placeholder.");
        end if;
      elsif raisesDatabaseError(execute(state.statement)) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Execute: Question mark in line comment interpreted as parameter placeholder.");
        end if;
      end if;
      # Reset statement before a fetch is done to make sure that the next prepare works.
      state.statement := sqlStatement.value;

      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                     " WHERE " & state.fieldName & " = /*'*/?/*'*/")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Prepare: Quotation characters in comments are interpreted as literal. (1)");
        end if;
      elsif raisesRangeError(bind(state.statement, 1, "x")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Bind: Quotation characters in comments are interpreted as literal. (1)");
        end if;
      end if;

      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                     " WHERE " & state.fieldName & " = /*'*/'\\'/*'*/")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Prepare: Quotation characters in comments are interpreted as literal. (2)");
        end if;
      end if;

      if raisesDatabaseError(
          state.statement := prepare(testDb, "SELECT * FROM " & state.tableName &
                                     " WHERE " & state.fieldName & " = '/*'")) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Prepare: Comment sequence in literal triggers error.");
        end if;
      elsif raisesDatabaseError(execute(state.statement)) then
        state.okay := FALSE;
        if state.details then
          writeln(" *** Execute: Comment sequence in literal triggers error.");
        end if;
      end if;
      # Reset statement before a fetch is done to make sure that the next prepare works.
      state.statement := sqlStatement.value;

    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Preparing and executing prepared statements works okay.");
    else
      writeln(" *** Preparing and executing prepared statements does not work okay.");
    end if;
  end func;


const proc: testBooleanField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("booleanTest", "booleanField", FALSE);
    var array boolean: expect is 0 times FALSE;
    var boolean: booleanColumn is FALSE;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is 0_/1_;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testBooleanField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= FALSE);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,  NULL,  expect &:= FALSE);

      insert(testDb, state,    "0", expect &:= FALSE);
      insert(testDb, state,    "1", expect &:=  TRUE);

      insert(state, FALSE,  expect &:= FALSE);
      insert(state,  TRUE,  expect &:=  TRUE);

      insert(state,     0,  expect &:= FALSE);
      insert(state,     1,  expect &:=  TRUE);

      insert(state,     0_, expect &:= FALSE);
      insert(state,     1_, expect &:=  TRUE);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        booleanColumn := column(state.statement, 1, boolean);
        if booleanColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found boolean " <& booleanColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected NULL found boolean " <& booleanColumn);
          end if;
        end if;
(*
        integerColumn := column(state.statement, 1, integer);
        if integerColumn <> ord(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found integer " <& integerColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected NULL found integer " <& integerColumn);
          end if;
        end if;
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> bigInteger(ord(expect[row])) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> bigRational(ord(expect[row])) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigRational " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
*)
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBooleanField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching booleanFields works okay.");
    else
      writeln(" *** Inserting and fetching booleanFields does not work okay.");
    end if;
  end func;


const proc: testInt8Field (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("int8Test", "int8Field", FALSE);
    var array integer: expect is 0 times 0;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is 0_/1_;
    var float: floatColumn is 0.0;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testInt8Field: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & int8Type(databaseKind) & ")");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= 0);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:=    0);

      insert(testDb, state,   "0", expect &:=   0);
      insert(testDb, state,   "1", expect &:=   1);
      insert(testDb, state, "127", expect &:= 127);
      if maxInt8Value(databaseKind) = 255 then
        insert(testDb, state,  "255", expect &:=  255);
      else
        insert(testDb, state,   "-1", expect &:=   -1);
        insert(testDb, state, "-128", expect &:= -128);
      end if;

      insert(state,   0, expect &:=   0);
      insert(state,   1, expect &:=   1);
      insert(state, 127, expect &:= 127);
      if maxInt8Value(databaseKind) = 255 then
        insert(state,  255, expect &:=  255);
        if int8Type(databaseKind) = "TINYINT" and withRangeCheck(databaseKind) then
          insert(state, 256, expect &:= 256, RANGE_CHECK);
          insert(state,  -1, expect &:=  -1, RANGE_CHECK);
        end if;
      else
        insert(state,   -1, expect &:=   -1);
        insert(state, -128, expect &:= -128);
        if int8Type(databaseKind) = "TINYINT" and withRangeCheck(databaseKind) then
          insert(state,  128, expect &:=  128, RANGE_CHECK);
          insert(state, -129, expect &:= -129, RANGE_CHECK);
        end if;
      end if;

      insert(state,   0_, expect &:=   0);
      insert(state,   1_, expect &:=   1);
      insert(state, 127_, expect &:= 127);
      if maxInt8Value(databaseKind) = 255 then
        insert(state,  255_, expect &:=  255);
        if int8Type(databaseKind) = "TINYINT" and withRangeCheck(databaseKind) then
          insert(state, 256_, expect &:= 256, RANGE_CHECK);
          insert(state,  -1_, expect &:=  -1, RANGE_CHECK);
        end if;
      else
        insert(state,   -1_, expect &:=   -1);
        insert(state, -128_, expect &:= -128);
        if int8Type(databaseKind) = "TINYINT" and withRangeCheck(databaseKind) then
          insert(state,  128_, expect &:=  128, RANGE_CHECK);
          insert(state, -129_, expect &:= -129, RANGE_CHECK);
        end if;
      end if;

      insert(state, FALSE, expect &:= 0);
      insert(state,  TRUE, expect &:= 1);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        integerColumn := column(state.statement, 1, integer);
        if integerColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found integer " <& integerColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected NULL found integer " <& integerColumn);
          end if;
        end if;
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> bigInteger(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> bigRational(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigRational " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
        floatColumn := column(state.statement, 1, float);
        if floatColumn <> float(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found float " <& floatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected NULL found float " <& floatColumn);
          end if;
        end if;
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt8Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching int8Fields works okay.");
    else
      writeln(" *** Inserting and fetching int8Fields does not work okay.");
    end if;
  end func;


const proc: testInt16Field (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("int16Test", "int16Field", FALSE);
    var array integer: expect is 0 times 0;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is 0_/1_;
    var float: floatColumn is 0.0;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testInt16Field: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " SMALLINT)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= 0);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL, expect &:= 0);

      insert(testDb, state,      "0", expect &:=      0);
      insert(testDb, state,      "1", expect &:=      1);
      insert(testDb, state,  "32767", expect &:=  32767);
      insert(testDb, state,     "-1", expect &:=     -1);
      insert(testDb, state, "-32768", expect &:= -32768);

      insert(state,      0, expect &:=      0);
      insert(state,      1, expect &:=      1);
      insert(state,  32767, expect &:=  32767);
      insert(state,     -1, expect &:=     -1);
      insert(state, -32768, expect &:= -32768);
      if withRangeCheck(databaseKind) then
        insert(state,  32768, expect &:=  32768, RANGE_CHECK);
        insert(state, -32769, expect &:= -32769, RANGE_CHECK);
      end if;

      insert(state,      0_, expect &:=      0);
      insert(state,      1_, expect &:=      1);
      insert(state,  32767_, expect &:=  32767);
      insert(state,     -1_, expect &:=     -1);
      insert(state, -32768_, expect &:= -32768);
      if withRangeCheck(databaseKind) then
        insert(state,  32768_, expect &:=  32768, RANGE_CHECK);
        insert(state, -32769_, expect &:=  32769, RANGE_CHECK);
      end if;

      insert(state, FALSE, expect &:= 0);
      insert(state,  TRUE, expect &:= 1);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        integerColumn := column(state.statement, 1, integer);
        if integerColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found integer " <& integerColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected NULL found integer " <& integerColumn);
          end if;
        end if;
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> bigInteger(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> bigRational(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigRational " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
        floatColumn := column(state.statement, 1, float);
        if floatColumn <> float(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found float " <& floatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected NULL found float " <& floatColumn);
          end if;
        end if;
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt16Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching int16Fields works okay.");
    else
      writeln(" *** Inserting and fetching int16Fields does not work okay.");
    end if;
  end func;


const proc: testInt32Field (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("int32Test", "int32Field", FALSE);
    var array integer: expect is 0 times 0;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is 0_/1_;
    var float: floatColumn is 0.0;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testInt32Field: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " INTEGER)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= 0);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL, expect &:= 0);

      insert(testDb, state,           "0", expect &:=           0);
      insert(testDb, state,           "1", expect &:=           1);
      insert(testDb, state,  "2147483647", expect &:=  2147483647);
      insert(testDb, state,          "-1", expect &:=          -1);
      insert(testDb, state, "-2147483648", expect &:= -2147483648);

      insert(state,           0, expect &:=           0);
      insert(state,           1, expect &:=           1);
      insert(state,     1048578, expect &:=     1048578); # Zero bytes in binary representation.
      insert(state,  2147483647, expect &:=  2147483647);
      insert(state,          -1, expect &:=          -1);
      insert(state, -2147483648, expect &:= -2147483648);
      if withRangeCheck(databaseKind) then
        insert(state,  2147483648, expect &:=  2147483648, RANGE_CHECK);
        insert(state, -2147483649, expect &:= -2147483649, RANGE_CHECK);
      end if;

      insert(state,           0_, expect &:=           0);
      insert(state,           1_, expect &:=           1);
      insert(state,     1048578_, expect &:=     1048578); # Zero bytes in binary representation.
      insert(state,  2147483647_, expect &:=  2147483647);
      insert(state,          -1_, expect &:=          -1);
      insert(state, -2147483648_, expect &:= -2147483648);
      if withRangeCheck(databaseKind) then
        insert(state,  2147483648_, expect &:=  2147483648, RANGE_CHECK);
        insert(state, -2147483649_, expect &:= -2147483649, RANGE_CHECK);
      end if;

      insert(state, FALSE, expect &:= 0);
      insert(state,  TRUE, expect &:= 1);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        integerColumn := column(state.statement, 1, integer);
        if integerColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found integer " <& integerColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected NULL found integer " <& integerColumn);
          end if;
        end if;
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> bigInteger(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> bigRational(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigRational " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
        floatColumn := column(state.statement, 1, float);
        if floatColumn <> float(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found float " <& floatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected NULL found float " <& floatColumn);
          end if;
        end if;
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt32Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching int32Fields works okay.");
    else
      writeln(" *** Inserting and fetching int32Fields does not work okay.");
    end if;
  end func;


const proc: testInt64Field (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("int64Test", "int64Field", FALSE);
    var array integer: expect is 0 times 0;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is 0_/1_;
    var float: floatColumn is 0.0;
    var string: floatAsString is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testInt64Field: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & int64Type(databaseKind) & ")");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= 0);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL, expect &:= 0);

      insert(testDb, state,                    "0", expect &:=                         0);
      insert(testDb, state,                    "1", expect &:=                         1);
      insert(testDb, state,             "16777215", expect &:=                  16777215); # Maximum odd float
      insert(testDb, state,             "16777216", expect &:=                  16777216);
      insert(testDb, state,             "16777217", expect &:=                  16777217);
      insert(testDb, state,     "9007199254740991", expect &:=          9007199254740991); # Maximum odd double
      insert(testDb, state,     "9007199254740992", expect &:=          9007199254740992);
      insert(testDb, state,     "9007199254740993", expect &:=          9007199254740993);
      insert(testDb, state,  "9223372036854775807", expect &:=       9223372036854775807);
      insert(testDb, state,                   "-1", expect &:=                        -1);
      insert(testDb, state,            "-16777215", expect &:=                 -16777215); # Minimum odd float
      insert(testDb, state,            "-16777216", expect &:=                 -16777216);
      insert(testDb, state,            "-16777217", expect &:=                 -16777217);
      insert(testDb, state,    "-9007199254740991", expect &:=         -9007199254740991); # Minimum odd double
      insert(testDb, state,    "-9007199254740992", expect &:=         -9007199254740992);
      insert(testDb, state,    "-9007199254740993", expect &:=         -9007199254740993);
      insert(testDb, state, "-9223372036854775808", expect &:= pred(-9223372036854775807));

      insert(state,                         0,  expect &:=                         0);
      insert(state,                         1,  expect &:=                         1);
      insert(state,                  16777215,  expect &:=                  16777215); # Maximum odd float
      insert(state,                  16777216,  expect &:=                  16777216);
      insert(state,                  16777217,  expect &:=                  16777217);
      insert(state,                4297064452,  expect &:=                4297064452); # Zero bytes in binary representation.
      insert(state,          9007199254740991,  expect &:=          9007199254740991); # Maximum odd double
      insert(state,          9007199254740992,  expect &:=          9007199254740992);
      insert(state,          9007199254740993,  expect &:=          9007199254740993);
      insert(state,       9223372036854775807,  expect &:=       9223372036854775807);
      insert(state,                        -1,  expect &:=                        -1);
      insert(state,                 -16777215,  expect &:=                 -16777215); # Maximum odd float
      insert(state,                 -16777216,  expect &:=                 -16777216);
      insert(state,                 -16777217,  expect &:=                 -16777217);
      insert(state,         -9007199254740991,  expect &:=         -9007199254740991); # Maximum odd double
      insert(state,         -9007199254740992,  expect &:=         -9007199254740992);
      insert(state,         -9007199254740993,  expect &:=         -9007199254740993);
      insert(state, pred(-9223372036854775807), expect &:= pred(-9223372036854775807));

      insert(state,                    0_, expect &:=                         0);
      insert(state,                    1_, expect &:=                         1);
      insert(state,             16777215_, expect &:=                  16777215); # Maximum odd float
      insert(state,             16777216_, expect &:=                  16777216);
      insert(state,             16777217_, expect &:=                  16777217);
      insert(state,           4297064452_, expect &:=                4297064452); # Zero bytes in binary representation.
      insert(state,     9007199254740991_, expect &:=          9007199254740991); # Maximum odd double
      insert(state,     9007199254740992_, expect &:=          9007199254740992);
      insert(state,     9007199254740993_, expect &:=          9007199254740993);
      insert(state,  9223372036854775807_, expect &:=       9223372036854775807);
      insert(state,                   -1_, expect &:=                        -1);
      insert(state,            -16777215_, expect &:=                 -16777215); # Maximum odd float
      insert(state,            -16777216_, expect &:=                 -16777216);
      insert(state,            -16777217_, expect &:=                 -16777217);
      insert(state,    -9007199254740991_, expect &:=         -9007199254740991); # Maximum odd double
      insert(state,    -9007199254740992_, expect &:=         -9007199254740992);
      insert(state,    -9007199254740993_, expect &:=         -9007199254740993);
      insert(state, -9223372036854775808_, expect &:= pred(-9223372036854775807));
      if withRangeCheck(databaseKind) then
        insert(state,  9223372036854775808_, expect &:= 0, RANGE_CHECK); # No valid value to expect.
        insert(state, -9223372036854775809_, expect &:= 0, RANGE_CHECK); # No valid value to expect.
      end if;

      insert(state, FALSE, expect &:= 0);
      insert(state,  TRUE, expect &:= 1);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        integerColumn := column(state.statement, 1, integer);
        if integerColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found integer " <& integerColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected NULL found integer " <& integerColumn);
          end if;
        end if;
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> bigInteger(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> bigRational(expect[row]) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
        floatAsString := str(float(str(expect[row])));
        if endsWith(floatAsString, ".0") and
            str(expect[row]) = floatAsString[.. length(floatAsString) - 2] then
          # The expected value is representable as float.
          floatColumn := column(state.statement, 1, float);
          if floatColumn <> float(str(expect[row])) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testInt64Field 2: Row: " <& row <&
                      " Expected " <& expect[row] <& " found float " <& floatColumn);
            end if;
          end if;
          if row <= 2 and not isNull(state.statement, 1) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testInt64Field: Row: " <& row <&
                      " Expected NULL found float " <& floatColumn);
            end if;
          end if;
        end if;
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testInt64Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching int64Fields works okay.");
    else
      writeln(" *** Inserting and fetching int64Fields does not work okay.");
    end if;
  end func;


const proc: testBigIntField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("bigIntTest", "bigIntField", FALSE);
    var array bigInteger: expect is 0 times 0_;
    var sqlStatement: statement is sqlStatement.value;
    var bigInteger: bigIntColumn is 0_;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testBigIntField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & bigIntType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,                      NULL,  expect &:=                    0_);
      insert(state,                         1,  expect &:=                    1_);
      insert(state,                         2,  expect &:=                    2_);
      insert(state,       -999999999999999999,  expect &:=  -999999999999999999_);
      insert(state,        999999999999999999,  expect &:=   999999999999999999_);
      insert(state,      -1000000000000000000,  expect &:= -1000000000000000000_);
      insert(state,       1000000000000000000,  expect &:=  1000000000000000000_);
      insert(state,      -1234567890123456789,  expect &:= -1234567890123456789_);
      insert(state,       1234567890123456789,  expect &:=  1234567890123456789_);
      insert(state, pred(-9223372036854775807), expect &:= -9223372036854775808_);
      insert(state,       9223372036854775807,  expect &:=  9223372036854775807_);

      insert(state,                    1_, expect &:=                    1_);
      insert(state,                    2_, expect &:=                    2_);
      insert(state,  -999999999999999999_, expect &:=  -999999999999999999_);
      insert(state,   999999999999999999_, expect &:=   999999999999999999_);
      insert(state, -1000000000000000000_, expect &:= -1000000000000000000_);
      insert(state,  1000000000000000000_, expect &:=  1000000000000000000_);
      insert(state, -1234567890123456789_, expect &:= -1234567890123456789_);
      insert(state,  1234567890123456789_, expect &:=  1234567890123456789_);
      insert(state, -9223372036854775808_, expect &:= -9223372036854775808_);
      insert(state,  9223372036854775807_, expect &:=  9223372036854775807_);
      if bigIntTypeBits(databaseKind) >= 127 then
        insert(state,  -9223372036854775809_, expect &:=  -9223372036854775809_);
        insert(state,   9223372036854775808_, expect &:=   9223372036854775808_);
        insert(state,  -9999999999999999999_, expect &:=  -9999999999999999999_);
        insert(state,   9999999999999999999_, expect &:=   9999999999999999999_);
        insert(state, -10000000000000000000_, expect &:= -10000000000000000000_);
        insert(state,  10000000000000000000_, expect &:=  10000000000000000000_);
        insert(state,  -9999999999999999999999999999999999999_, expect &:=  -9999999999999999999999999999999999999_);
        insert(state,   9999999999999999999999999999999999999_, expect &:=   9999999999999999999999999999999999999_);
        insert(state, -10000000000000000000000000000000000000_, expect &:= -10000000000000000000000000000000000000_);
        insert(state,  10000000000000000000000000000000000000_, expect &:=  10000000000000000000000000000000000000_);
        insert(state, -85070591730234615865843651857942052864_, expect &:= -85070591730234615865843651857942052864_);
        insert(state,  85070591730234615865843651857942052863_, expect &:=  85070591730234615865843651857942052863_);
      end if;
      if bigIntTypeBits(databaseKind) >= 216 then
        insert(state,   -85070591730234615865843651857942052864_, expect &:=   -85070591730234615865843651857942052864_);
        insert(state,    85070591730234615865843651857942052863_, expect &:=    85070591730234615865843651857942052863_);
        insert(state,   -99999999999999999999999999999999999999_, expect &:=   -99999999999999999999999999999999999999_);
        insert(state,    99999999999999999999999999999999999999_, expect &:=    99999999999999999999999999999999999999_);
        insert(state,  -100000000000000000000000000000000000000_, expect &:=  -100000000000000000000000000000000000000_);
        insert(state,   100000000000000000000000000000000000000_, expect &:=   100000000000000000000000000000000000000_);
        insert(state,  -170141183460469231731687303715884105728_, expect &:=  -170141183460469231731687303715884105728_);
        insert(state,   170141183460469231731687303715884105727_, expect &:=   170141183460469231731687303715884105727_);
        insert(state,  -340282366920938463463374607431768211455_, expect &:=  -340282366920938463463374607431768211455_);
        insert(state,   340282366920938463463374607431768211455_, expect &:=   340282366920938463463374607431768211455_);
        insert(state, -1000000000000000000000000000000000000000_, expect &:= -1000000000000000000000000000000000000000_);
        insert(state,  1000000000000000000000000000000000000000_, expect &:=  1000000000000000000000000000000000000000_);
      end if;

      insert(state, FALSE, expect &:= 0_);
      insert(state,  TRUE, expect &:= 1_);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigIntField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigIntField: Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row > 1 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.okay then
            writeln(" *** testBigIntField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching bigInteger fields works okay.");
    else
      writeln(" *** Inserting and fetching bigInteger fields does not work okay.");
    end if;
  end func;


const proc: testFloatField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("floatTest", "floatField", FALSE);
    var array float: expect is 0 times 0.0;
    var float: floatColumn is 0.0;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testFloatField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & floatType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,                       expect &:=                    0.0);
      insert(state, 1,                          expect &:=                    1.0);
      insert(state, pred(-9223372036854775807), expect &:= -9223372036854775808.0);
      insert(state, 9223372036854775807,        expect &:=  9223372036854775807.0);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,                    2_,      expect &:=                    2.0);
      insert(state, -9223372036854775809_,      expect &:= -9223372036854775809.0);
      insert(state,  9223372036854775808_,      expect &:=  9223372036854775808.0);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,  7_/2_,                     expect &:=  3.5);
      insert(state,  5000000_ / 1024_,          expect &:=  4882.8125);
      insert(state, -9223372036854775808_/1_,   expect &:= -9223372036854775808.0);
      insert(state,  9223372036854775807_/1_,   expect &:=  9223372036854775807.0);
      insInf(state, -1_/0_,                     expect &:= -Infinity);
      insInf(state,  1_/0_,                     expect &:=  Infinity);
      insNaN(state,  0_/0_,                     expect &:=  NaN);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,  4.125,                     expect &:=  4.125);
      insert(state,  0.0009765625,              expect &:=  0.0009765625);
      insert(state,  0.000244140625,            expect &:=  0.000244140625);
      insert(state,  9.5367431640625e-7,        expect &:=  9.5367431640625e-7);
      insert(state,  4.76837158203125e-7,       expect &:=  4.76837158203125e-7);
      insert(state,  2.384185791015625e-7,      expect &:=  2.384185791015625e-7);
      insert(state,  5.9604644775390625e-8,     expect &:=  5.9604644775390625e-8);
      insert(state, -9223372036854775808.0,     expect &:= -9223372036854775808.0);
      insert(state,  9223372036854775808.0,     expect &:=  9223372036854775808.0);
      insInf(state, -Infinity,                  expect &:= -Infinity);
      insInf(state,  Infinity,                  expect &:=  Infinity);
      insNaN(state,  NaN,                       expect &:=  NaN);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        floatColumn := column(state.statement, 1, float);
        if floatColumn <> expect[row] and
            (not isNaN(floatColumn) or not isNaN(expect[row])) then
          state.okay := FALSE;
          if state.okay then
            writeln(" *** testFloatField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found float " <& floatColumn);
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.okay then
            writeln(" *** testFloatField: Row: " <& row <&
                    " Expected NULL found float " <& floatColumn);
          end if;
        end if;
        if row > 1 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.okay then
            writeln(" *** testFloatField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching float fields works okay.");
    else
      writeln(" *** Inserting and fetching float fields does not work okay.");
    end if;
  end func;


const proc: testDoubleField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("doubleTest", "doubleField", FALSE);
    var array float: expect is 0 times 0.0;
    var float: doubleColumn is 0.0;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testDoubleField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                           " (" & state.fieldName & " " & doubleType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,                       expect &:=                    0.0);
      insert(state, 1,                          expect &:=                    1.0);
      insert(state, pred(-9223372036854775807), expect &:= -9223372036854775808.0);
      insert(state, 9223372036854775807,        expect &:=  9223372036854775807.0);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 2_,                         expect &:=                    2.0);
      insert(state, -9223372036854775809_,      expect &:= -9223372036854775809.0);
      insert(state, 9223372036854775808_,       expect &:=  9223372036854775808.0);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,  7_/2_,                     expect &:=  3.5);
      insert(state,  1234567654321_ / 781250_,  expect &:=  1580246.59753088);
      insert(state, -9223372036854775808_/1_,   expect &:= -9223372036854775808.0);
      insert(state,  9223372036854775807_/1_,   expect &:=  9223372036854775807.0);
      insInf(state, -1_/0_,                     expect &:= -Infinity);
      insInf(state,  1_/0_,                     expect &:=  Infinity);
      insNaN(state,  0_/0_,                     expect &:=  NaN);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,  4.125,                     expect &:=  4.125);
      insert(state,  0.0009765625,              expect &:=  0.0009765625);
      insert(state,  0.000244140625,            expect &:=  0.000244140625);
      insert(state,  9.5367431640625e-7,        expect &:=  9.5367431640625e-7);
      insert(state,  4.76837158203125e-7,       expect &:=  4.76837158203125e-7);
      insert(state,  2.384185791015625e-7,      expect &:=  2.384185791015625e-7);
      insert(state,  5.9604644775390625e-8,     expect &:=  5.9604644775390625e-8);
      insert(state, -9223372036854775808.0,     expect &:= -9223372036854775808.0);
      insert(state,  9223372036854775808.0,     expect &:=  9223372036854775808.0);
      insInf(state, -Infinity,                  expect &:= -Infinity);
      insInf(state,  Infinity,                  expect &:=  Infinity);
      insNaN(state,  NaN,                       expect &:=  NaN);

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        doubleColumn := column(state.statement, 1, float);
        if doubleColumn <> expect[row] and
            (not isNaN(doubleColumn) or not isNaN(expect[row])) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testDoubleField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found double " <& doubleColumn);
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testDoubleField: Row: " <& row <&
                    " Expected NULL found double " <& doubleColumn);
          end if;
        end if;
        if row > 1 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testDoubleField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching double fields works okay.");
    else
      writeln(" *** Inserting and fetching double fields does not work okay.");
    end if;
  end func;


const proc: testBigRatField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("bigRatTest", "bigRatField", FALSE);
    var array bigRational: expect is 0 times 0_/1_;
    var bigRational: bigRatColumn is bigRational.value;
    var float: floatColumn is 0.0;
    var integer: row is 1;
    var bigInteger: numerator is 0_;
  begin
    if state.details then
      writeln("testBigRatField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & bigRatType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,                      NULL,  expect &:=                    0_/1_);
      insert(state,                         0,  expect &:=                    0_/1_);
      insert(state,                         1,  expect &:=                    1_/1_);
      insert(state, pred(-9223372036854775807), expect &:= -9223372036854775808_/1_);
      insert(state,       9223372036854775807,  expect &:=  9223372036854775807_/1_);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,                    2_,      expect &:=                    2_/1_);
      insert(state, -9223372036854775809_,      expect &:= -9223372036854775809_/1_);
      insert(state,  9223372036854775808_,      expect &:=  9223372036854775808_/1_);

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                           " (" & state.fieldName & ") VALUES (?)");
      insert(state,  0_/1_,                     expect &:=  0_/1_);
      insert(state,  1_/1_,                     expect &:=  1_/1_);
      insert(state,  7_/2_,                     expect &:=  7_/2_);
      insert(state,  9223372036854775807_/1_,   expect &:=  9223372036854775807_/1_);
      insert(state,  9223372036854775808_/1_,   expect &:=  9223372036854775808_/1_);
      insert(state,  12345678901234500000000_/1_,
          expect &:= 12345678901234500000000_/1_);
      insert(state,  123456789012345600000000_/1_,
          expect &:= 123456789012345600000000_/1_);
      insert(state,  12345678901234567890123400000000_/1_,
          expect &:= 12345678901234567890123400000000_/1_);
      insert(state,  12345678901234567890123456789012000_/1_,
          expect &:= 12345678901234567890123456789012000_/1_);
      insert(state,  1234567890123450_/1_,
          expect &:= 1234567890123450_/1_);
      insert(state,  12345678901234560_/1_,
          expect &:= 12345678901234560_/1_);
      insert(state,  1234567890123456789012340_/1_,
          expect &:= 1234567890123456789012340_/1_);
      insert(state,  123456789012345678901234567890120_/1_,
          expect &:= 123456789012345678901234567890120_/1_);
      insert(state,  123456789012345_/1_,
          expect &:= 123456789012345_/1_);
      insert(state,  1234567890123456_/1_,
          expect &:= 1234567890123456_/1_);
      insert(state,  123456789012345678901234_/1_,
          expect &:= 123456789012345678901234_/1_);
      insert(state,  12345678901234567890123456789012_/1_,
          expect &:= 12345678901234567890123456789012_/1_);
      insert(state,  123456789012345_/10_,
          expect &:= 123456789012345_/10_);
      insert(state,  1234567890123456_/10_,
          expect &:= 1234567890123456_/10_);
      insert(state,  123456789012345678901234_/10_,
          expect &:= 123456789012345678901234_/10_);
      insert(state,  12345678901234567890123456789012_/10_,
          expect &:= 12345678901234567890123456789012_/10_);
      insert(state,  123456789012345_/10000000_,
          expect &:= 123456789012345_/10000000_);
      insert(state,  1234567890123456_/100000000_,
          expect &:= 1234567890123456_/100000000_);
      insert(state,  123456789012345678901234_/1000000000000_,
          expect &:= 123456789012345678901234_/1000000000000_);
      insert(state,  12345678901234567890123456789012_/10000000000000000_,
          expect &:= 12345678901234567890123456789012_/10000000000000000_);
      insert(state,  123456789012345678901234567890123456789_/100000000000000000000_,
          expect &:= 123456789012345678901234567890123456789_/100000000000000000000_);
      insert(state,  123456789012345_/100000000000000_,
          expect &:= 123456789012345_/100000000000000_);
      insert(state,  1234567890123456_/1000000000000000_,
          expect &:= 1234567890123456_/1000000000000000_);
      insert(state,  123456789012345678901234_/100000000000000000000000_,
          expect &:= 123456789012345678901234_/100000000000000000000000_);
      insert(state,  1234567890123456789012345678901_/1000000000000000000000000000000_,
          expect &:= 1234567890123456789012345678901_/1000000000000000000000000000000_);
      insert(state,  123456789012345_/1000000000000000_,
          expect &:= 123456789012345_/1000000000000000_);
      insert(state,  1234567890123456_/10000000000000000_,
          expect &:= 1234567890123456_/10000000000000000_);
      insert(state,  123456789012345678901234_/1000000000000000000000000_,
          expect &:= 123456789012345678901234_/1000000000000000000000000_);
      insert(state,  123456789012345678901234567891_/1000000000000000000000000000000_,
          expect &:= 123456789012345678901234567891_/1000000000000000000000000000000_);
      insert(state,  123456789012345_/10000000000000000_,
          expect &:= 123456789012345_/10000000000000000_);
      insert(state,  1234567890123456_/100000000000000000_,
          expect &:= 1234567890123456_/100000000000000000_);
      insert(state,  123456789012345678901234_/10000000000000000000000000_,
          expect &:= 123456789012345678901234_/10000000000000000000000000_);
      insert(state,  12345678901234567890123456789_/1000000000000000000000000000000_,
          expect &:= 12345678901234567890123456789_/1000000000000000000000000000000_);
      insert(state,  123456789012345_/10000000000000000000_,
          expect &:= 123456789012345_/10000000000000000000_);
      insert(state,  1234567890123456_/100000000000000000000_,
          expect &:= 1234567890123456_/100000000000000000000_);
      insert(state,  123456789012345678901234_/1000000000000000000000000000000_,
          expect &:= 123456789012345678901234_/1000000000000000000000000000000_);
      insert(state, -0_/1_,                     expect &:= -0_/1_);
      insert(state, -1_/1_,                     expect &:= -1_/1_);
      insert(state, -7_/2_,                     expect &:= -7_/2_);
      insert(state, -9223372036854775807_/1_,   expect &:= -9223372036854775807_/1_);
      insert(state, -9223372036854775808_/1_,   expect &:= -9223372036854775808_/1_);
      insert(state,  -12345678901234500000000_/1_,
          expect &:= -12345678901234500000000_/1_);
      insert(state,  -123456789012345600000000_/1_,
          expect &:= -123456789012345600000000_/1_);
      insert(state,  -12345678901234567890123400000000_/1_,
          expect &:= -12345678901234567890123400000000_/1_);
      insert(state,  -1234567890123456789012345678901200_/1_,
          expect &:= -1234567890123456789012345678901200_/1_);
      insert(state,  -1234567890123450_/1_,
          expect &:= -1234567890123450_/1_);
      insert(state,  -12345678901234560_/1_,
          expect &:= -12345678901234560_/1_);
      insert(state,  -1234567890123456789012340_/1_,
          expect &:= -1234567890123456789012340_/1_);
      insert(state,  -123456789012345678901234567890120_/1_,
          expect &:= -123456789012345678901234567890120_/1_);
      insert(state,  -123456789012345_/1_,
          expect &:= -123456789012345_/1_);
      insert(state,  -1234567890123456_/1_,
          expect &:= -1234567890123456_/1_);
      insert(state,  -123456789012345678901234_/1_,
          expect &:= -123456789012345678901234_/1_);
      insert(state,  -12345678901234567890123456789012_/1_,
          expect &:= -12345678901234567890123456789012_/1_);
      insert(state,  -123456789012345_/10_,
          expect &:= -123456789012345_/10_);
      insert(state,  -1234567890123456_/10_,
          expect &:= -1234567890123456_/10_);
      insert(state,  -123456789012345678901234_/10_,
          expect &:= -123456789012345678901234_/10_);
      insert(state,  -12345678901234567890123456789012_/10_,
          expect &:= -12345678901234567890123456789012_/10_);
      insert(state,  -123456789012345_/10000000_,
          expect &:= -123456789012345_/10000000_);
      insert(state,  -1234567890123456_/100000000_,
          expect &:= -1234567890123456_/100000000_);
      insert(state,  -123456789012345678901234_/1000000000000_,
          expect &:= -123456789012345678901234_/1000000000000_);
      insert(state,  -12345678901234567890123456789012_/10000000000000000_,
          expect &:= -12345678901234567890123456789012_/10000000000000000_);
      insert(state,  -123456789012345678901234567890123456789_/100000000000000000000_,
          expect &:= -123456789012345678901234567890123456789_/100000000000000000000_);
      insert(state,  -123456789012345_/100000000000000_,
          expect &:= -123456789012345_/100000000000000_);
      insert(state,  -1234567890123456_/1000000000000000_,
          expect &:= -1234567890123456_/1000000000000000_);
      insert(state,  -123456789012345678901234_/100000000000000000000000_,
          expect &:= -123456789012345678901234_/100000000000000000000000_);
      insert(state,  -1234567890123456789012345678901_/1000000000000000000000000000000_,
          expect &:= -1234567890123456789012345678901_/1000000000000000000000000000000_);
      insert(state,  -123456789012345_/1000000000000000_,
          expect &:= -123456789012345_/1000000000000000_);
      insert(state,  -1234567890123456_/10000000000000000_,
          expect &:= -1234567890123456_/10000000000000000_);
      insert(state,  -123456789012345678901234_/1000000000000000000000000_,
          expect &:= -123456789012345678901234_/1000000000000000000000000_);
      insert(state,  -123456789012345678901234567891_/1000000000000000000000000000000_,
          expect &:= -123456789012345678901234567891_/1000000000000000000000000000000_);
      insert(state,  -123456789012345_/10000000000000000_,
          expect &:= -123456789012345_/10000000000000000_);
      insert(state,  -1234567890123456_/100000000000000000_,
          expect &:= -1234567890123456_/100000000000000000_);
      insert(state,  -123456789012345678901234_/10000000000000000000000000_,
          expect &:= -123456789012345678901234_/10000000000000000000000000_);
      insert(state,  -12345678901234567890123456789_/1000000000000000000000000000000_,
          expect &:= -12345678901234567890123456789_/1000000000000000000000000000000_);
      insert(state,  -123456789012345_/10000000000000000000_,
          expect &:= -123456789012345_/10000000000000000000_);
      insert(state,  -1234567890123456_/100000000000000000000_,
          expect &:= -1234567890123456_/100000000000000000000_);
      insert(state,  -123456789012345678901234_/1000000000000000000000000000000_,
          expect &:= -123456789012345678901234_/1000000000000000000000000000000_);
      if supportsFloatingDecimals(databaseKind) then
        insert(state,   12345678901234567890123456789012345678900000000_/1_,
            expect &:=  12345678901234567890123456789012345678900000000_/1_);
        insert(state,   1234567890123456789012345678901234567890_/1_,
            expect &:=  1234567890123456789012345678901234567890_/1_);
        insert(state,   123456789012345678901234567890123456789_/1_,
            expect &:=  123456789012345678901234567890123456789_/1_);
        insert(state,   123456789012345678901234567890123456789_/10_,
            expect &:=  123456789012345678901234567890123456789_/10_);
        insert(state,   123456789012345678901234567890123456789_/100000000000000000000000000000000000000_,
            expect &:=  123456789012345678901234567890123456789_/100000000000000000000000000000000000000_);
        insert(state,   123456789012345678901234567890123456789_/1000000000000000000000000000000000000000_,
            expect &:=  123456789012345678901234567890123456789_/1000000000000000000000000000000000000000_);
        insert(state,   123456789012345678901234567890123456789_/10000000000000000000000000000000000000000_,
            expect &:=  123456789012345678901234567890123456789_/10000000000000000000000000000000000000000_);
        insert(state,   12345678901234567890123456789012_/10000000000000000000000000000000000000000_,
            expect &:=  12345678901234567890123456789012_/10000000000000000000000000000000000000000_);
        insert(state,   123456789012345678901234567890123456789_/100000000000000000000000000000000000000000000000_,
            expect &:=  123456789012345678901234567890123456789_/100000000000000000000000000000000000000000000000_);
        insert(state,  -12345678901234567890123456789012345678900000000_/1_,
            expect &:= -12345678901234567890123456789012345678900000000_/1_);
        insert(state,  -1234567890123456789012345678901234567890_/1_,
            expect &:= -1234567890123456789012345678901234567890_/1_);
        insert(state,  -123456789012345678901234567890123456789_/1_,
            expect &:= -123456789012345678901234567890123456789_/1_);
        insert(state,  -123456789012345678901234567890123456789_/10_,
            expect &:= -123456789012345678901234567890123456789_/10_);
        insert(state,  -123456789012345678901234567890123456789_/100000000000000000000000000000000000000_,
            expect &:= -123456789012345678901234567890123456789_/100000000000000000000000000000000000000_);
        insert(state,  -123456789012345678901234567890123456789_/1000000000000000000000000000000000000000_,
            expect &:= -123456789012345678901234567890123456789_/1000000000000000000000000000000000000000_);
        insert(state,  -123456789012345678901234567890123456789_/10000000000000000000000000000000000000000_,
            expect &:= -123456789012345678901234567890123456789_/10000000000000000000000000000000000000000_);
        insert(state,  -12345678901234567890123456789012_/10000000000000000000000000000000000000000_,
            expect &:= -12345678901234567890123456789012_/10000000000000000000000000000000000000000_);
        insert(state,  -123456789012345678901234567890123456789_/100000000000000000000000000000000000000000000000_,
            expect &:= -123456789012345678901234567890123456789_/100000000000000000000000000000000000000000000000_);
      end if;
      insInf(state, -1_/0_,                     expect &:= -1_/0_);
      insInf(state,  1_/0_,                     expect &:=  1_/0_);
      insNaN(state,  0_/0_,                     expect &:=  0_/0_);
      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigRatField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found " <& bigRatColumn);
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigRatField: Row: " <& row <&
                    " Expected NULL found " <& bigRatColumn);
          end if;
        end if;
        floatColumn := column(state.statement, 1, float);
        if floatColumn <> float(str(expect[row])) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigRatField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found float " <& floatColumn);
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigRatField: Row: " <& row <&
                    " Expected NULL found float " <& floatColumn);
          end if;
        end if;
        if row > 1 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBigRatField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching bigRational fields works okay.");
    else
      writeln(" *** Inserting and fetching bigRational fields does not work okay.");
    end if;
  end func;


const proc: testAnyIntegerField (in database: testDb, inout testState: state,
    in string: fieldType, in integer: precision, in integer: sqlIntLiteralBits) is func
  local
    var array bigInteger: expect is 0 times 0_;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is bigRational.value;
    var float: floatColumn is 0.0;
    var string: floatAsString is "";
    var integer: row is 1;
  begin
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & fieldType & ")");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= 0_);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:= 0_);

      insert(testDb, state,  "0", expect &:=  0_);
      insert(testDb, state,  "1", expect &:=  1_);
      insert(testDb, state, "-1", expect &:= -1_);
      if precision >= 18 then
        insert(testDb, state,  "123456789012345678", expect &:=  123456789012345678_);
        insert(testDb, state,  "999999999999999999", expect &:=  999999999999999999_);
        insert(testDb, state, "-123456789012345678", expect &:= -123456789012345678_);
        insert(testDb, state, "-999999999999999999", expect &:= -999999999999999999_);
      end if;
      if sqlIntLiteralBits = 64 then
        insert(testDb, state,  "1000000000000000000", expect &:=  1000000000000000000_);
        insert(testDb, state,  "1234567890123456789", expect &:=  1234567890123456789_);
        insert(testDb, state,  "9223372036854775807", expect &:=  9223372036854775807_);
        insert(testDb, state, "-1000000000000000000", expect &:= -1000000000000000000_);
        insert(testDb, state, "-1234567890123456789", expect &:= -1234567890123456789_);
        insert(testDb, state, "-9223372036854775808", expect &:= -9223372036854775808_);
      end if;
      if sqlIntLiteralBits > 64 then
        insert(testDb, state,  "9223372036854775808", expect &:=  9223372036854775808_);
        insert(testDb, state,  "9999999999999999999", expect &:=  9999999999999999999_);
        insert(testDb, state, "-9223372036854775809", expect &:= -9223372036854775809_);
        insert(testDb, state, "-9999999999999999999", expect &:= -9999999999999999999_);
        if precision >= 19 then
          insert(testDb, state,  "1000000000000000000", expect &:=  1000000000000000000_);
          insert(testDb, state,  "1234567890123456789", expect &:=  1234567890123456789_);
          insert(testDb, state,  "9223372036854775807", expect &:=  9223372036854775807_);
          insert(testDb, state,  "9999999999999999999", expect &:=  9999999999999999999_);
          insert(testDb, state, "-1000000000000000000", expect &:= -1000000000000000000_);
          insert(testDb, state, "-1234567890123456789", expect &:= -1234567890123456789_);
          insert(testDb, state, "-9223372036854775808", expect &:= -9223372036854775808_);
          insert(testDb, state, "-9999999999999999999", expect &:= -9999999999999999999_);
        end if;
        if precision >= 38 then
          insert(testDb, state,  "12345678901234567890123456789012345678",
                      expect &:=  12345678901234567890123456789012345678_);
          insert(testDb, state,  "99999999999999999999999999999999999999",
                      expect &:=  99999999999999999999999999999999999999_);
          insert(testDb, state, "-12345678901234567890123456789012345678",
                      expect &:= -12345678901234567890123456789012345678_);
          insert(testDb, state, "-99999999999999999999999999999999999999",
                      expect &:= -99999999999999999999999999999999999999_);
        end if;
        if precision >= 65 then
          insert(testDb, state,  "12345678901234567890123456789012345678901234567890123456789012345",
                      expect &:=  12345678901234567890123456789012345678901234567890123456789012345_);
          insert(testDb, state,  "99999999999999999999999999999999999999999999999999999999999999999",
                      expect &:=  99999999999999999999999999999999999999999999999999999999999999999_);
          insert(testDb, state, "-12345678901234567890123456789012345678901234567890123456789012345",
                      expect &:= -12345678901234567890123456789012345678901234567890123456789012345_);
          insert(testDb, state, "-99999999999999999999999999999999999999999999999999999999999999999",
                      expect &:= -99999999999999999999999999999999999999999999999999999999999999999_);
        end if;
        if precision >= 100 then
          insert(testDb, state,
                        "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
              expect &:= 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_);
          insert(testDb, state,
                        "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
              expect &:= 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_);
          insert(testDb, state,
                        "-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
              expect &:= -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_);
          insert(testDb, state,
                        "-9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
              expect &:= -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_);
        end if;
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0, expect &:= 0_);
      insert(state, 1, expect &:= 1_);
      insert(state, -1, expect &:= -1_);
      if precision >= 18 then
        insert(state,  123456789012345678, expect &:=  123456789012345678_);
        insert(state,  999999999999999999, expect &:=  999999999999999999_);
        insert(state, -123456789012345678, expect &:= -123456789012345678_);
        insert(state, -999999999999999999, expect &:= -999999999999999999_);
      end if;
      if precision >= 19 then
        insert(state,       1000000000000000000,  expect &:=  1000000000000000000_);
        insert(state,       1234567890123456789,  expect &:=  1234567890123456789_);
        insert(state,       9223372036854775807,  expect &:=  9223372036854775807_);
        insert(state,      -1000000000000000000,  expect &:= -1000000000000000000_);
        insert(state,      -1234567890123456789,  expect &:= -1234567890123456789_);
        insert(state, pred(-9223372036854775807), expect &:= -9223372036854775808_);
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_, expect &:= 0_);
      insert(state, 1_, expect &:= 1_);
      insert(state, -1_, expect &:= -1_);
      if precision >= 18 then
        insert(state,  123456789012345678_, expect &:=  123456789012345678_);
        insert(state,  999999999999999999_, expect &:=  999999999999999999_);
        insert(state, -123456789012345678_, expect &:= -123456789012345678_);
        insert(state, -999999999999999999_, expect &:= -999999999999999999_);
      end if;
      if precision >= 19 then
        insert(state,  1000000000000000000_, expect &:=  1000000000000000000_);
        insert(state,  1234567890123456789_, expect &:=  1234567890123456789_);
        insert(state,  9223372036854775807_, expect &:=  9223372036854775807_);
        insert(state,  9223372036854775808_, expect &:=  9223372036854775808_);
        insert(state,  9999999999999999999_, expect &:=  9999999999999999999_);
        insert(state, -1000000000000000000_, expect &:= -1000000000000000000_);
        insert(state, -1234567890123456789_, expect &:= -1234567890123456789_);
        insert(state, -9223372036854775808_, expect &:= -9223372036854775808_);
        insert(state, -9223372036854775809_, expect &:= -9223372036854775809_);
        insert(state, -9999999999999999999_, expect &:= -9999999999999999999_);
      end if;
      if precision >= 38 then
        insert(state,   12345678901234567890123456789012345678_,
            expect &:=  12345678901234567890123456789012345678_);
        insert(state,   99999999999999999999999999999999999999_,
            expect &:=  99999999999999999999999999999999999999_);
        insert(state,  -12345678901234567890123456789012345678_,
            expect &:= -12345678901234567890123456789012345678_);
        insert(state,  -99999999999999999999999999999999999999_,
            expect &:= -99999999999999999999999999999999999999_);
      end if;
      if precision >= 65 then
        insert(state,   12345678901234567890123456789012345678901234567890123456789012345_,
            expect &:=  12345678901234567890123456789012345678901234567890123456789012345_);
        insert(state,   99999999999999999999999999999999999999999999999999999999999999999_,
            expect &:=  99999999999999999999999999999999999999999999999999999999999999999_);
        insert(state,  -12345678901234567890123456789012345678901234567890123456789012345_,
            expect &:= -12345678901234567890123456789012345678901234567890123456789012345_);
        insert(state,  -99999999999999999999999999999999999999999999999999999999999999999_,
            expect &:= -99999999999999999999999999999999999999999999999999999999999999999_);
      end if;
      if precision >= 100 then
        insert(state,   1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_,
            expect &:=  1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_);
        insert(state,   9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_,
            expect &:=  9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_);
        insert(state,  -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_,
            expect &:= -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_);
        insert(state,  -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_,
            expect &:= -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_);
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_/1_, expect &:= 0_);
      insert(state, 1_/1_, expect &:= 1_);
      insert(state, -1_/1_, expect &:= -1_);
      if precision >= 18 then
        insert(state,  123456789012345678_/1_, expect &:=  123456789012345678_);
        insert(state,  999999999999999999_/1_, expect &:=  999999999999999999_);
        insert(state, -123456789012345678_/1_, expect &:= -123456789012345678_);
        insert(state, -999999999999999999_/1_, expect &:= -999999999999999999_);
      end if;
      if precision >= 19 then
        insert(state,  1000000000000000000_/1_, expect &:=  1000000000000000000_);
        insert(state,  1234567890123456789_/1_, expect &:=  1234567890123456789_);
        insert(state,  9223372036854775807_/1_, expect &:=  9223372036854775807_);
        insert(state,  9223372036854775808_/1_, expect &:=  9223372036854775808_);
        insert(state,  9999999999999999999_/1_, expect &:=  9999999999999999999_);
        insert(state, -1000000000000000000_/1_, expect &:= -1000000000000000000_);
        insert(state, -1234567890123456789_/1_, expect &:= -1234567890123456789_);
        insert(state, -9223372036854775808_/1_, expect &:= -9223372036854775808_);
        insert(state, -9223372036854775809_/1_, expect &:= -9223372036854775809_);
        insert(state, -9999999999999999999_/1_, expect &:= -9999999999999999999_);
      end if;
      if precision >= 38 then
        insert(state,   12345678901234567890123456789012345678_/1_,
            expect &:=  12345678901234567890123456789012345678_);
        insert(state,   99999999999999999999999999999999999999_/1_,
            expect &:=  99999999999999999999999999999999999999_);
        insert(state,  -12345678901234567890123456789012345678_/1_,
            expect &:= -12345678901234567890123456789012345678_);
        insert(state,  -99999999999999999999999999999999999999_/1_,
            expect &:= -99999999999999999999999999999999999999_);
      end if;
      if precision >= 65 then
        insert(state,   12345678901234567890123456789012345678901234567890123456789012345_/1_,
            expect &:=  12345678901234567890123456789012345678901234567890123456789012345_);
        insert(state,   99999999999999999999999999999999999999999999999999999999999999999_/1_,
            expect &:=  99999999999999999999999999999999999999999999999999999999999999999_);
        insert(state,  -12345678901234567890123456789012345678901234567890123456789012345_/1_,
            expect &:= -12345678901234567890123456789012345678901234567890123456789012345_);
        insert(state,  -99999999999999999999999999999999999999999999999999999999999999999_/1_,
            expect &:= -99999999999999999999999999999999999999999999999999999999999999999_);
      end if;
      if precision >= 100 then
        insert(state,   1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_/1_,
            expect &:=  1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_);
        insert(state,   9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_/1_,
            expect &:=  9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_);
        insert(state,  -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_/1_,
            expect &:= -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_);
        insert(state,  -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_/1_,
            expect &:= -9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999_);
      end if;

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        if expect[row] >= bigInteger(integer.first) and
            expect[row] <= bigInteger(integer.last) then
          integerColumn := column(state.statement, 1, integer);
          if integerColumn <> integer(expect[row]) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                      " Expected " <& expect[row] <& " found integer " <& integerColumn);
            end if;
          end if;
          if row <= 2 and not isNull(state.statement, 1) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                      " Expected NULL found integer " <& integerColumn);
            end if;
          end if;
        end if;
        bigIntColumn := column(state.statement, 1, bigInteger);
        if bigIntColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigInteger " <& bigIntColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                    " Expected NULL found bigInteger " <& bigIntColumn);
          end if;
        end if;
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> expect[row] / 1_ then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigRational " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
        floatAsString := str(float(str(expect[row])));
        if endsWith(floatAsString, ".0") and
            expect[row] = bigInteger(floatAsString[.. length(floatAsString) - 2]) then
          # The expected value is representable as float.
          floatColumn := column(state.statement, 1, float);
          if floatColumn <> float(str(expect[row])) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyIntegerField: Row: " <& row <&
                      " Expected " <& expect[row] <& " found float " <& floatColumn);
            end if;
          end if;
          if row <= 2 and not isNull(state.statement, 1) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyIntegerField: Row: " <& row <&
                    " Expected NULL found float " <& floatColumn);
            end if;
          end if;
        end if;
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyIntegerField " <& state.tableName <& ": Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);
  end func;


const proc: testDecimalIntField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("decimalTest", "decimalField", FALSE);
  begin
    if state.details then
      writeln("testDecimalIntField: " <& databaseKind);
    end if;
    testAnyIntegerField(testDb, state, decimalIntType(databaseKind),
                        maxDecimalPrecision(databaseKind), sqlIntLiteralBits(databaseKind));

    if state.okay then
      writeln("Inserting and fetching decimal fields with integers works okay.");
    else
      writeln(" *** Inserting and fetching decimal fields with integers does not work okay.");
    end if;
  end func;


const proc: testNumericIntField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("numericTest", "numericField", FALSE);
  begin
    if state.details then
      writeln("testNumericIntField: " <& databaseKind);
    end if;
    testAnyIntegerField(testDb, state, numericIntType(databaseKind),
                        maxNumericPrecision(databaseKind), sqlIntLiteralBits(databaseKind));

    if state.okay then
      writeln("Inserting and fetching numeric fields with integers works okay.");
    else
      writeln(" *** Inserting and fetching numeric fields with integers does not work okay.");
    end if;
  end func;


const proc: testAnyRationalField (in database: testDb, inout testState: state,
    in string: fieldType, in integer: precision, in integer: literalPrecision,
    in integer: scale) is func
  local
    var bigRational: bigValue is bigRational.value;
    var array bigRational: expect is 0 times 0_/1_;
    var bigRational: bigRatColumn is bigRational.value;
    var float: floatColumn is 0.0;
    var integer: integerColumn is 0;
    var integer: row is 1;
  begin
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & fieldType & ")");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= 0_/1_);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:= 0_/1_);

      if scale > 0 then
        # Positive values
        insert(testDb, state, str(1_ / 10_ ** scale), expect &:= 1_ / 10_ ** scale);
        bigValue := pred(10_ ** literalPrecision) / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := 10_ ** pred(literalPrecision) / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := 1234567890_ / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := 123456789012345_ / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := (1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - literalPrecision)) / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);

        # Negative values:
        insert(testDb, state, str(-1_ / 10_ ** scale), expect &:= -1_ / 10_ ** scale);
        bigValue := -pred(10_ ** literalPrecision) / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := -10_ ** pred(literalPrecision) / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := -1234567890_ / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := -123456789012345_ / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
        bigValue := -(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - literalPrecision)) / 10_ ** scale;
        insert(testDb, state, str(bigValue), expect &:= bigValue);
      else
        # We use integer literals here, because some databases (Firebird)
        # refuse some floating point literals (999999999999999999.0),
        # when the scale is zero and the literalPrecision is at the maximum (18).
        # Positive values
        insert(testDb, state, 10_ ** (-scale) / 1_ digits 0, expect &:= 10_ ** (-scale) / 1_);
        bigValue := pred(10_ ** literalPrecision) * 10_ ** (-scale) / 1_;
        insert(testDb, state, bigValue digits 0, expect &:= bigValue);
        bigValue := (1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - literalPrecision)) * 10_ ** (-scale) / 1_;
        insert(testDb, state, bigValue digits 0, expect &:= bigValue);

        # Negative values:
        insert(testDb, state, -10_ ** (-scale) / 1_ digits 0, expect &:= -10_ ** (-scale) / 1_);
        bigValue := -pred(10_ ** literalPrecision) * 10_ ** (-scale) / 1_;
        insert(testDb, state, bigValue digits 0, expect &:= bigValue);
        bigValue := -(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - literalPrecision)) * 10_ ** (-scale) / 1_;
        insert(testDb, state, bigValue digits 0, expect &:= bigValue);
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_/1_, expect &:= 0_/1_);
      if scale > 0 then
        # Positive values
        insert(state, 1_ / 10_ ** scale, expect &:= 1_ / 10_ ** scale);
        bigValue := pred(10_ ** precision) / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := 10_ ** pred(precision) / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := 1234567890_ / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := 123456789012345_ / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := (1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - precision)) / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);

        # Negative values:
        insert(state, -1_ / 10_ ** scale, expect &:= -1_ / 10_ ** scale);
        bigValue := -pred(10_ ** precision) / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := -10_ ** pred(precision) / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := -1234567890_ / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := -123456789012345_ / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := -(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - precision)) / 10_ ** scale;
        insert(state, bigValue, expect &:= bigValue);
      else
        # Positive values
        insert(state, 10_ ** (-scale) / 1_, expect &:= 10_ ** (-scale) / 1_);
        bigValue := pred(10_ ** precision) * 10_ ** (-scale) / 1_;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := (1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - precision)) * 10_ ** (-scale) / 1_;
        insert(state, bigValue, expect &:= bigValue);

        # Negative values:
        insert(state, -10_ ** (-scale) / 1_, expect &:= -10_ ** (-scale) / 1_);
        bigValue := -pred(10_ ** precision) * 10_ ** (-scale) / 1_;
        insert(state, bigValue, expect &:= bigValue);
        bigValue := -(1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890_ div
                     10_ ** (100 - precision)) * 10_ ** (-scale) / 1_;
        insert(state, bigValue, expect &:= bigValue);
      end if;
      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        bigRatColumn := column(state.statement, 1, bigRational);
        if bigRatColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyRationalField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found bigRational " <& bigRatColumn);
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyRationalField: Row: " <& row <&
                    " Expected NULL found bigRational " <& bigRatColumn);
          end if;
        end if;
        if expect[row] = bigRational(str(float(str(expect[row])))) then
          # The expected value is representable as float.
          floatColumn := column(state.statement, 1, float);
          if floatColumn <> float(str(expect[row])) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyRationalField: Row: " <& row <&
                      " Expected " <& expect[row] <& " found float " <& floatColumn);
              # writeln("numerator: " <& expect[row].numerator);
              # writeln("denominator: " <& expect[row].denominator);
            end if;
          end if;
          if row <= 2 and not isNull(state.statement, 1) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyRationalField: Row: " <& row <&
                      " Expected NULL found float " <& floatColumn);
            end if;
          end if;
        end if;
        if scale = 0 and trunc(expect[row]) >= bigInteger(integer.first) and
            trunc(expect[row]) <= bigInteger(integer.last) then
          integerColumn := column(state.statement, 1, integer);
          if integerColumn <> integer(expect[row] digits 0) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyRationalField: Row: " <& row <&
                      " Expected " <& expect[row] digits 0 <& " found integer " <& integerColumn);
            end if;
          end if;
          if row <= 2 and not isNull(state.statement, 1) then
            state.okay := FALSE;
            if state.details then
              writeln(" *** testAnyRationalField: Row: " <& row <&
                    " Expected NULL found integer " <& integerColumn);
            end if;
          end if;
        end if;
        if row > 2 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testAnyRationalField " <& state.tableName <& ": Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);
  end func;


const proc: testDecimalField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("decimalTest", "decimalField", FALSE);
    var integer: scale is 0;
  begin
    if state.details then
      writeln("testDecimalField: " <& databaseKind);
    end if;
    for scale range minDecimalScale(databaseKind) to maxDecimalScale(databaseKind) do
      testAnyRationalField(testDb, state,
                           decimalType(databaseKind, scale),
                           maxDecimalPrecision(databaseKind),
                           maxDecimalLiteralPrecision(databaseKind), scale);
    end for;

    if state.okay then
      writeln("Inserting and fetching decimal fields works okay.");
    else
      writeln(" *** Inserting and fetching decimal fields does not work okay.");
    end if;
  end func;


const proc: testNumericField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("numericTest", "numericField", FALSE);
    var integer: scale is 0;
  begin
    if state.details then
      writeln("testNumericField: " <& databaseKind);
    end if;
    for scale range minNumericScale(databaseKind) to maxNumericScale(databaseKind) do
      testAnyRationalField(testDb, state,
                           numericType(databaseKind, scale),
                           maxNumericPrecision(databaseKind),
                           maxNumericLiteralPrecision(databaseKind), scale);
    end for;

    if state.okay then
      writeln("Inserting and fetching numeric fields works okay.");
    else
      writeln(" *** Inserting and fetching numeric fields does not work okay.");
    end if;
  end func;


const proc: testChar1AsciiField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("char1AsciiTest", "char1AsciiField", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testChar1AsciiField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:= "");

      insert(testDb, state, "''",      expect &:= "");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "'\0;'", expect &:= "\0;");
      end if;
      insert(testDb, state, "'\1;'",   expect &:= "\1;");
      insert(testDb, state, "'\2;'",   expect &:= "\2;");
      insert(testDb, state, "'\3;'",   expect &:= "\3;");
      insert(testDb, state, "'\4;'",   expect &:= "\4;");
      insert(testDb, state, "'\5;'",   expect &:= "\5;");
      insert(testDb, state, "'\6;'",   expect &:= "\6;");
      insert(testDb, state, "'\7;'",   expect &:= "\7;");
      insert(testDb, state, "'\8;'",   expect &:= "\8;");
      insert(testDb, state, "'\9;'",   expect &:= "\9;");
      insert(testDb, state, "'\10;'",  expect &:= "\10;");
      insert(testDb, state, "'\11;'",  expect &:= "\11;");
      insert(testDb, state, "'\12;'",  expect &:= "\12;");
      insert(testDb, state, "'\13;'",  expect &:= "\13;");
      insert(testDb, state, "'\14;'",  expect &:= "\14;");
      insert(testDb, state, "'\15;'",  expect &:= "\15;");
      insert(testDb, state, "'\16;'",  expect &:= "\16;");
      insert(testDb, state, "'\17;'",  expect &:= "\17;");
      insert(testDb, state, "'\18;'",  expect &:= "\18;");
      insert(testDb, state, "'\19;'",  expect &:= "\19;");
      insert(testDb, state, "'\20;'",  expect &:= "\20;");
      insert(testDb, state, "'\21;'",  expect &:= "\21;");
      insert(testDb, state, "'\22;'",  expect &:= "\22;");
      insert(testDb, state, "'\23;'",  expect &:= "\23;");
      insert(testDb, state, "'\24;'",  expect &:= "\24;");
      insert(testDb, state, "'\25;'",  expect &:= "\25;");
      insert(testDb, state, "'\26;'",  expect &:= "\26;");
      insert(testDb, state, "'\27;'",  expect &:= "\27;");
      insert(testDb, state, "'\28;'",  expect &:= "\28;");
      insert(testDb, state, "'\29;'",  expect &:= "\29;");
      insert(testDb, state, "'\30;'",  expect &:= "\30;");
      insert(testDb, state, "'\31;'",  expect &:= "\31;");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(testDb, state, "' '",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(testDb, state, "' '",   expect &:= "");
      end if;
      insert(testDb, state, "'!'",     expect &:= "!");
      insert(testDb, state, "'\"'",    expect &:= "\"");
      insert(testDb, state, "'#'",     expect &:= "#");
      insert(testDb, state, "'$'",     expect &:= "$");
      insert(testDb, state, "'%'",     expect &:= "%");
      insert(testDb, state, "'&'",     expect &:= "&");
      insert(testDb, state, "''''",    expect &:= "'");
      insert(testDb, state, "'('",     expect &:= "(");
      insert(testDb, state, "')'",     expect &:= ")");
      insert(testDb, state, "'*'",     expect &:= "*");
      insert(testDb, state, "'+'",     expect &:= "+");
      insert(testDb, state, "','",     expect &:= ",");
      insert(testDb, state, "'-'",     expect &:= "-");
      insert(testDb, state, "'.'",     expect &:= ".");
      insert(testDb, state, "'/'",     expect &:= "/");
      insert(testDb, state, "'0'",     expect &:= "0");
      insert(testDb, state, "'1'",     expect &:= "1");
      insert(testDb, state, "'9'",     expect &:= "9");
      insert(testDb, state, "':'",     expect &:= ":");
      insert(testDb, state, "';'",     expect &:= ";");
      insert(testDb, state, "'<'",     expect &:= "<");
      insert(testDb, state, "'='",     expect &:= "=");
      insert(testDb, state, "'>'",     expect &:= ">");
      insert(testDb, state, "'?'",     expect &:= "?");
      insert(testDb, state, "'@'",     expect &:= "@");
      insert(testDb, state, "'A'",     expect &:= "A");
      insert(testDb, state, "'Z'",     expect &:= "Z");
      insert(testDb, state, "'['",     expect &:= "[");
      insert(testDb, state, "'\\'",    expect &:= "\\");
      insert(testDb, state, "']'",     expect &:= "]");
      insert(testDb, state, "'^'",     expect &:= "^");
      insert(testDb, state, "'_'",     expect &:= "_");
      insert(testDb, state, "'`'",     expect &:= "`");
      insert(testDb, state, "'a'",     expect &:= "a");
      insert(testDb, state, "'z'",     expect &:= "z");
      insert(testDb, state, "'{'",     expect &:= "{");
      insert(testDb, state, "'|'",     expect &:= "|");
      insert(testDb, state, "'}'",     expect &:= "}");
      insert(testDb, state, "'~'",     expect &:= "~");
      insert(testDb, state, "'\127;'", expect &:= "\127;");

      insert(testDb, state, "0",     expect &:= "0");
      insert(testDb, state, "1",     expect &:= "1");
      insert(testDb, state, "9",     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, "",    expect &:= "");
      if nullAllowedInString(databaseKind) then
        insert(state, "\0;", expect &:= "\0;");
      end if;
      insert(state, "\1;",   expect &:= "\1;");
      insert(state, "\2;",   expect &:= "\2;");
      insert(state, "\3;",   expect &:= "\3;");
      insert(state, "\4;",   expect &:= "\4;");
      insert(state, "\5;",   expect &:= "\5;");
      insert(state, "\6;",   expect &:= "\6;");
      insert(state, "\7;",   expect &:= "\7;");
      insert(state, "\8;",   expect &:= "\8;");
      insert(state, "\9;",   expect &:= "\9;");
      insert(state, "\10;",  expect &:= "\10;");
      insert(state, "\11;",  expect &:= "\11;");
      insert(state, "\12;",  expect &:= "\12;");
      insert(state, "\13;",  expect &:= "\13;");
      insert(state, "\14;",  expect &:= "\14;");
      insert(state, "\15;",  expect &:= "\15;");
      insert(state, "\16;",  expect &:= "\16;");
      insert(state, "\17;",  expect &:= "\17;");
      insert(state, "\18;",  expect &:= "\18;");
      insert(state, "\19;",  expect &:= "\19;");
      insert(state, "\20;",  expect &:= "\20;");
      insert(state, "\21;",  expect &:= "\21;");
      insert(state, "\22;",  expect &:= "\22;");
      insert(state, "\23;",  expect &:= "\23;");
      insert(state, "\24;",  expect &:= "\24;");
      insert(state, "\25;",  expect &:= "\25;");
      insert(state, "\26;",  expect &:= "\26;");
      insert(state, "\27;",  expect &:= "\27;");
      insert(state, "\28;",  expect &:= "\28;");
      insert(state, "\29;",  expect &:= "\29;");
      insert(state, "\30;",  expect &:= "\30;");
      insert(state, "\31;",  expect &:= "\31;");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(state, " ",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(state, " ",   expect &:= "");
      end if;
      insert(state, "!",     expect &:= "!");
      insert(state, "\"",    expect &:= "\"");
      insert(state, "#",     expect &:= "#");
      insert(state, "$",     expect &:= "$");
      insert(state, "%",     expect &:= "%");
      insert(state, "&",     expect &:= "&");
      insert(state, "'",     expect &:= "'");
      insert(state, "(",     expect &:= "(");
      insert(state, ")",     expect &:= ")");
      insert(state, "*",     expect &:= "*");
      insert(state, "+",     expect &:= "+");
      insert(state, ",",     expect &:= ",");
      insert(state, "-",     expect &:= "-");
      insert(state, ".",     expect &:= ".");
      insert(state, "/",     expect &:= "/");
      insert(state, "0",     expect &:= "0");
      insert(state, "1",     expect &:= "1");
      insert(state, "9",     expect &:= "9");
      insert(state, ":",     expect &:= ":");
      insert(state, ";",     expect &:= ";");
      insert(state, "<",     expect &:= "<");
      insert(state, "=",     expect &:= "=");
      insert(state, ">",     expect &:= ">");
      insert(state, "?",     expect &:= "?");
      insert(state, "@",     expect &:= "@");
      insert(state, "A",     expect &:= "A");
      insert(state, "Z",     expect &:= "Z");
      insert(state, "[",     expect &:= "[");
      insert(state, "\\",    expect &:= "\\");
      insert(state, "]",     expect &:= "]");
      insert(state, "^",     expect &:= "^");
      insert(state, "_",     expect &:= "_");
      insert(state, "`",     expect &:= "`");
      insert(state, "a",     expect &:= "a");
      insert(state, "z",     expect &:= "z");
      insert(state, "{",     expect &:= "{");
      insert(state, "|",     expect &:= "|");
      insert(state, "}",     expect &:= "}");
      insert(state, "~",     expect &:= "~");
      insert(state, "\127;", expect &:= "\127;");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0,     expect &:= "0");
      insert(state, 1,     expect &:= "1");
      insert(state, 9,     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_,    expect &:= "0");
      insert(state, 1_,    expect &:= "1");
      insert(state, 9_,    expect &:= "9");

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1AsciiField: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found " <& literal(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1AsciiField: Row: " <& row <&
                    " Expected NULL found " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1AsciiField: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching char(1) fields with ASCII characters works okay.");
    else
      writeln(" *** Inserting and fetching char(1) fields with ASCII characters does not work okay.");
    end if;
  end func;


const proc: testChar1Latin1Field (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("char1Latin1Test", "char1Latin1Field", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testChar1Latin1Field: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:= "");

      insert(testDb, state, "''",      expect &:= "");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "'\0;'", expect &:= "\0;");
      end if;
      insert(testDb, state, "'\1;'",   expect &:= "\1;");
      insert(testDb, state, "'\b'",    expect &:= "\b");
      insert(testDb, state, "'\t'",    expect &:= "\t");
      insert(testDb, state, "'\n'",    expect &:= "\n");
      insert(testDb, state, "'\e'",    expect &:= "\e");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(testDb, state, "' '",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(testDb, state, "' '",   expect &:= "");
      end if;
      insert(testDb, state, "'!'",     expect &:= "!");
      insert(testDb, state, "'\"'",    expect &:= "\"");
      insert(testDb, state, "''''",    expect &:= "'");
      insert(testDb, state, "'0'",     expect &:= "0");
      insert(testDb, state, "'1'",     expect &:= "1");
      insert(testDb, state, "'9'",     expect &:= "9");
      insert(testDb, state, "'?'",     expect &:= "?");
      insert(testDb, state, "'X'",     expect &:= "X");
      insert(testDb, state, "'\\'",    expect &:= "\\");
      insert(testDb, state, "'^'",     expect &:= "^");
      insert(testDb, state, "'`'",     expect &:= "`");
      insert(testDb, state, "'a'",     expect &:= "a");
      insert(testDb, state, "'~'",     expect &:= "~");
      insert(testDb, state, "'\127;'", expect &:= "\127;");
      insert(testDb, state, "'\128;'", expect &:= "\128;");
      insert(testDb, state, "'\129;'", expect &:= "\129;");
      insert(testDb, state, "'\130;'", expect &:= "\130;");
      insert(testDb, state, "'\131;'", expect &:= "\131;");
      insert(testDb, state, "'\132;'", expect &:= "\132;");
      insert(testDb, state, "'\133;'", expect &:= "\133;");
      insert(testDb, state, "'\134;'", expect &:= "\134;");
      insert(testDb, state, "'\135;'", expect &:= "\135;");
      insert(testDb, state, "'\136;'", expect &:= "\136;");
      insert(testDb, state, "'\137;'", expect &:= "\137;");
      insert(testDb, state, "'\138;'", expect &:= "\138;");
      insert(testDb, state, "'\139;'", expect &:= "\139;");
      insert(testDb, state, "'\140;'", expect &:= "\140;");
      insert(testDb, state, "'\141;'", expect &:= "\141;");
      insert(testDb, state, "'\142;'", expect &:= "\142;");
      insert(testDb, state, "'\143;'", expect &:= "\143;");
      insert(testDb, state, "'\144;'", expect &:= "\144;");
      insert(testDb, state, "'\145;'", expect &:= "\145;");
      insert(testDb, state, "'\146;'", expect &:= "\146;");
      insert(testDb, state, "'\147;'", expect &:= "\147;");
      insert(testDb, state, "'\148;'", expect &:= "\148;");
      insert(testDb, state, "'\149;'", expect &:= "\149;");
      insert(testDb, state, "'\150;'", expect &:= "\150;");
      insert(testDb, state, "'\151;'", expect &:= "\151;");
      insert(testDb, state, "'\152;'", expect &:= "\152;");
      insert(testDb, state, "'\153;'", expect &:= "\153;");
      insert(testDb, state, "'\154;'", expect &:= "\154;");
      insert(testDb, state, "'\155;'", expect &:= "\155;");
      insert(testDb, state, "'\156;'", expect &:= "\156;");
      insert(testDb, state, "'\157;'", expect &:= "\157;");
      insert(testDb, state, "'\158;'", expect &:= "\158;");
      insert(testDb, state, "'\159;'", expect &:= "\159;");
      insert(testDb, state, "'\160;'", expect &:= "\160;");
      insert(testDb, state, "'¡'",     expect &:= "¡");
      insert(testDb, state, "'¤'",     expect &:= "¤");
      insert(testDb, state, "'§'",     expect &:= "§");
      insert(testDb, state, "'¬'",     expect &:= "¬");
      insert(testDb, state, "'­'",     expect &:= "­");
      insert(testDb, state, "'µ'",     expect &:= "µ");
      insert(testDb, state, "'¿'",     expect &:= "¿");
      insert(testDb, state, "'Ä'",     expect &:= "Ä");
      insert(testDb, state, "'×'",     expect &:= "×");
      insert(testDb, state, "'ß'",     expect &:= "ß");
      insert(testDb, state, "'ä'",     expect &:= "ä");
      insert(testDb, state, "'÷'",     expect &:= "÷");
      insert(testDb, state, "'ÿ'",     expect &:= "ÿ");

      insert(testDb, state, "0",     expect &:= "0");
      insert(testDb, state, "1",     expect &:= "1");
      insert(testDb, state, "9",     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, "",    expect &:= "");
      if nullAllowedInString(databaseKind) then
        insert(state, "\0;", expect &:= "\0;");
      end if;
      insert(state, "\1;",   expect &:= "\1;");
      insert(state, "\b",    expect &:= "\b");
      insert(state, "\t",    expect &:= "\t");
      insert(state, "\n",    expect &:= "\n");
      insert(state, "\e",    expect &:= "\e");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(state, " ",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(state, " ",   expect &:= "");
      end if;
      insert(state, "!",     expect &:= "!");
      insert(state, "\"",    expect &:= "\"");
      insert(state, "'",     expect &:= "'");
      insert(state, "0",     expect &:= "0");
      insert(state, "1",     expect &:= "1");
      insert(state, "9",     expect &:= "9");
      insert(state, "?",     expect &:= "?");
      insert(state, "X",     expect &:= "X");
      insert(state, "\\",    expect &:= "\\");
      insert(state, "^",     expect &:= "^");
      insert(state, "`",     expect &:= "`");
      insert(state, "a",     expect &:= "a");
      insert(state, "~",     expect &:= "~");
      insert(state, "\127;", expect &:= "\127;");
      insert(state, "\128;", expect &:= "\128;");
      insert(state, "\129;", expect &:= "\129;");
      insert(state, "\130;", expect &:= "\130;");
      insert(state, "\131;", expect &:= "\131;");
      insert(state, "\132;", expect &:= "\132;");
      insert(state, "\133;", expect &:= "\133;");
      insert(state, "\134;", expect &:= "\134;");
      insert(state, "\135;", expect &:= "\135;");
      insert(state, "\136;", expect &:= "\136;");
      insert(state, "\137;", expect &:= "\137;");
      insert(state, "\138;", expect &:= "\138;");
      insert(state, "\139;", expect &:= "\139;");
      insert(state, "\140;", expect &:= "\140;");
      insert(state, "\141;", expect &:= "\141;");
      insert(state, "\142;", expect &:= "\142;");
      insert(state, "\143;", expect &:= "\143;");
      insert(state, "\144;", expect &:= "\144;");
      insert(state, "\145;", expect &:= "\145;");
      insert(state, "\146;", expect &:= "\146;");
      insert(state, "\147;", expect &:= "\147;");
      insert(state, "\148;", expect &:= "\148;");
      insert(state, "\149;", expect &:= "\149;");
      insert(state, "\150;", expect &:= "\150;");
      insert(state, "\151;", expect &:= "\151;");
      insert(state, "\152;", expect &:= "\152;");
      insert(state, "\153;", expect &:= "\153;");
      insert(state, "\154;", expect &:= "\154;");
      insert(state, "\155;", expect &:= "\155;");
      insert(state, "\156;", expect &:= "\156;");
      insert(state, "\157;", expect &:= "\157;");
      insert(state, "\158;", expect &:= "\158;");
      insert(state, "\159;", expect &:= "\159;");
      insert(state, "\160;", expect &:= "\160;");
      insert(state, "¡",     expect &:= "¡");
      insert(state, "¤",     expect &:= "¤");
      insert(state, "§",     expect &:= "§");
      insert(state, "¬",     expect &:= "¬");
      insert(state, "­",     expect &:= "­");
      insert(state, "µ",     expect &:= "µ");
      insert(state, "¿",     expect &:= "¿");
      insert(state, "Ä",     expect &:= "Ä");
      insert(state, "×",     expect &:= "×");
      insert(state, "ß",     expect &:= "ß");
      insert(state, "ä",     expect &:= "ä");
      insert(state, "÷",     expect &:= "÷");
      insert(state, "ÿ",     expect &:= "ÿ");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0,     expect &:= "0");
      insert(state, 1,     expect &:= "1");
      insert(state, 9,     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_,    expect &:= "0");
      insert(state, 1_,    expect &:= "1");
      insert(state, 9_,    expect &:= "9");

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1Latin1Field: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found " <& literal(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1Latin1Field: Row: " <& row <&
                    " Expected NULL found " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1Latin1Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching char(1) fields with LATIN-1 characters works okay.");
    else
      writeln(" *** Inserting and fetching char(1) fields with LATIN-1 characters does not work okay.");
    end if;
  end func;


const proc: testCodePageField (in database: testDb, in integer: codePage,
    in dbCategory: databaseKind) is func
  local
    var testState: state is testState("codePageTest", "codePageField", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testCodePageField: Code page: " <& codePage <& " " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:= "");

      insert(testDb, state, "''",      expect &:= "");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "'\0;'", expect &:= "\0;");
      end if;
      insert(testDb, state, "'\1;'",   expect &:= "\1;");
      insert(testDb, state, "'\b'",    expect &:= "\b");
      insert(testDb, state, "'\t'",    expect &:= "\t");
      insert(testDb, state, "'\n'",    expect &:= "\n");
      insert(testDb, state, "'\e'",    expect &:= "\e");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(testDb, state, "' '",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(testDb, state, "' '",   expect &:= "");
      end if;
      insert(testDb, state, "'!'",     expect &:= "!");
      insert(testDb, state, "'\"'",    expect &:= "\"");
      insert(testDb, state, "''''",    expect &:= "'");
      insert(testDb, state, "'0'",     expect &:= "0");
      insert(testDb, state, "'1'",     expect &:= "1");
      insert(testDb, state, "'9'",     expect &:= "9");
      insert(testDb, state, "'?'",     expect &:= "?");
      insert(testDb, state, "'X'",     expect &:= "X");
      insert(testDb, state, "'\\'",    expect &:= "\\");
      insert(testDb, state, "'^'",     expect &:= "^");
      insert(testDb, state, "'`'",     expect &:= "`");
      insert(testDb, state, "'a'",     expect &:= "a");
      insert(testDb, state, "'~'",     expect &:= "~");
      insert(testDb, state, "'\127;'", expect &:= "\127;");

      insert(testDb, state, "'€'",     expect &:= "€");
      insert(testDb, state, "'\129;'", expect &:= "\129;");
      insert(testDb, state, "'‚'",     expect &:= "‚");
      # insert(testDb, state, "'\131;'", expect &:= "\131;");
      insert(testDb, state, "'„'",     expect &:= "„");
      insert(testDb, state, "'…'",     expect &:= "…");
      insert(testDb, state, "'†'",     expect &:= "†");
      insert(testDb, state, "'‡'",     expect &:= "‡");
      # insert(testDb, state, "'\136;'", expect &:= "\136;");
      insert(testDb, state, "'‰'",     expect &:= "‰");
      insert(testDb, state, "'Š'",     expect &:= "Š");
      insert(testDb, state, "'‹'",     expect &:= "‹");
      if codePage = 1250 then
        insert(testDb, state, "'Ś'",   expect &:= "Ś");
        insert(testDb, state, "'Ť'",   expect &:= "Ť");
      elsif codePage = 1252 then
        insert(testDb, state, "'Œ'",   expect &:= "Œ");
      end if;
      insert(testDb, state, "'Ž'",     expect &:= "Ž");
      if codePage = 1250 then
        insert(testDb, state, "'Ź'",   expect &:= "Ź");
      end if;
      insert(testDb, state, "'\144;'", expect &:= "\144;");
      insert(testDb, state, "'‘'",     expect &:= "‘");
      insert(testDb, state, "'’'",     expect &:= "’");
      insert(testDb, state, "'“'",     expect &:= "“");
      insert(testDb, state, "'”'",     expect &:= "”");
      insert(testDb, state, "'•'",     expect &:= "•");
      insert(testDb, state, "'–'",     expect &:= "–");
      insert(testDb, state, "'—'",     expect &:= "—");
      # insert(testDb, state, "'\152;'", expect &:= "\152;");
      insert(testDb, state, "'™'",     expect &:= "™");
      insert(testDb, state, "'š'",     expect &:= "š");
      insert(testDb, state, "'›'",     expect &:= "›");
      if codePage = 1250 then
        insert(testDb, state, "'ś'",   expect &:= "ś");
        insert(testDb, state, "'ť'",   expect &:= "ť");
      elsif codePage = 1252 then
        insert(testDb, state, "'œ'",   expect &:= "œ");
      end if;
      insert(testDb, state, "'ž'",     expect &:= "ž");
      if codePage = 1250 then
        insert(testDb, state, "'ź'",   expect &:= "ź");
      elsif codePage = 1252 then
        insert(testDb, state, "'Ÿ'",   expect &:= "Ÿ");
      end if;

      insert(testDb, state, "'\160;'", expect &:= "\160;");
      insert(testDb, state, "'¡'",     expect &:= "¡");
      insert(testDb, state, "'¤'",     expect &:= "¤");
      insert(testDb, state, "'§'",     expect &:= "§");
      insert(testDb, state, "'¬'",     expect &:= "¬");
      insert(testDb, state, "'­'",     expect &:= "­");
      insert(testDb, state, "'µ'",     expect &:= "µ");
      insert(testDb, state, "'¿'",     expect &:= "¿");
      insert(testDb, state, "'Ä'",     expect &:= "Ä");
      insert(testDb, state, "'×'",     expect &:= "×");
      insert(testDb, state, "'ß'",     expect &:= "ß");
      insert(testDb, state, "'ä'",     expect &:= "ä");
      insert(testDb, state, "'÷'",     expect &:= "÷");
      insert(testDb, state, "'ÿ'",     expect &:= "ÿ");

      insert(testDb, state, "0",     expect &:= "0");
      insert(testDb, state, "1",     expect &:= "1");
      insert(testDb, state, "9",     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, "",    expect &:= "");
      if nullAllowedInString(databaseKind) then
        insert(state, "\0;", expect &:= "\0;");
      end if;
      insert(state, "\1;",   expect &:= "\1;");
      insert(state, "\b",    expect &:= "\b");
      insert(state, "\t",    expect &:= "\t");
      insert(state, "\n",    expect &:= "\n");
      insert(state, "\e",    expect &:= "\e");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(state, " ",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(state, " ",   expect &:= "");
      end if;
      insert(state, "!",     expect &:= "!");
      insert(state, "\"",    expect &:= "\"");
      insert(state, "'",     expect &:= "'");
      insert(state, "0",     expect &:= "0");
      insert(state, "1",     expect &:= "1");
      insert(state, "9",     expect &:= "9");
      insert(state, "?",     expect &:= "?");
      insert(state, "X",     expect &:= "X");
      insert(state, "\\",    expect &:= "\\");
      insert(state, "^",     expect &:= "^");
      insert(state, "`",     expect &:= "`");
      insert(state, "a",     expect &:= "a");
      insert(state, "~",     expect &:= "~");
      insert(state, "\127;", expect &:= "\127;");

      insert(state, "€",     expect &:= "€");
      insert(state, "\129;", expect &:= "\129;");
      insert(state, "‚",     expect &:= "‚");
      # insert(state, "\131;", expect &:= "\131;");
      insert(state, "„",     expect &:= "„");
      insert(state, "…",     expect &:= "…");
      insert(state, "†",     expect &:= "†");
      insert(state, "‡",     expect &:= "‡");
      # insert(state, "\136;", expect &:= "\136;");
      insert(state, "‰",     expect &:= "‰");
      insert(state, "Š",     expect &:= "Š");
      insert(state, "‹",     expect &:= "‹");
      if codePage = 1250 then
        insert(state, "Ś",   expect &:= "Ś");
        insert(state, "Ť",   expect &:= "Ť");
      elsif codePage = 1252 then
        insert(state, "Œ",   expect &:= "Œ");
      end if;
      insert(state, "Ž",     expect &:= "Ž");
      if codePage = 1250 then
        insert(state, "Ź",   expect &:= "Ź");
      end if;
      insert(state, "\144;", expect &:= "\144;");
      insert(state, "‘",     expect &:= "‘");
      insert(state, "’",     expect &:= "’");
      insert(state, "“",     expect &:= "“");
      insert(state, "”",     expect &:= "”");
      insert(state, "•",     expect &:= "•");
      insert(state, "–",     expect &:= "–");
      insert(state, "—",     expect &:= "—");
      # insert(state, "\152;", expect &:= "\152;");
      insert(state, "™",     expect &:= "™");
      insert(state, "š",     expect &:= "š");
      insert(state, "›",     expect &:= "›");
      if codePage = 1250 then
        insert(state, "ś",   expect &:= "ś");
        insert(state, "ť",   expect &:= "ť");
      elsif codePage = 1252 then
        insert(state, "œ",   expect &:= "œ");
      end if;
      insert(state, "ž",     expect &:= "ž");
      if codePage = 1250 then
        insert(state, "ź",   expect &:= "ź");
      elsif codePage = 1252 then
        insert(state, "Ÿ",   expect &:= "Ÿ");
      end if;

      insert(state, "\160;", expect &:= "\160;");
      insert(state, "¡",     expect &:= "¡");
      insert(state, "¤",     expect &:= "¤");
      insert(state, "§",     expect &:= "§");
      insert(state, "¬",     expect &:= "¬");
      insert(state, "­",     expect &:= "­");
      insert(state, "µ",     expect &:= "µ");
      insert(state, "¿",     expect &:= "¿");
      insert(state, "Ä",     expect &:= "Ä");
      insert(state, "×",     expect &:= "×");
      insert(state, "ß",     expect &:= "ß");
      insert(state, "ä",     expect &:= "ä");
      insert(state, "÷",     expect &:= "÷");
      insert(state, "ÿ",     expect &:= "ÿ");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0,     expect &:= "0");
      insert(state, 1,     expect &:= "1");
      insert(state, 9,     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_,    expect &:= "0");
      insert(state, 1_,    expect &:= "1");
      insert(state, 9_,    expect &:= "9");

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCodePageField: Code page: " <& codePage <& " Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found " <& literal(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCodePageField: Code page: " <& codePage <& " Row: " <& row <&
                    " Expected NULL found " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCodePageField: Code page: " <& codePage <& " Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching char(1) fields with code page " <& codePage <& " characters works okay.");
    else
      writeln(" *** Inserting and fetching char(1) fields with code page " <& codePage <& " characters does not work okay.");
    end if;
  end func;


const proc: testChar1Field (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("char1Test", "char1Field", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testChar1Field: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR)");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,  expect &:= "");

      insert(testDb, state, "''",      expect &:= "");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "'\0;'", expect &:= "\0;");
      end if;
      insert(testDb, state, "'\1;'",   expect &:= "\1;");
      insert(testDb, state, "'\2;'",   expect &:= "\2;");
      insert(testDb, state, "'\3;'",   expect &:= "\3;");
      insert(testDb, state, "'\4;'",   expect &:= "\4;");
      insert(testDb, state, "'\5;'",   expect &:= "\5;");
      insert(testDb, state, "'\6;'",   expect &:= "\6;");
      insert(testDb, state, "'\7;'",   expect &:= "\7;");
      insert(testDb, state, "'\8;'",   expect &:= "\8;");
      insert(testDb, state, "'\9;'",   expect &:= "\9;");
      insert(testDb, state, "'\10;'",  expect &:= "\10;");
      insert(testDb, state, "'\11;'",  expect &:= "\11;");
      insert(testDb, state, "'\12;'",  expect &:= "\12;");
      insert(testDb, state, "'\13;'",  expect &:= "\13;");
      insert(testDb, state, "'\14;'",  expect &:= "\14;");
      insert(testDb, state, "'\15;'",  expect &:= "\15;");
      insert(testDb, state, "'\16;'",  expect &:= "\16;");
      insert(testDb, state, "'\17;'",  expect &:= "\17;");
      insert(testDb, state, "'\18;'",  expect &:= "\18;");
      insert(testDb, state, "'\19;'",  expect &:= "\19;");
      insert(testDb, state, "'\20;'",  expect &:= "\20;");
      insert(testDb, state, "'\21;'",  expect &:= "\21;");
      insert(testDb, state, "'\22;'",  expect &:= "\22;");
      insert(testDb, state, "'\23;'",  expect &:= "\23;");
      insert(testDb, state, "'\24;'",  expect &:= "\24;");
      insert(testDb, state, "'\25;'",  expect &:= "\25;");
      insert(testDb, state, "'\26;'",  expect &:= "\26;");
      insert(testDb, state, "'\27;'",  expect &:= "\27;");
      insert(testDb, state, "'\28;'",  expect &:= "\28;");
      insert(testDb, state, "'\29;'",  expect &:= "\29;");
      insert(testDb, state, "'\30;'",  expect &:= "\30;");
      insert(testDb, state, "'\31;'",  expect &:= "\31;");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(testDb, state, "' '",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(testDb, state, "' '",   expect &:= "");
      end if;
      insert(testDb, state, "'!'",     expect &:= "!");
      insert(testDb, state, "'\"'",    expect &:= "\"");
      insert(testDb, state, "'#'",     expect &:= "#");
      insert(testDb, state, "'$'",     expect &:= "$");
      insert(testDb, state, "'%'",     expect &:= "%");
      insert(testDb, state, "'&'",     expect &:= "&");
      insert(testDb, state, "''''",    expect &:= "'");
      insert(testDb, state, "'('",     expect &:= "(");
      insert(testDb, state, "')'",     expect &:= ")");
      insert(testDb, state, "'*'",     expect &:= "*");
      insert(testDb, state, "'+'",     expect &:= "+");
      insert(testDb, state, "','",     expect &:= ",");
      insert(testDb, state, "'-'",     expect &:= "-");
      insert(testDb, state, "'.'",     expect &:= ".");
      insert(testDb, state, "'/'",     expect &:= "/");
      insert(testDb, state, "'0'",     expect &:= "0");
      insert(testDb, state, "'1'",     expect &:= "1");
      insert(testDb, state, "'9'",     expect &:= "9");
      insert(testDb, state, "':'",     expect &:= ":");
      insert(testDb, state, "';'",     expect &:= ";");
      insert(testDb, state, "'<'",     expect &:= "<");
      insert(testDb, state, "'='",     expect &:= "=");
      insert(testDb, state, "'>'",     expect &:= ">");
      insert(testDb, state, "'?'",     expect &:= "?");
      insert(testDb, state, "'@'",     expect &:= "@");
      insert(testDb, state, "'A'",     expect &:= "A");
      insert(testDb, state, "'Z'",     expect &:= "Z");
      insert(testDb, state, "'['",     expect &:= "[");
      insert(testDb, state, "'\\'",    expect &:= "\\");
      insert(testDb, state, "']'",     expect &:= "]");
      insert(testDb, state, "'^'",     expect &:= "^");
      insert(testDb, state, "'_'",     expect &:= "_");
      insert(testDb, state, "'`'",     expect &:= "`");
      insert(testDb, state, "'a'",     expect &:= "a");
      insert(testDb, state, "'z'",     expect &:= "z");
      insert(testDb, state, "'{'",     expect &:= "{");
      insert(testDb, state, "'|'",     expect &:= "|");
      insert(testDb, state, "'}'",     expect &:= "}");
      insert(testDb, state, "'~'",     expect &:= "~");
      insert(testDb, state, "'\127;'", expect &:= "\127;");
      if maxChar1FieldCharacter(databaseKind) >= '\255;' then
        insert(testDb, state, "'\160;'", expect &:= "\160;");
        insert(testDb, state, "'¡'",     expect &:= "¡");
        insert(testDb, state, "'¤'",     expect &:= "¤");
        insert(testDb, state, "'§'",     expect &:= "§");
        insert(testDb, state, "'¬'",     expect &:= "¬");
        insert(testDb, state, "'­'",     expect &:= "­");
        insert(testDb, state, "'µ'",     expect &:= "µ");
        insert(testDb, state, "'¿'",     expect &:= "¿");
        insert(testDb, state, "'Ä'",     expect &:= "Ä");
        insert(testDb, state, "'×'",     expect &:= "×");
        insert(testDb, state, "'ß'",     expect &:= "ß");
        insert(testDb, state, "'ä'",     expect &:= "ä");
        insert(testDb, state, "'÷'",     expect &:= "÷");
        insert(testDb, state, "'ÿ'",     expect &:= "ÿ");
      end if;
      if maxChar1FieldCharacter(databaseKind) >= '\8888;' then # Code page 1252
        insert(testDb, state, "'Œ'",        expect &:= "Œ");
        insert(testDb, state, "'Š'",        expect &:= "Š");
        insert(testDb, state, "'ƒ'",        expect &:= "ƒ");
        insert(testDb, state, "'†'",        expect &:= "†");
        insert(testDb, state, "'•'",        expect &:= "•");
        insert(testDb, state, "'‰'",        expect &:= "‰");
        insert(testDb, state, "'€'",        expect &:= "€");
        insert(testDb, state, "'™'",        expect &:= "™");
      end if;
      if maxChar1FieldCharacter(databaseKind) >= '\65535;' then
        insert(testDb, state, "'Ā'",        expect &:= "Ā");
        insert(testDb, state, "'\65535;'",  expect &:= "\65535;");
      end if;
      if maxChar1FieldCharacter(databaseKind) >= '\16#10ffff;' then
        insert(testDb, state, "'\65536;'",  expect &:= "\65536;");
        insert(testDb, state, "'\16#10ffff;'",  expect &:= "\16#10ffff;");
      end if;

      insert(testDb, state, "0",     expect &:= "0");
      insert(testDb, state, "1",     expect &:= "1");
      insert(testDb, state, "9",     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, "",    expect &:= "");
      if nullAllowedInString(databaseKind) then
        insert(state, "\0;", expect &:= "\0;");
      end if;
      insert(state, "\1;",   expect &:= "\1;");
      insert(state, "\2;",   expect &:= "\2;");
      insert(state, "\3;",   expect &:= "\3;");
      insert(state, "\4;",   expect &:= "\4;");
      insert(state, "\5;",   expect &:= "\5;");
      insert(state, "\6;",   expect &:= "\6;");
      insert(state, "\7;",   expect &:= "\7;");
      insert(state, "\8;",   expect &:= "\8;");
      insert(state, "\9;",   expect &:= "\9;");
      insert(state, "\10;",  expect &:= "\10;");
      insert(state, "\11;",  expect &:= "\11;");
      insert(state, "\12;",  expect &:= "\12;");
      insert(state, "\13;",  expect &:= "\13;");
      insert(state, "\14;",  expect &:= "\14;");
      insert(state, "\15;",  expect &:= "\15;");
      insert(state, "\16;",  expect &:= "\16;");
      insert(state, "\17;",  expect &:= "\17;");
      insert(state, "\18;",  expect &:= "\18;");
      insert(state, "\19;",  expect &:= "\19;");
      insert(state, "\20;",  expect &:= "\20;");
      insert(state, "\21;",  expect &:= "\21;");
      insert(state, "\22;",  expect &:= "\22;");
      insert(state, "\23;",  expect &:= "\23;");
      insert(state, "\24;",  expect &:= "\24;");
      insert(state, "\25;",  expect &:= "\25;");
      insert(state, "\26;",  expect &:= "\26;");
      insert(state, "\27;",  expect &:= "\27;");
      insert(state, "\28;",  expect &:= "\28;");
      insert(state, "\29;",  expect &:= "\29;");
      insert(state, "\30;",  expect &:= "\30;");
      insert(state, "\31;",  expect &:= "\31;");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(state, " ",   expect &:= " ");
      else
        # Reading a char field removes trailing spaces.
        insert(state, " ",   expect &:= "");
      end if;
      insert(state, "!",     expect &:= "!");
      insert(state, "\"",    expect &:= "\"");
      insert(state, "#",     expect &:= "#");
      insert(state, "$",     expect &:= "$");
      insert(state, "%",     expect &:= "%");
      insert(state, "&",     expect &:= "&");
      insert(state, "'",     expect &:= "'");
      insert(state, "(",     expect &:= "(");
      insert(state, ")",     expect &:= ")");
      insert(state, "*",     expect &:= "*");
      insert(state, "+",     expect &:= "+");
      insert(state, ",",     expect &:= ",");
      insert(state, "-",     expect &:= "-");
      insert(state, ".",     expect &:= ".");
      insert(state, "/",     expect &:= "/");
      insert(state, "0",     expect &:= "0");
      insert(state, "1",     expect &:= "1");
      insert(state, "9",     expect &:= "9");
      insert(state, ":",     expect &:= ":");
      insert(state, ";",     expect &:= ";");
      insert(state, "<",     expect &:= "<");
      insert(state, "=",     expect &:= "=");
      insert(state, ">",     expect &:= ">");
      insert(state, "?",     expect &:= "?");
      insert(state, "@",     expect &:= "@");
      insert(state, "A",     expect &:= "A");
      insert(state, "Z",     expect &:= "Z");
      insert(state, "[",     expect &:= "[");
      insert(state, "\\",    expect &:= "\\");
      insert(state, "]",     expect &:= "]");
      insert(state, "^",     expect &:= "^");
      insert(state, "_",     expect &:= "_");
      insert(state, "`",     expect &:= "`");
      insert(state, "a",     expect &:= "a");
      insert(state, "z",     expect &:= "z");
      insert(state, "{",     expect &:= "{");
      insert(state, "|",     expect &:= "|");
      insert(state, "}",     expect &:= "}");
      insert(state, "~",     expect &:= "~");
      insert(state, "\127;", expect &:= "\127;");
      if maxChar1FieldCharacter(databaseKind) >= '\255;' then
        insert(state, "\160;", expect &:= "\160;");
        insert(state, "¡",     expect &:= "¡");
        insert(state, "¤",     expect &:= "¤");
        insert(state, "§",     expect &:= "§");
        insert(state, "¬",     expect &:= "¬");
        insert(state, "­",     expect &:= "­");
        insert(state, "µ",     expect &:= "µ");
        insert(state, "¿",     expect &:= "¿");
        insert(state, "Ä",     expect &:= "Ä");
        insert(state, "×",     expect &:= "×");
        insert(state, "ß",     expect &:= "ß");
        insert(state, "ä",     expect &:= "ä");
        insert(state, "÷",     expect &:= "÷");
        insert(state, "ÿ",     expect &:= "ÿ");
      end if;
      if maxChar1FieldCharacter(databaseKind) >= '\8888;' then # Code page 1252
        insert(state, "Œ",        expect &:= "Œ");
        insert(state, "Š",        expect &:= "Š");
        insert(state, "ƒ",        expect &:= "ƒ");
        insert(state, "†",        expect &:= "†");
        insert(state, "•",        expect &:= "•");
        insert(state, "‰",        expect &:= "‰");
        insert(state, "€",        expect &:= "€");
        insert(state, "™",        expect &:= "™");
      end if;
      if maxChar1FieldCharacter(databaseKind) >= '\65535;' then
        insert(state, "Ā",        expect &:= "Ā");
        insert(state, "\65535;",  expect &:= "\65535;");
      end if;
      if maxChar1FieldCharacter(databaseKind) >= '\16#10ffff;' then
        insert(state, "\65536;",      expect &:= "\65536;");
        insert(state, "\16#10ffff;",  expect &:= "\16#10ffff;");
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0,     expect &:= "0");
      insert(state, 1,     expect &:= "1");
      insert(state, 9,     expect &:= "9");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, 0_,    expect &:= "0");
      insert(state, 1_,    expect &:= "1");
      insert(state, 9_,    expect &:= "9");

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1Field: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found " <& literal(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1Field: Row: " <& row <&
                    " Expected NULL found " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testChar1Field: Row: " <& row <&
                    " Expected " <& expect[row] <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching char(1) fields works okay.");
    else
      writeln(" *** Inserting and fetching char(1) fields does not work okay.");
    end if;
  end func;


const proc: testCharField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("charTest", "charField", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testCharField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR(32))");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,         expect &:= "");

      insert(testDb, state, "''",           expect &:= "");
      insert(testDb, state, "'X'",          expect &:= "X");
      insert(testDb, state, "'Ab2'",        expect &:= "Ab2");
      insert(testDb, state, "'abcdefghij'", expect &:= "abcdefghij");
      insert(testDb, state, "'ÄäÖöÜüß'",    expect &:= "ÄäÖöÜüß");
      insert(testDb, state, "'€µ¹²³¼½«»¬'", expect &:= "€µ¹²³¼½«»¬");
      # Chars that are defined with differend ordinal in code page 1252:
      insert(testDb, state, "'€‚ƒ„…†‡ˆ‰'",  expect &:= "€‚ƒ„…†‡ˆ‰");
      insert(testDb, state, "'Š‹ŒŽ‘’“”•'",  expect &:= "Š‹ŒŽ‘’“”•");
      insert(testDb, state, "'–—˜™š›œžŸ'",  expect &:= "–—˜™š›œžŸ");
      insert(testDb, state, "'" & "x" mult 32 & "'",  expect &:= "x" mult 32);
      insert(testDb, state, "'A B'",        expect &:= "A B");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "'A\0;B'",    expect &:= "A\0;B");
      end if;
      insert(testDb, state, "'\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;'",
                 expect &:= "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;");
      insert(testDb, state, "' !\"#$%&''()*+,-./'",         expect &:= " !\"#$%&'()*+,-./");
      insert(testDb, state, "'0123456789:;<=>?@'",          expect &:= "0123456789:;<=>?@");
      insert(testDb, state, "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'", expect &:= "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      insert(testDb, state, "'[\\]^_`{|}~\127;'",           expect &:= "[\\]^_`{|}~\127;");
      insert(testDb, state, "'abcdefghijklmnopqrstuvwxyz'", expect &:= "abcdefghijklmnopqrstuvwxyz");
      insert(testDb, state, "'\160;¡¢£¤¥¦§¨©ª«¬­®¯'",       expect &:= "\160;¡¢£¤¥¦§¨©ª«¬­®¯");
      insert(testDb, state, "'°±²³´µ¶·¸¹º»¼½¾¿'",           expect &:= "°±²³´µ¶·¸¹º»¼½¾¿");
      insert(testDb, state, "'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ'",           expect &:= "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ");
      insert(testDb, state, "'ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß'",           expect &:= "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß");
      insert(testDb, state, "'àáâãäåæçèéêëìíîï'",           expect &:= "àáâãäåæçèéêëìíîï");
      insert(testDb, state, "'ðñòóôõö÷øùúûüýþÿ'",           expect &:= "ðñòóôõö÷øùúûüýþÿ");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(testDb, state, "'a '",       expect &:= "a ");
        # insert(testDb, state, "'" & "b" mult 3999 & " '", expect &:= "b" mult 3999 & " ");
      else
        # Reading a char field removes trailing spaces.
        insert(testDb, state, "'a '",       expect &:= "a");
        # insert(testDb, state, "'" & "b" mult 3999 & " '", expect &:= "b" mult 3999);
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, "",           expect &:= "");
      insert(state, "X",          expect &:= "X");
      insert(state, "Ab2",        expect &:= "Ab2");
      insert(state, "abcdefghij", expect &:= "abcdefghij");
      insert(state, "ÄäÖöÜüß",    expect &:= "ÄäÖöÜüß");
      insert(state, "€µ¹²³¼½«»¬", expect &:= "€µ¹²³¼½«»¬");
      # Chars that are defined with differend ordinal in code page 1252:
      insert(state, "€‚ƒ„…†‡ˆ‰",  expect &:= "€‚ƒ„…†‡ˆ‰");
      insert(state, "Š‹ŒŽ‘’“”•",  expect &:= "Š‹ŒŽ‘’“”•");
      insert(state, "–—˜™š›œžŸ",  expect &:= "–—˜™š›œžŸ");
      insert(state, "x" mult 32,  expect &:= "x" mult 32);
      insert(state, "A B",        expect &:= "A B");
      if nullAllowedInString(databaseKind) then
        insert(state, "A\0;B",    expect &:= "A\0;B");
      end if;
      insert(state, "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                    \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                    \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;",
         expect &:= "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                    \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                    \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;");
      insert(state, " !\"#$%&'()*+,-./",          expect &:= " !\"#$%&'()*+,-./");
      insert(state, "0123456789:;<=>?@",          expect &:= "0123456789:;<=>?@");
      insert(state, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", expect &:= "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      insert(state, "[\\]^_`{|}~\127;",           expect &:= "[\\]^_`{|}~\127;");
      insert(state, "abcdefghijklmnopqrstuvwxyz", expect &:= "abcdefghijklmnopqrstuvwxyz");
      insert(state, "\160;¡¢£¤¥¦§¨©ª«¬­®¯",       expect &:= "\160;¡¢£¤¥¦§¨©ª«¬­®¯");
      insert(state, "°±²³´µ¶·¸¹º»¼½¾¿",           expect &:= "°±²³´µ¶·¸¹º»¼½¾¿");
      insert(state, "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ",           expect &:= "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ");
      insert(state, "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß",           expect &:= "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß");
      insert(state, "àáâãäåæçèéêëìíîï",           expect &:= "àáâãäåæçèéêëìíîï");
      insert(state, "ðñòóôõö÷øùúûüýþÿ",           expect &:= "ðñòóôõö÷øùúûüýþÿ");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(state, "a ",         expect &:= "a ");
        # insert(state, "b" mult 3999 & " ",      expect &:= "b" mult 3999 & " ");
      else
        # Reading a char field removes trailing spaces.
        insert(state, "a ",         expect &:= "a");
        # insert(state, "b" mult 3999 & " ",      expect &:= "b" mult 3999);
      end if;

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,          0,   expect &:= "0");
      insert(state,          1,   expect &:= "1");
      insert(state, 1234567890,   expect &:= "1234567890");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,          0_,  expect &:= "0");
      insert(state,          1_,  expect &:= "1");
      insert(state, 1234567890_,  expect &:= "1234567890");

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCharField: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found string " <& literal(stringColumn));
            writeln("Expected length: " <& length(expect[row]));
            writeln("Length found:    " <& length(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCharField: Row: " <& row <&
                    " Expected NULL found string " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCharField: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching char fields works okay.");
    else
      writeln(" *** Inserting and fetching char fields does not work okay.");
    end if;
  end func;


const proc: testCharField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("charTest", "charField", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testCharField2: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " CHAR(32))");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,         expect &:= "");

      insert(testDb, state, "\"\"",           expect &:= "");
      insert(testDb, state, "\"X\"",          expect &:= "X");
      insert(testDb, state, "\"Ab2\"",        expect &:= "Ab2");
      insert(testDb, state, "\"abcdefghij\"", expect &:= "abcdefghij");
      insert(testDb, state, "\"ÄäÖöÜüß\"",    expect &:= "ÄäÖöÜüß");
      insert(testDb, state, "\"€µ¹²³¼½«»¬\"", expect &:= "€µ¹²³¼½«»¬");
      # Chars that are defined with differend ordinal in code page 1252:
      insert(testDb, state, "\"€‚ƒ„…†‡ˆ‰\"",  expect &:= "€‚ƒ„…†‡ˆ‰");
      insert(testDb, state, "\"Š‹ŒŽ‘’“”•\"",  expect &:= "Š‹ŒŽ‘’“”•");
      insert(testDb, state, "\"–—˜™š›œžŸ\"",  expect &:= "–—˜™š›œžŸ");
      insert(testDb, state, "\"" & "x" mult 32 & "\"",  expect &:= "x" mult 32);
      insert(testDb, state, "\"A B\"",        expect &:= "A B");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "\"A\0;B\"",    expect &:= "A\0;B");
      end if;
      insert(testDb, state, "\"\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;\"",
                 expect &:= "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;");
      insert(testDb, state, "\" !\"\"#$%&'()*+,-./\"",         expect &:= " !\"#$%&'()*+,-./");
      insert(testDb, state, "\"0123456789:;<=>?@\"",          expect &:= "0123456789:;<=>?@");
      insert(testDb, state, "\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"", expect &:= "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      insert(testDb, state, "\"[\\]^_`{|}~\127;\"",           expect &:= "[\\]^_`{|}~\127;");
      insert(testDb, state, "\"abcdefghijklmnopqrstuvwxyz\"", expect &:= "abcdefghijklmnopqrstuvwxyz");
      insert(testDb, state, "\"\160;¡¢£¤¥¦§¨©ª«¬­®¯\"",       expect &:= "\160;¡¢£¤¥¦§¨©ª«¬­®¯");
      insert(testDb, state, "\"°±²³´µ¶·¸¹º»¼½¾¿\"",           expect &:= "°±²³´µ¶·¸¹º»¼½¾¿");
      insert(testDb, state, "\"ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ\"",           expect &:= "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ");
      insert(testDb, state, "\"ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß\"",           expect &:= "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß");
      insert(testDb, state, "\"àáâãäåæçèéêëìíîï\"",           expect &:= "àáâãäåæçèéêëìíîï");
      insert(testDb, state, "\"ðñòóôõö÷øùúûüýþÿ\"",           expect &:= "ðñòóôõö÷øùúûüýþÿ");
      if charFieldPreservesTrailingSpaces(databaseKind) then
        # Reading a char field preserves trailing spaces.
        insert(testDb, state, "\"a \"",       expect &:= "a ");
        # insert(testDb, state, "\"" & "b" mult 3999 & " \"", expect &:= "b" mult 3999 & " ");
      else
        # Reading a char field removes trailing spaces.
        insert(testDb, state, "\"a \"",       expect &:= "a");
        # insert(testDb, state, "\"" & "b" mult 3999 & " \"", expect &:= "b" mult 3999);
      end if;

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCharField2: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found string " <& literal(stringColumn));
            writeln("Expected length: " <& length(expect[row]));
            writeln("Length found:    " <& length(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCharField2: Row: " <& row <&
                    " Expected NULL found string " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testCharField2: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching char fields works okay.");
    else
      writeln(" *** Inserting and fetching char fields does not work okay.");
    end if;
  end func;


const proc: testVarcharField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("varcharTest", "varcharField", FALSE);
    var array string: expect is 0 times "";
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testVarcharField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & varcharType(databaseKind) & "(32))");
      execute(state.statement);
      insert(testDb, state, "NULL", expect &:= "");
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,         expect &:= "");

      insert(testDb, state, "''",           expect &:= "");
      insert(testDb, state, "'X'",          expect &:= "X");
      insert(testDb, state, "'Ab2'",        expect &:= "Ab2");
      insert(testDb, state, "'abcdefghij'", expect &:= "abcdefghij");
      insert(testDb, state, "'ÄäÖöÜüß'",    expect &:= "ÄäÖöÜüß");
      insert(testDb, state, "'€µ¹²³¼½«»¬'", expect &:= "€µ¹²³¼½«»¬");
      # Chars that are defined with differend ordinal in code page 1252:
      insert(testDb, state, "'€‚ƒ„…†‡ˆ‰'",  expect &:= "€‚ƒ„…†‡ˆ‰");
      insert(testDb, state, "'Š‹ŒŽ‘’“”•'",  expect &:= "Š‹ŒŽ‘’“”•");
      insert(testDb, state, "'–—˜™š›œžŸ'",  expect &:= "–—˜™š›œžŸ");
      insert(testDb, state, "'" & "x" mult 32 & "'",  expect &:= "x" mult 32);
      insert(testDb, state, "'A B'",        expect &:= "A B");
      if nullAllowedInStringLiteral(databaseKind) then
        insert(testDb, state, "'A\0;B'",    expect &:= "A\0;B");
      end if;
      insert(testDb, state, "'\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;'",
                 expect &:= "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;");
      insert(testDb, state, "' !\"#$%&''()*+,-./'",         expect &:= " !\"#$%&'()*+,-./");
      insert(testDb, state, "'0123456789:;<=>?@'",          expect &:= "0123456789:;<=>?@");
      insert(testDb, state, "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'", expect &:= "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      insert(testDb, state, "'[\\]^_`{|}~\127;'",           expect &:= "[\\]^_`{|}~\127;");
      insert(testDb, state, "'abcdefghijklmnopqrstuvwxyz'", expect &:= "abcdefghijklmnopqrstuvwxyz");
      insert(testDb, state, "'\160;¡¢£¤¥¦§¨©ª«¬­®¯'",       expect &:= "\160;¡¢£¤¥¦§¨©ª«¬­®¯");
      insert(testDb, state, "'°±²³´µ¶·¸¹º»¼½¾¿'",           expect &:= "°±²³´µ¶·¸¹º»¼½¾¿");
      insert(testDb, state, "'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ'",           expect &:= "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ");
      insert(testDb, state, "'ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß'",           expect &:= "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß");
      insert(testDb, state, "'àáâãäåæçèéêëìíîï'",           expect &:= "àáâãäåæçèéêëìíîï");
      insert(testDb, state, "'ðñòóôõö÷øùúûüýþÿ'",           expect &:= "ðñòóôõö÷øùúûüýþÿ");
      # Reading a varchar field preserves trailing spaces.
      insert(testDb, state, "'a '",         expect &:= "a ");
      # insert(testDb, state, "'" & "b" mult 3999 & " '",     expect &:= "b" mult 3999 & " ");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, "",           expect &:= "");
      insert(state, "X",          expect &:= "X");
      insert(state, "Ab2",        expect &:= "Ab2");
      insert(state, "abcdefghij", expect &:= "abcdefghij");
      insert(state, "ÄäÖöÜüß",    expect &:= "ÄäÖöÜüß");
      insert(state, "€µ¹²³¼½«»¬", expect &:= "€µ¹²³¼½«»¬");
      # Chars that are defined with differend ordinal in code page 1252:
      insert(state, "€‚ƒ„…†‡ˆ‰",  expect &:= "€‚ƒ„…†‡ˆ‰");
      insert(state, "Š‹ŒŽ‘’“”•",  expect &:= "Š‹ŒŽ‘’“”•");
      insert(state, "–—˜™š›œžŸ",  expect &:= "–—˜™š›œžŸ");
      insert(state, "x" mult 32,  expect &:= "x" mult 32);
      insert(state, "A B",        expect &:= "A B");
      if nullAllowedInString(databaseKind) then
        insert(state, "A\0;B",    expect &:= "A\0;B");
      end if;
      insert(state, "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                    \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                    \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;",
         expect &:= "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                    \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                    \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;");
      insert(state, " !\"#$%&'()*+,-./",          expect &:= " !\"#$%&'()*+,-./");
      insert(state, "0123456789:;<=>?@",          expect &:= "0123456789:;<=>?@");
      insert(state, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", expect &:= "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      insert(state, "[\\]^_`{|}~\127;",           expect &:= "[\\]^_`{|}~\127;");
      insert(state, "abcdefghijklmnopqrstuvwxyz", expect &:= "abcdefghijklmnopqrstuvwxyz");
      insert(state, "\160;¡¢£¤¥¦§¨©ª«¬­®¯",       expect &:= "\160;¡¢£¤¥¦§¨©ª«¬­®¯");
      insert(state, "°±²³´µ¶·¸¹º»¼½¾¿",           expect &:= "°±²³´µ¶·¸¹º»¼½¾¿");
      insert(state, "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ",           expect &:= "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ");
      insert(state, "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß",           expect &:= "ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß");
      insert(state, "àáâãäåæçèéêëìíîï",           expect &:= "àáâãäåæçèéêëìíîï");
      insert(state, "ðñòóôõö÷øùúûüýþÿ",           expect &:= "ðñòóôõö÷øùúûüýþÿ");
      # Reading a varchar field preserves trailing spaces.
      insert(state, "a ",         expect &:= "a ");
      # insert(state, "b" mult 3999 & " ",          expect &:= "b" mult 3999 & " ");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,          0,   expect &:= "0");
      insert(state,          1,   expect &:= "1");
      insert(state, 1234567890,   expect &:= "1234567890");

      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state,          0_,  expect &:= "0");
      insert(state,          1_,  expect &:= "1");
      insert(state, 1234567890_,  expect &:= "1234567890");
#      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
#                           " (" & state.fieldName & ") VALUES (N'" <&
#                           "\128;\129;\130;\131;\132;\133;\134;\135;\
#                           \\136;\137;\138;\139;\140;\141;\142;\143;\144;\145;\146;\147;\148;\
#                           \\149;\150;\151;\152;\153;\154;\155;\156;\157;\158;\159;" & "')");
#      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
#                           " (" & state.fieldName & ") VALUES (\
#                           \CHAR(128) + CHAR(129) + CHAR(130) + CHAR(131) + CHAR(132) + CHAR(133) + CHAR(134) + CHAR(135) + CHAR(136) + CHAR(137) + \
#                           \CHAR(138) + CHAR(139) + CHAR(140) + CHAR(141) + CHAR(142) + CHAR(143) + CHAR(144) + CHAR(145) + CHAR(146) + CHAR(147) + \
#                           \CHAR(148) + CHAR(149) + CHAR(150) + CHAR(151) + CHAR(152) + CHAR(153) + CHAR(154) + CHAR(155) + CHAR(156) + CHAR(157) + \
#                           \CHAR(158) + CHAR(159))");
#      execute(state.statement);
#      expect &:= "\128;\129;\130;\131;\132;\133;\134;\135;\
#                 \\136;\137;\138;\139;\140;\141;\142;\143;\144;\145;\146;\147;\148;\
#                 \\149;\150;\151;\152;\153;\154;\155;\156;\157;\158;\159;";
      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testVarcharField: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <&
                    " found string " <& literal(stringColumn));
            writeln("Expected length: " <& length(expect[row]));
            writeln("Length found:    " <& length(stringColumn));
          end if;
        end if;
        if row <= 2 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testVarcharField: Row: " <& row <&
                    " Expected NULL found string " <& stringColumn);
          end if;
        end if;
        if expect[row] <> "" and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testVarcharField: Row: " <& row <&
                    " Expected " <& literal(expect[row]) <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching varchar fields works okay.");
    else
      writeln(" *** Inserting and fetching varchar fields does not work okay.");
    end if;
  end func;


const proc: testBlobField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("blobTest", "blobField", FALSE);
    var array bstring: expect is 0 times bstring("");
    var bstring: bstringColumn is bstring("");
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    if state.details then
      writeln("testBlobField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                         " (" & state.fieldName & " " & blobType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                           " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,                  expect &:= bstring(""));
      insert(state, bstring(""),           expect &:= bstring(""));
      insert(state, bstring("X"),          expect &:= bstring("X"));
      insert(state, bstring("Ab2"),        expect &:= bstring("Ab2"));
      insert(state, bstring("abcdefghij"), expect &:= bstring("abcdefghij"));
      insert(state, bstring("ÄäÖöÜüß"),    expect &:= bstring("ÄäÖöÜüß"));
      insert(state, bstring("µ¹²³¼½«»¬"),  expect &:= bstring("µ¹²³¼½«»¬"));
      insert(state, bstring("x" mult 65535), expect &:= bstring("x" mult 65535));
      insert(state, bstring("y" mult 1000000), expect &:= bstring("y" mult 1000000));
      # BStrings that contain all possible byte values (from 0 to 255):
      insert(state, bstring("\0;\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;\127;"),
         expect &:= bstring("\0;\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                            \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                            \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;\127;"));
      insert(state, bstring("\128;\129;\130;\131;\132;\133;\134;\135;\
                            \\136;\137;\138;\139;\140;\141;\142;\143;\144;\145;\146;\147;\148;\
                            \\149;\150;\151;\152;\153;\154;\155;\156;\157;\158;\159;\160;"),
         expect &:= bstring("\128;\129;\130;\131;\132;\133;\134;\135;\
                            \\136;\137;\138;\139;\140;\141;\142;\143;\144;\145;\146;\147;\148;\
                            \\149;\150;\151;\152;\153;\154;\155;\156;\157;\158;\159;\160;"));
      insert(state, bstring(" !\"#$%&'()*+,-./"),          expect &:= bstring(" !\"#$%&'()*+,-./"));
      insert(state, bstring("0123456789:;<=>?@"),          expect &:= bstring("0123456789:;<=>?@"));
      insert(state, bstring("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), expect &:= bstring("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
      insert(state, bstring("[\\]^_`{|}~\127;"),           expect &:= bstring("[\\]^_`{|}~\127;"));
      insert(state, bstring("abcdefghijklmnopqrstuvwxyz"), expect &:= bstring("abcdefghijklmnopqrstuvwxyz"));
      insert(state, bstring("\160;¡¢£¤¥¦§¨©ª«¬­®¯"),       expect &:= bstring("\160;¡¢£¤¥¦§¨©ª«¬­®¯"));
      insert(state, bstring("°±²³´µ¶·¸¹º»¼½¾¿"),           expect &:= bstring("°±²³´µ¶·¸¹º»¼½¾¿"));
      insert(state, bstring("ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"),           expect &:= bstring("ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ"));
      insert(state, bstring("ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß"),           expect &:= bstring("ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß"));
      insert(state, bstring("àáâãäåæçèéêëìíîï"),           expect &:= bstring("àáâãäåæçèéêëìíîï"));
      insert(state, bstring("ðñòóôõö÷øùúûüýþÿ"),           expect &:= bstring("ðñòóôõö÷øùúûüýþÿ"));
      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        bstringColumn := column(state.statement, 1, bstring);
        if bstringColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            if length(expect[row]) <= 1000 and length(bstringColumn) <= 1000 then
              writeln(" *** testBlobField: Row: " <& row <&
                      " Expected " <& literal(str(expect[row])) <&
                      " found bstring " <& literal(str(bstringColumn)));
            else
              writeln(" *** testBlobField: Row: " <& row <&
                      " Expected length: " <& length(expect[row]) <&
                      " bstring length found: " <& length(bstringColumn));
            end if;
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBlobField: Row: " <& row <&
                    " Expected NULL found " <& bstringColumn);
          end if;
        end if;
        stringColumn := column(state.statement, 1, string);
        if stringColumn <> string(expect[row]) then
          state.okay := FALSE;
          if state.details then
            if length(expect[row]) <= 1000 and length(stringColumn) <= 1000 then
              writeln(" *** testBlobField: Row: " <& row <&
                      " Expected " <& literal(str(expect[row])) <&
                      " found string " <& literal(str(stringColumn)));
            else
              writeln(" *** testBlobField: Row: " <& row <&
                      " Expected length: " <& length(expect[row]) <&
                      " string length found: " <& length(stringColumn));
            end if;
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBlobField: Row: " <& row <&
                    " Expected NULL found " <& stringColumn);
          end if;
        end if;
        if length(expect[row]) <> 0 and isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testBlobField: Row: " <& row <&
                    " Expected " <& literal(str(expect[row])) <& " found NULL");
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching blob fields works okay.");
    else
      writeln(" *** Inserting and fetching blob fields does not work okay.");
    end if;
  end func;


const proc: testTimeField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("timeTest", "timeField", FALSE);
    var array time: expect is 0 times time.value;
    var time: timeColumn is time.value;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testTimeField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & timeType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,                           expect &:= time.value);
      insert(state, time("0-1-1 0:0:0"),            expect &:= time("0-1-1 0:0:0"));
      insert(state, time("1900-1-1 0:0:0"),         expect &:= time("1900-1-1 0:0:0"));
      insert(state, time("1961-1-9 6:1:0"),         expect &:= time("1961-1-9 6:1:0"));
      insert(state, time("1970-1-1 0:0:0"),         expect &:= time("1970-1-1 0:0:0"));
      insert(state, time("1980-1-1 0:0:0"),         expect &:= time("1980-1-1 0:0:0"));
      insert(state, time("2010-11-12 13:14:15.16"), expect &:= time("2010-11-12 13:14:15.16"));
      insert(state, time("2011-11-11 11:11:11.11"), expect &:= time("2011-11-11 11:11:11.11"));
      insert(state, time("2012-12-12 12:12:12.12"), expect &:= time("2012-12-12 12:12:12.12"));

      insert(state, setLocalTZ(time("0-1-1 0:0:0")),            expect &:= setLocalTZ(time("0-1-1 0:0:0")));
      insert(state, setLocalTZ(time("1900-1-1 0:0:0")),         expect &:= setLocalTZ(time("1900-1-1 0:0:0")));
      insert(state, setLocalTZ(time("1961-1-9 6:1:0")),         expect &:= setLocalTZ(time("1961-1-9 6:1:0")));
      insert(state, setLocalTZ(time("1970-1-1 0:0:0")),         expect &:= setLocalTZ(time("1970-1-1 0:0:0")));
      insert(state, setLocalTZ(time("1980-1-1 0:0:0")),         expect &:= setLocalTZ(time("1980-1-1 0:0:0")));
      insert(state, setLocalTZ(time("2010-11-12 13:14:15.16")), expect &:= setLocalTZ(time("2010-11-12 13:14:15.16")));
      insert(state, setLocalTZ(time("2011-11-11 11:11:11.11")), expect &:= setLocalTZ(time("2011-11-11 11:11:11.11")));
      insert(state, setLocalTZ(time("2012-12-12 12:12:12.12")), expect &:= setLocalTZ(time("2012-12-12 12:12:12.12")));

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        timeColumn := column(state.statement, 1, time);
        if timeColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testTimeField: Row: " <& row <&
                    " Expected " <& literal(str(expect[row])) <&
                    " found " <& literal(str(timeColumn)));
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testTimeField: Row: " <& row <&
                    " Expected NULL found " <& timeColumn);
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching time fields works okay.");
    else
      writeln(" *** Inserting and fetching time fields does not work okay.");
    end if;
  end func;


const proc: testDurationField (in database: testDb, in dbCategory: databaseKind) is func
  local
    var testState: state is testState("durationTest", "durationField", FALSE);
    var array duration: expect is 0 times duration.value;
    var duration: durationColumn is duration.value;
    var integer: row is 1;
  begin
    if state.details then
      writeln("testDurationField: " <& databaseKind);
    end if;
    block
      state.statement := prepare(testDb, "CREATE TABLE " & state.tableName &
                                 " (" & state.fieldName & " " & durationType(databaseKind) & ")");
      execute(state.statement);
      state.statement := prepare(testDb, "INSERT INTO " & state.tableName &
                                 " (" & state.fieldName & ") VALUES (?)");
      insert(state, NULL,                       expect &:= duration.value);
      insert(state, duration("P0D"),            expect &:= duration("P0D"));
      insert(state, duration("P1Y"),            expect &:= duration("P1Y"));
      insert(state, duration("P1M"),            expect &:= duration("P1M"));
      insert(state, duration("P1D"),            expect &:= duration("P1D"));
      insert(state, duration("PT1H"),           expect &:= duration("PT1H"));
      insert(state, duration("PT1M"),           expect &:= duration("PT1M"));
      insert(state, duration("PT1S"),           expect &:= duration("PT1S"));
      insert(state, duration("PT0.1S"),         expect &:= duration("PT0.1S"));
      insert(state, duration("P-1Y"),           expect &:= duration("P-1Y"));
      insert(state, duration("P-1M"),           expect &:= duration("P-1M"));
      insert(state, duration("P-1D"),           expect &:= duration("P-1D"));
      insert(state, duration("PT-1H"),          expect &:= duration("PT-1H"));
      insert(state, duration("PT-1M"),          expect &:= duration("PT-1M"));
      insert(state, duration("PT-1S"),          expect &:= duration("PT-1S"));
      insert(state, duration("PT-0.1S"),        expect &:= duration("PT-0.1S"));
      insert(state, duration("P1Y1M"),          expect &:= duration("P1Y1M"));
      insert(state, duration("P1Y-1M"),         expect &:= duration("P1Y-1M"));
      insert(state, duration("P1Y1D"),          expect &:= duration("P1Y1D"));
      insert(state, duration("P1Y-1D"),         expect &:= duration("P1Y-1D"));
      insert(state, duration("P1YT1H"),         expect &:= duration("P1YT1H"));
      insert(state, duration("P1YT-1H"),        expect &:= duration("P1YT-1H"));
      insert(state, duration("P1YT1M"),         expect &:= duration("P1YT1M"));
      insert(state, duration("P1YT-1M"),        expect &:= duration("P1YT-1M"));
      insert(state, duration("P1YT1S"),         expect &:= duration("P1YT1S"));
      insert(state, duration("P1YT-1S"),        expect &:= duration("P1YT-1S"));
      insert(state, duration("P1YT0.1S"),       expect &:= duration("P1YT0.1S"));
      insert(state, duration("P1YT-0.1S"),      expect &:= duration("P1YT-0.1S"));
      insert(state, duration("P-1Y1M"),         expect &:= duration("P-1Y1M"));
      insert(state, duration("P-1Y-1M"),        expect &:= duration("P-1Y-1M"));
      insert(state, duration("P-1Y1D"),         expect &:= duration("P-1Y1D"));
      insert(state, duration("P-1Y-1D"),        expect &:= duration("P-1Y-1D"));
      insert(state, duration("P-1YT1H"),        expect &:= duration("P-1YT1H"));
      insert(state, duration("P-1YT-1H"),       expect &:= duration("P-1YT-1H"));
      insert(state, duration("P-1YT1M"),        expect &:= duration("P-1YT1M"));
      insert(state, duration("P-1YT-1M"),       expect &:= duration("P-1YT-1M"));
      insert(state, duration("P-1YT1S"),        expect &:= duration("P-1YT1S"));
      insert(state, duration("P-1YT-1S"),       expect &:= duration("P-1YT-1S"));
      insert(state, duration("P-1YT0.1S"),      expect &:= duration("P-1YT0.1S"));
      insert(state, duration("P-1YT-0.1S"),     expect &:= duration("P-1YT-0.1S"));

      state.statement := prepare(testDb, "SELECT * FROM " & state.tableName);
      execute(state.statement);
      while fetch(state.statement) do
        durationColumn := column(state.statement, 1, duration);
        if durationColumn <> expect[row] then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testDurationField: Row: " <& row <&
                    " Expected " <& literal(str(expect[row])) <&
                    " found " <& literal(str(durationColumn)));
          end if;
        end if;
        if row = 1 and not isNull(state.statement, 1) then
          state.okay := FALSE;
          if state.details then
            writeln(" *** testDurationField: Row: " <& row <&
                    " Expected NULL found " <& durationColumn);
          end if;
        end if;
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        state.okay := FALSE;
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        state.okay := FALSE;
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        state.okay := FALSE;
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    state.statement := prepare(testDb, "DROP TABLE " & state.tableName);
    execute(state.statement);

    if state.okay then
      writeln("Inserting and fetching duration fields works okay.");
    else
      writeln(" *** Inserting and fetching duration fields does not work okay.");
    end if;
  end func;


const proc: testFloatField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "floatTest";
    var sqlStatement: statement is sqlStatement.value;
    var float: floatColumn is 0.0;
    var integer: row is 1;
  begin
    writeln("testFloatField2: " <& databaseKind);
    statement := prepare(testDb, "CREATE TABLE " & tableName & " (floatField " & floatType(databaseKind) & ")");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (floatField FLOAT)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (floatField REAL)");
    execute(statement);
    block
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (floatField) VALUES (NULL)");
      statement := prepare(testDb, "INSERT INTO " & tableName & " (floatField) VALUES (-9223372036854775808.0)");
      execute(statement);
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        writeln(column(statement, 1, float));
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testBigRatField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "bigRatTest";
    var sqlStatement: statement is sqlStatement.value;
    var bigRational: bigRatColumn is bigRational.value;
    var integer: row is 1;
  begin
    writeln("testBigRatField2: " <& databaseKind);
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (bigRatField " & bigRatType(databaseKind) & ")");
    statement := prepare(testDb, "CREATE TABLE " & tableName & " (bigRatField NUMERIC(38,15))");
    execute(statement);
    block
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (bigRatField) VALUES (1)");
      statement := prepare(testDb, "INSERT INTO " & tableName & " (bigRatField) VALUES (?)"); bind(statement, 1, 1_ / 1_);
      execute(statement);
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        writeln(column(statement, 1, bigRational));
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testBlobField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "blobTest";
    var sqlStatement: statement is sqlStatement.value;
    var bstring: bstringColumn is bstring("");
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    writeln("testBlobField2: " <& databaseKind);
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (blobField " & blobType(databaseKind) & ")");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (blobField BLOB)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (blobField VARBINARY(MAX))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (blobField VARCHAR(MAX))");
    # statement := prepare(testDb, "CREATE TABLE blobTest (int32Field INTEGER, blobField BLOB, varcharField VARCHAR(32))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (int32Field INTEGER, blobField " & blobType(databaseKind) & ", varcharField " & varcharType(databaseKind) & "(32))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (int32Field INTEGER, blobField " & blobType(databaseKind) & ", numericField " & numericType(databaseKind) & ")");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (int32Field INTEGER, numericField " & numericType(databaseKind) & ")");
    execute(statement);
    block
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES (NULL)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES (0xfedcba9876543210)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES ('some text')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES (?)"); bind(statement, 1, bstring(""));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES (?)"); bind(statement, 1, bstring("\16#fe;\16#dc;\16#ba;\16#98;\16#76;\16#54;\16#32;\16#10;"));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES (?)"); bind(statement, 1, bstring("abcdefgh" mult 1024 * 1024 * 16));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (blobField) VALUES (?)"); bind(statement, 1, "abcdefgh" mult 1024 * 1024);
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (int32Field, blobField, varcharField) VALUES (123, 0xfedcba9876543210, 'some text')");
       statement := prepare(testDb, "INSERT INTO " & tableName & " (int32Field, blobField, numericField) VALUES (123, 0xfedcba9876543210, 0.123456789)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (int32Field, numericField) VALUES (123, 0.123456789)");
      execute(statement);
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        # writeln(column(statement, 1, bstring));
        # writeln(column(statement, 1, string));
         writeln(column(statement, 1, integer));
         writeln(column(statement, 2, bstring));
        # writeln(column(statement, 2, bigRational));
        # writeln(column(statement, 3, string));
         writeln(column(statement, 3, bigRational));
        # writeln(length(column(statement, 1, bstring)));
        # writeln(length(column(statement, 1, string)));
       incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testTimeField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "timeTest";
    var array time: expect is 0 times time.value;
    var sqlStatement: statement is sqlStatement.value;
    var integer: row is 1;
  begin
    writeln("testTimeField2: " <& databaseKind);
     statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField TIMESTAMP)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField TIMESTAMPTZ)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField DATE)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField DATETIME)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField DATETIME2(7))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField TIME)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (timeField TIMETZ)");
    execute(statement);
    block
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('0010-11-12 13:14:15 BC')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('0001-01-01 00:00:00 BC')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('0001-01-01 00:00:00')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('2017-03-14')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('2017-03-14 15:16:17.18')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('1893-04-01 00:00:00.00')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('1893-03-31 23:54:38.999999')");  # UTC+1:5 ??
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('1893-03-31 23:54:39.00')");      # UTC+1
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (current_date)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (getdate)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (current_timestamp)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('16:17:18.19')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES ('2010-11-12 13:14:15.16')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (?)"); bind(statement, 1, time(NOW));
       statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (?)"); bind(statement, 1, toUTC(time(NOW)));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (?)"); bind(statement, 1, setLocalTZ(time("2010-11-12 13:14:15.16")));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (?)"); bind(statement, 1, setLocalTZ(time("-10-11-12 13:14:15")));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (?)"); bind(statement, 1, time("0-1-1 0:0:0"));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (timeField) VALUES (?)"); bind(statement, 1, setLocalTZ(time("0-1-1 0:0:0")));
      execute(statement);
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        writeln(" *** testTimeField: Row: " <& row <& " Found " <& literal(str(column(statement, 1, time))));
        # writeln(" *** testTimeField: Row: " <& row <& " Found " <& column(statement, 1, string));
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testDurationField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "durationTest";
    var array duration: expect is 0 times duration.value;
    var sqlStatement: statement is sqlStatement.value;
    var duration: durationColumn is duration.value;
    var integer: row is 1;
  begin
    writeln("testDurationField2: " <& databaseKind);
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField TIMESTAMP)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField TIMESTAMPTZ)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField DATE)");
     statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField DATETIME)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField TIME)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField TIMETZ)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField INTERVAL)");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField VARCHAR(32))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (durationField INTERVAL DAY(3) TO SECOND(6))");
    execute(statement);
    block
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('2017-03-14')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('2017-03-14 15:16:17.18')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES (current_date)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES (current_timestamp)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('16:17:18.19')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('2010-11-12T13:14:15')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('2010-11-12 13:14:15.16')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('0010-11-12 13:14:15.16')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('P1Y2M3DT4H5M6S')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('P1Y2M3DT4H5M6.7S')");
       statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('P1Y2M3DT4H5M6.7890123S')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES ('P-1Y-2M-3DT-4H-5M-6.7S')");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES (?)"); bind(statement, 1, time(NOW));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES (?)"); bind(statement, 1, duration("P2010Y11M12DT13H14M15.16S"));
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (durationField) VALUES (?)"); bind(statement, 1, duration("P10Y11M12DT13H14M15.16S"));
      execute(statement);
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        durationColumn := column(statement, 1, duration);
        writeln(" *** testDurationField: Row: " <& row <&
                " Found " <& literal(str(durationColumn)));
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testNumericField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "numericTest";
    var sqlStatement: statement is sqlStatement.value;
    var integer: integerColumn is 0;
    var bigInteger: bigIntColumn is 0_;
    var bigRational: bigRatColumn is 0_/1_;
    var float: floatColumn is 0.0;
    var integer: row is 1;
  begin
    writeln("testNumericField2: " <& databaseKind);
     statement := prepare(testDb, "CREATE TABLE " & tableName & " (numericField NUMERIC(38,15))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (numericField NUMERIC(38,30))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (numericField NUMERIC(38,38))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (numericField NUMERIC(38,0))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (numericField NUMERIC(38,-1))");
    # statement := prepare(testDb, "CREATE TABLE " & tableName & " (numericField NUMERIC(38,15))");
    execute(statement);
    block
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (NULL)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (12345.678901)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (-12345.678901)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (0.12345678901234567890123456789012345678)");
       statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (0.00000000000000000000000000000000000001)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (-12345.678901234567890123456789012345)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (-0.12345678901234567890123456789012345678)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (-12345678901234567890123456789012345678)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (-123456789012345678901234567890123456780)");
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (?)"); bind(statement, 1, 12345.678901);
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (?)"); bind(statement, 1, -12345678901234567890123456789012345678_);
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (?)"); bind(statement, 1, -12345678901234567890123456789012345678_ / 1_);
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (?)"); bind(statement, 1, 12345678901234560000000_/1_);
      # statement := prepare(testDb, "INSERT INTO " & tableName & " (numericField) VALUES (?)"); bind(statement, 1, 123456789012345600000000_/1_);
      execute(statement);
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        writeln("isNull: " <& isNull(statement, 1));
        writeln(column(statement, 1, float));
        writeln(column(statement, 1, bigRational));
        writeln(column(statement, 1, bigInteger));
        writeln(column(statement, 1, integer));
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testVarcharField2 (in database: testDb, in dbCategory: databaseKind) is func
  local
    const string: tableName is "varcharTest2";
    var array string: expect is 0 times "";
    var sqlStatement: statement is sqlStatement.value;
    var string: stringColumn is "";
    var integer: row is 1;
  begin
    writeln("testVarcharField: " <& databaseKind);
    statement := prepare(testDb, "CREATE TABLE " & tableName &
                         " (varcharField varchar(32765))");
    execute(statement);
    block
      statement := prepare(testDb, "INSERT INTO " & tableName &
                           " (varcharField) VALUES (?)");
     # insert(statement, "09876543210987654321",  expect &:= "09876543210987654321");
     # insert(statement, "123456789012345678901",  expect &:= "123456789012345678901");
      insert(statement, "x" mult 32765, expect &:= "x" mult 32765);
(*
      insert(statement, NULL,         expect &:= "");
      insert(statement, "",           expect &:= "");
      insert(statement, "X",          expect &:= "X");
      insert(statement, "Ab2",        expect &:= "Ab2");
      insert(statement, "abcdefghij", expect &:= "abcdefghij");
      insert(statement, "ÄäÖöÜüß",    expect &:= "ÄäÖöÜüß");
      insert(statement, "€µ¹²³¼½«»¬", expect &:= "€µ¹²³¼½«»¬");
      insert(statement, "x" mult 32,  expect &:= "x" mult 32);
      insert(statement, "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                        \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                        \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;",
                        expect &:= "\1;\2;\3;\4;\5;\6;\7;\8;\9;\
                        \\10;\11;\12;\13;\14;\15;\16;\17;\18;\19;\20;\
                        \\21;\22;\23;\24;\25;\26;\27;\28;\29;\30;\31;");
      statement := prepare(testDb, "INSERT INTO " & tableName &
                           " (varcharField) VALUES (?)");
      insert(statement,          0,   expect &:= "0");
      insert(statement,          1,   expect &:= "1");
      insert(statement, 1234567890,   expect &:= "1234567890");
      statement := prepare(testDb, "INSERT INTO " & tableName &
                           " (varcharField) VALUES (?)");
      insert(statement,          0_,  expect &:= "0");
      insert(statement,          1_,  expect &:= "1");
      insert(statement, 1234567890_,  expect &:= "1234567890");
*)
#      statement := prepare(testDb, "INSERT INTO " & tableName &
#                           " (varcharField) VALUES (N'" <&
#                           "\128;\129;\130;\131;\132;\133;\134;\135;\
#                           \\136;\137;\138;\139;\140;\141;\142;\143;\144;\145;\146;\147;\148;\
#                           \\149;\150;\151;\152;\153;\154;\155;\156;\157;\158;\159;" & "')");
#      statement := prepare(testDb, "INSERT INTO " & tableName &
#                           " (varcharField) VALUES (\
#                           \CHAR(128) + CHAR(129) + CHAR(130) + CHAR(131) + CHAR(132) + CHAR(133) + CHAR(134) + CHAR(135) + CHAR(136) + CHAR(137) + \
#                           \CHAR(138) + CHAR(139) + CHAR(140) + CHAR(141) + CHAR(142) + CHAR(143) + CHAR(144) + CHAR(145) + CHAR(146) + CHAR(147) + \
#                           \CHAR(148) + CHAR(149) + CHAR(150) + CHAR(151) + CHAR(152) + CHAR(153) + CHAR(154) + CHAR(155) + CHAR(156) + CHAR(157) + \
#                           \CHAR(158) + CHAR(159))");
#      execute(statement);
#      expect &:= "\128;\129;\130;\131;\132;\133;\134;\135;\
#                 \\136;\137;\138;\139;\140;\141;\142;\143;\144;\145;\146;\147;\148;\
#                 \\149;\150;\151;\152;\153;\154;\155;\156;\157;\158;\159;";
      statement := prepare(testDb, "SELECT * FROM " & tableName);
      execute(statement);
      while fetch(statement) do
        stringColumn := column(statement, 1, string);
        # writeln("testVarcharField: Row: " <& row <& ": " <& stringColumn);
        writeln("testVarcharField: Row: " <& row <& " length: " <& length(stringColumn));
        incr(row);
      end while;
    exception
      catch RANGE_ERROR:
        writeln(" *** RANGE_ERROR was raised");
      catch FILE_ERROR:
        writeln(" *** FILE_ERROR was raised");
      catch DATABASE_ERROR:
        writeln(" *** DATABASE_ERROR was raised");
    end block;
    statement := prepare(testDb, "DROP TABLE " & tableName);
    execute(statement);
  end func;


const proc: testDb (in database: testDb, in dbCategory: databaseKind) is func
  begin
    testPrepareAndExecute(testDb, databaseKind);
    testBooleanField(testDb, databaseKind);
    testInt8Field(testDb, databaseKind);
    testInt16Field(testDb, databaseKind);
    testInt32Field(testDb, databaseKind);
    testInt64Field(testDb, databaseKind);
    testBigIntField(testDb, databaseKind);
    testFloatField(testDb, databaseKind);
    testDoubleField(testDb, databaseKind);
    # testBigRatField(testDb, databaseKind);
    testDecimalIntField(testDb, databaseKind);
    testNumericIntField(testDb, databaseKind);
    testDecimalField(testDb, databaseKind);
    testNumericField(testDb, databaseKind);
    testChar1AsciiField(testDb, databaseKind);
    testChar1Latin1Field(testDb, databaseKind);
    testCodePageField(testDb, 1250, databaseKind);
    testCodePageField(testDb, 1252, databaseKind);
    testChar1Field(testDb, databaseKind);
    testCharField(testDb, databaseKind);
    testVarcharField(testDb, databaseKind);
    testBlobField(testDb, databaseKind);
    testTimeField(testDb, databaseKind);
    testDurationField(testDb, databaseKind);

    # testFloatField2(testDb, databaseKind);
    # testBigRatField2(testDb, databaseKind);
    # testCharField2(testDb, databaseKind);
    # testBlobField2(testDb, databaseKind);
    # testTimeField2(testDb, databaseKind);
    # testDurationField2(testDb, databaseKind);
    # testNumericField2(testDb, databaseKind);
    # testVarcharField2(testDb, databaseKind);
  end func;


const func boolean: failed (in proc: statement) is func
  result
    var boolean: failed is FALSE;
  begin
    block
      statement;
    exception
      catch RANGE_ERROR:
        failed := TRUE;
      catch FILE_ERROR:
        failed := TRUE;
      catch DATABASE_ERROR:
        failed := TRUE;
    end block;
  end func;


const type: connectData is new struct
    var dbCategory: driver       is NO_DB;
    var string:     dbName       is "";
    var string:     user         is "";
    var string:     password     is "";
    var dbCategory: databaseKind is NO_DB;
  end struct;


const func connectData: connectData (in dbCategory: driver, in string: dbName,
    in string: user, in string: password, in dbCategory: databaseKind) is func
  result
    var connectData: connData is connectData.value;
  begin
    connData.driver       := driver;
    connData.dbName       := dbName;
    connData.user         := user;
    connData.password     := password;
    connData.databaseKind := databaseKind;
  end func;


const proc: testDatabase (in connectData: dbConnectData) is func
  local
    var database: testDb is database.value;
  begin
    writeln("Test " <& dbConnectData.dbName <& " with driver " <&
            dbConnectData.driver);
    block
      testDb := openDatabase(dbConnectData.driver,
                             dbConnectData.dbName,
                             dbConnectData.user,
                             dbConnectData.password);
    exception
      catch DATABASE_ERROR:
        writeln(" *** Cannot open database " <& dbConnectData.dbName <&
                " with driver " <& dbConnectData.driver);
        writeln(" *** Database error: " <& errMessage(DATABASE_ERROR));
      otherwise:
        writeln(" *** Cannot open database " <& dbConnectData.dbName <&
                " with driver " <& dbConnectData.driver);
    end block;
    if testDb <> database.value then
      testDb(testDb, dbConnectData.databaseKind);
      close(testDb);
    end if;
  end func;


const proc: main is func
  local
    const array connectData: dbConnectDataList is [] (
        connectData(DB_ODBC,       "test1",            "", "", DB_ODBC),
        connectData(DB_FIRE,       "test",             "test", "test", DB_FIRE),
        # connectData(DB_MYSQL,      "penguin/test",     "test", "test", DB_MYSQL),
        # connectData(DB_ODBC,       "penguin_mysql",    "test", "test", DB_MYSQL),
        # connectData(DB_POSTGRESQL, "penguin/test",     "test", "test", DB_POSTGRESQL),
        # connectData(DB_ODBC,       "penguin_postgres", "test", "test", DB_POSTGRESQL),
        connectData(DB_MYSQL,      "test",             "test", "test", DB_MYSQL),
        connectData(DB_SQLITE,     "test",             "test", "test", DB_SQLITE),
        connectData(DB_POSTGRESQL, "test",             "test", "test", DB_POSTGRESQL),
        # connectData(DB_OCI,        "XE",               "test", "test", DB_OCI),
        # connectData(DB_OCI,        "192.168.2.99/XE",  "test", "test", DB_OCI),
        connectData(DB_ODBC,       "wurx",             "test", "test", DB_MYSQL),
        connectData(DB_ODBC,       "my",               "test", "test", DB_MYSQL),
        connectData(DB_ODBC,       "lite",             "test", "test", DB_SQLITE),
        connectData(DB_ODBC,       "post",             "test", "test", DB_POSTGRESQL));
    var integer: index is 0;
    var string: driverName is "";
    var string: databaseKindName is "";
    var connectData: dbConnectData is connectData.value;
  begin
    OUT := STD_CONSOLE;
    if length(argv(PROGRAM)) = 0 or length(argv(PROGRAM)) > 5 then
      writeln("usage: chkdb driver [dbName [user [password [databaseKind]]]]");
      writeln("       chkdb all");
    elsif length(argv(PROGRAM)) = 1 and argv(PROGRAM)[1] = "all" then
      for key index range dbConnectDataList do
        testDatabase(dbConnectDataList[index]);
      end for;
    else
      driverName := argv(PROGRAM)[1];
      dbConnectData.driver := dbCategory(driverName);
      if dbConnectData.driver = NO_DB then
        writeln("*** " <& literal(driverName) <& " is not a valid database driver.");
      else
        dbConnectData.dbName := "test";
        dbConnectData.user := "test";
        dbConnectData.password := "test";
        if length(argv(PROGRAM)) >= 2 then
          dbConnectData.dbName := argv(PROGRAM)[2];
          if length(argv(PROGRAM)) >= 3 then
            dbConnectData.user := argv(PROGRAM)[3];
            if length(argv(PROGRAM)) >= 4 then
              dbConnectData.password := argv(PROGRAM)[4];
            end if;
          end if;
        end if;
        if length(argv(PROGRAM)) = 5 then
          databaseKindName := argv(PROGRAM)[5];
          dbConnectData.databaseKind := dbCategory(databaseKindName);
          if dbConnectData.databaseKind = NO_DB then
            writeln("*** " <& literal(databaseKindName) <& " is not a valid database kind.");
          end if;
        elsif dbConnectData.driver = DB_ODBC then
          dbConnectData.databaseKind := dbCategory(dbConnectData.dbName);
          if dbConnectData.databaseKind = NO_DB then
            dbConnectData.databaseKind := dbConnectData.driver;
          end if;
        else
          dbConnectData.databaseKind := dbConnectData.driver;
        end if;
        if dbConnectData.databaseKind <> NO_DB then
          testDatabase(dbConnectData);
        end if;
      end if;
    end if;
  end func;
