public Long combiInsert( RowMetaInterface rowMeta, Object[] row, Long val_key, Long val_crc )
    throws KettleDatabaseException {
    String debug = "Combination insert";
    DatabaseMeta databaseMeta = meta.getDatabaseMeta();
    try {
      if ( data.prepStatementInsert == null ) { // first time: construct prepared statement
        debug = "First: construct prepared statement";

        data.insertRowMeta = new RowMeta();

        /*
         * Construct the SQL statement...
         *
         * INSERT INTO d_test(keyfield, [crcfield,] keylookup[]) VALUES(val_key, [val_crc], row values with keynrs[]) ;
         */

        String sql = "";
        sql += "INSERT INTO " + data.schemaTable + ( "( " );
        boolean comma = false;

        if ( !isAutoIncrement() ) {
          // NO AUTOINCREMENT
          sql += databaseMeta.quoteField( meta.getTechnicalKeyField() );
          data.insertRowMeta.addValueMeta( new ValueMetaInteger( meta.getTechnicalKeyField() ) );
          comma = true;
        } else if ( databaseMeta.needsPlaceHolder() ) {
          sql += "0"; // placeholder on informix! Will be replaced in table by real autoinc value.
          data.insertRowMeta.addValueMeta( new ValueMetaInteger( meta.getTechnicalKeyField() ) );
          comma = true;
        }

        if ( meta.useHash() ) {
          if ( comma ) {
            sql += ", ";
          }
          sql += databaseMeta.quoteField( meta.getHashField() );
          data.insertRowMeta.addValueMeta( new ValueMetaInteger( meta.getHashField() ) );
          comma = true;
        }

        if ( !Utils.isEmpty( meta.getLastUpdateField() ) ) {
          if ( comma ) {
            sql += ", ";
          }
          sql += databaseMeta.quoteField( meta.getLastUpdateField() );
          data.insertRowMeta
            .addValueMeta( new ValueMetaDate( meta.getLastUpdateField() ) );
          comma = true;
        }

        for ( int i = 0; i < meta.getKeyLookup().length; i++ ) {
          if ( comma ) {
            sql += ", ";
          }
          sql += databaseMeta.quoteField( meta.getKeyLookup()[ i ] );
          data.insertRowMeta.addValueMeta( rowMeta.getValueMeta( data.keynrs[ i ] ) );
          comma = true;
        }

        sql += ") VALUES (";

        comma = false;

        if ( !isAutoIncrement() ) {
          sql += '?';
          comma = true;
        }
        if ( meta.useHash() ) {
          if ( comma ) {
            sql += ',';
          }
          sql += '?';
          comma = true;
        }
        if ( !Utils.isEmpty( meta.getLastUpdateField() ) ) {
          if ( comma ) {
            sql += ',';
          }
          sql += '?';
          comma = true;
        }

        for ( int i = 0; i < meta.getKeyLookup().length; i++ ) {
          if ( comma ) {
            sql += ',';
          } else {
            comma = true;
          }
          sql += '?';
        }

        sql += " )";

        String sqlStatement = sql;
        try {
          debug = "First: prepare statement";
          if ( isAutoIncrement() && databaseMeta.supportsAutoGeneratedKeys() ) {
            logDetailed( "SQL with return keys: " + sqlStatement );
            data.prepStatementInsert =
              data.db.getConnection().prepareStatement(
                databaseMeta.stripCR( sqlStatement ), Statement.RETURN_GENERATED_KEYS );
          } else {
            logDetailed( "SQL without return keys: " + sqlStatement );
            data.prepStatementInsert =
              data.db.getConnection().prepareStatement( databaseMeta.stripCR( sqlStatement ) );
          }
        } catch ( SQLException ex ) {
          throw new KettleDatabaseException( "Unable to prepare combi insert statement : "
            + Const.CR + sqlStatement, ex );
        } catch ( Exception ex ) {
          throw new KettleDatabaseException( "Unable to prepare combi insert statement : "
            + Const.CR + sqlStatement, ex );
        }
      }

      debug = "Create new insert row rins";
      Object[] insertRow = new Object[ data.insertRowMeta.size() ];
      int insertIndex = 0;

      if ( !isAutoIncrement() ) {
        insertRow[ insertIndex ] = val_key;
        insertIndex++;
      }
      if ( meta.useHash() ) {
        insertRow[ insertIndex ] = val_crc;
        insertIndex++;
      }
      if ( !Utils.isEmpty( meta.getLastUpdateField() ) ) {
        insertRow[ insertIndex ] = new Date();
        insertIndex++;
      }
      for ( int i = 0; i < data.keynrs.length; i++ ) {
        insertRow[ insertIndex ] = row[ data.keynrs[ i ] ];
        insertIndex++;
      }

      if ( isRowLevel() ) {
        logRowlevel( "rins=" + data.insertRowMeta.getString( insertRow ) );
      }

      debug = "Set values on insert";
      // INSERT NEW VALUE!
      data.db.setValues( data.insertRowMeta, insertRow, data.prepStatementInsert );

      debug = "Insert row";
      data.db.insertRow( data.prepStatementInsert );

      debug = "Retrieve key";
      if ( isAutoIncrement() && databaseMeta.supportsAutoGeneratedKeys() ) {
        ResultSet keys = null;
        try {
          keys = data.prepStatementInsert.getGeneratedKeys(); // 1 key
          if ( keys.next() ) {
            val_key = new Long( keys.getLong( 1 ) );
          } else {
            throw new KettleDatabaseException( "Unable to retrieve auto-increment of combi insert key : "
              + meta.getTechnicalKeyField() + ", no fields in resultset" );
          }
        } catch ( SQLException ex ) {
          throw new KettleDatabaseException( "Unable to retrieve auto-increment of combi insert key : "
            + meta.getTechnicalKeyField(), ex );
        } finally {
          try {
            if ( keys != null ) {
              keys.close();
            }
          } catch ( SQLException ex ) {
            throw new KettleDatabaseException( "Unable to retrieve auto-increment of combi insert key : "
              + meta.getTechnicalKeyField(), ex );
          }
        }
      }
    } catch ( Exception e ) {
      logError( Const.getStackTracker( e ) );
      throw new KettleDatabaseException( "Unexpected error in combination insert in part ["
        + debug + "] : " + e.toString(), e );
    }

    return val_key;
  }
@Test
  public void testCombiInsert() throws Exception {
    combinationLookup.combiInsert( any( RowMetaInterface.class ), any( Object[].class ), anyLong(), anyLong() );
    verify( databaseMeta, times( 2 ) ).supportsAutoGeneratedKeys();
  }