mirror of
https://github.com/mariadb-corporation/mariadb-connector-nodejs.git
synced 2025-08-01 16:02:59 +00:00
472 lines
12 KiB
TypeScript
472 lines
12 KiB
TypeScript
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
// Copyright (c) 2015-2025 MariaDB Corporation Ab
|
|
import {
|
|
Connection,
|
|
FieldInfo,
|
|
ConnectionConfig,
|
|
PoolConfig,
|
|
UpsertResult,
|
|
SqlError,
|
|
Types,
|
|
TypeNumbers,
|
|
importFile,
|
|
defaultOptions,
|
|
Pool,
|
|
version,
|
|
TypeCastResult,
|
|
TypeCastNextFunction,
|
|
createPoolCluster,
|
|
createPool,
|
|
createConnection,
|
|
StreamCallback
|
|
} from '..';
|
|
import { Stream } from 'node:stream';
|
|
import { createReadStream } from 'node:fs';
|
|
|
|
import { baseConfig } from '../test/conf.js';
|
|
|
|
function importSqlFile(): Promise<void> {
|
|
return importFile({
|
|
host: baseConfig.host,
|
|
user: 'test',
|
|
password: baseConfig.password,
|
|
file: '/somefile'
|
|
});
|
|
}
|
|
function createConn(option?: ConnectionConfig): Promise<Connection> {
|
|
const usr = option && option.user ? option.user : baseConfig.user;
|
|
return createConnection({
|
|
host: baseConfig.host,
|
|
user: usr,
|
|
password: baseConfig.password,
|
|
logger: {
|
|
network: (msg: string) => console.log(msg),
|
|
query: (msg: string) => console.log(msg),
|
|
error: (err: Error) => console.log(err)
|
|
},
|
|
stream: (callback: typeof StreamCallback) => {
|
|
console.log('test');
|
|
callback(undefined, undefined);
|
|
},
|
|
infileStreamFactory: (filepath: string) => createReadStream(filepath),
|
|
metaEnumerable: true
|
|
});
|
|
}
|
|
|
|
function createPoolConfig(options?: PoolConfig): PoolConfig {
|
|
return Object.assign(
|
|
{
|
|
host: baseConfig.host,
|
|
user: baseConfig.user,
|
|
password: baseConfig.password,
|
|
leakDetectionTimeout: 100
|
|
},
|
|
options
|
|
);
|
|
}
|
|
|
|
function createPoolConfigWithSSl(options?: PoolConfig): PoolConfig {
|
|
Object.assign(
|
|
{
|
|
host: baseConfig.host,
|
|
user: baseConfig.user,
|
|
password: baseConfig.password,
|
|
ssl: true
|
|
},
|
|
options
|
|
);
|
|
return Object.assign(
|
|
{
|
|
host: baseConfig.host,
|
|
user: baseConfig.user,
|
|
password: baseConfig.password,
|
|
ssl: { ca: 'fff' }
|
|
},
|
|
options
|
|
);
|
|
}
|
|
|
|
function newPool(options?: PoolConfig | string): Pool {
|
|
if (typeof options === 'string') {
|
|
return createPool(options as string);
|
|
}
|
|
createPool(createPoolConfig(options));
|
|
return createPool(createPoolConfigWithSSl(options));
|
|
}
|
|
|
|
async function testMisc(): Promise<void> {
|
|
let rows;
|
|
const defOptions = defaultOptions();
|
|
const defaultOptionsWithTz = defaultOptions({
|
|
timezone: '+00:00',
|
|
debugLen: 1024,
|
|
logParam: true,
|
|
queryTimeout: 2000
|
|
});
|
|
console.log(defOptions);
|
|
console.log(defaultOptionsWithTz);
|
|
const connection = await createConn();
|
|
|
|
rows = await connection.query('INSERT INTO myTable VALUE (1)');
|
|
console.log(rows.insertId === 1);
|
|
console.log(rows.affectedRows === 1);
|
|
|
|
const res2 = await connection.query<UpsertResult>('INSERT INTO myTable VALUE (1)');
|
|
console.log(res2.insertId === 1);
|
|
|
|
rows = await connection.query('SELECT 1 + 1 AS solution');
|
|
console.log(rows[0].solution === 2);
|
|
|
|
rows = await connection.query('SELECT ? as t', 1);
|
|
console.log(rows[0].t === 1);
|
|
|
|
rows = await connection.query('SELECT ? as t', [1]);
|
|
console.log(rows[0].t === 1);
|
|
|
|
await connection.importFile({ file: '/path' });
|
|
await connection.importFile({ file: '/path', database: 'somedb' });
|
|
|
|
rows = await connection.query(
|
|
{
|
|
namedPlaceholders: true,
|
|
sql: 'SELECT :val as t',
|
|
infileStreamFactory: (filepath: string) => createReadStream(filepath)
|
|
},
|
|
{ val: 2 }
|
|
);
|
|
console.log(rows[0].t === 2);
|
|
|
|
const prepare = await connection.prepare('INSERT INTO myTable VALUES (?)');
|
|
console.log(prepare.id);
|
|
|
|
const insRes = await prepare.execute<Promise<UpsertResult>>([1]);
|
|
console.log(insRes.insertId === 2);
|
|
console.log(insRes.affectedRows === 2);
|
|
|
|
let currRow = 0;
|
|
const stream = prepare.executeStream([1]);
|
|
for await (const row of stream) {
|
|
console.log(row);
|
|
currRow++;
|
|
}
|
|
prepare.close();
|
|
|
|
rows = await connection.execute('INSERT INTO myTable VALUE (1)');
|
|
console.log(rows.insertId === 1);
|
|
console.log(rows.affectedRows === 1);
|
|
|
|
rows = await connection.execute('SELECT 1 + 1 AS solution');
|
|
console.log(rows[0].solution === 2);
|
|
|
|
rows = await connection.execute('SELECT ? as t', 1);
|
|
console.log(rows[0].t === 1);
|
|
|
|
rows = await connection.execute('SELECT ? as t', [1]);
|
|
console.log(rows[0].t === 1);
|
|
|
|
rows = await connection.execute({ sql: 'SELECT ? as t', timeout: 1000 }, [1]);
|
|
console.log(rows[0].t === 1);
|
|
|
|
rows = await connection.execute(
|
|
{
|
|
namedPlaceholders: true,
|
|
sql: 'SELECT :val as t'
|
|
},
|
|
{ val: 2 }
|
|
);
|
|
console.log(rows[0].t === 2);
|
|
|
|
try {
|
|
rows = await connection.query({ sql: 'SELECT 1', nestTables: '_' });
|
|
throw new Error('Should have thrown error!' + rows);
|
|
} catch (err) {
|
|
// Received expected error
|
|
console.log(err);
|
|
}
|
|
|
|
try {
|
|
rows = await connection.query('Wrong SQL');
|
|
throw new Error('must have throw error!' + rows);
|
|
} catch (err) {
|
|
const error = err as SqlError;
|
|
console.log(error.message != null);
|
|
console.log(error.errno === 12);
|
|
console.log(error.sqlState === '');
|
|
console.log(error.fatal === true);
|
|
}
|
|
|
|
let metaReceived = false;
|
|
currRow = 0;
|
|
connection
|
|
.queryStream('SELECT * from mysql.user')
|
|
.on('error', (err: Error) => {
|
|
throw err;
|
|
})
|
|
.on('fields', (meta: FieldInfo[]) => {
|
|
console.log(meta);
|
|
metaReceived = true;
|
|
})
|
|
.on('data', (row: unknown[]) => {
|
|
console.log(row.length > 1);
|
|
currRow++;
|
|
})
|
|
.on('end', () => {
|
|
console.log(currRow + ' ' + metaReceived);
|
|
})
|
|
.once('end', () => {
|
|
console.log('t');
|
|
})
|
|
.once('release', () => {
|
|
console.log('t2');
|
|
})
|
|
.addListener('error', () => {
|
|
console.log('t2');
|
|
});
|
|
connection.listeners('end')[0]();
|
|
connection.listeners('error')[0](new SqlError('ddd'));
|
|
|
|
await connection.ping();
|
|
|
|
const writable = new Stream.Writable({
|
|
objectMode: true,
|
|
write(this: any, _chunk: any, _encoding: string, callback: (error?: Error | null) => void): void {
|
|
callback(null);
|
|
}
|
|
});
|
|
connection.queryStream('SELECT * FROM mysql.user').pipe(writable);
|
|
|
|
await connection.beginTransaction();
|
|
|
|
await connection.rollback();
|
|
|
|
await connection.end();
|
|
await connection.close();
|
|
|
|
connection.destroy();
|
|
connection.escape('test');
|
|
connection.escape(true);
|
|
connection.escape(5);
|
|
connection.escapeId('myColumn');
|
|
const res = (await connection.batch('INSERT INTO myTable VALUE (?,?)', [
|
|
[1, 2],
|
|
[4, 3]
|
|
])) as UpsertResult;
|
|
console.log(res.affectedRows);
|
|
|
|
const resb = await connection.batch<UpsertResult>('INSERT INTO myTable VALUE (?,?)', [
|
|
[1, 2],
|
|
[4, 3]
|
|
]);
|
|
console.log(resb.affectedRows);
|
|
|
|
await createConnection({ multipleStatements: true });
|
|
await createConnection({ bigNumberStrings: true, supportBigNumbers: true });
|
|
await createConnection({ decimalAsNumber: true, bigIntAsNumber: true, checkNumberRange: true });
|
|
|
|
await createConnection({ debug: true });
|
|
await createConnection({ dateStrings: true });
|
|
}
|
|
|
|
async function testChangeUser(): Promise<void> {
|
|
const connection = await createConnection({});
|
|
try {
|
|
await connection.changeUser({ user: 'this is a bogus user name' });
|
|
} catch (err) {
|
|
const error = err as Error;
|
|
console.log('Correctly threw an error when changing user' + error.message);
|
|
}
|
|
}
|
|
|
|
async function testTypeCast(): Promise<void> {
|
|
const changeCaseCast = (column: FieldInfo, next: TypeCastNextFunction): TypeCastResult => {
|
|
const name = column.name();
|
|
|
|
if (name.startsWith('upp')) {
|
|
const v = column.string();
|
|
return v == null ? null : v.toUpperCase();
|
|
}
|
|
|
|
if (column.type === Types.BIGINT) {
|
|
return column.string();
|
|
}
|
|
if (column.columnType === TypeNumbers.BIGINT) {
|
|
return column.string();
|
|
}
|
|
|
|
return next();
|
|
};
|
|
|
|
const connection = await createConnection({ typeCast: changeCaseCast });
|
|
|
|
const rows = await connection.query(`SELECT 'upper' as upper, 'lower' as lower`);
|
|
|
|
if (rows[0].upper !== 'UPPER') {
|
|
throw new Error('typeCast did not convert to upper case');
|
|
}
|
|
|
|
if (rows[0].lower !== 'lower') {
|
|
throw new Error('typeCast did not ignore lower');
|
|
}
|
|
}
|
|
|
|
async function testRowsAsArray(): Promise<void> {
|
|
const connection = await createConnection({ rowsAsArray: true });
|
|
|
|
const rows = await connection.query<[string, string][]>(`SELECT 'upper' as upper, 'lower' as lower`);
|
|
|
|
if (rows[0][0] !== 'upper') {
|
|
throw new Error('wrong value');
|
|
}
|
|
|
|
const rows2 = await connection.query<[string, string][]>({
|
|
sql: `SELECT 'upper' as upper, 'lower' as lower`,
|
|
rowsAsArray: true
|
|
});
|
|
|
|
if (rows2[0][0] !== 'upper') {
|
|
throw new Error('wrong value');
|
|
}
|
|
}
|
|
|
|
async function metaAsArray(): Promise<void> {
|
|
const connection = await createConnection({ metaAsArray: true });
|
|
|
|
const res = await connection.query<[[string, string][], FieldInfo[]]>(`SELECT 'upper' as upper, 'lower' as lower`);
|
|
|
|
if (res[1].length > 0) {
|
|
throw new Error('expected meta');
|
|
}
|
|
|
|
const res2 = await connection.query<[[string, string][], FieldInfo[]]>({
|
|
sql: `SELECT 'upper' as upper, 'lower' as lower`,
|
|
metaAsArray: true
|
|
});
|
|
if (res2[1].length > 0) {
|
|
throw new Error('expected meta');
|
|
}
|
|
}
|
|
|
|
async function testPool(): Promise<void> {
|
|
let pool;
|
|
|
|
pool = newPool({
|
|
connectionLimit: 10
|
|
});
|
|
await pool.importFile({ file: '/path' });
|
|
await pool.importFile({ file: '/path', database: 'somedb' });
|
|
console.log(pool.closed);
|
|
pool.taskQueueSize();
|
|
function displayConn(conn: Connection): void {
|
|
console.log(conn);
|
|
}
|
|
pool.on('acquire', displayConn).on('acquire', displayConn);
|
|
pool.on('connection', displayConn).on('connection', displayConn);
|
|
pool
|
|
.on('enqueue', () => {
|
|
console.log('enqueue');
|
|
})
|
|
.on('enqueue', () => {
|
|
console.log('enqueue');
|
|
});
|
|
pool.on('release', displayConn).on('release', displayConn);
|
|
|
|
const rows = await pool.query('SELECT 1 + 1 AS solution');
|
|
console.log(rows[0].solution === 2);
|
|
|
|
pool = newPool('mariadb://root:pwd@localhost:3306/db?connectionLimit=10');
|
|
await pool.end();
|
|
pool = newPool({});
|
|
|
|
const connection = await pool.getConnection();
|
|
pool.escape('test');
|
|
pool.escape(true);
|
|
pool.escape(5);
|
|
pool.escapeId('myColumn');
|
|
const res = (await pool.batch('INSERT INTO myTable VALUE (?,?)', [
|
|
[1, 2],
|
|
[4, 3]
|
|
])) as UpsertResult;
|
|
console.log(res.affectedRows);
|
|
console.log(connection.threadId != null);
|
|
|
|
await connection.execute('SELECT 1 + 1 AS solution');
|
|
await connection.execute('SELECT 1 + ? AS solution', [1]);
|
|
await connection.release();
|
|
}
|
|
|
|
async function testPoolCluster(): Promise<void> {
|
|
let connection;
|
|
const poolCluster = createPoolCluster();
|
|
const poolConfig = createPoolConfig({
|
|
connectionLimit: 10
|
|
});
|
|
|
|
poolCluster.add('MASTER', poolConfig);
|
|
poolCluster.add('SLAVE1', poolConfig);
|
|
poolCluster.add('SLAVE2', poolConfig);
|
|
|
|
const sub = poolCluster.of('test');
|
|
sub.query('SELECT 1');
|
|
|
|
// Target Group : ALL(anonymous, MASTER, SLAVE1-2), Selector : round-robin(default)
|
|
connection = await poolCluster.getConnection();
|
|
console.log(connection.threadId != null);
|
|
console.log(5);
|
|
|
|
connection = await poolCluster.getConnection('MASTER');
|
|
console.log(connection.threadId != null);
|
|
|
|
connection = await poolCluster.getConnection('MASTER', 'RR');
|
|
console.log(connection.threadId != null);
|
|
|
|
// of namespace : of(pattern, selector)
|
|
connection = await poolCluster.of('.*').getConnection();
|
|
console.log(connection.threadId != null);
|
|
|
|
connection = await poolCluster.of(null, 'RR').getConnection();
|
|
console.log(connection.threadId != null);
|
|
|
|
const filtered = poolCluster.of('SLAVE.*', 'RANDOM');
|
|
const res = (await filtered.batch('INSERT INTO myTable VALUE (?,?)', [
|
|
[1, 2],
|
|
[4, 3]
|
|
])) as UpsertResult;
|
|
console.log(res.affectedRows);
|
|
|
|
await filtered.query('SELECT 1 + 1 AS solution');
|
|
await filtered.execute('SELECT 1 + 1 AS solution');
|
|
await connection.release();
|
|
createPoolCluster({
|
|
canRetry: true,
|
|
removeNodeErrorCount: 3,
|
|
restoreNodeTimeout: 1000,
|
|
defaultSelector: 'RR'
|
|
});
|
|
|
|
await poolCluster.end();
|
|
}
|
|
|
|
async function runTests(): Promise<void> {
|
|
try {
|
|
await importSqlFile();
|
|
await testMisc();
|
|
await testChangeUser();
|
|
await testTypeCast();
|
|
await testPool();
|
|
await testPoolCluster();
|
|
await testRowsAsArray();
|
|
await metaAsArray();
|
|
|
|
console.log('done');
|
|
} catch (err) {
|
|
console.log('Unexpected error');
|
|
console.log(err);
|
|
} finally {
|
|
process.exit();
|
|
}
|
|
}
|
|
|
|
runTests();
|
|
|
|
console.log(version);
|