All Products
Search
Document Center

PolarDB:Use OSS foreign tables to access OSS data

Last Updated:Mar 28, 2026

PolarDB lets you query CSV-formatted cold data directly in Object Storage Service (OSS) without importing it into your database. This reduces storage costs for infrequently accessed data while keeping the data queryable through standard SQL.

Quick start

The following example shows the complete workflow: create a server, create a foreign table, and query data.

-- Step 1: Create an OSS server (newer cluster syntax)
CREATE SERVER my_oss_server
FOREIGN DATA WRAPPER oss OPTIONS
(
  EXTRA_SERVER_INFO '{"oss_endpoint": "oss-cn-hangzhou-internal.aliyuncs.com",
    "oss_bucket": "my-bucket",
    "oss_access_key_id": "LTAI5t...",
    "oss_access_key_secret": "...",
    "oss_prefix": "csv-data"}'
);

-- Step 2: Create an OSS foreign table (maps to csv-data/orders.CSV in OSS)
CREATE TABLE orders (
  id INT,
  amount DECIMAL(10,2),
  created_at DATETIME
) ENGINE=CSV CONNECTION="my_oss_server";

-- Step 3: Query the data
SELECT id, amount FROM orders WHERE amount > 100;

Verify the table was created with the CSV engine:

SHOW CREATE TABLE orders;

The output must contain ENGINE=CSV. If it does not, your cluster version does not support OSS foreign tables. See Prerequisites.

For details on each step, see Set up OSS foreign tables.

Prerequisites

Before you begin, ensure that you have:

How it works

OSS foreign tables map directly to CSV files stored in an OSS bucket. Queries run against the foreign table read from OSS in real time — no data is copied into PolarDB.

The workflow has four steps:

  1. Create an OSS server — registers the OSS connection (bucket, endpoint, credentials).

  2. Upload data — place your CSV files in the OSS directory configured on the server.

  3. Create an OSS foreign table — binds a PolarDB table to a specific CSV file.

  4. Query data — use standard SQL SELECT statements against the foreign table.

OSS foreign table workflow

Limitations

  • Only CSV-formatted data is supported. Compressed CSV files and geospatial data types are not supported.

  • Supported operations: CREATE, SELECT, and DROP. DROP removes the table definition from PolarDB but does not delete the CSV file in OSS.

  • OSS foreign tables do not support indexes, partitioning, or transactions.

  • NULL values require MySQL 8.0.1 revision 8.0.1.1.28 or later, or MySQL 8.0.2 revision 8.0.2.2.5 or later.

Important

The .CSV file extension must be uppercase. A file named t1.csv (lowercase) will not be found. Name files t1.CSV.

Supported data types

Numeric types

Data typeSizeSigned rangeUnsigned rangeDescription
TINYINT1 byte-128 to 1270 to 255Small integer
SMALLINT2 bytes-32768 to 327670 to 65535Integer
MEDIUMINT3 bytes-8388608 to 83886070 to 16777215Integer
INT or INTEGER4 bytes-2147483648 to 21474836470 to 4294967295Integer
BIGINT8 bytes-9,223,372,036,854,775,808 to 92233720368547758070 to 18446744073709551615Big integer
FLOAT4 bytes-3.402823466E+38 to -1.175494351E-38; 0; 1.175494351E-38 to 3.402823466351E+380; 1.175494351E-38 to 3.402823466E+38Single-precision float
DOUBLE8 bytes-2.2250738585072014E-308 to -1.7976931348623157E+308; 0; 1.7976931348623157E+308 to 2.2250738585072014E-3080; 1.7976931348623157E+308 to 2.2250738585072014E-308Double-precision float
DECIMALM+2 if M>D, else D+2Depends on M and DDepends on M and DDecimal

Date and time types

Data typeSizeRangeFormatDescription
DATE3 bytes1000-01-01 to 9999-12-31YYYY-MM-DDDate
TIME3 bytes-838:59:59 to 838:59:59HH:MM:SSTime or duration
YEAR1 byte1901 to 2155YYYYYear
DATETIME8 bytes1000-01-01 00:00:00 to 9999-12-31 23:59:59YYYY-MM-DD HH:MM:SSCombined date and time
TIMESTAMP4 bytes1970-01-01 00:00:00 to 2038-01-19 03:14:07YYYY-MM-DD HH:MM:SSTimestamp
Important

For DATETIME and TIMESTAMP, month and day must always be two digits (for example, 2020-01-01, not 2020-1-1). Single-digit values cause pushdown queries to fail silently.

String types

Data typeSizeDescription
CHAR0–255 bytesFixed-length string
VARCHAR0–65535 bytesVariable-length string
TINYBLOB0–255 bytesBinary object up to 255 bytes
TINYTEXT0–255 bytesShort string
BLOB0–65535 bytesBinary object
TEXT0–65535 bytesString
MEDIUMBLOB0–16777215 bytesMedium binary object
MEDIUMTEXT0–16777215 bytesMedium string
LONGBLOB0–4294967295 bytesLong binary object
LONGTEXT0–4294967295 bytesLong string

Set up OSS foreign tables

Step 1: Create an OSS server

An OSS server stores the connection configuration for your OSS bucket. All other methods of connecting to OSS have been disabled for security reasons.

Permissions

SERVERS_ADMIN permission is required to create an OSS server. Check your current permissions:

SHOW GRANTS FOR username;

If you do not have SERVERS_ADMIN permission, follow the steps for your account type:

  • Standard account without SERVERS_ADMIN: Ask a privileged account to run:

    GRANT SERVERS_ADMIN ON *.* TO 'users'@'%' WITH GRANT OPTION;
  • Privileged account without SERVERS_ADMIN: In the PolarDB console, go to the cluster details page, choose Settings and Management > Accounts, find your privileged account on the User Account tab, and click Reset Permissions.

After creating an OSS server, a privileged account can view server details (with access keys encrypted):

SELECT Server_name, Extra_server_info FROM mysql.servers;

Choose the correct syntax for your cluster version

Two syntax variants exist for CREATE SERVER. Check your revision version first (see Query the engine version) and use the matching syntax.

Statements for clusters of later versions

Newer clusters (MySQL 8.0.1 revision 8.0.1.1.28 or later, or MySQL 8.0.2 revision 8.0.2.2.5 or later):

CREATE SERVER <server_name>
FOREIGN DATA WRAPPER oss OPTIONS
(
  [DATABASE '<my_database_name>',]
  EXTRA_SERVER_INFO '{"oss_endpoint": "<my_oss_endpoint>","oss_bucket": "<my_oss_bucket>","oss_access_key_id": "<my_oss_access_key_id>","oss_access_key_secret": "<my_oss_access_key_secret>","oss_prefix":"<my_oss_prefix>","oss_sts_token":"<my_oss_sts_token>"}'
);

Statements for clusters of earlier versions

Older clusters (MySQL 8.0.1 revision 8.0.1.1.25.4 to 8.0.1.1.28, or MySQL 8.0.2 revision 8.0.2.2.1 to 8.0.2.2.5):

oss_prefix and oss_sts_token are not supported in this version.

CREATE SERVER <server_name>
FOREIGN DATA WRAPPER oss OPTIONS
(
  [DATABASE '<my_database_name>',]
  EXTRA_SERVER_INFO '{"oss_endpoint": "<my_oss_endpoint>","oss_bucket": "<my_oss_bucket>","oss_access_key_id":"<my_oss_access_key_id>","oss_access_key_secret":"<my_oss_access_key_secret>"}'
);

Parameters

ParameterRequiredDescription
server_nameYesA globally unique name for the OSS server. Up to 64 characters, case-insensitive. Names longer than 64 characters are truncated automatically.
my_database_nameNoAn OSS directory path for CSV files. Has the same effect as oss_prefix. Use oss_prefix instead when available. If both are set, the final path is oss_prefix/DATABASE.
my_oss_endpointYesThe OSS endpoint. Use an internal endpoint (containing internal) when connecting from an Alibaba Cloud server to avoid internet traffic charges — for example, oss-cn-xxx-internal.aliyuncs.com.
my_oss_bucketYesThe OSS bucket where your CSV files are stored. Deploy the bucket in the same zone as your PolarDB cluster to reduce latency.
my_oss_access_key_idYesThe AccessKey ID of your RAM user or Alibaba Cloud account.
my_oss_access_key_secretYesThe AccessKey secret of your RAM user or Alibaba Cloud account.
my_oss_prefixNoThe OSS directory path for CSV files. (Newer clusters only.)
my_oss_sts_tokenNoA temporary credential from Security Token Service (STS). Required if you use STS temporary access credentials. Supported on MySQL 8.0.1 revision 8.0.1.1.29 or later, or MySQL 8.0.2 revision 8.0.2.2.6 or later.
oss_sts_token has a default expiration time. When it expires, reset all EXTRA_SERVER_INFO values using ALTER SERVER:
ALTER SERVER server_name OPTIONS(EXTRA_SERVER_INFO '{"oss_endpoint": "<my_oss_endpoint>",
"oss_bucket": "<my_oss_bucket>", "oss_access_key_id": "<my_oss_access_key_id>",
"oss_access_key_secret": "<my_oss_access_key_secret>", "oss_prefix":"<my_oss_prefix>", "oss_sts_token": "<my_oss_sts_token>"}');

Step 2: Upload data

Use ossutil to upload local CSV files to your OSS bucket. Follow these naming rules:

  • Place CSV files in the DATABASE or oss_prefix directory configured on your OSS server.

  • Name each file <foreign_table_name>.CSV. The .CSV extension must be uppercase. For example, if the table is named t1, the file must be t1.CSV.

  • Make sure the CSV columns match the columns defined in the OSS foreign table.

For existing MySQL data, upload the data file directly and create the OSS foreign table based on the same table definition.

Step 3: Create an OSS foreign table

After creating an OSS server, create a foreign table to bind it to a specific CSV file:

CREATE TABLE <table_name> (create_definition,...) ENGINE=CSV CONNECTION="<connection_string>";

The connection_string is a /-separated path with up to three components:

server_name[/oss_path][/data_file_name]
ComponentRequiredDescription
server_nameYesThe OSS server name from CREATE SERVER.
oss_pathNoA subdirectory path within the oss_prefix in OSS. Requires MySQL 8.0.1 revision 8.0.1.1.28 or later, or MySQL 8.0.2 revision 8.0.2.2.5 or later. If specified, data_file_name is also required.
data_file_nameNoThe name of the data file, without the .CSV extension. If omitted, the table name is used.

Example — connect to a file in a subdirectory:

CREATE TABLE t1 (id INT) ENGINE=CSV CONNECTION="server_name/a/b/c/d/t1";

This connects t1 to the file at oss_prefix/a/b/c/d/t1.CSV in OSS.

Example — point a table to a file with a different name:

-- Queries t2.CSV in the oss_prefix path
CREATE TABLE t1 (id INT) ENGINE=CSV CONNECTION="server_name/t2";

After creation, verify the table was created with the CSV engine:

SHOW CREATE TABLE t1;

Check that the output contains ENGINE=CSV. If it does not, your cluster version is too old to support OSS foreign tables. See Prerequisites.

Step 4: Query data

Run standard SQL statements against OSS foreign tables:

-- Count rows
SELECT count(*) FROM t1;

-- Filter by range
SELECT id FROM t1 WHERE id < 10 AND id > 1;

-- Point lookup
SELECT id FROM t1 WHERE id = 3;

-- Join with another table
SELECT id FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.name LIKE "%er%";

If a query produces a warning instead of an error, run SHOW WARNINGS; to view the details.

Common errors

ErrorCauseFix
OSS error: No corresponding data file on the OSS engine.The CSV file was not found in OSS.Verify the file exists at the correct path and that the name matches <table_name>.CSV with an uppercase .CSV extension.
There is not enough memory space for OSS transmission. Currently requested memory %d.Insufficient memory for the OSS query.Increase loose_csv_max_oss_threads on the Parameters page, or run FLUSH TABLE to release threads held by other OSS tables.
ERROR 8054 (HY000): OSS error: ... Couldn't connect to server ... aliyuncs.com:80The cluster cannot reach the OSS server.Confirm the cluster and OSS bucket are in the same zone. If not, move them to the same zone. If they are already in the same zone, switch to a public endpoint and contact Alibaba Cloud support if the error persists.

Work with NULL values

NULL value support requires MySQL 8.0.1 revision 8.0.1.1.28 or later, or MySQL 8.0.2 revision 8.0.2.2.5 or later.

Insert NULL values

Set NULL_MARKER when creating the table to tell PolarDB which string in the CSV represents a NULL value. The default is NULL.

CREATE TABLE `t1` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `time` timestamp NULL DEFAULT NULL
) ENGINE=CSV NULL_MARKER='NULL' CONNECTION='server_name';

In the CSV file, any field whose value matches NULL_MARKER (without enclosing double quotation marks) is treated as NULL. If the value is enclosed in double quotation marks, it is treated as a string.

NULL_MARKER restrictions:

  • Cannot be a number-only string or empty

  • Cannot contain ", \n, \r, or ,

Example:

CSV contents:

1,"xiaohong","2022-01-01 00:00:00"
NULL,"xiaoming","2022-02-01 00:00:00"
3,NULL,"2022-03-01 00:00:00"
4,"xiaowang",NULL

Query result:

SELECT * FROM t1;
+------+----------+---------------------+
| id   | name     | time                |
+------+----------+---------------------+
|    1 | xiaohong | 2022-01-01 00:00:00 |
| NULL | xiaoming | 2022-02-01 00:00:00 |
|    3 | NULL     | 2022-03-01 00:00:00 |
|    4 | xiaowang | NULL                |
+------+----------+---------------------+

To check the current NULL_MARKER value for a table:

SHOW CREATE TABLE t1;

Read NULL values

When PolarDB reads a NULL value from a CSV file:

  • Column allows NULL: the field is set to NULL.

  • Column is NOT NULL: behavior depends on sql_mode.

    • STRICT_TRANS_TABLES: an error is reported.

    • Other modes: the field is set to the column's default value, or the MySQL type default if no default is defined. Run SHOW WARNINGS; to view the warning details.

To check the current sql_mode:

SHOW VARIABLES LIKE "sql_mode";

To change sql_mode, go to Settings and Management > Parameters in the PolarDB console. For details, see Modify parameters.

Example — NOT NULL column with a NULL value in the CSV:

Table:

CREATE TABLE `t` (
  `id` int(11) NOT NULL
) ENGINE=CSV
CONNECTION="server_name";

CSV (t.csv):

NULL
2

With STRICT_TRANS_TABLES:

SELECT * FROM t;
ERROR 1364 (HY000): Field 'id' doesn't have a default value

Without STRICT_TRANS_TABLES:

SELECT * FROM t;
+----+
| id |
+----+
|  0 |
|  2 |
+----+
2 rows in set, 1 warning (0.00 sec)

The value 0 is the MySQL default for INT. To see the warning:

SHOW WARNINGS;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1364 | Field 'id' doesn't have a default value |
+---------+------+-----------------------------------------+

Query optimization

Engine condition pushdown sends eligible WHERE conditions to OSS for evaluation, reducing the data transferred to PolarDB. It is disabled by default.

Enable pushdown for the current session:

SET SESSION optimizer_switch='engine_condition_pushdown=on';

Pushdown requirements

For a condition to be pushed down, all of the following must be true:

  • The CSV file is UTF-8 encoded.

  • Only a single file is queried (no JOIN, ORDER BY, GROUP BY, or HAVING).

  • The WHERE clause uses only supported operators: >, <, >=, <=, ==, LIKE, IN, AND, OR, +, -, *, /.

  • The WHERE clause contains no aggregation (for example, WHERE max(age) > 100 is not allowed).

  • The query has at most 1,000 columns, each column name at most 1,024 bytes.

  • The LIKE clause has at most 5 wildcards (%).

  • The IN clause has at most 1,024 constants.

  • Each column and row in the CSV is at most 256 KB.

  • The SQL statement is at most 16 KB, with at most 20 expressions after WHERE and at most 100 aggregation operations.

View the execution plan

Use EXPLAIN to see which conditions are pushed down.

Default format:

EXPLAIN SELECT count(*) FROM `t1` WHERE `id` > 5 AND `id` < 100 AND `name` LIKE "%1%%%%%" GROUP BY `id` ORDER BY `id` DESC;

Conditions listed after With pushed engine condition are evaluated in OSS. All other conditions are evaluated locally.

Tree format (requires PolarDB for MySQL 8.0.2):

EXPLAIN FORMAT=tree SELECT count(*) FROM `t1` WHERE `id` > 5 AND `id` < 100 AND `name` LIKE "%1%%%%%" GROUP BY `id` ORDER BY `id` DESC;

Conditions listed after engine conditions: are evaluated in OSS.

JSON format:

EXPLAIN FORMAT=json SELECT count(*) FROM `t1` WHERE `id` > 5 AND `id` < 100 AND `name` LIKE "%1%%%%%" GROUP BY `id` ORDER BY `id` DESC;

Conditions in the engine_condition field are evaluated in OSS.

Disable pushdown

If your data contains characters incompatible with pushdown, you will see:

OSS error: The current query does not support engine condition pushdown. You need to use NO_ECP() hint or set optimizer_switch = 'engine_condition_pushdown=OFF' to turn off the condition push down function.

Disable pushdown per query with a hint:

SELECT /*+ NO_ECP(t1) */ `j` FROM `t1` WHERE `j` LIKE "%c%" LIMIT 10;

Disable pushdown for the current session:

SET SESSION optimizer_switch='engine_condition_pushdown=off';

Check the current status:

SELECT @@optimizer_switch;

Parameters

Configure these parameters in the PolarDB console under Settings and Management > Parameters.

ParameterLevelDefaultValid rangeDescription
loose_csv_oss_buff_sizeSession134217728 bytes4096–134217728Memory allocated per OSS thread.
loose_csv_max_oss_threadsGlobal11–100Maximum number of concurrent OSS threads.

Maximum OSS memory usage = loose_csv_max_oss_threads × loose_csv_oss_buff_size.

Keep total OSS memory usage below 5% of the node's memory capacity to avoid out-of-memory issues.

Synchronize OSS server information between nodes

The primary node and all read-only nodes in a PolarDB cluster share the same OSS server configuration. Changes to the OSS server are synchronized to read-only nodes in a lock-free manner, so operations on each node remain independent.

If a read-only node thread holds the OSS server lock, synchronization may be delayed. To manually trigger synchronization for a specific node, run either of the following (replace pi-bpxxxxxxxx with the actual node ID):

/*force_node='pi-bpxxxxxxxx'*/ FLUSH PRIVILEGES;
/*force_node='pi-bpxxxxxxxx'*/ FLUSH TABLE oss_foreign_table;