Updated November, 23 2023
Gateway configuration#
There are quite a few options available for the Liberatii Gateway. They can be changed in an API file or using REST API.
To enable a verbose trace to specify in the config file:
1verbose = 2
Or use REST API like this:
1$ curl http://lgw/gateway/config -b '{"verbose": 2}'
To get the current value, use:
1$ curl http://lgw/gateway/config/verbose
Some options may require a restart, for the local LGW use:
1$ sudo systemctl restart lgw
And with REST API:
1$ curl -X POST http://lgw/gateway/restart
When migrating data (if not a direct connection is used), we need to disable query timeouts:
1query_timeout = 0
2query_wait_timeout = 0
Azure Flexible server instance requires connection to be encrypted:
1server_tls_sslmode = allow
Info-queries interface#
The translator needs to run some meta-data queries sometimes, it uses a special role that should have read access to dbt and pg_catalog schemas. To specify the credentials, use:
1infoquery_user = lgw
2infoquery_password = lgwpwd
Numeric conversion#
1translator_option_num_family_cast = disable # or enable
To use numerics instead of ints/shorts etc.
Translator scripts debugging#
To use a TypeScript translator instead of the compiled one, use:
1translator_mode = script
2# script, fast, none
Ignore TO_CHAR cast#
For columns in logical conditions, this option ignores TO_CHAR cast if the second operand is string literal
like WHERE TO_CHAR(column)='string'
. Enable - removes TO_CHAR.
1translator_option_str_cond_cast = enable # or disable
Query wrapping#
The translator receives not only Oracle-dialect queries but also sometimes PG dialect. JDBC/ODBC drivers, OCI and IDEs run some meta-data queries too, and they can use PG dialect for this. For the migrating applications, we may want to run queries in different dialects too.
By default, the translator tries to translate everything, and if it encounters parser errors, it assumes it is a PG dialect query and tries to run it as is. This, however, doesn’t always work because the query can be perfectly parsed as Oracle, but it doesn’t run on PG. For example, because the translator translated all identifiers into upper case, while they are lower case by default on PG.
To manage this, the translator uses special wrappers. Everything between $LBR$
delimiters is considered to be an Oracle dialect query. For example:
1$LBR$ select 1 from dual $LBR$
Everything between $LBR!$
is considered to be a PG dialect query:
1$LBR!$ select 1 from "DUAL" $LBR!$
Queries without these tags are still attempted to be translated from Oracle to PG. To disable this, execute this query:
1$LBRC$ wrap=on $LBRC$
After this, all queries without tags are considered PG dialect queries. To disable this run query:
1$LBRC$ wrap=off $LBRC$
To check the current value, execute:
1$LBRC$ wrap $LBRC$
The value is stored connection-wide. Each connection should run the setting if it needs anything not default.
There are also legacy variants for these queries used in some drivers. They still work, but they shouldn’t be used in any new one - $LBR wrap$
/ $LBR nowrap$
/ $LBR check$
.
Severity#
By default, the translator, even if it encounters any error, tries to execute the resulting query. So even if everything is broken, the DB will return the error report. Sometimes, however, we want it to raise an exception. Such behavior can be specified using a dedicated pgbouncer option:
1translator_severity=1
The default value is 0
- trying to execute the resulting query in any case. If the value is 1, the query will respond with an exception if the translator reports any error. And if the value is 2, the query throws an exception if the translator finds any warning.
This option can be specified for a connection. For this, execute the following query:
1$LBRC$ severity = 2 $LBRC$
And to check the current value, run:
1$LBRC$ severity $LBRC$
Translator cache#
The query translation can be cached. The cache is stored in the LGW memory. All numeric and string constants are replaced with @LBR$PH$<num> placeholder. So, no data should be stored in the memory, but the DB schema is kept in the memory.
Add the following in the pgbouncer config to enable the translator cache:
1tqc = enable
2tqc_size = 5000
The tqc_size
parameters specify how many queries can be cached - the least recent records will be removed on overflow.
It’s possible to disable DDLs caching since it’s quite unlikely they are executed more than once. Use:
1tqc_ddl = disable
If the query is translated fast enough by the translator, there is no need to cache it. Use the following to specify such threshold in milliseconds:
1tqc_mem_threshold = 5
The cache may be loaded from the DB on startup. This way, we can specify manual translation if the query has hardcoded constants.
1tqc_db_load = enable
It will load the queries from dbt.query_translation
view, and it should have the following columns:
input_query
- Oracle dialect queryoutput_query
- the translated PG dialect queryinit_query
- the queries we execute before executing output_querybindNames
- comma-separated list of bind parameters in the querybindIndexes
- comma-separated list of bind indexes in the queryerror
- translator error messagewarning
- translator warning messagesqlType
- the type of the query, e.g. CREATE, UPDATE, etc
The input_query
parameters can contain multiple queries if they are separated by /
in a separate line.
The translator can dump the query into the database (in lbr$data.cache_input
table). So it won’t be translated again even after the translator restarts. To enable this use:
1tqc_db_store = enable
2tqc_db_threshold = 1000
Here tqc_db_threshold
is the minimum translation time in milliseconds to dump the query into the DB. In this example, only the queries taking more than a second are dumped.
It’s also possible to disable automatic translation:
1tqc_auto_translate = disable
In this case, only the queries from the DB will be translated, the remaining will throw an exception. This is needed if we need to improve the security and the application doesn’t use binds and only generates queries by concatenation parameters into the query itself. So, to avoid SQL injection, we can first run a learning pass where all possible queries are executed and stored in the DB. Next, we disable auto-translation, so if any SQL is injected, it won’t work.
Manual query translation#
The query cache described above can be used to add manual query translation in case anything isn’t supported by the gateway or there is an option with better
performance. For this, just add a row into lbr$data.manual_translation
(with the same columns as dbt.query_translation
described above). This requires restarting the gateway.
Since the input_query
must be in a special normalized form, it’s easier to record the input query
in lbr$data.cache_input
with tqc_db_store = enabled; tqc_db_threshold = 0
and move the row
from lbr$data.cache_input
to lbr$data.manual_translation
replacing output_query
to the new custom translation. It should use the same placeholders for copying the constants between the queries.
There is also another table for the query replacement, but it’s only matched by the query input text, so case sensitive and doesn’t have placeholders for constants. For
this, just insert a query into dbt.manual_translation_table
with the following columns:
id
- a unique identifier.src
- the input query to be matched.dst
- the query will be replaced with.bind
- the list of bind variables.
For example:
1--PostgreSQL
2INSERT INTO dbt.manual_translation_table(src,dst) VALUES(
3 'BEGIN user_context_pkg.set_app_user_lang($1)',
4 'CALL "lbr$m$DBMS_APPLICATION_INFO$SET_CLIENT_INFO"($1)');
Meta-queries cache#
Sometimes, the translator needs to query schema information from the DB. This adds some latency, but it can be cached. To enable this, use these pgbouncer config options:
1tmqc = enable
2tmqc_size = 5000
The size parameter specifies how many queries can be cached.
The cache is stored in the LGW memory. It may contain some information about schema.