Raw SQL

Last updated

This page explains how to run hand-written SQL with read(), write(), and query() when the query builder cannot express what you need.

Three Entry Points

Method Pool When the pool is missing
read<T>(sql, params?) Read pool Throws Read pool connection is not available.
write<T>(sql, params?) Write pool Throws Write pool connection is not available.
query<T>(sql, params?, target?) target; when omitted, the target of the most recent table() call Throws read connection is not available. or write connection is not available.

All three run through query(), so they get the same slow query log and automatic connection release.

Parameter Binding

Pass values through ? placeholders and let mysql2 escape them:

import MySQLPool from "@pardnchiu/mysql-pool";
import type { ResultSetHeader, RowDataPacket } from "mysql2/promise";

try {
  const rows = await MySQLPool.read<RowDataPacket[]>(
    "SELECT id, name FROM users WHERE deleted_at IS NULL AND (role = ? OR role = ?)",
    ["admin", "editor"]
  );

  const header = await MySQLPool.write<ResultSetHeader>(
    "UPDATE users SET last_login = NOW() WHERE id = ?",
    [rows[0].id]
  );

  console.log(header.affectedRows);
} catch (err) {
  console.error("raw query failed:", err);
}

The generic T is only a type assertion. The return value is the first element of mysql2's query() result: an array of rows for SELECT and a ResultSetHeader for INSERT/UPDATE/DELETE.

When to Use Raw SQL

Need Reason
IS NULL, IS NOT NULL, OR, BETWEEN where() only produces column operator ? joined with AND
GROUP BY, HAVING, subqueries, UNION The builder has no matching methods
DELETE There is no delete() method
Joins with several conditions in ON The join methods accept a single comparison
Transactions Every query() checks out a fresh pooled connection and releases it immediately; see Known Limitations
中文