Skip to content

Commit 5eeb4f6

Browse files
authored
Merge pull request #1610 from jmordica/master
Types, tests, and commands Oh my! 🌪️
2 parents 5022b54 + 1d27a27 commit 5eeb4f6

File tree

15 files changed

+241
-11
lines changed

15 files changed

+241
-11
lines changed

index.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
Connection as PromiseConnection,
33
Pool as PromisePool,
4-
PoolConnection as PromisePoolConnection
4+
PoolConnection as PromisePoolConnection,
55
} from './promise';
66

77
import * as mysql from './typings/mysql';
@@ -74,6 +74,13 @@ export interface Connection extends mysql.Connection {
7474
promise(promiseImpl?: PromiseConstructor): PromiseConnection;
7575
unprepare(sql: string): mysql.PrepareStatementInfo;
7676
prepare(sql: string, callback?: (err: mysql.QueryError | null, statement: mysql.PrepareStatementInfo) => any): mysql.Prepare;
77+
serverHandshake(args: any): any;
78+
writeOk(args?: mysql.OkPacketParams): void;
79+
writeError(args?: mysql.ErrorPacketParams): void;
80+
writeEof(warnings?: number, statusFlags?: number): void;
81+
writeTextResult(rows?: Array<any>, columns?: Array<any>): void;
82+
writePacket(packet: any): void;
83+
sequenceId: number;
7784
}
7885

7986
export interface PoolConnection extends mysql.PoolConnection, Connection {
@@ -153,6 +160,8 @@ export interface Pool extends mysql.Connection {
153160
promise(promiseImpl?: PromiseConstructor): PromisePool;
154161
unprepare(sql: string): mysql.PrepareStatementInfo;
155162
prepare(sql: string, callback?: (err: mysql.QueryError | null, statement: mysql.PrepareStatementInfo) => any): mysql.Prepare;
163+
164+
config: mysql.PoolOptions;
156165
}
157166

158167
type authPlugins = (pluginMetadata: {

lib/commands/prepare.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ class Prepare extends Command {
5555
}
5656
const cmdPacket = new Packets.PrepareStatement(
5757
this.query,
58-
connection.config.charsetNumber
58+
connection.config.charsetNumber,
59+
this.options.values
5960
);
6061
connection.writePacket(cmdPacket.toPacket(1));
6162
return Prepare.prototype.prepareHeader;

lib/commands/server_handshake.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,41 @@ class ServerHandshake extends Command {
6969
return ServerHandshake.prototype.dispatchCommands;
7070
}
7171

72+
_isStatement(query, name) {
73+
const firstWord = query.split(' ')[0].toUpperCase();
74+
return firstWord === name;
75+
}
76+
7277
dispatchCommands(packet, connection) {
7378
// command from client to server
7479
let knownCommand = true;
7580
const encoding = connection.clientHelloReply.encoding;
7681
const commandCode = packet.readInt8();
7782
switch (commandCode) {
83+
case CommandCode.STMT_PREPARE:
84+
if (connection.listeners('stmt_prepare').length) {
85+
const query = packet.readString(undefined, encoding);
86+
connection.emit('stmt_prepare', query);
87+
} else {
88+
connection.writeError({
89+
code: Errors.HA_ERR_INTERNAL_ERROR,
90+
message:
91+
'No query handler for prepared statements.'
92+
});
93+
}
94+
break;
95+
case CommandCode.STMT_EXECUTE:
96+
if (connection.listeners('stmt_execute').length) {
97+
const { stmtId, flags, iterationCount, values } = Packets.Execute.fromPacket(packet, encoding);
98+
connection.emit('stmt_execute', stmtId, flags, iterationCount, values);
99+
} else {
100+
connection.writeError({
101+
code: Errors.HA_ERR_INTERNAL_ERROR,
102+
message:
103+
'No query handler for execute statements.'
104+
});
105+
}
106+
break;
78107
case CommandCode.QUIT:
79108
if (connection.listeners('quit').length) {
80109
connection.emit('quit');
@@ -93,7 +122,13 @@ class ServerHandshake extends Command {
93122
case CommandCode.QUERY:
94123
if (connection.listeners('query').length) {
95124
const query = packet.readString(undefined, encoding);
96-
connection.emit('query', query);
125+
if (this._isStatement(query, 'PREPARE') || this._isStatement(query, 'SET')) {
126+
connection.emit('stmt_prepare', query);
127+
}
128+
else if (this._isStatement(query, 'EXECUTE')) {
129+
connection.emit('stmt_execute', null, null, null, null, query);
130+
}
131+
else connection.emit('query', query);
97132
} else {
98133
connection.writeError({
99134
code: Errors.HA_ERR_INTERNAL_ERROR,

lib/packets/execute.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,68 @@ class Execute {
8181
this.timezone = timezone;
8282
}
8383

84+
static fromPacket(packet, encoding) {
85+
const stmtId = packet.readInt32();
86+
const flags = packet.readInt8();
87+
const iterationCount = packet.readInt32();
88+
89+
let i = packet.offset;
90+
while (i < packet.end - 1) {
91+
if((packet.buffer[i+1] === Types.VAR_STRING
92+
|| packet.buffer[i+1] === Types.NULL
93+
|| packet.buffer[i+1] === Types.DOUBLE
94+
|| packet.buffer[i+1] === Types.TINY
95+
|| packet.buffer[i+1] === Types.DATETIME
96+
|| packet.buffer[i+1] === Types.JSON) && packet.buffer[i] === 1 && packet.buffer[i+2] === 0) {
97+
break;
98+
}
99+
else {
100+
packet.readInt8()
101+
}
102+
i++;
103+
}
104+
105+
const types = [];
106+
107+
for(let i = packet.offset + 1; i < packet.end - 1; i++) {
108+
if((packet.buffer[i] === Types.VAR_STRING
109+
|| packet.buffer[i] === Types.NULL
110+
|| packet.buffer[i] === Types.DOUBLE
111+
|| packet.buffer[i] === Types.TINY
112+
|| packet.buffer[i] === Types.DATETIME
113+
|| packet.buffer[i] === Types.JSON) && packet.buffer[i + 1] === 0) {
114+
types.push(packet.buffer[i]);
115+
packet.skip(2);
116+
}
117+
}
118+
119+
packet.skip(1);
120+
121+
const values = [];
122+
for(let i = 0; i < types.length; i++) {
123+
if(types[i] === Types.VAR_STRING) {
124+
values.push(packet.readLengthCodedString(encoding))
125+
}
126+
else if(types[i] === Types.DOUBLE) {
127+
values.push(packet.readDouble())
128+
}
129+
else if(types[i] === Types.TINY) {
130+
values.push(packet.readInt8())
131+
}
132+
else if(types[i] === Types.DATETIME) {
133+
values.push(packet.readDateTime())
134+
}
135+
else if(types[i] === Types.JSON) {
136+
values.push(JSON.parse(packet.readLengthCodedString(encoding)))
137+
}
138+
if(types[i] === Types.NULL) {
139+
values.push(null)
140+
}
141+
}
142+
143+
return { stmtId, flags, iterationCount, values };
144+
}
145+
84146
toPacket() {
85147
// TODO: don't try to calculate packet length in advance, allocate some big buffer in advance (header + 256 bytes?)
86148
// and copy + reallocate if not enough

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"precommit": "lint-staged",
2020
"eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check",
2121
"wait-port": "wait-on",
22-
"type-test": "node ./node_modules/typescript/bin/tsc -p tests.json && mocha typings/test"
22+
"type-test": "node ./node_modules/typescript/bin/tsc -p tests.json && mocha typings/test --timeout 10000"
2323
},
2424
"lint-staged": {
2525
"*.js": [

promise.d.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
FieldPacket,
66
QueryOptions,
77
ConnectionOptions,
8-
PoolOptions
8+
PoolOptions,
9+
Pool as CorePool
910
} from './index';
1011

1112
import { EventEmitter } from 'events';
@@ -131,6 +132,13 @@ export interface Pool extends EventEmitter {
131132
on(event: 'release', listener: (connection: PoolConnection) => any): this;
132133
on(event: 'enqueue', listener: () => any): this;
133134
end(): Promise<void>;
135+
136+
escape(value: any): string;
137+
escapeId(value: string): string;
138+
escapeId(values: string[]): string;
139+
format(sql: string, values?: any | any[] | { [param: string]: any }): string;
140+
141+
pool: CorePool;
134142
}
135143

136144
export function createConnection(connectionUri: string): Promise<Connection>;
@@ -143,3 +151,7 @@ export interface PreparedStatementInfo {
143151
close(): Promise<void>;
144152
execute(parameters: any[]): Promise<[RowDataPacket[][] | RowDataPacket[] | OkPacket | OkPacket[] | ResultSetHeader, FieldPacket[]]>;
145153
}
154+
155+
export interface PromisePoolConnection extends Connection {
156+
destroy(): any;
157+
}

tests.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"listEmittedFiles": false
77
},
88
"files": [
9-
"typings/test/server.ts"
9+
"typings/test/server.ts",
10+
"typings/test/connection.ts",
11+
"typings/test/pool.ts"
1012
],
1113
"typeRoots": [
1214
"./typings",

typings/mysql/index.d.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Server = require('./lib/Server');
1414

1515
export function createConnection(connectionUri: string): Connection;
1616
export function createConnection(config: BaseConnection.ConnectionOptions): Connection;
17-
export function createPool(config: BasePool.PoolOptions): Pool;
17+
export function createPool(config: BasePool.PoolOptions): BasePool;
1818
export function createPoolCluster(config?: BasePoolCluster.PoolClusterOptions): PoolCluster;
1919
export function escape(value: any): string;
2020
export function escapeId(value: any): string;
@@ -24,6 +24,7 @@ export function format(sql: string, values: any, stringifyObjects?: boolean, tim
2424
export function raw(sql: string): {
2525
toSqlString: () => string
2626
};
27+
export function createServer(handler: (conn: BaseConnection) => any): Server;
2728

2829
export {
2930
ConnectionOptions,
@@ -42,6 +43,4 @@ export interface PoolConnection extends BasePoolConnection {}
4243
export interface Pool extends BasePool {}
4344
export interface PoolCluster extends BasePoolCluster {}
4445
export interface Query extends BaseQuery {}
45-
export interface Prepare extends BasePrepare {}
46-
47-
export function createServer(handler: (conn: BaseConnection) => any): Server;
46+
export interface Prepare extends BasePrepare {}

typings/mysql/lib/Connection.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ declare class Connection extends EventEmitter {
271271
on(event: string, listener: Function): this;
272272

273273
rollback(callback: (err: Query.QueryError | null) => void): void;
274+
275+
execute(sql: string, values: Array<any>, cb: (err: any, rows: Array<any>, fields: Array<any>) => any): any;
276+
277+
unprepare(sql: string): any;
278+
279+
serverHandshake(args: any): any;
274280
}
275281

276282
export = Connection;

typings/mysql/lib/Pool.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ declare class Pool extends EventEmitter {
6060

6161
on(event: string, listener: Function): this;
6262
on(event: 'connection', listener: (connection: PoolConnection) => any): this;
63+
64+
promise(promiseImpl?: any): any;
6365
}
6466

6567
export = Pool;

0 commit comments

Comments
 (0)