mariadb-load-data

MariaDB-specific syntax and behavior for LOAD DATA [LOCAL] INFILE (and LOAD XML) — the LOCAL vs server-side fork and its security/privilege requirements (local_infile, FILE privilege, secure_file_priv

SKILL.md

GitHub

LOAD DATA INFILE in MariaDB

Last updated: 2026-06-24

This skill covers the delta between standard SQL bulk loading and MariaDB's LOAD DATA [LOCAL] INFILE (and the LOAD XML companion). It assumes the agent knows the basic LOAD DATA INFILE 'f' INTO TABLE t form. The inverse — writing a file — is SELECT … INTO OUTFILE (see mariadb-select); the command-line wrapper is mariadb-import (see mariadb-import).

Default context: Assume MariaDB 11.8 LTS (GA May 2025) unless the user states another version. Anything available in current LTS branches (10.6, 10.11, 11.4, 11.8) is shown without a "since" annotation; only newer-than-10.6 features carry one.

What LLMs Often Miss

If the agent writes / assumes… …prefer the MariaDB form
LOAD DATA LOCAL INFILE … will just work once the client passes the file LOCAL needs both ends enabled: the client/connector must allow local-infile (often off by default in drivers — the usual real blocker) and the server's local_infile system variable must be ON (it is, by default). Otherwise: "the … server or client has disabled the local infile capability"
Without LOCAL, the path is relative to the client / current directory Without LOCAL the server reads the file from its own filesystem. The connecting user needs the global FILE privilege, and if secure_file_priv is set the file must sit under that directory
Adds REPLACE / IGNORE to a LOCAL load to control duplicate-key handling With LOCAL the server can't error mid-stream, so a bare LOAD DATA LOCAL silently treats duplicate-key rows as IGNORE (keeps the old row, warns) — your intent to error is dropped. For reliable REPLACE/error semantics, load from a server-side file (no LOCAL)
Expects strict mode to error on bad/truncated values during a LOCAL load With LOCAL, strict-mode abort-on-warning is disabled — bad data is coerced and warned, never aborted. (A non-LOCAL load honors strict mode.) Always check SHOW WARNINGS after loading
Assumes LOAD DATA errors out like an INSERT on bad rows Even without LOCAL, LOAD DATA leans toward warn-and-coerce — too-few/too-many fields per row raise warnings, not errors. Inspect SHOW WARNINGS every time
LOAD DATA INFILE 'x.csv' INTO TABLE t and expects CSV parsing MariaDB's defaults are tab-separated fields, newline-terminated lines, backslash escape — not CSV. For real CSV: FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
Post-filters or pre-strips a header row Use IGNORE 1 LINES (LINES and ROWS are interchangeable) to skip header rows
Loads a value that needs transforming, then fixes it with a follow-up UPDATE Read the raw field into a user variable and transform in SET in the same pass: (id, @raw) SET created = STR_TO_DATE(@raw, '%m/%d/%Y')
Relies on SET NAMES / character_set_client for the file's encoding LOAD DATA ignores those — use the CHARACTER SET clause. The default read charset is character_set_database; CHARACTER SET binary means no conversion
Adds LOW_PRIORITY / CONCURRENT to speed up an InnoDB load Both affect only table-lock engines (CONCURRENT is MyISAM-specific); no effect on InnoDB. They're also mutually exclusive
Uses LOAD DATA machinery (FIELDS/LINES) for XML LOAD XML has no FIELDS/LINES — it uses ROWS IDENTIFIED BY '<tag>' (angle brackets literal; default <row>)

Syntax

LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_name
    [PARTITION (p, …)]
    [CHARACTER SET charset_name]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']]
    [IGNORE number {LINES | ROWS}]
    [(col_name_or_user_var, …)]
    [SET col_name = expr, …];

Defaults when a clause is omitted: fields \t, lines \n, escape \.

-- Real CSV with a header row and an on-the-fly transform:
LOAD DATA LOCAL INFILE '/tmp/data.csv'
  INTO TABLE t
  FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
  LINES TERMINATED BY '\n'
  IGNORE 1 LINES
  (a, @raw_b) SET b = UPPER(@raw_b);

-- Server-side load (needs FILE privilege; path under secure_file_priv):
LOAD DATA INFILE '/var/lib/mysql-files/import.tsv' REPLACE INTO TABLE t;

LOAD XML

LOAD XML [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE [db_name.]tbl_name
    [CHARACTER SET charset_name]
    [ROWS IDENTIFIED BY '<tagname>']
    [IGNORE number {LINES | ROWS}]
    [(column_or_user_var, …)]
    [SET col_name = expr, …];

LOCAL, REPLACE/IGNORE, CHARACTER SET, the column list, and SET behave exactly as for LOAD DATA — including the LOCAL duplicate/strict-mode caveats above. ROWS IDENTIFIED BY names the row element (default <row>); MariaDB auto-detects the common attribute/element layouts.

Security checklist

  • No LOCAL → server reads its own filesystem; requires FILE privilege; path constrained by secure_file_priv (and secure_file_priv = NULL disables file I/O entirely).
  • LOCAL → client streams the file; needs the connector's local-infile option on and server local_infile = ON; relaxes duplicate and strict-mode handling (see traps above).

See Also

<sub>This page is: Copyright © 2026 MariaDB. All rights reserved.</sub>