Skip to content

Command Line Interface

The Command Line Interface (CLI) allows you to use DaRT Reader programmatically and has features that are unavailable in the GUI such as validation of checksums, writing to databases that are not included by default and renaming database fields and tables.

Prerequisites

The CLI is an executable with filename beye. It is located in the installation directory on Windows and under Contents/MacOS on Mac OS. As part of the installation process it is automatically added to the PATH on Windows. If you want to add beye to the PATH on Mac OS add the following line to ~/.zshrc:

export PATH="/INSTALL-DIR/Contents/MacOS:$PATH

Open a shell and type

beye --help

to verify that you can execute the CLI.

You will also need a valid license key to use the commands of the CLI: Open the GUI, in the menu bar go to File > Settings and enter the license key.

CLI commands

The CLI provides the following commands:

inspect

Inspect a DaRT extract:

beye inspect <directory-file>
The argument directory-file is a path to the directory file. All payload files belonging to the extract must be in the same directory as the directory file.

The inspect command will return meta information about the extract and the payload files. Following is an example output:

Client (key)..............................
Logical system..................S10CLNT300
DaRT release...........................2.7
SAP release............................740
User..............................abcabcab
Request date....................2025-08-08
Request time......................10:09:08
Fiscal year...........................2019
Compressed..............................No
Separator used...........................;
Unicode mode...........................Yes
Number of end of line bytes..............2
Codepage..............................4103

t2
File exists..................true
Expected file size.......11840096
Actual file size.........11840096
Difference......................0
The example output shows that the extract is from a SAP system called S10 and client 300. The DaRT release is 2.7. A user with username "abcabcab" created the extract on 2025-08-08 for fiscal year 2019. The extract has one payload file called "t2" and it exists on the file system. The file size of the payload file as indicated in the extract matches the file size on the file system of 11,840,096 bytes.

Expected file size is taken from the record every payload file ends with, in which DaRT notes down the size of the finished file. Difference holds it against the size the file actually has, and has to read 0.

list

List segments in an extract:

beye list [--sep-size <integer>] <directoryFile> [<segment-name>]

This command lists the segments contained in the extract or if segment-name is provided gives field names and their descriptions of the segment. The sep-size option increases the whitespace between columns; it defaults to 5 and is refused if it is negative.

For example running:

beye list some-directory-file_DR

will write the segment names, a description of the segment and the number of rows of the segment of the extract described by list some-directory-file_DR. For example:

FTR_GDPDU_XSTR_AT02               Finanzgeschäftsvorgangstypen                                           372
FTR_GDPDU_XSTR_AT07               Typ von Bewegungen und Konditionen                                      90
FTR_GDPDU_XSTR_AT10               Finanzgeschäftsart-Bezeichnung                                         145
FTR_GDPDU_XSTR_AT10B              Customizing Bewertung                                                  244
FTR_GDPDU_XSTR_AT30               Formeltabelle für Finanzmathematik                                       3
FTR_GDPDU_XSTR_AT40               Berechnungstypen des Finanzstromrechners                                60
FTR_GDPDU_XSTR_ATMA               Art eines Rahmenvertrags                                                 1
FTR_GDPDU_XSTR_TRACC_AA_REF       Kontierungsreferenz der parallelen Bewertungsbereiche                   20
FTR_GDPDU_XSTR_TRDC_DFLOWTYPE     Definition Fortschreibungsarten                                        882
FTR_GDPDU_XSTR_TRGC_VAL_AREA      Bewertungsbereiche                                                       3
FTR_GDPDU_XSTR_TWPOB              Portfoliobestand                                                        13
FVD_GDPDU_XSTR_T056P              Referenzzinssatztabelle                                                  9
FVD_GDPDU_XSTR_TD01               Sicherheitsarten                                                        15
FVD_GDPDU_XSTR_TZFB               Berechnungsbasis                                                        19
FVD_GDPDU_XSTR_TZST               Stornierungsgründe                                                       7
TXW_ACCCAT                        Kontierungstypen                                                        15
...

Executing the command with a segment name will return the field position, the field name and the field description:

beye list some-directory-file_DR TXW_ACCCAT

1     MANDT     Mandant                             
2     KNTTP     Kontierungstyp                      
3     KNTTX     Bezeichnung des Kontierungstyps  

Here, as throughout the CLI it is important that the payload files are in the same directory as the directory file.

save

Save extract to a database:

beye save --config <config-file> [-u username] [-p]
          [--verify-digest | --no-verify-digest] [--verify-rows | --no-verify-rows]

This command saves the extract in a relational database. The config-file is a superset of JSON called Human-Optimized Config Object Notation (HOCON). It has the following structure. If you want to use one of the bundled database drivers:

{
  "path-to-directory-file": "/path/to/directory-file",
  "robust": false,
  // this property is currently not honoured but needs to be provided  
  "chunk-size-per-connection": 5000,
  // the number of rows that each chunk contains per connection
  "number-connections": 5,
  // the number of database connections,
  "read-parallelism": 8,
  // optional, the number of segments read from the extract at the same time. Default 8
  "table-prefix": null,
  // optionally prefix all tables by this. Useful for writing to schemas
  "encoding": null,
  // optional hint for encoding of the extract,
  "selected-segments": {
    "type": "include", // Use "include" to include segments and "exclude" to exclude segments
    "segments": ["TXW_BI_HD", "TXW_BI_POS"], // an array of segments to include or exclude
  }, // optional if null all segments will be written
  "table-rename":{
    "TXW_FI_POS" : { // old table name, e.g. TXW_FI_POS
      "BSEG": {  // new table name, e.g. BSEG
        "DMBTR": "AMOUNT" // old field name (e.g. DMBTR), new field name (e.g. AMOUNT)
      }
    }
  }// use this if you want to rename tables and/or fields
  "auto-commit": false,
  // true if SQL statements should be auto commited false otherwise. Default false
  "connection-timeout": 30000,
  // maximum number of milliseconds that a client will wait for a connection.
  // Default 30000, must be at least 250
  "max-lifetime": 1800000,
  // maximum lifetime of a connection in the connection pool in milliseconds.
  // Default 1800000, must be at least 30000 - the pool discards anything smaller
  // and silently uses its own default instead
  "validation-timeout": 5000,
  // maximum milliseconds the pool may spend checking that a connection is still alive.
  // Default 5000, must be at least 250
  "verify-digest": true,
  // optional, compare every written value against the extract after writing. Default true
  "verify-rows": true,
  // optional, compare a sample of written records row by row. Default false
  "verify-sample-size": 1000,
  // optional, how many records of each segment the row check compares. Default 1000
  "database-connection": {
    "type": "bundled-driver",
      "connection-string": "jdbc:postgresql://localhost:5432/mydb",
      "username": "auser", // optional user name. Can also be passed on command line
      "password": "secret", // optional password. Better to pass on command line
      "driver": "postgres" // one of: sqlite, h2, mssql, postgres, duckdb, access
  }
}

If you want to use your own driver the structure is the same except for the property database-connection:

{
  "path-to-directory-file": "/path/to/directory-file",
  "robust": false,
  // this property is currently not honoured but needs to be provided  
  "chunk-size-per-connection": 5000,
  // the number of rows that each chunk contains per connection
  "number-connections": 5,
  // the number of database connections,
  "read-parallelism": 8,
  // optional, the number of segments read from the extract at the same time. Default 8
  "table-prefix": null,
  // optionally prefix all tables by this. Useful for writing to schemas
  "encoding": null,
  // optional hint for encoding of the extract,
  "selected-segments": {
    "type": "include", // Use "include" to include segments and "exclude" to exclude segments
    "segments": ["TXW_BI_HD", "TXW_BI_POS"] // an array of segments to include or exclude
  }, // optional if null all segments will be written
  "table-rename":{
    "TXW_FI_POS" : { // old table name, e.g. TXW_FI_POS
      "BSEG": {  // new table name, e.g. BSEG
        "DMBTR": "AMOUNT" // old field name (e.g. DMBTR), new field name (e.g. AMOUNT)
      }
    }
  }// use this if you want to rename tables and/or fields
  "auto-commit": false,
  // true if SQL statements should be auto commited false otherwise. Default false
  "connection-timeout": 30000,
  // maximum number of milliseconds that a client will wait for a connection.
  // Default 30000, must be at least 250
  "max-lifetime": 1800000,
  // maximum lifetime of a connection in the connection pool in milliseconds.
  // Default 1800000, must be at least 30000 - the pool discards anything smaller
  // and silently uses its own default instead
  "validation-timeout": 5000,
  // maximum milliseconds the pool may spend checking that a connection is still alive.
  // Default 5000, must be at least 250 
  "database-connection": {
    "type": "own-driver",
    "connection-string": "jdbc:postgresql://localhost:5432/mydb",
    "driver-class-name": "org.postgresql.Driver",
    // the class name of the JDBC driver
    "username": "auser",
    // optional user name. Can also be passed on command line
    "password": "secret",
    // optional password. Better to pass on command line
    "field-mapping": {
      "BigDecimal": {
        "type-name": "NUMERIC", // amounts and quantities. Give a type that keeps decimal places
        "has-length": false // true if to integrate length information as in CHAR(3), i.e. length = 3
      },
      "String": {
        "type-name": "VARCHAR", // map the string type in the extract to the postgres type VARCHAR
        "has-length": true
      },
      "Int": {
        "type-name": "INTEGER",
        "has-length": false
      },
      "Date": {
        "type-name": "DATE",
        "has-length": false
      },
      "Float": {
        "type-name": "FLOAT",
        "has-length": false
      },
      "BigInt": {
        "type-name": "BIGINT",
        "has-length": false
      },
      "LocalTime": {
        "type-name": "TIME",
        "has-length": false
      },
      "YearMonth": {
        "type-name": "DATE",
        "has-length": false
      },
      "Char": {
        "type-name": "CHAR",
        "has-length": true
      }
    }
  }
}
If you want to use your own driver make sure that the driver is on the class path (see Advanced Configuration).

With number-connections and chunk-size-per-connection you can tune the throughput. For example, with number-connections = 5 and chunk-size-per-connection = 5000, batches of 5000 rows will be written in 5 parallel connections. The higher the throughput the more memory will be consumed. If you encounter memory exceptions you need to provide the underlying JVM process with more memory (see Advanced Configuration).

Those two properties describe the writing side. read-parallelism describes the reading side: how many segments are read from the extract and parsed at the same time. Reading is usually the slower half of a run, so raising this shortens the run; the default is 8.

DaRT Reader quotes every table and field name it writes into SQL, so a name that is a reserved keyword of the SQL dialect — ORDER, GROUP, USER — is written and read back without further ado. The quoting character is the one the JDBC driver names for itself, which is how a driver of your own is covered as well.

Because a quoted name is taken literally, the database carries the spelling of the extract: TXW_FI_POS in upper case, and a name you give under table-rename exactly as you wrote it. This is worth knowing on databases that fold an unquoted name — PostgreSQL folds to lower case, Oracle to upper. A query you saved against a database that an earlier version of DaRT Reader wrote may therefore have to quote the name too:

SELECT * FROM "TXW_FI_POS"

Use the table-prefix property to put every table behind the same prefix. It works in two ways, and a trailing dot is what tells them apart:

  • "table-prefix": "myschema." — with the dot the prefix qualifies the table with a schema, and on most databases writes into the myschema schema. The schema has to exist already; DaRT Reader does not create it. The prefix goes into the statement exactly as you write it, so a schema name that needs quoting can carry its own quotes.
  • "table-prefix": "peter_" — without the dot the prefix is part of the table name, and TXW_FI_POS arrives as peter_TXW_FI_POS. Prefix and name are quoted together as one identifier, so the note about quoting above applies to the prefixed name as a whole.

Renaming tables and fields

table-rename maps a segment to a new table name and, within it, fields to new field names:

"table-rename": {
  "TXW_FI_POS": {
    "BSEG": {
      "DMBTR": "AMOUNT"
    }
  }
}

The rename is checked against the extract before anything is written, and a configuration that cannot be carried out is refused with a message naming what has to change:

  • Each table needs a name of its own. A segment renamed to a name another segment already carries collides just as two renamed segments do. Swapping two names — A to B and B to A — is not a collision and is carried out as asked.
  • Each segment gets one name. Naming the same segment twice — in one entry or in two — is refused rather than one of the names silently applying.
  • Each field needs a name of its own. A field renamed to a name another field of the segment already carries collides in the same way.
  • A name cannot be blank.
  • The segments DaRT writes about itself keep their names and their fields — the directory, the metadata, the selections and the check sums, among them TXW_META, TXW_CHKSUM, TXW_SEL and TXW_SELPAR. beye validate reads them back to compare the database against the extract. Use table-prefix if the extract has to be kept apart from what is already in the database: it puts every table behind the same prefix — a schema with a trailing dot, part of the table name without one — and takes nothing away.

Amounts and quantities

Amounts, quantities and rates come out of the extract as decimal numbers, and the column they are written to has to be able to hold their decimal places. Amounts usually carry two, quantities three and exchange rates five.

The bundled drivers use these types:

Database Column type
PostgreSQL NUMERIC
H2 DECIMAL(38,8)
SQL Server DECIMAL(38,8)
DuckDB DECIMAL(38,8)
SQLite REAL

NUMERIC in PostgreSQL is exact and has no fixed size. DECIMAL(38,8) holds up to eight decimal places; a value with more is rounded to eight, and a value whose whole-number part does not fit is rejected by the database rather than shortened.

SQLite is the exception. It has no exact decimal type — REAL is a floating point number, and a NUMERIC column falls back to the same thing. Amounts and quantities of an ordinary size are unaffected, but a figure with more than roughly sixteen digits in total loses its last places. If that matters for your data, use one of the other databases.

If you write with your own JDBC driver, the column type comes from field-mapping in the config file and the note under Advanced configuration applies.

The figures are the ones the extract holds

DaRT Reader writes the number that stands in the extract. It does not scale it, and in particular it does not apply the decimal places a currency happens to use.

Checking what was written

The progress the command prints counts records; it says that rows arrived, not what is in them. Two checks look at the values themselves, and you can ask for either, both or neither.

beye save -c config.json --verify-digest --verify-rows
beye save -c config.json --no-verify-digest

A flag on the command line wins over the configuration file, and the configuration file over the default. Both checks run after the last record is written, over the same connection pool.

--verify-digest, on unless you switch it off. Reads back every value of every row, and folds each one into a running total for its column. What is compared is those totals — one per column, one over whole rows, and the number of rows — not each cell against its counterpart. Every cell is covered and a changed value changes its column's total, but the answer comes at the level of the column: it names the segment and the column that does not agree, and cannot name the row.

Values do not match for table TXW_FI_POS: 1 column(s) hold different values than the extract does: DMBTR.
Compared every value in 287 table(s): 286 matched, 1 did not.

It changes nothing about the database, but it is not free: it reads every value back. Measured on a test extract of 287 segments and 99 016 records written to a local SQLite file, the write takes about 1.1 seconds and the comparison another 0.9 — so a run of that shape takes roughly three quarters again as long. Against a database that is slower to write to, which is every target that is not a local file, the share is smaller.

--verify-rows, on unless you turn it off. Compares 1 000 records of each segment record by record, and names the row and the byte in the extract where a value differs:

Rows do not match for table TXW_FI_POS: 1 of 1000 sampled record(s) hold different values than the extract does
the record at byte 2094776 of volume 1 - DMBTR: the extract has -21257.60, the database has -21256.60

⚠️ This check adds two columns, DR_VOL_ID and DR_SRC_OFFSET, to every table it writes. They are how a row is found again, and they cannot be added afterwards — a database written without them can never have its rows compared.

A segment holding fewer records than the sample size is compared in full, and the report says so. A sample of 1 000 finds a defect affecting 0.46 % of the rows about 99 times in 100, and one affecting a single row in a million essentially never. It is not the check that finds the single damaged row — the digest is. What this one is for is the systematic defect, where a whole column goes wrong: there a hundred records are already certain, and what the sample adds is the example row.

The command answers with 0 when everything asked for came out right, 2 when either check found a difference, and 1 when the run did not get that far. That is the same answer validate gives, so a script can tell a database that came out wrong from a run that never happened.

validate

Validate database against extract:

beye validate [directory-file] --config <config-file> [-u username] [-p]

The DaRT extract contains information about sums for fields in some segments. These are called check sums. The validate command calculates these sums in the database and compares them to what is contained in the extract (as fixed numbers). Reuse the config-file that was used to save the extract.

An example output looks like this:

Check sums match for table TXW_FI_NGL_POS (22 group(s)).
Check sums match for table TXW_FI_POS (22 group(s)).
Compared 44 group(s) in 2 table(s): 44 matched, 0 did not.

The extract contains two checksums (the SAP default) and they match. The last line is the summary: it says how many groups were compared and how many of them came out as the extract expects, so a run over a large extract does not have to be read line by line.

Exit codes. The command answers in its exit code as well as on screen, so a script or a pipeline can act on the result:

Code Meaning
0 Every group that was compared came out as the extract expects.
2 At least one group did not. The report names each one.
1 The check could not be made at all — the configuration could not be read, the extract could not be opened, or the database could not be reached.

When a table cannot be checked. The totals in TXW_CHKSUM are whole numbers without a decimal separator, so the sum from the database has to be shifted by the decimal places of the field before the two can be compared. DaRT Reader reads those decimal places off the extract itself, from the way the extract writes the field. If the extract does not establish them, the table is reported as

Check sums not applicable for table TXW_FI_POS: the extract holds no readable value in DMBTR, so
the scale of the total is unknown.

and the run does not report a mismatch — nothing was compared, which is a different statement from "the figures disagree". Such a table is counted separately in the summary.

The password. -p asks for the password on the terminal. A run with no terminal — a pipeline, a scheduler, a command behind a pipe — has nowhere to ask, and the command says so instead of failing obscurely; put the password into the database-connection section of the configuration file instead.

What is covered. The SAP default writes TXW_CHKSUM entries for TXW_FI_POS and TXW_FI_NGL_POS only. Even a run that passes has therefore compared the totals of two segments, not of everything the extract holds. The per-segment checksum over the bytes, described below, does cover every segment and runs on every save.

These are business sums: totals over amount fields per company code and period, which answer whether the figures add up. They are not the same as the per-segment checksum over the bytes SAP wrote, which save and write-to-file check by themselves on every run and which needs no separate command — see The database does not match the extract for what happens when that one disagrees. The two are worth having side by side: a total can still come out right over a record that was read wrongly, and the byte checksum says nothing about whether the figures are the ones the tax authority expects.

write-to-file

Export segments to CSV:

beye write-to-file <directory-file> <output-dir> <segment-names...>
                   [-f <field-separator>] [-d <decimal-separator>]

segment-names... is a white space separated list of segments to write. Every name has to be one the extract holds — beye list <directory-file> prints them. A name that is not in the extract ends the command with exit code 1 before any file is created:

Segment TXW_CUSTX is not part of extract. Nothing was written.

For example

beye write-to-file director-file_DR ./ TXW_CUST TXW_COMPC

will write two CSV files (TXW_CUST.csv and TXW_COMPC.csv) including the segments TXW_CUST and TXW_COMPC to the current working directory.

The two separators

Option Meaning
-f, --field-separator The character between two fields, for example ;
-d, --decimal-separator The character inside an amount, for example ,

The two belong together, and there are two combinations in practice: , with . for an English-speaking spreadsheet, ; with , for a German one. A German Excel opens a comma separated file as a single column; and a file that does open in columns but whose amounts carry the wrong decimal separator brings them in as text, which cannot be summed — the worse of the two, because it looks like it worked.

beye write-to-file director-file_DR ./ TXW_FI_POS -f ";" -d ","

Left out, both come from your settings file (CSV field separator and CSV decimal separator in the window's settings dialog), and failing that from your machine's region — ; and , in Germany, Austria, France and Italy, , and . in the United Kingdom, the United States and Switzerland. The region is read, not the language of the interface: an English interface on a machine set to Germany writes German numbers, and Switzerland writes German with a decimal point.

Combinations that cannot work are refused before anything is written, because the file they produce cannot be read back:

The field separator and the decimal separator are both ','. Amounts are not written in quotes, so
every one of them would split its row in two. Use ';' with ',' or ',' with '.'.
A double quote cannot be the field separator: it is what encloses a text field.

What the file looks like

The rest of the format is not settable, and is what RFC 4180 describes and what Excel itself writes:

  • Every text field is enclosed in double quotes, and a double quote inside one is doubled, so a text field holding the separator does not split the row.
  • Numbers, dates and times are written without quotes. A whole number never carries a decimal separator, and dates and times are ISO 8601 — 2020-08-04, 14:07:05.
  • Every record ends with a carriage return and a line feed, the last one included.
  • The first line holds the field names, with the same separator as the records. A field name is written exactly as a text field is, so a name carrying a double quote — one a table-rename gave it — is enclosed and doubled like any other value.
  • The encoding is UTF-8, and each file opens with a byte order mark: the three bytes EF BB BF. They are what tells Excel on Windows that the file is UTF-8 when it is opened by double-click; without them it applies the ANSI code page and umlauts arrive wrong. Turn them off with CSV byte order mark in the window's settings dialog if you read these files with something that shows the mark as part of the first column name.

How much is written at a time comes from your settings file — Number of connections, Chunk size per connection and Segments read at once, the same three the window's settings dialog offers and the same file it writes (where the settings are stored).

Each file is named after the segment, followed by .csv. If a file of that name already exists in output-dir, the operation aborts with a message that the file already exists. The command does not check beforehand whether a conflicting file exists; it writes the segments in batches as it encounters them in the extract, so some segments may already have been written when the conflict occurs.

File TXW_COMPC.csv already exists.

Log level

The CLI writes its log to %LOCALAPPDATA%\DaRT Reader on Windows and ~/Library/Logs/DaRT Reader on macOS, at level warn. To see more, set DART_READER_LOG_LEVEL for the run:

DART_READER_LOG_LEVEL=debug beye save --config-file /path/to/config.conf

Accepted values are error, warn, info, debug and trace. A value that is not one of these leaves the default in place rather than silencing the log.

Advanced configuration

The CLI can be configured through the config file described in the save command. However, sometimes it is necessary to provide extra parameters to the underlying JVM process. Specifically two use cases are common:

  1. You want to provide the JVM process with more memory
  2. You want to put your own JDBC driver on the classpath

From the installation directory follow the relative path app on Windows and Contents/app on Mac OS and open the file beye.cfg. At the end of this file in a section called [JavaOptions] make the following additions:

1. To enter the location of the JDBC that should be on the class path add, on macOS:

java-options=--class-path
java-options=$APPDIR/*:/path/to/JDBC/driver/*
and on Windows:
java-options=--class-path
java-options=$APPDIR/*;C:/path/to/JDBC/driver/*
Substitute the second path with the directory containing the JDBC driver, and keep $APPDIR/*, which holds the application's own jars.

⚠️ The separator differs by platform. Windows separates class path entries with a semicolon, macOS with a colon. Using a colon on Windows makes $APPDIR/*:C: one entry, and the driver is then reported as missing with Failed to load driver class.

Forward slashes work on Windows and save you from doubling backslashes.

2. To increase memory add

java-options=-Xms512m
java-options=-Xmx4g
This adds 512mb initial heap size and 4g maximum heap size. Increase these parameters if the JVM process runs out of memory.

Example: writing to MySQL with your own driver

MySQL is not among the bundled databases, so it shows both halves of the procedure: putting a driver on the class path, and describing it in the config file.

1. Get the driver. Download Connector/J from https://dev.mysql.com/downloads/connector/j/ and put the jar in a directory of its own, say /Users/you/jdbc-drivers. Keep it separate from the installation directory so that an update of DaRT Reader does not remove it.

2. Put it on the class path. Append to the [JavaOptions] section of beye.cfg, as described under Advanced configuration — with a semicolon on Windows and a colon on macOS:

java-options=--class-path
java-options=$APPDIR/*;C:/jdbc-drivers/*

Both entries are needed: $APPDIR/* keeps the application's own jars on the class path, and the second path adds the driver.

3. Describe the driver in the config file. Use own-driver instead of bundled-driver. Where a bundled driver knows how to map DaRT data types to database column types, your own driver does not, so the mapping is given explicitly in field-mapping:

{
  "path-to-directory-file": "/path/to/directory-file",
  "robust": false,
  "chunk-size-per-connection": 5000,
  "number-connections": 2,
  "database-connection": {
    "type": "own-driver",
    "connection-string": "jdbc:mysql://localhost:3306/dart",
    "driver-class-name": "com.mysql.cj.jdbc.Driver",
    "username": "dartuser",
    "password": "dartpw",
    "field-mapping": {
      "BigDecimal": { "type-name": "DECIMAL(38,8)", "has-length": false },
      "String":     { "type-name": "VARCHAR",       "has-length": true  },
      "Char":       { "type-name": "CHAR",          "has-length": true  },
      "Int":        { "type-name": "INTEGER",       "has-length": false },
      "Date":       { "type-name": "DATE",          "has-length": false },
      "Float":      { "type-name": "DOUBLE PRECISION", "has-length": false },
      "BigInt":     { "type-name": "BIGINT",        "has-length": false },
      "LocalTime":  { "type-name": "TIME",          "has-length": false },
      "YearMonth":  { "type-name": "DATE",          "has-length": false }
    }
  }
}

has-length says whether the column type takes a length, so VARCHAR becomes VARCHAR(50) for a field 50 characters wide while INTEGER stays as it is. All nine entries have to be present.

A field whose length is missing from the extract cannot be given a type that takes one; the segment is then refused rather than written a column short, see A column cannot be declared.

⚠️ BigDecimal carries the amounts and the quantities, so give it a type that keeps decimal places. A type with a fixed scale silently rounds every value that has more places than the scale allows — MONEY holds two places in PostgreSQL and four in SQL Server, and a plain DECIMAL without a scale means no decimal places at all on some systems. Quantities in SAP commonly carry three places and exchange rates five, so this is not a corner case. The bundled drivers use NUMERIC where the database offers an unbounded one and DECIMAL(38,8) otherwise.

Float is used for the ABAP floating point type, which is an eight byte value. Name the eight byte type of your database here — DOUBLE PRECISION on most databases, FLOAT on SQL Server. REAL is the four byte type on PostgreSQL and SQL Server and loses roughly half the digits.

BigInt carries ABAP INT8, the eight byte integer, while INT1, INT2 and INT4 all go to Int. Name an eight byte integer here — BIGINT on most databases. A four byte one such as INTEGER or Access's LONG cannot hold the type it is standing in for.

⚠️ The field mapping decides the column type, not how the value is sent. Dates and times are handed to the driver as java.time.LocalDate, LocalTime and YearMonth whatever you write here, which is what JDBC 4.2 prescribes. Not every driver implements it: some report support for JDBC 4.2 and still refuse those values, with a message along the lines of An attempt was made to get a data value of type 'DATE' from a data value of type 'java.time.LocalDate'. Mapping the columns to VARCHAR does not help, it only changes the type named in the message. The affected segments then stay empty and the command reports the database as incomplete.

This is a gap in the driver rather than a matter of it being strict, and there is no setting that works around it. The bundled drivers are unaffected. If your driver is one of these, leave the segments carrying date and time columns out with selected-segments.

Then run the save command as usual:

beye save --config-file /path/to/config.conf

ℹ️ Note: The connection string here keeps its jdbc:mysql: prefix. The rule about leaving the prefix out applies to the GUI, not to the config file.

ℹ️ Note: The key is username, not user. An unknown key is ignored rather than reported, and the connection then fails with Access denied for user ''.

ℹ️ Note: A MySQL 8.4 server that does not use TLS additionally needs ?allowPublicKeyRetrieval=true in the connection string, because its default authentication will otherwise refuse to send the password.