This topic has been verified against the actual behavior of OBLOADER & OBDUMPER V4.3.7.
A control file defines column-level processing rules for a table during data import or export. You can use a control file when the column order in a data file differs from the table schema, when you need to skip fields, generate constant or sequence values, clean or mask data, or process a fixed-length file.
This topic applies to both OBLOADER and OBDUMPER. However, their data flows differ:
- OBLOADER: file field → column mapping or position parsing → preprocessing → target table column.
- OBDUMPER: database table column → column selection and ordering → preprocessing → output file field.
To get started quickly, read Quick start and Control file activation rules first. For more complex scenarios, see the sections about column mapping, position definitions, preprocessing functions, and conditional expressions.
For detailed information about each tool, see OBLOADER control files and OBDUMPER control files.
Features and scope
Requirement |
OBLOADER |
OBDUMPER |
Core syntax |
|---|---|---|---|
| Map fields in an import file to target columns | Supported | Not applicable | map(n) |
| Select and order the columns to process | Supported | Supported | List column names in the required order |
| Define fields in a fixed-length POS file | Supported | Supported | position(...) |
| Skip placeholder bytes in a POS file | Supported | Not applicable | _FILLER position(...) |
| Generate constants or in-memory sequence values | Supported | Supported | CONSTANT, SEQUENCE |
| Generate values from a database sequence | Supported | Not applicable | DB_SEQUENCE |
| Clean string, date, numeric, and other data | Supported | Supported | Preprocessing functions |
| Perform conditional conversions | Supported | Supported | CASE ... WHEN ... END |
| Mask sensitive fields, compute digests, or encrypt or decrypt values | Supported | Supported | MASK, SM3_DIGEST, SM4_ENCRYPT, and other functions |
Control files can be used with data in the CSV, CUT, SQL, POS, ORC, Parquet, and Avro formats. position applies only to fixed-length POS files. When OBLOADER imports a POS file by using --pos, you must provide a control file. To export a POS file, OBDUMPER still uses --cut. Set the column delimiter to an empty string and define column lengths in a control file. Control files are not used to process data in DDL or MIX mode.
Quick start
Import data: reorder and clean columns, and populate a default value
Assume that the target table customer contains the following columns:
id, name, status, created_at, source
Each row in the import file contains four fields in the following order: id, name, status, and created_at. The following processing is required:
- Remove leading and trailing spaces from
id,name, andstatus. - Convert
nameto lowercase. - Convert the status values
AandItoACTIVEandINACTIVE, respectively, and convert all other status values toUNKNOWN. - Convert a time value from the
yyyyMMddHHmmssformat to theyyyy-MM-dd HH:mm:ssformat. - Write the constant
legacyto the target columnsource.
Create customer.ctrl with the following content:
lang=java
(
id "trim(id)" map(1),
name "lower(trim(name))" map(2),
status "case upper(trim(status)) when 'A' then 'ACTIVE' when 'I' then 'INACTIVE' else 'UNKNOWN' end" map(3),
created_at "to_timestamp(created_at,'yyyyMMddHHmmss','yyyy-MM-dd HH:mm:ss')" map(4),
source "constant('legacy')"
);
Place the control file in the /data/controls directory and specify the directory by using --ctl-path:
./obloader \
-h xx.x.x.x -P 2883 -u 'test@mysql#cluster_a' -p '******' \
-D USERA --table customer \
--csv -f /data/customer.csv \
--ctl-path /data/controls
Export data: select, order, and mask columns
When you export the customer table, assume that you want to export only the id, name, status, and created_at columns. You also want to retain the first character of name while masking the other characters and convert status to uppercase:
lang=java
(
id "none",
name "mask_show_first_n(name,'X','x','n',1)",
status "upper(status)",
created_at "none"
);
./obdumper \
-h xx.x.x.x -P 2883 -u 'test@mysql#cluster_a' -p '******' \
-D USERA --table customer \
--csv -f /data/output \
--ctl-path /data/controls
Control file activation rules
Control file and table names
When --ctl-path points to a directory, control files must be named in the <table_name>.ctrl format. A control file base name is matched with a table name based on the following rules:
In MySQL compatible mode, matching follows the
lower_case_table_namessetting of the target database. Matching is case-sensitive when the value is0and case-insensitive when the value is1or2.In Oracle compatible mode, unquoted table names are normalized to uppercase before matching.
The tool recursively searches for
.ctrlfiles in the directory specified by--ctl-pathand all its subdirectories. Therefore, you can organize control files by schema or business module.Within the same task, make sure that only one control file matches each table. If multiple control files in different directories match the same table, the configuration read later may overwrite the configuration read earlier. The tool does not report the duplicate files, which may cause an unintended configuration to take effect.
When OBLOADER imports only one table and --ctl-path points directly to a .ctrl file, OBLOADER binds the file directly to the table without validating the control file name against the table name.
Startup options
Use --ctl-path <file_or_directory> to enable a control file. Take note of the following conflicts:
- For OBLOADER,
--ctl-pathand--auto-column-mappingcannot be used together. --ctl-pathcannot be used together with command-line options that include or exclude columns or exclude columns by data type. To select columns, define all column selection rules in the control file.- When OBLOADER imports a POS file by using
--pos, you must specify--ctl-path. To export a POS file, OBDUMPER uses--cut. Set the column delimiter to an empty string and use--ctl-pathto specify the control file.
Verify that a control file takes effect
After the task starts, check the logs. If a control file is successfully parsed, a log similar to the following one is generated:
Parse ctrl definition: "/data/controls/customer.ctrl" success
Common logs indicating that a control file did not take effect include:
The control file: "{}" is unexpected, ignore it
The control file: "{}" is invalid, ignore it.
No valid control file was defined for the table: "..."
If one of these logs appears, check whether the control file base name follows the table-name matching rules of the target database. Also check the target table scope, file extension, syntax, and the actual path specified by --ctl-path.
Basic syntax
A control file has the following basic structure:
lang=java
(
<column> [position(<start>:<end>) | position(<length>)] ["<function>"] [map(<index>)],
...
);
Observe the following rules:
langis required. We recommend that you uselang=java. The syntax also acceptslang=sql. Both settings use the built-in function execution engine of the tool and do not push expressions down to the database for execution.serveris optional. You can set it toserver=mysqlorserver=oracleto select the semantics of a small number of functions that behave differently in the two compatibility modes. If you specify this setting, place it after thelangsetting. If you omit it, MySQL semantics are used.- Enclose column definitions in a pair of parentheses and separate them with commas. Do not add a comma after the last column definition.
- End the file with a semicolon (
;). - Enclose preprocessing functions or expressions in double quotation marks and string constants in single quotation marks.
- Keywords and function names are case-insensitive. For readability, function names in the examples are lowercase.
- Control files do not support inline comments starting with
--or//. Do not put explanatory text in a.ctrlfile. - In MySQL compatible mode, control file column names are always matched in a case-insensitive manner. In Oracle compatible mode, column names that are not enclosed in square brackets or backticks are normalized to uppercase before matching. Column names enclosed in square brackets or backticks preserve their case and must exactly match the database column names, for example,
`Order`or[Order].
Column definition semantics
Meaning of column names
In OBLOADER, the column name in each column definition is the name of a target table column. In OBDUMPER, it is the name of a source table column to query and export.
Only columns listed in a control file participate in data processing for the current task. Therefore, use the control file to manage column selection, exclusion, and output order.
Sequential mapping by default
During import, if you do not specify map(n), ordinary columns consume file fields in the order in which the columns are defined in the control file. Generated columns do not consume file fields.
For example, assume that a file contains the fields id,name,status in this order:
lang=java
(
id,
name,
status,
source "constant('legacy')"
);
The id, name, and status columns read the first, second, and third file fields, respectively. The source column is assigned a generated constant.
Explicit mapping by using map(n)
map(n) applies only to OBLOADER. It specifies that the current target column reads the n-th field in the file, where n starts from 1.
For example, assume that file fields are ordered as name,unused,id and the target table requires id,name,name_copy,source:
lang=java
(
id map(3),
name map(1),
name_copy map(1),
source "constant('legacy')"
);
This example also demonstrates that:
- You can reorder file fields.
- You can skip an unnecessary file field, such as the second field
unused. - Multiple target columns can map to the same file field.
- Generated columns do not require
map(n).
If you need to reorder, skip, or reuse fields, explicitly specify map(n) for all ordinary columns. This prevents implicit mapping errors if column definitions are reordered later.
Preprocessing reads only the current column
A column name in a preprocessing expression represents the input value of the current column. Cross-column references are not supported. Therefore, the following expression cannot concatenate the first_name and last_name columns:
full_name "concat(first_name,last_name)"
The arguments to concat must be transformations of the current column or constants. For example, use the following expression to add a prefix to the current name column:
name "concat('user_',trim(name))"
Fixed-length POS files
Absolute positions
position(start:end) specifies the start and end byte positions of a field in a row. Both start and end start from 1, and both positions are included.
Assume that each row has the following structure:
0001ALICE XXCN
- Bytes 1 to 4:
id. - Bytes 5 to 14:
name. - Bytes 15 to 16: placeholder bytes that are not imported.
- Bytes 17 to 18:
region.
lang=java
(
id position(1:4) "trim(id)",
name position(5:14) "rtrim(name)",
_FILLER position(15:16),
region position(17:18) "upper(region)"
);
_FILLER is a placeholder field for POS import. OBLOADER reads the field but does not write it to the target table. You can define multiple _FILLER fields in the same control file.
Relative lengths
position(length) specifies that the current field is length bytes long and starts immediately after the preceding field. Example:
lang=java
(
id position(4) "trim(id)",
name position(10) "rtrim(name)",
region position(2) "upper(region)"
);
To reduce maintenance costs, use either absolute positions or relative lengths consistently in the same control file. Do not mix the two forms.
Truncation and padding during POS export
When OBDUMPER exports a POS file, position also determines the field width:
- If the processed value exceeds the specified number of bytes, the tool truncates the value without splitting a multibyte character.
- If the value is shorter than the specified width, the tool pads spaces on the right.
- If the value is
NULL, the tool fills the entire field with spaces.
POS positions are measured in bytes, not characters. If data contains Chinese characters, emoji, or other multibyte characters, calculate the width based on the file encoding specified for the task.
Generated columns
The value of a generated column does not come from the current input field. During import, a generated column does not consume a file field.
Syntax |
Purpose |
Example |
|---|---|---|
CONSTANT('value') |
Generates the same string for each record | source "constant('legacy')" |
SEQUENCE(initial) |
Generates an in-memory sequence starting from initial, with a default increment of 1 |
id "sequence(1000)" |
SEQUENCE(initial,increment) |
Generates an in-memory sequence starting from initial and increasing or decreasing by increment |
id "sequence(1000,10)" |
DB_SEQUENCE('sequence_name') |
Uses nextval of a target database sequence during import |
id "db_sequence('SEQ_CUSTOMER')" |
DB_SEQUENCE is intended mainly for import in Oracle compatible mode. Before you use it, make sure that the sequence exists in the target database and that the import account has permission to access it. Enclose the sequence name in single quotation marks.
An in-memory SEQUENCE guarantees that generated values are unique within the same task. However, do not rely on a strict ordering relationship between values generated by parallel tasks and row numbers in the source file.
Preprocessing functions
This section describes commonly used functions and their key behaviors. For complete function signatures and parameter descriptions, see OBLOADER preprocessing functions and OBDUMPER preprocessing functions.
Function composition
Preprocessing functions can be nested. The innermost function is executed first. Examples:
name "lower(trim(name))"
code "replace(upper(trim(code)),'-','')"
The first expression removes leading and trailing spaces before converting the value to lowercase. The second expression executes trim, upper, and replace in sequence.
A column name in a function must refer to the current column. Constants can also be used as function arguments, for example, concat('CN-',trim(code)).
Null, empty, and invalid values
NULLindicates no value. An empty string''and a string that contains only spaces are not necessarily equivalent toNULL.NVL(value,default)returns the default value only when the input isNULL.NANVL(value,default)returns the default value when the input isNULL, a blank string, or a value that cannot be recognized as a number. For a valid numeric value, it returns the value after removing leading and trailing spaces.- Some functions throw conversion exceptions if a date format, numeric argument, or hexadecimal string is invalid. During import, the row typically enters the bad-data handling process. Check the error logs and bad file when troubleshooting the issue.
General and string functions
Function |
Purpose |
Key information |
|---|---|---|
NONE |
Does not modify the current column | Can be omitted; specifying it explicitly improves readability |
LOWER(value) |
Converts the value to lowercase | Returns NULL for a NULL input |
UPPER(value) |
Converts the value to uppercase | Returns NULL for a NULL input |
TRIM(value) |
Removes leading and trailing whitespace | Does not remove spaces in the middle |
LTRIM(value[,set]) |
Removes consecutive characters contained in set from the left |
If set is omitted, a single space is used |
RTRIM(value[,set]) |
Removes consecutive characters contained in set from the right |
If set is omitted, a single space is used |
SUBSTR(value,start[,length]) |
Extracts a substring by character | The start position begins at 1; 0 is treated as 1; a negative value specifies a position from the end |
SUBSTRING(value,start[,length]) |
Alias of SUBSTR |
Has the same semantics as SUBSTR |
LENGTH(value) |
Returns the number of characters | When server=oracle, an empty string follows NULL semantics |
LPAD(value,length[,pad]) |
Pads or truncates the value on the left to the specified number of characters | pad defaults to a space |
RPAD(value,length[,pad]) |
Pads or truncates the value on the right to the specified number of characters | pad defaults to a space |
LPADB(value,byte_length[,pad]) |
Pads or truncates the value on the left to the specified number of bytes | pad must be a single-byte character |
RPADB(value,byte_length[,pad]) |
Pads or truncates the value on the right to the specified number of bytes | pad must be a single-byte character |
CONCAT(left,right) |
Concatenates two arguments | Arguments can be transformations of the current column or constants |
REPLACE(value,search,replacement) |
Replaces all matching substrings | We recommend that you always specify the third argument; passing NULL returns NULL |
REVERSE(value) |
Reverses a string | Reverses the string by character |
CONVERT(value,target_charset) |
Converts the value from the default character set of the tool JVM to the target character set | Character set names are recognized by Java Charset |
CONVERT(value,source_charset,target_charset) |
Converts the value between the specified character sets | Returns NULL and writes an error log if conversion fails |
ASCII(value) |
Returns the Unicode code point of the first character | For example, ASCII('A') returns 65 |
CHR(value) |
Converts an integer to a character | The input must be parseable as an integer |
NCHR(value) |
Converts a Unicode code point to a character | The input must be parseable as an integer |
RAWTOHEX(value) |
Converts string bytes to hexadecimal text | For example, converts hello to 68656c6c6f |
HEXTORAW(value) |
Converts hexadecimal text back to a UTF-8 string | A 0x prefix is allowed; invalid hexadecimal text causes an error |
Null and numeric functions
Function |
Purpose |
Example |
|---|---|---|
NVL(value,default) |
Returns the default value when the input is NULL |
nvl(status,'UNKNOWN') |
NANVL(value,default) |
Returns the default value when the input is NULL, blank, or non-numeric |
nanvl(amount,'0') |
TRUNC(number[,scale]) |
Truncates a number to the specified decimal places without rounding | trunc(amount,2) |
Date and time functions
Date formats use Java date format strings. Common fields include yyyy for year, MM for month, dd for day, HH for hour in the 24-hour format, mm for minute, ss for second, and SSS for millisecond. Note that MM and mm have different meanings.
Function |
Purpose |
Key information |
|---|---|---|
SYSDATE |
Returns the current time of the host where the tool runs | Format: yyyy-MM-dd HH:mm:ss |
SYSTIMESTAMP |
Returns the current time of the host where the tool runs | Format: yyyy-MM-dd HH:mm:ss.SSS |
TO_TIMESTAMP(value,source_pattern[,target_pattern]) |
Validates and converts a time string | target_pattern defaults to yyyy-MM-dd HH:mm:ss.SSS; returns NULL only when value is NULL and reports an error if a non-null value cannot be converted |
TMSFMT(value,source_pattern,default[,target_pattern]) |
Converts a time string and returns default if conversion fails |
target_pattern is optional and defaults to yyyy-MM-dd HH:mm:ss.SSS |
DATE_ADD(value,amount,unit[,pattern]) |
Adds or subtracts a specified unit from a date | Supports YEAR, MONTH, WEEK, and DAY; the default format is yyyy-MM-dd |
TIMESTAMP_ADD(value,amount,unit[,pattern]) |
Adds or subtracts a specified unit from a time value | Supports YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, and MILLISECOND; the default format is yyyy-MM-dd HH:mm:ss |
DATE_TRUNC(value,unit[,pattern]) |
Truncates a date to a specified unit | Supports YEAR, MONTH, and DAY |
TIMESTAMP_TRUNC(value,unit[,pattern]) |
Truncates a time value to a specified unit | Supports YEAR, MONTH, DAY, HOUR, MINUTE, and SECOND |
YEAR(value) |
Extracts the year | The input format is yyyy-MM-dd HH:mm:ss |
MONTH(value) |
Extracts the month | Returns a value from JANUARY to DECEMBER |
HOUR(value) |
Extracts the hour | The input format is yyyy-MM-dd HH:mm:ss |
MINUTE(value) |
Extracts the minute | The input format is yyyy-MM-dd HH:mm:ss |
WEEK(value) |
Extracts the week number in the year | The input format is yyyy-MM-dd HH:mm:ss |
DAY_OF_MONTH(value) |
Extracts the day of the month | The input format is yyyy-MM-dd HH:mm:ss |
DAY_OF_WEEK(value) |
Extracts the day of the week | Returns a value from MONDAY to SUNDAY |
DAY_OF_YEAR(value) |
Extracts the day of the year | The input format is yyyy-MM-dd HH:mm:ss |
In the current version, YEAR, MONTH, HOUR, MINUTE, WEEK, and the DAY_OF_* functions expect the default input format yyyy-MM-dd HH:mm:ss. If a source value uses a different format, use TO_TIMESTAMP to convert it to this format before extracting the required part.
When OBLOADER uses TO_TIMESTAMP and cannot convert a non-null value, the function throws an exception. OBLOADER marks the record as bad and writes it to ob-loader-dumper.bad. Whether the task fails depends on configurations such as the maximum number of errors.
Examples:
created_at "to_timestamp(created_at,'yyyyMMddHHmmss','yyyy-MM-dd HH:mm:ss')"
created_at "tmsfmt(created_at,'yyyyMMddHHmmss','1970-01-01 00:00:00','yyyy-MM-dd HH:mm:ss')"
expire_date "date_add(expire_date,30,'DAY','yyyy-MM-dd')"
created_at "timestamp_trunc(created_at,'DAY','yyyy-MM-dd HH:mm:ss')"
SYSDATE and SYSTIMESTAMP use the clock and time zone of the host where OBLOADER or OBDUMPER runs, rather than those of the target database server. If time precision is important, verify the host clock and time zone settings first.
Masking functions
Masking functions replace uppercase letters, lowercase letters, and digits based on their character types. The default replacement characters are X, x, and n, respectively. Symbols, spaces, and other characters remain unchanged.
Function |
Purpose |
|---|---|
MASK(value[,upper[,lower[,digit]]]) |
Masks all uppercase letters, lowercase letters, and digits |
MASK_FIRST_N(value,upper,lower,digit,n) |
Masks only the first n characters |
MASK_LAST_N(value,upper,lower,digit,n) |
Masks only the last n characters |
MASK_SHOW_FIRST_N(value,upper,lower,digit,n) |
Retains the first n characters and masks the remaining characters |
MASK_SHOW_LAST_N(value,upper,lower,digit,n) |
Retains the last n characters and masks the remaining characters |
Examples:
phone "mask_show_last_n(phone,'X','x','*',4)"
name "mask_show_first_n(name,'X','x','n',1)"
id_card "mask_first_n(id_card,'X','x','*',14)"
For functions whose names contain N, explicitly specify all replacement characters and the n argument. This prevents the actual masking range from differing from the expected range because an argument is omitted.
Digest, encryption, and decryption functions
Function |
Purpose |
Key information |
|---|---|---|
SM3_DIGEST(value[,encoding]) |
Calculates an SM3 digest | encoding supports HEX and BASE64 and defaults to HEX |
SM4_ENCRYPT(value,base64_key[,encoding]) |
Encrypts a value by using SM4 | The key argument is a Base64-encoded key; the output defaults to BASE64, and HEX is also supported |
SM4_DECRYPT(value,base64_key[,encoding]) |
Decrypts a value by using SM4 | encoding must match the ciphertext encoding and defaults to BASE64 |
Examples:
id_card "sm3_digest(id_card,'HEX')"
secret "sm4_encrypt(secret,'MDEyMzQ1Njc4OWFiY2RlZg==','BASE64')"
secret "sm4_decrypt(secret,'MDEyMzQ1Njc4OWFiY2RlZg==','BASE64')"
Do not store encryption or decryption keys in public repositories, chat records, or configurations without access control. Before use, verify the key length, Base64 encoding, and ciphertext encoding.
Conditional expressions
Conditional expressions select different conversion results based on the value of the current column. Simple CASE and searched CASE expressions are supported.
For separate reference topics about conditional expressions, see OBLOADER conditional expressions and OBDUMPER conditional expressions.
Simple CASE
A simple CASE expression compares the result of an expression with multiple constants in sequence:
CASE <expression>
WHEN <constant> THEN <result>
[WHEN <constant> THEN <result> ...]
[ELSE <result>]
END
Example:
lang=java
(
status "case upper(trim(status)) when 'A' then 'ACTIVE' when 'I' then 'INACTIVE' else 'UNKNOWN' end"
);
Searched CASE
A searched CASE expression evaluates multiple Boolean conditions in sequence:
CASE
WHEN <condition> THEN <result>
[WHEN <condition> THEN <result> ...]
[ELSE <result>]
END
The following example converts a null value to UNKNOWN, retains the values A and B, and converts all other values to OTHER:
lang=java
(
code "case when code is null then 'UNKNOWN' when code in ('A','B') then code else 'OTHER' end"
);
The following example performs a numeric comparison:
lang=java
(
score "case when nanvl(score,'0')>=60 then 'PASS' else 'FAIL' end"
);
Conditional operators
Type |
Supported syntax |
Description |
|---|---|---|
| Null check | IS NULL, IS NOT NULL |
Checks for NULL |
| Set membership | IN (...,...), NOT IN (...,...) |
An IN list must contain at least two constants |
| Equality comparison | =, !=, <> |
Compares strings |
| Relational comparison | >, <, >=, <= |
The current implementation parses values as long integers before comparison and does not apply to decimals or ordinary strings |
| Logical AND | AND, && |
Both conditions must be true |
| Logical OR | OR, || |
At least one condition must be true |
| Logical NOT | NOT |
Negates the following condition |
| Integer arithmetic | +, -, *, /, DIV, %, MOD |
Performs calculations using long integers |
Take note of the following considerations when you use conditional expressions:
- You can use only the current column value, constants, and function results derived from the current column. Cross-column conditions are not supported.
- If no
WHENclause matches and noELSEclause is specified, the result isNULL. - Relational comparisons use long-integer semantics. If a value can be blank or non-numeric, use
NANVLto convert it first. - Integer division discards the fractional part. Division by zero causes processing of the record to fail.
XOR,BETWEEN, and<=>are not supported.- Explicit grouping of complex Boolean conditions with parentheses is not supported. Split a complex rule into multiple simple
WHENclauses and verify the result with a small data sample.
Common scenarios
Scenario 1: The file column order differs from the table column order
Explicitly specify map(n) for all ordinary columns:
lang=java
(
id map(3),
name "trim(name)" map(1),
status "upper(status)" map(4),
created_at "to_timestamp(created_at,'yyyyMMddHHmmss','yyyy-MM-dd HH:mm:ss')" map(2)
);
Scenario 2: Discard fields from a file
For delimited text, you do not need to define dummy columns for fields that you want to discard. Skip the corresponding field indexes directly:
lang=java
(
id map(1),
name map(3),
status map(5)
);
For a POS file, use _FILLER position(...) to explicitly identify a placeholder region.
Scenario 3: Use a default time when the date format is invalid
If an invalid date must be written to the bad file, use TO_TIMESTAMP:
created_at "to_timestamp(created_at,'yyyyMMddHHmmss','yyyy-MM-dd HH:mm:ss')"
If your business requirements allow the import to continue with a fixed time value, use TMSFMT:
created_at "tmsfmt(created_at,'yyyyMMddHHmmss','1970-01-01 00:00:00','yyyy-MM-dd HH:mm:ss')"
These two strategies produce different data-quality outcomes. Select a strategy based on your business requirements. Do not use a default value for invalid dates solely to reduce the number of reported errors.
Scenario 4: Mask sensitive data during export
lang=java
(
customer_id,
customer_name "mask_show_first_n(customer_name,'X','x','n',1)",
phone "mask_show_last_n(phone,'X','x','*',4)",
id_card "sm3_digest(id_card,'HEX')"
);
Before a full export, run the export task with a small data sample and verify the masking result. In addition to checking whether the task succeeds, verify that sensitive columns are transformed as expected.
Limitations and considerations
- A control file provides column-level processing. It is not a SQL query engine. Expressions are executed locally by OBLOADER or OBDUMPER and are not pushed down to the database.
- An expression can read only the current column. Cross-column calculations are not supported.
- Inline comments are not supported in control files.
- Do not use Groovy script expressions. The current version does not expose them as a supported public capability.
- The matching rules for column names and for control file base names and table names depend on the database compatibility mode and identifier notation. For more information, see Control file activation rules and Basic syntax.
LPADandRPADmeasure values in characters.LPADB,RPADB, and POSpositionmeasure values in bytes.SYSDATEandSYSTIMESTAMPuse the clock of the host where OBLOADER or OBDUMPER runs.- Relational comparisons and arithmetic operations in conditional expressions use long-integer semantics and do not apply to decimal-precision calculations.
- Before using a new or modified control file for a full task, verify it with a small data sample.
Troubleshooting
Symptom |
Possible cause |
Solution |
|---|---|---|
| The control file does not take effect | The control file base name does not follow the table-name matching rules of the target database | In MySQL compatible mode, check lower_case_table_names. In Oracle compatible mode, check the uppercase-normalized form of the unquoted table name |
| The control file is ignored | The task does not select the corresponding table, or the directory contains unrelated .ctrl files |
Check the table selection options and the unexpected, ignore log |
| A syntax error occurs at startup | A comma, right parenthesis, or final semicolon is missing; quotation marks are unmatched; or the file contains a comment | Start with a minimal control file and restore the configuration one column at a time |
| Imported column values are misaligned | map(n) indexes start from 1, or the field consumption order of generated and ordinary columns was misunderstood |
Explicitly specify map(n) for all ordinary columns and verify the mappings with a one-row sample |
| POS fields are misaligned | Positions were calculated by character instead of byte, or the file encoding differs from the expected encoding | Recalculate the byte range of each field based on the actual file encoding |
| Date conversion fails | The format string does not match the actual data. A common cause is confusing MM with mm |
Test valid, boundary, and invalid date values separately |
| An invalid date is not replaced with the default value | TO_TIMESTAMP is used |
If your business requirements allow a default value, use TMSFMT |
A relational comparison in CASE returns an unexpected result |
The compared value is not a long integer | Use equality-based enumeration, or compare decimal and string ranges upstream |
| Many records are written to the bad file | Preprocessing function arguments are invalid, or the source data does not match the assumptions | Check the first root-cause log and create a minimal reproduction with the original value of the failed column |
| Exported masked data differs from expectations | Replacement characters or the n argument were omitted, or the meanings of retained and masked characters were interpreted incorrectly |
Explicitly specify all five arguments and verify the output with a small export sample |
Pre-production checklist
- The control file base name follows the table-name matching rules of the target database, and the file name has the
.ctrlextension. --ctl-pathpoints to the correct file or directory.- The file starts with
lang=java, column definitions are separated by commas, and the file ends with);. - The file does not contain comments starting with
--or//. - Column selection, exclusion, and ordering rules are defined only in the control file, without conflicting command-line options.
- When fields are reordered, skipped, or reused during import,
map(n)is explicitly specified for all ordinary columns. - Positions in a POS file are verified in bytes based on the actual file encoding.
- All date formats are verified with real samples, and either the error or default-value strategy is selected explicitly.
- Conditional expressions do not contain cross-column references, decimal relational comparisons, or unsupported operators.
- Masking, digest, encryption, and decryption output is verified with a small sample, and keys are not exposed in public locations.
- The control file parsing success log appears in the task logs for the small data sample.
- Actual import or export results are inspected instead of judging success only from the final task status.
