Query Attributes
Query Attributes support is approaching stability.
History
| Version | Changes |
|---|---|
| v3.21.0 |
Query attributes let you attach metadata key-value pairs to individual SQL statements. The server can read these values using functions like mysql_query_attribute_string() โ without any changes to the SQL text or session state.
Typical use cases include request tracing, audit context, tenant identifiers, and passing out-of-band hints to server-side components or plugins.
Requires MySQL 8.0.25+. The component_query_attributes server component must be installed for the server to expose attributes via SQL functions such as mysql_query_attribute_string(). To install it, run INSTALL COMPONENT "file://component_query_attributes" as a privileged user.
How It Worksโ
MySQL2 negotiates the CLIENT_QUERY_ATTRIBUTES capability flag during the handshake (enabled by default). When this capability is active, both connection.query() and connection.execute() encode any attributes you provide into the extended wire protocol for COM_QUERY and COM_STMT_EXECUTE packets.
On the server side, retrieve attribute values inside a query with:
SELECT mysql_query_attribute_string('attribute_name');
Basic Usageโ
Pass an attributes object in the query options. Each key becomes an attribute name and each value is sent alongside the SQL statement.
- Promise
- Callback
import mysql from 'mysql2/promise';
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
});
const [rows] = await connection.query({
sql: `SELECT mysql_query_attribute_string('request_id') AS request_id`,
attributes: {
request_id: 'abc-123',
},
});
console.log(rows); // [ { request_id: 'abc-123' } ]
await connection.end();
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
});
connection.query(
{
sql: `SELECT mysql_query_attribute_string('request_id') AS request_id`,
attributes: {
request_id: 'abc-123',
},
},
(err, rows) => {
if (err) throw err;
console.log(rows); // [ { request_id: 'abc-123' } ]
connection.end();
}
);
With Prepared Statementsโ
Query attributes also work with connection.execute() (prepared statements). Attributes and bind parameters are encoded together in the COM_STMT_EXECUTE packet.
- Promise
- Callback
import mysql from 'mysql2/promise';
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
});
const [rows] = await connection.execute({
sql: 'SELECT ? + ? AS sum, mysql_query_attribute_string(?) AS trace_id',
values: [1, 2, 'trace_id'],
attributes: {
trace_id: 'txn-456',
},
});
console.log(rows); // [ { sum: 3, trace_id: 'txn-456' } ]
await connection.end();
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
});
connection.execute(
{
sql: 'SELECT ? + ? AS sum, mysql_query_attribute_string(?) AS trace_id',
values: [1, 2, 'trace_id'],
attributes: {
trace_id: 'txn-456',
},
},
(err, rows) => {
if (err) throw err;
console.log(rows); // [ { sum: 3, trace_id: 'txn-456' } ]
connection.end();
}
);
Multiple Attributesโ
You can send any number of attributes per query.
- Promise
- Callback
const [rows] = await connection.query({
sql: `
SELECT
mysql_query_attribute_string('user_id') AS user_id,
mysql_query_attribute_string('tenant') AS tenant,
mysql_query_attribute_string('request_time') AS request_time
`,
attributes: {
user_id: 42,
tenant: 'acme-corp',
request_time: new Date(),
},
});
console.log(rows);
connection.query(
{
sql: `
SELECT
mysql_query_attribute_string('user_id') AS user_id,
mysql_query_attribute_string('tenant') AS tenant,
mysql_query_attribute_string('request_time') AS request_time
`,
attributes: {
user_id: 42,
tenant: 'acme-corp',
request_time: new Date(),
},
},
(err, rows) => {
if (err) throw err;
console.log(rows);
}
);
With Connection Poolsโ
Attributes are per-query, so they work identically with pools โ no special configuration needed.
- Promise
- Callback
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'test',
});
const [rows] = await pool.query({
sql: `SELECT mysql_query_attribute_string('source') AS source`,
attributes: { source: 'background-worker' },
});
console.log(rows); // [ { source: 'background-worker' } ]
await pool.end();
const mysql = require('mysql2');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'test',
});
pool.query(
{
sql: `SELECT mysql_query_attribute_string('source') AS source`,
attributes: { source: 'background-worker' },
},
(err, rows) => {
if (err) throw err;
console.log(rows); // [ { source: 'background-worker' } ]
pool.end();
}
);
Supported Attribute Typesโ
Attribute values are serialized from JavaScript to MySQL types using the same rules as bind parameters:
| JavaScript Type | MySQL Wire Type | Notes |
|---|---|---|
string | VAR_STRING | |
number | DOUBLE | 64-bit floating point |
boolean | TINY | true โ 1, false โ 0 |
null | NULL | Server sees a NULL attribute |
Date | DATETIME | |
Buffer | VAR_STRING | Raw bytes |
Passing undefined as an attribute value will throw a TypeError.
TypeScriptโ
The attributes option is fully typed in the QueryOptions interface:
import mysql, { QueryOptions } from 'mysql2/promise';
const connection = await mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test',
});
const options: QueryOptions = {
sql: `SELECT mysql_query_attribute_string('request_id') AS rid`,
attributes: {
request_id: 'abc-123',
priority: 1,
debug: true,
timestamp: new Date(),
},
};
const [rows] = await connection.query(options);
Disabling Query Attributesโ
The CLIENT_QUERY_ATTRIBUTES capability is enabled by default. If the server does not support it, query attributes are automatically disabled โ no manual configuration is needed. You can also explicitly exclude the flag:
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
flags: ['-CLIENT_QUERY_ATTRIBUTES'],
});
When the flag is disabled (either automatically or explicitly), any attributes you pass are silently ignored and the standard (non-extended) packet format is used.
Notesโ
- Query attributes are per-statement โ they are not stored on the connection or session.
- Attributes are independent of bind parameters (
values). You can use both simultaneously. - The server must have
component_query_attributesinstalled formysql_query_attribute_string()to work. Without it, attribute functions are not available.