cloudsqlmysql SQL dialect (Google Cloud SQL for MySQL).cloudsqlmysqlcloudsqlmysqlmysql2CloudSQLMySQLDialect extendsMySQLDialect. It changes only three things:cloudsqlsuperuser cannot run.| Property | Value |
|---|---|
| Dialect name | cloudsqlmysql |
| Extends | MySQLDialect |
| Driver | mysql2 |
| Optional SDK | @google-cloud/cloud-sql-connector, google-auth-library |
| Default port | 3306 |
| Cloud SDK | @google-cloud/cloud-sql-connector, google-auth-library (connector / IAM only) |
| Local runtime | none — hosted service only |
$ npm install ts-prorm-orm mysql2$ pnpm add ts-prorm-orm mysql2$ yarn add ts-prorm-orm mysql2$ bun add ts-prorm-orm mysql2mysql2 is the same driver the mysql dialect uses. Add @google-cloud/cloud-sql-connector and google-auth-library only for the token-based logins under Managed and cloud variants.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'cloudsqlmysql',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: { instanceConnectionName: 'my-project:us-central1:my-instance' },
},
});import { DataTypes } from 'ts-prorm-orm';
const User = db.define('User', {
id: { type: DataTypes.INTEGER(), primaryKey: true, autoIncrement: true },
email: { type: DataTypes.STRING(255), allowNull: false, unique: true },
name: { type: DataTypes.STRING(100) },
});
await db.sync(); // CREATE TABLE — see Generated SQL belowimport { Op } from 'ts-prorm-orm';
await User.create({ email: 'ada@example.com', name: 'Ada' });
const page = await User.findAll({
where: { name: { [Op.like]: 'A%' } },
order: [['id', 'ASC']],
limit: 20,
offset: 40,
});import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'cloudsqlmysql',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
instanceConnectionName: 'my-project:us-central1:my-instance',
ipType: 'PRIVATE', // 'PUBLIC' (default) | 'PRIVATE' | 'PSC'
},
},
});@google-cloud/cloud-sql-connector installed next to the driver.import { Prorm } from 'ts-prorm-orm';
export const db = new Prorm({
dialect: 'cloudsqlmysql',
database: 'app',
username: 'app-sa', // the IAM principal; no password
dialectOptions: {
cloudSql: {
instanceConnectionName: 'my-project:us-central1:my-instance',
iam: true, // automatic IAM database authentication
},
},
});@google-cloud/cloud-sql-connector installed next to the driver.import { Prorm } from 'ts-prorm-orm';
import * as fs from 'fs';
export const db = new Prorm({
dialect: 'cloudsqlmysql',
host: '10.0.0.5',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
mode: 'direct',
instanceConnectionName: 'my-project:us-central1:my-instance',
serverCa: fs.readFileSync('server-ca.pem', 'utf8'),
},
},
});query(), captured with the driver stubbed out. The model layer inlines the values.db.sync() for the model in the Quick start:CREATE TABLEawait User.create({ email: 'ada@example.com', name: 'Ada' });
await User.update({ name: 'Grace' }, { where: { email: 'ada@example.com' } });
await User.destroy({ where: { id: 1 } });await User.upsert({ id: 1, email: 'ada@example.com', name: 'Ada Lovelace' });upsert() also reads the row by primary key before and after the write, to return the instance.await User.findAll({
where: { name: { [Op.like]: 'A%' } },
order: [['id', 'ASC']],
limit: 20,
offset: 40,
});[!NOTE] No JSON operator in the WHERE builder DataTypes.JSON()maps toJSON, but this dialect's WHERE builder refusesOp.containson it: Cannot render WHERE condition at meta.contains' is not implemented by its WHERE builder. The statement was not sent, because running it without the condition would match rows it should not.
await db.transaction(async (t) => {
await User.create({ email: 'grace@example.com', name: 'Grace' }, { transaction: t });
// A nested transaction is a SAVEPOINT; its failure undoes only its own work.
await db
.transaction(async (sp) => {
await User.destroy({ where: { id: 1 }, transaction: sp });
throw new Error('keep the row');
}, { transaction: t })
.catch(() => {});
});SKIP LOCKED)lock with skipLocked inside a transaction:await db.transaction(async (t) => {
await User.findAll({ where: { name: 'Ada' }, lock: { level: 'UPDATE', skipLocked: true }, transaction: t });
});STRING → VARCHAR(255), auto-increment INTEGER → INT, BOOLEAN → TINYINT(1), DATE → DATETIME, UUID → CHAR(36), JSON → JSON. The type comparison lines the major engines up side by side.dialectOptions.cloudSql.const db = new Prorm({
dialect: 'cloudsqlmysql',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: { instanceConnectionName: 'my-project:us-central1:my-instance', ipType: 'PUBLIC' },
},
});connect(). Its stream factory goesssl, because the tunnel is already TLS. The MySQL baseconnect(), and the retries reuse the same connector. Thedisconnect() or when connecting finally fails.cloudSql.connector passes your own Connector, and the dialect never closesimport * as fs from 'fs';
const db = new Prorm({
dialect: 'cloudsqlmysql',
host: '10.0.0.5',
database: 'app',
username: 'app',
password: process.env.DB_PASSWORD,
dialectOptions: {
cloudSql: {
serverCa: fs.readFileSync('server-ca.pem', 'utf8'),
// shared / customer-managed CA, connecting by DNS name:
// serverName: '1a23b4cd5e67.1a2b345c6d27.us-central1.sql.goog', // must equal host
// clientCert, clientKey: for ssl_mode TRUSTED_CLIENT_CERTIFICATE_REQUIRED
},
},
});serverCa, ssl is { rejectUnauthorized: true, ca }, and mysql2VERIFY_CA. mysql2 can check the host name (verifyIdentity)host. So for VERIFY_IDENTITY:host set to the instance's DNS name, which a shared orserverName to the same name.serverName that differs from host, or a host that is an IP address, iscloudsql_iam_authentication. IAM is not@. For a service account, that@project.iam.gserviceaccount.com. It must be lowercase.cloudSql.iamPrincipal builds it (googleIamDatabaseUser(email, 'mysql')).cloudSql.iam | What happens |
|---|---|
'automatic' | The connector logs in inside its tunnel (authType: 'IAM'). No password is sent. Needs the connector. |
'manual' | An OAuth2 access token is sent as a cleartext password through mysql2's mysql_clear_password auth plugin (mysqlAuth), with a current token for each new connection. password is removed, because the server switches to the cleartext plugin for IAM users. The plugin refuses to send the token over a connection with no TLS and no connector stream. |
true | 'automatic' with the connector, 'manual' when direct |
cloudsqlsuperuser role has every static MySQL privilege except SUPERFILE, and it can run no DDL on the mysql system database. query()CloudSqlUnsupportedError:| Statement | Instead |
|---|---|
GRANT ... SUPER ... / GRANT ... FILE ... | Database flags for server settings; gcloud import/export for files |
GRANT ALL [PRIVILEGES] ON *.* (global ALL includes SUPER and FILE) | GRANT cloudsqlsuperuser TO <user>, or list the privileges |
SELECT ... INTO OUTFILE / INTO DUMPFILE | gcloud sql export csv INSTANCE gs://BUCKET/FILE.csv --database=DB --query=..., or the console |
LOAD DATA INFILE / LOAD XML INFILE without LOCAL | LOAD DATA LOCAL INFILE (reads the file on the client), or gcloud sql import csv |
CREATE/ALTER/DROP/TRUNCATE TABLE|VIEW|TRIGGER|PROCEDURE|FUNCTION|EVENT mysql.…, RENAME TABLE mysql.…, CREATE INDEX … ON mysql.…, DROP DATABASE mysql | CREATE USER / GRANT, gcloud sql users, or the console |
mysql.* (SELECT User FROM mysql.user) are allowed.SET GLOBAL. On MySQL 8.0, Cloud SQL grantscloudsqlsuperuser a Cloud SQL-specific privilege for changing some globalgcloud sql instances patch INSTANCE --database-flags=NAME=VALUE, or theCloudSQLMySQLDialect extends MySQLDialect (mysql) andObject.getOwnPropertyNames(CloudSQLMySQLDialect.prototype),| Area | Overridden |
|---|---|
| Connection and execution | connect, disconnect, query, resolvePoolConfig |
| Introspection | assertSupported |
capabilitiesOf() (src/transaction-support.ts),whereSupportOf() (src/query-builders/expression-render.ts),sqlObjectCapabilitiesOf() (src/sql-objects/capabilities.ts) and the| Capability | cloudsqlmysql |
|---|---|
| Transactions atomic | yes |
| Savepoints | yes |
| Isolation | up to SERIALIZABLE |
| Transaction modes | read-only yes; deferrable no; constraint deferral no |
| RETURNING | not emitted; returning is accepted and ignored |
| Upsert | INSERT … ON DUPLICATE KEY UPDATE |
| JSON column | JSON (JSONB → JSON) |
| Vector column | VECTOR(3) |
| Array column | VARCHAR(255) |
| WHERE builder | 28 operator keys plus the $json: prefix; $and/$or/$not; $exists: no; $in subquery: no; expression operands: no |
| SQL objects | family mysql: triggers yes; procedures yes; functions multiStatement; materialized views emulated (procedure); sequences no; enums inline; RLS no |
| Streaming | queryStream() present; LISTEN absent; createExtension() present |
tests/dialects/cloudsqlmysql.test.ts exercises the dialect with a mocked driver, so nothing touches a server.