Command Line Interface¶
The Command Line Interface (CLI) allows you to use DaRT Reader programmatically. One feature is available only here: writing to a database whose JDBC driver is not included, with a driver of your own (see Example: writing to MySQL with your own driver).
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:
Open a shell and type
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 it under Licence key.
CLI commands¶
The CLI provides the following commands:
inspect¶
Inspect a DaRT extract:
The argumentdirectory-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
Extract UUID....................00A1B2C3D4E5F60718293A4B5C6D7E8F
Fiscal year.................................................2019
Compressed....................................................No
Separator used.................................................;
Unicode mode.................................................Yes
Number of end of line bytes....................................2
Codepage.........................................4103 (UTF-16LE)
t2
File exists..................true
Expected file size.......11840096
Actual file size.........11840096
Difference......................0
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 [--search <text>] [--application <application>] [--sep-size <integer>] <directoryFile>
beye list [--sep-size <integer>] <directoryFile> <segment-name>
Without a segment name, this lists the segments of the extract in name order: the segment name, the SAP
application it belongs to, its description and the number of records it holds. These are the same segments, in the
same order and with the same applications, as the segment list of the graphical application. A segment for which the
directory states no record count shows nothing in the last column, which is different from 0.
...
TXW_ACCCAT MM Kontierungstypen 15
...
TXW_CLIENT Cross-application Mandanten (SAP-System) 1
TXW_CNTRY Cross-application Länder 245
...
TXW_FI_POS FI FI Belegposition 120
...
TXW_META Metadaten 3,058
...
Counts are grouped the way the computer's regional settings group them, so a German installation prints 3.058.
The application is the code the extract states: FI, SD, MM and so on. Two entries are not codes.
Cross-application stands for the * the extract uses for master and customising tables that belong to every
application. A segment for which the extract names no application has nothing in that column. These are usually
DaRT's own structures, such as TXW_META.
Two options narrow the list:
--search <text>keeps the segments whose name or description contains the text, ignoring case.--application <application>(short-a) keeps the segments of one application. It takes the code (FI),'*'orcross-applicationfor the cross-application tables, and'no application'for the segments without one. An application this extract does not carry is refused with the list of those it does carry.
If nothing matches, the command says so on the error output and exits 0.
With a segment name, the command prints the segment's fields: position, name and description, in the order they
stand in the record. --search and --application cannot be combined with a segment name.
The positions are aligned on the right, so a segment with ten or more fields indents the first nine by a space.
--sep-size sets the number of spaces between columns. It defaults to 5 and is refused if it is negative.
Here, as throughout the CLI it is important that the payload files are in the same directory as the directory file.
show¶
Print the first records of a segment:
This prints the first 1,000 records of the segment, one record per line under a heading line with the field names.
Values are printed as they would be written to a database: an amount stored as " 0.63000-" is printed as
-0.63000. Figures are aligned on the right and text on the left. --sep-size defaults to 2 here, because a record
is often thirty fields wide.
MANDT AFAPL AFASL NAFASL SAFASL ANHWSL AFATXT
400 0AR 0000 0000
400 0AR DG20 2020
400 0AR DG25 2010
...
400 0TR TRDV TRDV GWG 100 %
1,000 of 1,370 records, the first 1,000 as the extract holds them
The last line says what is printed against what the segment holds.
These records are not a sample. They are the first ones in the extract, which usually means one company code and one period, and nothing about the rest of the segment follows from them. The segment view of the graphical application ends with the same sentence.
A value the field's type cannot read, such as letters in an amount, is printed as the extract holds it with a ! in
front. A final line says how many values are marked. A write would put an empty cell where such a value stands, so the
mark shows what a database would lose; see Values could not be read.
An empty field is printed empty and carries no mark. The command exits 0 either way, because it shows the data and
does not check it.
With a demonstration key, show prints only the first record, the one a demo save writes into the table,
and the last line reads Demo - one record per segment, of the 1,370 this one holds instead of a count.
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. MS Access is always written through one,
// because its driver deadlocks when two connections write at the same time
"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. MS Access is always written through one,
// because its driver deadlocks when two connections write at the same time
"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
}
}
}
}
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).
number-connections does not apply to MS Access. An Access database is always written through
one connection, because the driver deadlocks when two connections write at the same time; the log
says so once per run. See An MS Access write stops moving.
ℹ️ Note: Writing to SQL Server is considerably faster if you add
;useBulkCopyForBatchInsert=trueto the connection string. The driver then sends each batch through its bulk copy path instead of as an ordinary insert. Nothing else about the run changes — the same batches, the same connections, the same completeness check afterwards.Measured against SQL Server 2022 in a container, one million rows of ten columns over four connections: about 23,000 rows per second without it and between 105,000 and 154,000 with it. The figures come from one machine and one server, so treat the factor as the order of magnitude rather than as a promise; the spread between repeated runs was noticeable.
The option is the driver's, not DaRT Reader's, and it brings a family of related settings (
bulkCopyForBatchInsertKeepNulls,…CheckConstraints,…FireTriggersand others) whose defaults differ from those of a plain insert. If your target table has constraints, triggers or computed columns, read Microsoft's description of the setting before switching it on, and check the result of a run against the source as you would with any new setting.
Those two properties describe the writing side. read-parallelism describes the reading side: how
many readers take the extract apart at the same time. Reading is usually the slower half of a run,
so raising this shortens the run; the default is 8.
A reader holds one payload file open, so this is also how many files a run has open at once. A single large table is shared among several readers as well — whether DaRT wrote it into one payload file or spread it over six.
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:
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 themyschemaschema. 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, andTXW_FI_POSarrives aspeter_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:
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 —
AtoBandBtoA— 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_SELandTXW_SELPAR.beye validatereads them back to compare the database against the extract. Usetable-prefixif 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.
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. 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_IDandDR_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:
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:
For example
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.
Left out, both come from your settings file (Field separator and Decimal separator under CSV
files in the window's settings), 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 '.'.
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-renamegave 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 Byte order mark under CSV files in the window's settings 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 offer and the same file it writes (where the settings are stored).
Each file is named after the segment, followed by .csv. Every name is checked before the first
file is created: if a file of any of those names is already in output-dir, nothing is created and
nothing is overwritten, and the message names every file in the way — so one run tells you
everything to move.
File TXW_COMPC.csv already exists. Nothing was written.
Files TXW_COMPC.csv, TXW_FI_HD.csv already exist. Nothing was written.
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:
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:
- You want to provide the JVM process with more memory
- 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:
and on Windows: 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 withFailed to load driver class.
Forward slashes work on Windows and save you from doubling backslashes.
2. To increase memory add
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:
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.
⚠️
BigDecimalcarries 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 —MONEYholds two places in PostgreSQL and four in SQL Server, and a plainDECIMALwithout 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 useNUMERICwhere the database offers an unbounded one andDECIMAL(38,8)otherwise.
Floatis used for the ABAP floating point type, which is an eight byte value. Name the eight byte type of your database here —DOUBLE PRECISIONon most databases,FLOATon SQL Server.REALis the four byte type on PostgreSQL and SQL Server and loses roughly half the digits.
BigIntcarries ABAPINT8, the eight byte integer, whileINT1,INT2andINT4all go toInt. Name an eight byte integer here —BIGINTon most databases. A four byte one such asINTEGERor Access'sLONGcannot 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,LocalTimeandYearMonthwhatever 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 toVARCHARdoes 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:
ℹ️ 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, notuser. An unknown key is ignored rather than reported, and the connection then fails withAccess denied for user ''.ℹ️ Note: A MySQL 8.4 server that does not use TLS additionally needs
?allowPublicKeyRetrieval=truein the connection string, because its default authentication will otherwise refuse to send the password.