diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9ddf2c3ccf..cd5f50be15 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" schedule: interval: "daily" + - package-ecosystem: "npm" + directory: "/website" + schedule: + interval: "weekly" diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 8dc9e9b142..084ac27828 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v2 # - name: Set up MySQL - # run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_ROOT_PASSWORD=${{ env.MYSQL_PASSWORD }} -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/examples/custom-conf:/etc/mysql/conf.d -v $PWD/examples/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} + # run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_ROOT_PASSWORD=${{ env.MYSQL_PASSWORD }} -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/test/fixtures/custom-conf:/etc/mysql/conf.d -v $PWD/test/fixtures/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: diff --git a/.github/workflows/ci-bun.yml b/.github/workflows/ci-bun.yml index b1d3c8cf34..d2d4c804bb 100644 --- a/.github/workflows/ci-bun.yml +++ b/.github/workflows/ci-bun.yml @@ -39,7 +39,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up MySQL - run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/examples/custom-conf:/etc/mysql/conf.d -v $PWD/examples/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} + run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/test/fixtures/custom-conf:/etc/mysql/conf.d -v $PWD/test/fixtures/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} - name: Set up Bun ${{ matrix.bun-version }} uses: oven-sh/setup-bun@v1 diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 61d0fe1216..3ef19aa64d 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -91,7 +91,7 @@ jobs: - name: Set up MySQL if: ${{ matrix.mysql-version }} - run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/examples/custom-conf:/etc/mysql/conf.d -v $PWD/examples/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} + run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/test/fixtures/custom-conf:/etc/mysql/conf.d -v $PWD/test/fixtures/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} - name: Set up Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 diff --git a/README.md b/README.md index 0a1e62f052..558032481a 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ [![Windows Build][appveyor-image]][appveyor-url] [![License][license-image]][license-url] -English | [简体中文](./documentation/zh-cn/) | [Português (BR)](./documentation/pt-br/) +English | [简体中文](https://sidorares.github.io/node-mysql2/zh-CN/docs) | [Português (BR)](https://sidorares.github.io/node-mysql2/pt-BR/docs) -> MySQL client for Node.js with focus on performance. Supports prepared statements, non-utf8 encodings, binary log protocol, compression, ssl [much more](./documentation/en). +> MySQL client for Node.js with focus on performance. Supports prepared statements, non-utf8 encodings, binary log protocol, compression, ssl [much more](https://sidorares.github.io/node-mysql2/docs). __Table of contents__ @@ -35,14 +35,14 @@ MySQL2 project is a continuation of [MySQL-Native][mysql-native]. Protocol parse MySQL2 is mostly API compatible with [mysqljs][node-mysql] and supports majority of features. MySQL2 also offers these additional features: - Faster / Better Performance - - [Prepared Statements](./documentation/en/Prepared-Statements.md) + - [Prepared Statements](https://sidorares.github.io/node-mysql2/docs/documentation/prepared-statements) - MySQL Binary Log Protocol - - [MySQL Server](./documentation/en/MySQL-Server.md) + - [MySQL Server](https://sidorares.github.io/node-mysql2/docs/documentation/mysql-server) - Extended support for Encoding and Collation - - [Promise Wrapper](./documentation/en/Promise-Wrapper.md) + - [Promise Wrapper](https://sidorares.github.io/node-mysql2/docs/documentation/promise-wrapper) - Compression - - SSL and [Authentication Switch](./documentation/en/Authentication-Switch.md) - - [Custom Streams](./documentation/en/Extras.md) + - SSL and [Authentication Switch](https://sidorares.github.io/node-mysql2/docs/documentation/authentication-switch) + - [Custom Streams](https://sidorares.github.io/node-mysql2/docs/documentation/extras) - [Pooling](#using-connection-pools) ## Installation @@ -59,7 +59,7 @@ If you are using TypeScript, you will need to install `@types/node`. npm install --save-dev @types/node ``` -> For TypeScript documentation and examples, see [here](./documentation/en/TypeScript-Examples.md). +> For TypeScript documentation and examples, see [here](https://sidorares.github.io/node-mysql2/docs/documentation/typescript-examples). ## First Query ```js @@ -262,7 +262,7 @@ If you find any other incompatibility with [Node MySQL][node-mysql], Please repo ## Documentation -You can find more detailed documentation [here](./documentation/en). You should also check various code [examples](./examples) to understand advanced concepts. +You can find more detailed documentation [here](https://sidorares.github.io/node-mysql2/docs/documentation). You should also check various code [examples](https://sidorares.github.io/node-mysql2/docs/examples) to understand advanced concepts. ## Acknowledgements diff --git a/documentation/en/Authentication-Switch.md b/documentation/en/Authentication-Switch.md deleted file mode 100644 index 9a6e270876..0000000000 --- a/documentation/en/Authentication-Switch.md +++ /dev/null @@ -1,109 +0,0 @@ -# Authentication switch request - -During the connection phase the server may ask the client to switch to a different auth method. -If the `authPlugins` connection config option is set, it must be an object where each key -is the name of a potential authentication plugin requested by the server, and the corresponding -value must be a function that optionally receives the connection config options and returns -another function, which in turn, optionally receives the switch request data. - -The plugin is loaded with a `({user,password,...})` signature, and each call has a `(pluginData)` -signature. Each call should make the plugin return any additional authentication data (`Buffer`) -that should be sent back to the server, either synchronously or asynchronously using a `Promise`, -or should yield an error accordingly. - -Example: (imaginary `ssh-key-auth` plugin) pseudo code - -```js -const conn = mysql.createConnection({ - user: 'test_user', - password: 'test', - database: 'test_database', - authPlugins: { - 'ssh-key-auth': function ({password}) { - return function (pluginData) { - return getPrivate(key) - .then(key => { - const response = encrypt(key, password, pluginData); - // continue handshake by sending response data - return response; - }) - .catch(err => { - // throw error to propagate error to connect/changeUser handlers - }); - }; - } - } -}); -``` - -There is also a deprecated API where if a `authSwitchHandler` connection config option is set -it must be a function that receives switch request data and responds via a callback. In this case, -the first invocation always has a `({pluginName, pluginData})` signature, following calls - `({pluginData})`. -The client replies with an opaque blob matching the requested plugin via `callback(null, data: Buffer)`. - -```js -const conn = mysql.createConnection({ - user: 'test_user', - password: 'test', - database: 'test_database', - authSwitchHandler: function ({pluginName, pluginData}, cb) { - if (pluginName === 'ssh-key-auth') { - getPrivateKey(key => { - const response = encrypt(key, pluginData); - // continue handshake by sending response data - // respond with error to propagate error to connect/changeUser handlers - cb(null, response); - }); - } else { - const err = new Error(`Unknown AuthSwitchRequest plugin name ${pluginName}`); - err.fatal = true; - cb(err); - } - } -}); -``` - -The initial handshake is always performed using `mysql_native_password` plugin. This will be possible to override in future versions. - -Note that if the `mysql_native_password` method is requested it will be handled internally according -to [Authentication::Native41]( https://dev.mysql.com/doc/internals/en/secure-password-authentication.html#packet-Authentication::Native41) -and no `authPlugins` function or the `authSwitchHandler` will be invoked. - -These MAY be called multiple times if the plugin algorithm requires multiple roundtrips of data -exchange between client and server. - -## Multi-factor authentication - -If the user requires multi-factor authentication in the server, the client will receive a `AuthNextFactor` -request, which is similar in structure to the regular authentication switch request and contains the name -and possible initial data for the additional authentication factor plugin (up to 3). Additional passwords -can be provided using the connection config options - `password2` and `password3`. Again, for each -authentication factor, multiple roundtrips of data exchange can be required by the plugin algoritm. - -```js -const conn = mysql.createConnection({ - user: 'test_user', - password: 'secret1', - password2: 'secret2', - password3: 'secret3', - database: 'test_database', - authPlugins: { - // password1 === password - 'auth-plugin1': function ({password1}) { - return function (serverPluginData) { - return clientPluginData(password1, serverPluginData); - }; - }, - 'auth-plugin2': function ({password2}) { - return function (serverPluginData) { - return clientPluginData(password2, serverPluginData); - }; - }, - 'auth-plugin3': function ({password3}) { - return function (serverPluginData) { - return clientPluginData(password3, serverPluginData); - }; - } - } -}); -``` diff --git a/documentation/en/Examples.md b/documentation/en/Examples.md deleted file mode 100644 index 8a1b138975..0000000000 --- a/documentation/en/Examples.md +++ /dev/null @@ -1,115 +0,0 @@ -# Examples - -## Simple SELECT - -```js -const mysql = require('mysql2'); -const connection = mysql.createConnection({user: 'test', database: 'test'}); - -connection.query('SELECT 1+1 as test1', (err, rows) => { - // -}); -``` - -## Prepared statement and parameters - -```js -const mysql = require('mysql2'); -const connection = mysql.createConnection({user: 'test', database: 'test'}); - -connection.execute('SELECT 1+? as test1', [10], (err, rows) => { - // -}); -``` - -## Connecting over encrypted connection - -```js -const fs = require('fs'); -const mysql = require('mysql2'); -const connection = mysql.createConnection({ - user: 'test', - database: 'test', - ssl: { - key: fs.readFileSync('./certs/client-key.pem'), - cert: fs.readFileSync('./certs/client-cert.pem') - } -}); -connection.query('SELECT 1+1 as test1', console.log); -``` - -You can use 'Amazon RDS' string as value to ssl property to connect to Amazon RDS mysql over ssl (in that case http://s3.amazonaws.com/rds-downloads/mysql-ssl-ca-cert.pem CA cert is used) - -```js -const mysql = require('mysql2'); -const connection = mysql.createConnection({ - user: 'foo', - password: 'bar', - host: 'db.id.ap-southeast-2.rds.amazonaws.com', - ssl: 'Amazon RDS' -}); - -connection.query('show status like \'Ssl_cipher\'', (err, res) => { - console.log(err, res); - connection.end(); -}); -``` - - -## Simple MySQL proxy server - -```js -const mysql = require('mysql2'); - -const server = mysql.createServer(); -server.listen(3307); -server.on('connection', conn => { - console.log('connection'); - - conn.serverHandshake({ - protocolVersion: 10, - serverVersion: 'node.js rocks', - connectionId: 1234, - statusFlags: 2, - characterSet: 8, - capabilityFlags: 0xffffff - }); - - conn.on('field_list', (table, fields) => { - console.log('field list:', table, fields); - conn.writeEof(); - }); - - const remote = mysql.createConnection({user: 'root', database: 'dbname', host:'server.example.com', password: 'secret'}); - - conn.on('query', sql => { - console.log(`proxying query: ${sql}`); - remote.query(sql, function (err) { - // overloaded args, either (err, result :object) - // or (err, rows :array, columns :array) - if (Array.isArray(arguments[1])) { - // response to a 'select', 'show' or similar - const rows = arguments[1], columns = arguments[2]; - console.log('rows', rows); - console.log('columns', columns); - conn.writeTextResult(rows, columns); - } else { - // response to an 'insert', 'update' or 'delete' - const result = arguments[1]; - console.log('result', result); - conn.writeOk(result); - } - }); - }); - - conn.on('end', remote.end.bind(remote)); -}); -``` - -## Examples using MySQL server API - - - [MySQL-pg-proxy](https://github.com/sidorares/mysql-pg-proxy) - MySQL to Postgres proxy server. - - [MySQLite.js](https://github.com/sidorares/mysqlite.js) - MySQL server with JS-only (emscripten compiled) sqlite backend. - - [SQL-engine](https://github.com/eugeneware/sql-engine) - MySQL server with LevelDB backend. - - [MySQL-osquery-proxy](https://github.com/sidorares/mysql-osquery-proxy) - Connect to [facebook osquery](https://osquery.io/) using MySQL client - - [PlyQL](https://github.com/implydata/plyql) - Connect to [Druid](http://druid.io/) using MySQL client diff --git a/documentation/en/Extras.md b/documentation/en/Extras.md deleted file mode 100644 index dbe9ede205..0000000000 --- a/documentation/en/Extras.md +++ /dev/null @@ -1,126 +0,0 @@ -# Extra Features - -## Named placeholders - -You can use named placeholders for parameters by setting `namedPlaceholders` config value or query/execute time option. Named placeholders are converted to unnamed `?` on the client (mysql protocol does not support named parameters). If you reference parameter multiple times under the same name it is sent to server multiple times. Unnamed placeholders can still be used by providing the values as an array instead of an object. - -```js -connection.config.namedPlaceholders = true; -connection.execute('select :x + :y as z', { x: 1, y: 2 }, (err, rows) => { - // statement prepared as "select ? + ? as z" and executed with [1,2] values - // rows returned: [ { z: 3 } ] -}); - -connection.execute('select :x + :x as z', { x: 1 }, (err, rows) => { - // select ? + ? as z, execute with [1, 1] -}); - -connection.query('select :x + :x as z', { x: 1 }, (err, rows) => { - // query select 1 + 1 as z -}); - -// unnamed placeholders are still valid if the values are provided in an array -connection.query('select ? + ? as z', [1, 1], (err, rows) => { - // query select 1 + 1 as z -}); -``` - - -## Receiving rows as array of columns instead of hash with column name as key: - -```js -const options = { sql: 'select A,B,C,D from foo', rowsAsArray: true }; -connection.query(options, (err, results) => { - /* results will be an array of arrays like this now: - [[ - 'field A value', - 'field B value', - 'field C value', - 'field D value', - ], ...] - */ -}); -``` - -## Sending tabular data with 'load infile' and local stream: - -In addition to sending local fs files you can send any stream using `infileStreamFactory` query option. If set, it has to be a function that return a readable stream. It gets file path from query as a parameter. - -Note: starting from version 2.0 `infileStreamFactory` is required parameter for `LOAD DATA LOCAL INFILE`. Response from server indicates that it wants access to a local file and no `infileStreamFactory` option is provided the query ends with error. - -```js -// local file -connection.query( - 'LOAD DATA LOCAL INFILE "/tmp/data.csv" INTO TABLE test FIELDS TERMINATED BY ? (id, title)', - onInserted1 -); -// local stream -const sql = - 'LOAD DATA LOCAL INFILE "mystream" INTO TABLE test FIELDS TERMINATED BY ? (id, title)'; -connection.query( - { - sql: sql, - infileStreamFactory: function(path) { - return getStream(); - } - }, - onInserted2 -); -``` - -The `infileStreamFactory` option may also be set at a connection-level: - -```js -const fs = require("fs"); -const mysql = require('mysql2'); - -const connection = mysql.createConnection({ - user: 'test', - database: 'test', - infileStreamFactory: path => { - // Validate file path - const validPaths = ['/tmp/data.csv']; - if (!validPaths.includes(path)) { - throw new Error(`invalid file path: ${path}: expected to be one of ${validPaths.join(',')}`); - } - return fs.createReadStream(path); - } -}); - -connection.query('LOAD DATA LOCAL INFILE "/tmp/data.csv" INTO TABLE test', onInserted); -``` - -## Connecting using custom stream: - -```js -const net = require('net'); -const mysql = require('mysql2'); -const shape = require('shaper'); -const connection = mysql.createConnection({ - user: 'test', - database: 'test', - stream: net.connect('/tmp/mysql.sock').pipe(shape(10)) // emulate 10 bytes/sec link -}); -connection.query('SELECT 1+1 as test1', console.log); -``` - -`stream` also can be a function. In that case function result has to be duplex stream, and it is used for connection transport. This is required if you connect pool using custom transport as new pooled connection needs new stream. [Example](https://github.com/sidorares/node-mysql2/issues/80) connecting over socks5 proxy: - -```js -const mysql = require('mysql2'); -const SocksConnection = require('socksjs'); -const pool = mysql.createPool({ - database: 'test', - user: 'foo', - password: 'bar', - stream: function(cb) { - const newStream = new SocksConnection( - { host: 'remote.host', port: 3306 }, - { host: 'localhost', port: 1080 } - ); - cb(null, newStream); - } -}); -``` - -In addition to password `createConnection()`, `createPool()` and `changeUser()` accept `passwordSha1` option. This is useful when implementing proxies as plaintext password might be not available. diff --git a/documentation/en/MySQL-Server.md b/documentation/en/MySQL-Server.md deleted file mode 100644 index 3406050b6a..0000000000 --- a/documentation/en/MySQL-Server.md +++ /dev/null @@ -1,36 +0,0 @@ -# MySQL Server API - -## Server - - * **createServer()** - creates server instance - * **Server.listen** - listen port / unix socket (same arguments as [net.Server.listen](http://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback)) - -#### Events - - * **connect** - new incoming connection. - -## Connection - - * **serverHandshake({serverVersion, protocolVersion, connectionId, statusFlags, characterSet, capabilityFlags})** - send server handshake initialisation packet, wait handshake response and start listening for commands - * **writeOk({affectedRows: num, insertId: num})** - send [OK packet](http://dev.mysql.com/doc/internals/en/overview.html#packet-OK_Packet) to client - * **writeEof(warnings, statusFlags)** - send EOF packet - * **writeTextResult(rows, fields)** - write query result to client. Rows and fields are in the same format as in `connection.query` callback. - * **writeColumns(fields)** - write fields + EOF packets. - * **writeTextRow(row)** - write array (not hash!) of values as result row - * TODO: binary protocol - -#### Events - -Every command packet received by the server will be emitted as a **packet** event with the parameters: - - * packet: Packet - The packet itself - * knownCommand: boolean - is this command known to the server - * commandCode: number - the parsed command code (first byte) - -In addition special events are emitted for [commands](https://dev.mysql.com/doc/internals/en/text-protocol.html) received from the client. If no listener is present a fallback behavior will be invoked. - - * **quit**() - Default: close the connection - * **init_db**(schemaName: string) - Default: return OK - * **query**(sql: string) - Please attach a listener to this. Default: return HA_ERR_INTERNAL_ERROR - * **field_list**(table: string, fields: string) - Default: return ER_WARN_DEPRECATED_SYNTAX - * **ping**() - Default: return OK diff --git a/documentation/en/Prepared-Statements.md b/documentation/en/Prepared-Statements.md deleted file mode 100644 index f500704842..0000000000 --- a/documentation/en/Prepared-Statements.md +++ /dev/null @@ -1,57 +0,0 @@ -# Prepared statements - -## Automatic creation, cached and re-used by connection - -Similar to `connection.query()`. - -```js -connection.execute('select 1 + ? + ? as result', [5, 6], (err, rows) => { - // rows: [ { result: 12 } ] - // internally 'select 1 + ? + ? as result' is prepared first. On subsequent calls cached statement is re-used -}); - -// close cached statement for 'select 1 + ? + ? as result'. noop if not in cache -connection.unprepare('select 1 + ? + ? as result'); -``` -Note that `connection.execute()` will cache the prepared statement for better performance, remove the cache with `connection.unprepare()` when you're done. - -## Manual prepare / execute - -Manually prepared statements doesn't comes with LRU cache and SHOULD be closed using `statement.close()` instead of `connection.unprepare()`. - -```js -connection.prepare('select ? + ? as tests', (err, statement) => { - // statement.parameters - array of column definitions, length === number of params, here 2 - // statement.columns - array of result column definitions. Can be empty if result schema is dynamic / not known - // statement.id - // statement.query - - statement.execute([1, 2], (err, rows, columns) => { - // -> [ { tests: 3 } ] - }); - - // don't use connection.unprepare(), it won't work! - // note that there is no callback here. There is no statement close ack at protocol level. - statement.close(); -}); -``` -Note that you should not use statement after connection reset (`changeUser()` or disconnect). Statement scope is connection, you need to prepare statement for each new connection in order to use it. - -# Configuration - -`maxPreparedStatements` : We keep the cached statements in a [lru-cache](https://github.com/isaacs/node-lru-cache). Default size is `16000` but you can use this option to override it. Any statements that are dropped from cache will be `closed`. - -# Serialization of bind parameters - -The bind parameter values passed to `execute` are serialized JS -> MySQL as: - -* `null` -> `NULL` -* `number` -> `DOUBLE` -* `boolean` -> `TINY` (0 for false, 1 for true) -* `object` -> depending on prototype: - * `Date` -> `DATETIME` - * `JSON` like object - `JSON` - * `Buffer` -> `VAR_STRING` -* Other -> `VAR_STRING` - -Passing in `undefined` or a `function` will result in an error. \ No newline at end of file diff --git a/documentation/en/Promise-Wrapper.md b/documentation/en/Promise-Wrapper.md deleted file mode 100644 index 646f7d5b22..0000000000 --- a/documentation/en/Promise-Wrapper.md +++ /dev/null @@ -1,60 +0,0 @@ -# Promise wrappers - -In addition to errback interface there is thin wrapper to expose Promise-based api - -## Basic Promise - -```js -/* eslint-env es6 */ -const mysql = require('mysql2/promise'); // or require('mysql2').createConnectionPromise -mysql.createConnection({ /* same parameters as for non-promise createConnection */ }) - .then(conn => conn.query('select foo from bar')) - .then(([rows, fields]) => console.log(rows[0].foo)); -``` - -```js -const pool = require('mysql2/promise').createPool({}); // or require('mysql2').createPoolPromise({}) or require('mysql2').createPool({}).promise() -pool.getConnection() - .then(conn => { - const res = conn.query('select foo from bar'); - conn.release(); - return res; - }).then(result => { - console.log(result[0][0].foo); - }).catch(err => { - console.log(err); // any of connection time or query time errors from above - }); -``` -## ES7 Async Await -```js -async function example1 () { - const mysql = require('mysql2/promise'); - const conn = await mysql.createConnection({ database: test }); - const [rows, fields] = await conn.execute('select ?+? as sum', [2, 2]); - await conn.end(); -} - -async function example2 () { - const mysql = require('mysql2/promise'); - const pool = mysql.createPool({database: test}); - // execute in parallel, next console.log in 3 seconds - await Promise.all([pool.query('select sleep(2)'), pool.query('select sleep(3)')]); - console.log('3 seconds after'); - await pool.end(); -} -``` - -## With [CO](https://github.com/tj/co) - -```js -const mysql = require('mysql2'); -const co = require('co'); -co(function * () { - const c = yield mysql.createConnectionPromise({user: 'root', namedPlaceholders: true }); - const rows = yield c.query('show databases'); - console.log(rows); - console.log(yield c.execute('select 1+:toAdd as qqq', {toAdd: 10})); - yield c.end(); -}); -``` -Examples in [/examples/promise-co-await](../../examples/promise-co-await) diff --git a/documentation/en/Readme.md b/documentation/en/Readme.md deleted file mode 100644 index 1f8fd848ca..0000000000 --- a/documentation/en/Readme.md +++ /dev/null @@ -1,51 +0,0 @@ -# Documentation - -## Introduction - -`Node-MySQL2` aims to be a drop in replacement for [node-mysql](https://github.com/mysqljs/mysql). Please check `node-mysql` for full documentation. - -**Note :** *If you see any API incompatibilities with `node-mysql`, please report via github issue.* - -Not only `Node-MySQL2` offers better performance over `node-mysql`, we also support these additional features. - -- [Prepared Statements](./Prepared-Statements.md) -- [Promise Wrapper](./Promise-Wrapper.md) -- [Authentication Switch](./Authentication-Switch.md) -- [More Features](./Extras.md) -- [MySQL Server](./MySQL-Server.md) -- Pooling -- SSL -- MySQL Compression -- Binary Log Protocol Client - -## Examples - -Please check these [examples](./Examples.md) for `node-mysql2`. - - -## Known incompatibilities with `node-mysql` - -- `zeroFill` flag is ignored in type conversion. -You need to check corresponding field's zeroFill flag and convert to string manually if this is of importance to you. - -- `DECIMAL` and `NEWDECIMAL` types always returned as `string` unless you pass this config option: -```javascript -{ - decimalNumbers: true -} -``` -**Note :** *This option could lose precision on the number as Javascript Number is a Float!* - -## Other Resources - - - [Wire protocol documentation](http://dev.mysql.com/doc/internals/en/client-server-protocol.html) - - [node-mysql](https://github.com/mysqljs/mysql) - Most popular node.js mysql client library - - [node-mariasql](https://github.com/mscdex/node-mariasql/) - Bindings to libmariasql. One of the fastest clients - - [node-libmysqlclient](https://github.com/Sannis/node-mysql-libmysqlclient) - Bindings to libmysqlclient - - [go-mysql](https://github.com/siddontang/go-mysql) - MySQL Go client (prepared statements, binlog protocol, server) - -## Benchmarks - - https://gist.github.com/sidorares/ffe9ee9c423f763e3b6b - - `npm run benchmarks` - - [node-mysql-benchmarks](https://github.com/mscdex/node-mysql-benchmarks) - - try to run example [benchmarks](../../benchmarks) on your system diff --git a/documentation/en/TypeScript-Examples.md b/documentation/en/TypeScript-Examples.md deleted file mode 100644 index 44f771cc02..0000000000 --- a/documentation/en/TypeScript-Examples.md +++ /dev/null @@ -1,306 +0,0 @@ -# Using MySQL2 with TypeScript - -## Installation -```bash -npm install --save mysql2 -npm install --save-dev @types/node -``` - -> The `@types/node` ensure the proper interaction between **TypeScript** and the **Node.js** modules used by **MySQL2** (*net*, *events*, *stream*, *tls*, etc.). - -Requires **TypeScript** `>=4.5.2`. - ---- - -## Usage -You can import **MySQL2** in two ways: -- By setting the `esModuleInterop` option to `true` in `tsconfig.json` -```ts -import mysql from 'mysql2'; -import mysql from 'mysql2/promise'; -``` - -- By setting the `esModuleInterop` option to `false` in `tsconfig.json` -```ts -import * as mysql from 'mysql2'; -import * as mysql from 'mysql2/promise'; -``` - -### Connection -```ts -import mysql, { ConnectionOptions } from 'mysql2'; - -const access: ConnectionOptions = { - user: 'test', - database: 'test', -}; - -const conn = mysql.createConnection(access); -``` - -### Pool Connection -```ts -import mysql, { PoolOptions } from 'mysql2'; - -const access: PoolOptions = { - user: 'test', - database: 'test', -}; - -const conn = mysql.createPool(access); -``` - -### Query and Execute -#### A simple query -```ts -conn.query('SELECT 1 + 1 AS `test`;', (_err, rows) => { - /** - * @rows: [ { test: 2 } ] - */ -}); - -conn.execute('SELECT 1 + 1 AS `test`;', (_err, rows) => { - /** - * @rows: [ { test: 2 } ] - */ -}); -``` - -The `rows` output will be these possible types: -- `RowDataPacket[]` -- `RowDataPacket[][]` -- `ResultSetHeader` -- `ResultSetHeader[]` -- `ProcedureCallPacket` - -In this example, you need to manually check the output types - ---- - -## Type Specification -### RowDataPacket[] -An array with the returned rows, for example: - -```ts -import mysql, { RowDataPacket } from 'mysql2'; - -const conn = mysql.createConnection({ - user: 'test', - database: 'test', -}); - -// SELECT -conn.query('SELECT 1 + 1 AS `test`;', (_err, rows) => { - console.log(rows); - /** - * @rows: [ { test: 2 } ] - */ -}); - -// SHOW -conn.query('SHOW TABLES FROM `test`;', (_err, rows) => { - console.log(rows); - /** - * @rows: [ { Tables_in_test: 'test' } ] - */ -}); -``` - -Using `rowsAsArray` option as `true`: - -```ts -import mysql, { RowDataPacket } from 'mysql2'; - -const conn = mysql.createConnection({ - user: 'test', - database: 'test', - rowsAsArray: true, -}); - -// SELECT -conn.query('SELECT 1 + 1 AS test, 2 + 2 AS test;', (_err, rows) => { - console.log(rows); - /** - * @rows: [ [ 2, 4 ] ] - */ -}); - -// SHOW -conn.query('SHOW TABLES FROM `test`;', (_err, rows) => { - console.log(rows); - /** - * @rows: [ [ 'test' ] ] - */ -}); -``` - ---- - -### RowDataPacket[][] -Using `multipleStatements`option as `true` with multiple queries: - ```ts - import mysql, { RowDataPacket } from 'mysql2'; - - const conn = mysql.createConnection({ - user: 'test', - database: 'test', - multipleStatements: true, - }); - - const sql = ` - SELECT 1 + 1 AS test; - SELECT 2 + 2 AS test; - `; - - conn.query(sql, (_err, rows) => { - console.log(rows); - /** - * @rows: [ [ { test: 2 } ], [ { test: 4 } ] ] - */ - }); - ``` - ---- - -### ResultSetHeader -For `INSERT`, `UPDATE`, `DELETE`, `TRUNCATE`, etc.: -```ts -import mysql, { ResultSetHeader } from 'mysql2'; - -const conn = mysql.createConnection({ - user: 'test', - database: 'test', -}); - -const sql = ` - SET @1 = 1; -`; - -conn.query(sql, (_err, result) => { - console.log(result); - /** - * @result: ResultSetHeader { - fieldCount: 0, - affectedRows: 0, - insertId: 0, - info: '', - serverStatus: 2, - warningStatus: 0, - changedRows: 0 - } - */ -}); -``` - ---- - -### ResultSetHeader[] -For multiples `INSERT`, `UPDATE`, `DELETE`, `TRUNCATE`, etc. when using `multipleStatements` as `true`: - -```ts -import mysql, { ResultSetHeader } from 'mysql2'; - -const conn = mysql.createConnection({ - user: 'test', - database: 'test', - multipleStatements: true, -}); - -const sql = ` - SET @1 = 1; - SET @2 = 2; -`; - -conn.query(sql, (_err, results) => { - console.log(results); - /** - * @results: [ - ResultSetHeader { - fieldCount: 0, - affectedRows: 0, - insertId: 0, - info: '', - serverStatus: 10, - warningStatus: 0, - changedRows: 0 - }, - ResultSetHeader { - fieldCount: 0, - affectedRows: 0, - insertId: 0, - info: '', - serverStatus: 2, - warningStatus: 0, - changedRows: 0 - } - ] - */ -}); -``` - ---- - -### ProcedureCallPacket -By performing a **Call Procedure** using `INSERT`, `UPDATE`, etc., the return will be a `ProcedureCallPacket` (even if you perform multiples queries and set `multipleStatements` to `true`): - -```ts -import mysql, { ProcedureCallPacket, ResultSetHeader } from 'mysql2'; - -const conn = mysql.createConnection({ - user: 'test', - database: 'test', -}); - -/** ResultSetHeader */ -conn.query('DROP PROCEDURE IF EXISTS myProcedure'); - -/** ResultSetHeader */ -conn.query(` - CREATE PROCEDURE myProcedure() - BEGIN - SET @1 = 1; - SET @2 = 2; - END - `); - -/** ProcedureCallPacket */ -const sql = 'CALL myProcedure()'; - -conn.query>(sql, (_err, result) => { - console.log(result); - /** - * @result: ResultSetHeader { - fieldCount: 0, - affectedRows: 0, - insertId: 0, - info: '', - serverStatus: 2, - warningStatus: 0, - changedRows: 0 - } - */ -}); -``` - -> For `CREATE PROCEDURE` and `DROP PROCEDURE`, these returns will be the *default* `ResultSetHeader`. - -By using `SELECT` and `SHOW` queries in a **Procedure Call**, it groups the results as: -```tsx -/** ProcedureCallPacket */ -[RowDataPacket[], ResultSetHeader] -``` - -For `ProcedureCallPacket`, please see the following examples. - ---- - -## Examples -You can also check some code examples using **MySQL2** and **TypeScript** to understand advanced concepts: - -- [Extending and using **Interfaces** with `RowDataPacket`](../../examples/typescript/row-data-packet.ts) -- [Extending and using **Interfaces** with `RowDataPacket` and `rowAsArray`](../../examples/typescript/row-data-packet-row-as-array.ts) -- [Extending and using **Interfaces** with `RowDataPacket` and `multipleStatements`](../../examples/typescript/row-data-packet-multi-statements.ts) -- [Extending and using **Interfaces** with `RowDataPacket`, `rowAsArray` and `multipleStatements`](../../examples/typescript/row-data-packet-row-as-array-multi-statements.ts) -- [Checking for `ResultSetHeader`, extending and using **Interfaces** with `RowDataPacket` from `ProcedureCallPacket`](../../examples/typescript/procedure-call-packet.ts) -- [Checking for `ResultSetHeader`, extending and using **Interfaces** with `RowDataPacket` and `rowAsArray` from `ProcedureCallPacket`](../../examples/typescript/procedure-call-packet-row-as-array.ts) -- [Creating a basic custom **MySQL2** **Class**](../../examples/typescript/basic-custom-class.ts) diff --git a/documentation/pt-br/README.md b/documentation/pt-br/README.md deleted file mode 100644 index 431afe5af6..0000000000 --- a/documentation/pt-br/README.md +++ /dev/null @@ -1,288 +0,0 @@ -## Node MySQL 2 - -[![Greenkeeper badge](https://badges.greenkeeper.io/sidorares/node-mysql2.svg)](https://greenkeeper.io/) -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Node.js Version][node-version-image]][node-version-url] -[![Linux Build][travis-image]][travis-url] -[![Windows Build][appveyor-image]][appveyor-url] -[![License][license-image]][license-url] - -[English](../..) | [简体中文](../zh-cn/) | Português (BR) - -> Cliente MySQL para Node.js com foco em performance. Suporta instruções preparadas (*prepared statements*), Codificações *non-utf8*, protocolo de log binário (*binary log protocol*), compressão, SSL e [muito mais](../en). - -__Lista de Conteúdos__ - - - [História e por que MySQL2](#história-e-por-que-mysql2) - - [Instalação](#instalação) - - [Primeira Consulta (*Query*)](#primeira-consulta-query) - - [Usando Instruções Preparadas (*Prepared Statements*)](#usando-instruções-preparadas-prepared-statements) - - [Usando Conjuntos de Conexões (*Pool*)](#usando-conjunto-de-conexões-pools) - - [Usando o *Promise Wrapper*](#usando-o-promise-wrapper) - - [Resultados em *Array*](#resultados-em-array) - - [Nível de Conexão](#resultados-em-array) - - [Nível de Consulta (*Query*)](#resultados-em-array) - - [API e Configuração](#api-e-configuração) - - [Documentação](#documentação) - - [Agradecimentos](#agradecimentos) - - [Contribuições](#contribuições) - -## História e por que MySQL2 - -O projeto MySQL2 é uma continuação do [MySQL-Native][mysql-native]. O código do analisador de protocolo (*protocol parser*) foi reescrito do zero e a API foi alterada para corresponder ao popular [mysqljs/mysql][node-mysql]. A equipe do MySQL2 está trabalhando em conjunto com a equipe do [mysqljs/mysql][node-mysql] para *fatorar* o código compartilhado e movê-lo para a organização [mysqljs][node-mysql]. - -O MySQL2 é maioritariamente compatível com a API do [mysqljs][node-mysql] e suporta a maioria de suas funcionalidades. O MySQL2 também oferece essas funcionalidades adicionais: - - - Desempenho mais rápido / melhor - - [Instruções Preparadas (*Prepared Statements*)](../en/Prepared-Statements.md) - - Protocolo de log binário MySQL (*MySQL Binary Log Protocol*) - - [Servidor MySQL](../en/MySQL-Server.md) - - Estende o suporte para *Encoding* and *Collation* - - [*Promise Wrapper*](../en/Promise-Wrapper.md) - - Compressão - - SSL e [*Authentication Switch*](../en/Authentication-Switch.md) - - [*Streams* Personalizados](../en/Extras.md) - - [Conjunto de Conexões (*Pooling*)](#using-connection-pools) - -## Instalação - -O MySQL2 não tem restrições nativas e pode ser instalado no Linux, Mac OS ou Windows sem qualquer problema. - -```bash -npm install --save mysql2 -``` - -## Primeira Consulta (*Query*) - -```js -// Obtém o cliente -const mysql = require('mysql2'); - -// Cria a conexão com o Banco de Dados -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -// Consulta simples -connection.query( - 'SELECT * FROM `table` WHERE `name` = "Page" AND `age` > 45', - function(err, results, fields) { - console.log(results); // "results" contêm as linhas retornadas pelo servidor - console.log(fields); // "fields" contêm metadados adicionais sobre os resultados, quando disponíveis - } -); - -// Utilizando espaços reservados (placeholders) -connection.query( - 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', - ['Page', 45], - function(err, results) { - console.log(results); - } -); -``` - -## Usando Instruções Preparadas (*Prepared Statements*) - -Com o MySQL2 você também pode obter Instruções Preparadas (Prepared Statements). Dessa forma o MySQL não precisa preparar um plano para a mesma consulta todas as vezes, resultando em um melhor desempenho. Se você não sabe por que isso é importante, veja essa discussão: - -- [Como as instruções preparadas (*prepared statements*) podem proteger contra ataques de injeção SQL](http://stackoverflow.com/questions/8263371/how-can-prepared-statements-protect-from-sql-injection-attacks) - - -O MySQL2 fornece o método auxiliar `execute` que irá preparar e consultar as declarações (*statements*) SQL. Além disso, você também pode usar os métodos `prepare` e `unprepare` para preparar ou desfazer a preparação de declarações (*statements*) manualmente, se necessário. - -```js -// Obtém o cliente -const mysql = require('mysql2'); - -// Cria a conexão com o Banco de Dados -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -// "execute" irá chamar internamente a preparação e a consulta (query) -connection.execute( - 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', - ['Rick C-137', 53], - function(err, results, fields) { - console.log(results); // "results" contêm as linhas retornadas pelo servidor - console.log(fields); // "fields" contêm metadados adicionais sobre os resultados, quando disponíveis - - // Se você executar a mesma declaração novamente, ela será selecionada a partir do LRU Cache - // O que economizará tempo de preparação da consulta e proporcionará melhor desempenho. - } -); -``` - -## Usando Conjunto de Conexões (*pools*) - -O conjunto de conexões (*pools*) ajuda a reduzir o tempo gasto na conexão com o servidor MySQL, reutilizando uma conexão anterior e deixando-as abertas ao invés de fechá-las quando você termina de usá-las. - -Isto melhora a latência das consultas (*queries*), pois evita toda a sobrecarga associada à criação de uma nova conexão. - -```js -// Obtém o cliente -const mysql = require('mysql2'); - -// Cria a conexão (pool). As definições específicadas do "createPool" são as predefinições padrões -const pool = mysql.createPool({ - host: 'localhost', - user: 'root', - database: 'test', - waitForConnections: true, - connectionLimit: 10, - maxIdle: 10, // Máximo de conexões inativas; o valor padrão é o mesmo que "connectionLimit" - idleTimeout: 60000, // Tempo limite das conexões inativas em milissegundos; o valor padrão é "60000" - queueLimit: 0, - enableKeepAlive: true, - keepAliveInitialDelay: 0 -}); -``` -O *pool* não estabelece todas as conexões previamente, mas as cria sob demanda até que o limite de conexões seja atingido. - -Você pode usar o *pool* da mesma maneira como em uma conexão (usando `pool.query()` e `pool.execute()`): -```js -// Para a inicialização do "pool", veja acima -pool.query("SELECT `field` FROM `table`", function(err, rows, fields) { - // A conexão é automaticamente liberada quando a consulta (query) é resolvida -}); -``` - -Alternativamente, também existe a possibilidade de adquirir manualmente uma conexão do pool e liberá-la posteriormente: -```js -// Para a inicialização do "pool", veja acima -pool.getConnection(function(err, conn) { - // Fazer algo com a conexão - conn.query(/* ... */); - // Não se esqueça de liberar a conexão quando terminar! - pool.releaseConnection(conn); -}); -``` - -## Usando o *Promise Wrapper* - -O MySQL2 também suporta *Promise* API. O que funciona muito bem com o ES7 *async await*. - -```js -async function main() { - // Obtém o cliente - const mysql = require('mysql2/promise'); - // Cria a conexão com o Banco de Dados - const connection = await mysql.createConnection({host:'localhost', user: 'root', database: 'test'}); - // Consulta no Banco de Dados - const [rows, fields] = await connection.execute('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Morty', 14]); -} -``` - -O MySQL2 usa o objeto *`Promise`* padrão disponível no escopo. Mas você pode escolher qual implementação de *`Promise`* deseja usar. -```js -// Obtém o cliente -const mysql = require('mysql2/promise'); - -// Obtém a implementação de "Promise" (nós usaremos o "bluebird") -const bluebird = require('bluebird'); - -// Cria a conexão, especificando o "bluebird" como "Promise" -const connection = await mysql.createConnection({host:'localhost', user: 'root', database: 'test', Promise: bluebird}); - -// Consulta no Banco de Dados -const [rows, fields] = await connection.execute('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Morty', 14]); -``` - -O MySQL2 também expõe o método .promise() em *Pools*, então você pode criar conexões "*promise/non-promise*" para o mesmo *pool*. -```js -async function main() { - // Obtém o cliente - const mysql = require('mysql2'); - // Cria o "pool" - const pool = mysql.createPool({host:'localhost', user: 'root', database: 'test'}); - // Agora obtém a instância "Promise wrapped" do "pool" - const promisePool = pool.promise(); - // Consulta no Banco de Dados usando "Promises" - const [rows,fields] = await promisePool.query("SELECT 1"); -} -``` - -O MySQL2 também expõe o método .promise() em conexões, para "atualizar" a conexão *non-promise* existente e usá-la como *promise*. -```js -// Obtém o cliente -const mysql = require('mysql2'); -// Cria a conexão -const con = mysql.createConnection( - {host:'localhost', user: 'root', database: 'test'} -); -con.promise().query("SELECT 1") - .then( ([rows,fields]) => { - console.log(rows); - }) - .catch(console.log) - .then( () => con.end()); -``` - -## Resultados em *Array* - -Se você tiver duas colunas com o mesmo nome, pode preferir receber os resultados como um *array*, em vez de um objeto, para evitar conflitos. Isso é uma divergência da biblioteca [Node MySQL][node-mysql]. - -Por exemplo: `select 1 as foo, 2 as foo`. - -Você pode habilitar essa configuração tanto no nível de conexão (aplica-se a todas as consultas), quanto no nível de consulta (aplica-se apenas a essa consulta específica). - -### Nível de Conexão -```js -const con = mysql.createConnection( - { host: 'localhost', database: 'test', user: 'root', rowsAsArray: true } -); -``` - -### Nível de Consulta (*Query*) -```js -con.query({ sql: 'select 1 as foo, 2 as foo', rowsAsArray: true }, function(err, results, fields) { - console.log(results); // nessa consulta, "results" contêm um array de arrays ao invés de um array de objetos - console.log(fields); // "fields" mantêm-se inalterados -}); -``` - -## API e Configuração - -O MySQL2 é maioritariamente compatível com a API do [Node MySQL][node-mysql]. Você deve consultar a documentação da API para ver todas as opções disponíveis. - -Uma incompatibilidade conhecida é que os valores em `DECIMAL` são retornados como *strings*, enquanto no [Node MySQL][node-mysql] eles são retornados como números. Isso inclui o resultado das funções `SUM()` e `AVG()` quando aplicadas a argumentos `INTEGER`. Isso é feito deliberadamente para evitar a perda de precisão - veja https://github.com/sidorares/node-mysql2/issues/935. - -Se você encontrar qualquer outra incompatibilidade com o [Node MySQL][node-mysql], por favor, reporte através do acompanhamento de *Issues*. Nós corrigiremos a incompatibilidade relatada como uma prioridade. - -## Documentação - -Você pode encontrar a documentação detalhada [aqui](../en) e também pode consultar vários [exemplos](../../examples) de código para compreender conceitos avançados. - -## Agradecimentos - - - O protocolo interno é escrito por @sidorares [MySQL-Native](https://github.com/sidorares/nodejs-mysql-native) - - *Constants*, interpolação de parâmetros SQL, *Pooling* e a classe `ConnectionConfig` foram retirados do [node-mysql](https://github.com/mysqljs/mysql) - - O Código de atualização SSL é baseado no [código](https://gist.github.com/TooTallNate/848444) feito por @TooTallNate - - *Flags* de API de conexão segura / comprimida compatíveis com o cliente [MariaSQL](https://github.com/mscdex/node-mariasql/). - - [Contribuidores](https://github.com/sidorares/node-mysql2/graphs/contributors) - -## Contribuições - -Quer melhorar algo no `node-mysql2`? Consulte o arquivo [Contributing.md](https://github.com/sidorares/node-mysql2/blob/master/Contributing.md) para instruções detalhadas sobre como começar. - - -[npm-image]: https://img.shields.io/npm/v/mysql2.svg -[npm-url]: https://npmjs.org/package/mysql2 -[node-version-image]: http://img.shields.io/node/v/mysql2.svg -[node-version-url]: http://nodejs.org/download/ -[travis-image]: https://img.shields.io/travis/sidorares/node-mysql2/master.svg?label=linux -[travis-url]: https://travis-ci.org/sidorares/node-mysql2 -[appveyor-image]: https://img.shields.io/appveyor/ci/sidorares/node-mysql2/master.svg?label=windows -[appveyor-url]: https://ci.appveyor.com/project/sidorares/node-mysql2 -[downloads-image]: https://img.shields.io/npm/dm/mysql2.svg -[downloads-url]: https://npmjs.org/package/mysql2 -[license-url]: https://github.com/sidorares/node-mysql2/blob/master/License -[license-image]: https://img.shields.io/npm/l/mysql2.svg?maxAge=2592000 -[node-mysql]: https://github.com/mysqljs/mysql -[mysql-native]: https://github.com/sidorares/nodejs-mysql-native diff --git a/documentation/zh-cn/README.md b/documentation/zh-cn/README.md deleted file mode 100644 index e8bd2d5497..0000000000 --- a/documentation/zh-cn/README.md +++ /dev/null @@ -1,282 +0,0 @@ -## Node MySQL 2 - -[![Greenkeeper badge](https://badges.greenkeeper.io/sidorares/node-mysql2.svg)](https://greenkeeper.io/) -[![NPM Version][npm-image]][npm-url] -[![NPM Downloads][downloads-image]][downloads-url] -[![Node.js Version][node-version-image]][node-version-url] -[![Linux Build][travis-image]][travis-url] -[![Windows Build][appveyor-image]][appveyor-url] -[![License][license-image]][license-url] - -[English](../..) | 简体中文 | [Português (BR)](../pt-br/) - -> 适用于Node.js的MySQL客户端,专注于性能优化。支持SQL预处理、非UTF-8编码支持、二进制文件编码支持、压缩和SSL等等 [查看更多](../en)。 - -__目录__ - - - [MySQL2的历史以及选择原因](#MySQL2的历史以及选择原因) - - [安装](#安装) - - [查询数据](#查询数据) - - [SQL预处理的使用](#SQL预处理的使用) - - [连接池的使用](#连接池的使用) - - [Promise封装](#Promise封装) - - [结果返回](#结果返回) - - [连接级别](#连接级别) - - [查询级别](#查询级别) - - [API配置项](#API配置项) - - [文档](#文档) - - [鸣谢](#鸣谢) - - [贡献](#贡献) - -## MySQL2的历史以及选择原因 - -MySQL2 项目是 [MySQL-Native][mysql-native] 的延续。 协议解析器代码从头开始重写,api 更改为匹配流行的 [mysqljs/mysql][node-mysql]。 MySQL2 团队正在与 [mysqljs/mysql][node-mysql] 团队合作,将共享代码分解并移至 [mysqljs][node-mysql] 组织下。 - -MySQL2 大部分 API 与 [mysqljs][node-mysql] 兼容,并支持大部分功能。 MySQL2 还提供了更多的附加功能: - - - 更快、更好的性能 - - [支持预处理](../en/Prepared-Statements.md) - - MySQL二进制日志协议 - - [MySQL Server](../en/MySQL-Server.md) - - 对编码和排序规则有很好的支持 - - [Promise封装](../en/Promise-Wrapper.md) - - 支持压缩 - - SSL 和 [Authentication Switch](../en/Authentication-Switch.md) - - [自定义流](../en/Extras.md) - - [连接池](#using-connection-pools) - -## 安装 - -MySQL2 可以跨平台使用,毫无疑问可以安装在 Linux、Mac OS 或 Windows 上。 - -```bash -npm install --save mysql2 -``` - -## 查询数据 - -```js -// 导入模块 -const mysql = require('mysql2'); - -// 创建一个数据库连接 -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -// 简单查询 -connection.query( - 'SELECT * FROM `table` WHERE `name` = "Page" AND `age` > 45', - function(err, results, fields) { - console.log(results); // 结果集 - console.log(fields); // 额外的元数据(如果有的话) - } -); - -// 使用占位符 -connection.query( - 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', - ['Page', 45], - function(err, results) { - console.log(results); - } -); -``` - -## SQL预处理的使用 - -使用 MySQL2,您还可以提前准备好SQL预处理语句。 使用准备好的SQL预处理语句,MySQL 不必每次都为相同的查询做准备,这会带来更好的性能。 如果您不知道为什么它们很重要,请查看这些讨论: - -- [如何防止预处理语句SQL注入攻击](http://stackoverflow.com/questions/8263371/how-can-prepared-statements-protect-from-sql-injection-attacks) - -MySQL2 提供了 `execute` 辅助函数,它将准备和查询语句。 您还可以使用 `prepare` / `unprepare` 方法手动准备/取消准备。 - -```js -// 导入模块 -const mysql = require('mysql2'); - -// 创建一个数据库连接 -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -// execute 将在内部调用 prepare 和 query -connection.execute( - 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', - ['Rick C-137', 53], - function(err, results, fields) { - console.log(results); // 结果集 - console.log(fields); // 额外元数据(如果有) - - // 如果再次执行相同的语句,他将从缓存中选取 - // 这能有效的节省准备查询时间获得更好的性能 - } -); -``` - -## 连接池的使用 - -连接池通过重用以前的连接来帮助减少连接到 MySQL 服务器所花费的时间,当你完成它们时让它们保持打开而不是关闭。 - -这改善了查询的延迟,因为您避免了建立新连接所带来的所有开销。 - -```js -// 导入模块 -const mysql = require('mysql2'); - -// 创建连接池,设置连接池的参数 -const pool = mysql.createPool({ - host: 'localhost', - user: 'root', - database: 'test', - waitForConnections: true, - connectionLimit: 10, - queueLimit: 0, - enableKeepAlive: true, - keepAliveInitialDelay: 0 -}); -``` -该池不会预先创建所有连接,而是根据需要创建它们,直到达到连接限制。 - -您可以像直接连接一样使用池(使用 `pool.query()` 和 `pool.execute()`): -```js -// For pool initialization, see above -pool.query("SELECT `field` FROM `table`", function(err, rows, fields) { - // Connection is automatically released when query resolves -}); -``` - -或者,也可以手动从池中获取连接并稍后返回: -```js -// For pool initialization, see above -pool.getConnection(function(err, conn) { - // Do something with the connection - conn.query(/* ... */); - // Don't forget to release the connection when finished! - pool.releaseConnection(conn); -}); -``` - -## Promise封装 - -MySQL2 也支持 Promise API。 这与 ES7 异步等待非常有效。 -```js -async function main() { - // get the client - const mysql = require('mysql2/promise'); - // create the connection - const connection = await mysql.createConnection({host:'localhost', user: 'root', database: 'test'}); - // query database - const [rows, fields] = await connection.execute('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Morty', 14]); -} -``` - -MySQL2 使用范围内可用的默认 `Promise` 对象。 但是你可以选择你想使用的 `Promise` 实现。 -```js -// get the client -const mysql = require('mysql2/promise'); - -// get the promise implementation, we will use bluebird -const bluebird = require('bluebird'); - -// create the connection, specify bluebird as Promise -const connection = await mysql.createConnection({host:'localhost', user: 'root', database: 'test', Promise: bluebird}); - -// query database -const [rows, fields] = await connection.execute('SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Morty', 14]); -``` - -MySQL2 还在 Pools 上公开了一个 .promise()函数,因此您可以从同一个池创建一个 promise/non-promise 连接。 -```js -async function main() { - // get the client - const mysql = require('mysql2'); - // create the pool - const pool = mysql.createPool({host:'localhost', user: 'root', database: 'test'}); - // now get a Promise wrapped instance of that pool - const promisePool = pool.promise(); - // query database using promises - const [rows,fields] = await promisePool.query("SELECT 1"); -} -``` - -MySQL2 在 Connections 上公开了一个 .promise*()函数,以“升级”现有的 non-promise 连接以使用 Promise。 -```js -// get the client -const mysql = require('mysql2'); -// create the connection -const con = mysql.createConnection( - {host:'localhost', user: 'root', database: 'test'} -); -con.promise().query("SELECT 1") - .then( ([rows,fields]) => { - console.log(rows); - }) - .catch(console.log) - .then( () => con.end()); -``` - -## 结果返回 - -如果你有两个相同名称的列,你可能希望以数组而不是对象的形式获取结果,为了防止冲突,这是与 [Node MySQL][node-mysql] 库的区别。 - -例如: `select 1 as foo, 2 as foo`. - -您可以在连接级别(适用于所有查询)或查询级别(仅适用于该特定查询)启用此设置。 - -### 连接级别 -```js -const con = mysql.createConnection( - { host: 'localhost', database: 'test', user: 'root', rowsAsArray: true } -); -``` - -### 查询级别 -```js -con.query({ sql: 'select 1 as foo, 2 as foo', rowsAsArray: true }, function(err, results, fields) { - console.log(results) // 返回数组而不是数组对象 - console.log(fields) // 无变化 -}); -``` - -## API配置项 - -MySQL2大部分的API与 [Node MySQL][node-mysql] 基本上相同,你应该查看他们的API文档来知道更多的API选项。 - -如果您发现与 [Node MySQL][node-mysql] 的任何不兼容问题,请通过`issue`报告。 我们将优先修复报告的不兼容问题。 - -## 文档 - -你可以在[这里](../en)获得更多的详细文档,并且你应该查阅各种代码[示例](../en/examples)来获得更高级的概念。 - -## 鸣谢 - - - 内部协议由@sidorares编写 [MySQL-Native](https://github.com/sidorares/nodejs-mysql-native) - - 常量、SQL参数插值、连接池、`ConnectionConfig` 类取自[node-mysql](https://github.com/mysqljs/mysql) - - 基于@TooTallNate的SSL代码升级[代码地址](https://gist.github.com/TooTallNate/848444) - - 与[MariaSQL](https://github.com/mscdex/node-mariasql/)客户端兼容安全连接/压缩连接 API。 - - [贡献者](https://github.com/sidorares/node-mysql2/graphs/contributors) - -## 贡献 - -如果要为`node-mysql2`做些贡献.请查阅 [Contributing.md](https://github.com/sidorares/node-mysql2/blob/master/Contributing.md) 来获得更多详细信息。 - - -[npm-image]: https://img.shields.io/npm/v/mysql2.svg -[npm-url]: https://npmjs.org/package/mysql2 -[node-version-image]: http://img.shields.io/node/v/mysql2.svg -[node-version-url]: http://nodejs.org/download/ -[travis-image]: https://img.shields.io/travis/sidorares/node-mysql2/master.svg?label=linux -[travis-url]: https://travis-ci.org/sidorares/node-mysql2 -[appveyor-image]: https://img.shields.io/appveyor/ci/sidorares/node-mysql2/master.svg?label=windows -[appveyor-url]: https://ci.appveyor.com/project/sidorares/node-mysql2 -[downloads-image]: https://img.shields.io/npm/dm/mysql2.svg -[downloads-url]: https://npmjs.org/package/mysql2 -[license-url]: https://github.com/sidorares/node-mysql2/blob/master/License -[license-image]: https://img.shields.io/npm/l/mysql2.svg?maxAge=2592000 -[node-mysql]: https://github.com/mysqljs/mysql -[mysql-native]: https://github.com/sidorares/nodejs-mysql-native diff --git a/examples/.eslintrc b/examples/.eslintrc deleted file mode 100644 index cc2e6812e1..0000000000 --- a/examples/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "rules": { - "no-undef": "off", - "no-unused-vars": "off", - "no-console": "off", - "no-unused-labels": "off" - } - } - \ No newline at end of file diff --git a/examples/binlog-watcher.js b/examples/binlog-watcher.js deleted file mode 100644 index 04f4105420..0000000000 --- a/examples/binlog-watcher.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -const mysql = require('mysql2'); -const through2 = require('through2'); - -const binlogStream = mysql.createBinlogStream({ - serverId: 123, // slave ID, first field in "show slave hosts" sql response - // you can also specify slave host, username, password and port - masterId: 0, - filename: 'mysql-bin.000007', - binlogPos: 120, - flags: 1 // 1 = "non-blocking mode" -}); - -binlogStream.pipe( - through2.obj((obj, enc, next) => { - console.log(obj); - next(); - }) -); diff --git a/examples/connect-over-socks.js b/examples/connect-over-socks.js deleted file mode 100644 index 8175b6a03f..0000000000 --- a/examples/connect-over-socks.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -const mysql = require('mysql2'); -const SocksConnection = require('socksjs'); - -// const socksProxy = new SocksConnection({ port: 3306 }); -// const conn = mysql.createConnection({ -// stream: socksProxy, -// }); - -// conn.query("select 1+1", function(err, rows, fields) { -// console.log(err, rows, fields); -// }); - -const conn1 = mysql.createPool({ - debug: 1, - stream: function() { - return new SocksConnection({ port: 3306 }); - } -}); - -conn1.execute('select sleep(1.1) as www', (err, rows, fields) => { - console.log(err, rows, fields); -}); - -conn1.execute('select sleep(1) as qqq', (err, rows, fields) => { - console.log(err, rows, fields); -}); - -conn1.execute('select sleep(1) as qqq', (err, rows, fields) => { - console.log(err, rows, fields); -}); diff --git a/examples/execute.js b/examples/execute.js deleted file mode 100644 index e40a6bfa9f..0000000000 --- a/examples/execute.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -// get the client -const mysql = require('mysql2'); - -// create the connection to database -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -connection.execute( - 'select ?+1 as qqq, ? as rrr, ? as yyy', - [1, null, 3], - (err, rows, fields) => { - console.log(err, rows, fields); - connection.execute( - 'select ?+1 as qqq, ? as rrr, ? as yyy', - [3, null, 3], - (err, rows, fields) => { - console.log(err, rows, fields); - connection.unprepare('select ?+1 as qqq, ? as rrr, ? as yyy'); - connection.execute( - 'select ?+1 as qqq, ? as rrr, ? as yyy', - [3, null, 3], - (err, rows, fields) => { - console.log(err, rows, fields); - } - ); - } - ); - } -); diff --git a/examples/mysqlproxy.js b/examples/mysqlproxy.js deleted file mode 100644 index 87443b4ee3..0000000000 --- a/examples/mysqlproxy.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; - -const mysql = require('mysql2'); -const ClientFlags = require('mysql2/lib/constants/client.js'); - -const server = mysql.createServer(); -server.listen(3307); - -server.on('connection', conn => { - console.log('connection'); - - conn.serverHandshake({ - protocolVersion: 10, - serverVersion: 'node.js rocks', - connectionId: 1234, - statusFlags: 2, - characterSet: 8, - capabilityFlags: 0xffffff ^ ClientFlags.COMPRESS - }); - - conn.on('field_list', (table, fields) => { - console.log('field list:', table, fields); - conn.writeEof(); - }); - - const remote = mysql.createConnection({ - user: 'root', - database: 'dbname', - host: 'server.example.com', - password: 'secret' - }); - - conn.on('query', sql => { - console.log(`proxying query: ${sql}`); - remote.query(sql, function(err) { - // overloaded args, either (err, result :object) - // or (err, rows :array, columns :array) - if (Array.isArray(arguments[1])) { - // response to a 'select', 'show' or similar - const rows = arguments[1], - columns = arguments[2]; - console.log('rows', rows); - console.log('columns', columns); - conn.writeTextResult(rows, columns); - } else { - // response to an 'insert', 'update' or 'delete' - const result = arguments[1]; - console.log('result', result); - conn.writeOk(result); - } - }); - }); - - conn.on('end', remote.end.bind(remote)); -}); diff --git a/examples/pass-sha.js b/examples/pass-sha.js deleted file mode 100644 index ae96362cb1..0000000000 --- a/examples/pass-sha.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const mysql = require('mysql2').createConnection({ - host: 'localhost', - user: 'root', - database: 'test', - passwordSha1: Buffer.from('8bb6118f8fd6935ad0876a3be34a717d32708ffd', 'hex') -}); - -mysql.execute( - 'select ?+1 as qqq, ? as rrr, ? as yyy', - [1, null, 3], - (err, rows, fields) => { - console.log(err, rows, fields); - } -); diff --git a/examples/pool-test.js b/examples/pool-test.js deleted file mode 100644 index 922e1c45ec..0000000000 --- a/examples/pool-test.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -const pool = require('mysql2').createPool({ - host: 'localhost', - user: 'root', - database: 'test', - password: 'root' -}); - -setInterval(() => { - for (let i = 0; i < 5; ++i) { - pool.query((err, db) => { - console.log(rows, fields); - // Connection is automatically released once query resolves - }); - } -}, 1000); - -setInterval(() => { - for (let i = 0; i < 5; ++i) { - pool.getConnection((err, db) => { - db.query('select sleep(0.5) as qqq', (err, rows, fields) => { - console.log(rows, fields); - db.release(); - }); - }); - } -}, 1000); diff --git a/examples/prepare.js b/examples/prepare.js deleted file mode 100644 index fb36f070c7..0000000000 --- a/examples/prepare.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -// get the client -const mysql = require('mysql2'); - -// create the connection to database -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -// execute will internally call prepare and query -connection.execute( - 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', - ['Rick C-137', 53], - (err, results, fields) => { - console.log(results); // results contains rows returned by server - console.log(fields); // fields contains extra meta data about results, if available - - // If you execute same statement again, it will be picked form a LRU cache - // which will save query preparation time and give better performance - } -); diff --git a/examples/promise-co-await/.babelrc b/examples/promise-co-await/.babelrc deleted file mode 100644 index 0d67235e87..0000000000 --- a/examples/promise-co-await/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["transform-async-to-generator"] -} diff --git a/examples/promise-co-await/await.js b/examples/promise-co-await/await.js deleted file mode 100644 index 84cc16d34b..0000000000 --- a/examples/promise-co-await/await.js +++ /dev/null @@ -1,68 +0,0 @@ -'use strict'; - -const mysql = require('mysql2/promise'); - -async function test() { - const c = await mysql.createConnection({ - port: 3306, - user: 'testuser', - namedPlaceholders: true, - password: 'testpassword' - }); - console.log('connected!'); - const [rows, fields] = await c.query('show databases'); - console.log(rows); - - try { - const [rows, fields] = await c.query('some invalid sql here'); - } catch (e) { - console.log('caught exception!', e); - } - - console.log(await c.execute('select sleep(0.5)')); - console.log('after first sleep'); - console.log(await c.execute('select sleep(0.5)')); - console.log('after second sleep'); - let start = +new Date(); - console.log( - await Promise.all([ - c.execute('select sleep(2.5)'), - c.execute('select sleep(2.5)') - ]) - ); - console.log( - 'after 2+3 parallel sleep which is in fact not parallel because commands are queued per connection' - ); - let end = +new Date(); - console.log(end - start); - await c.end(); - - const p = mysql.createPool({ - port: 3306, - user: 'testuser', - namedPlaceholders: true, - password: 'testpassword' - }); - console.log(await p.execute('select sleep(0.5)')); - console.log('after first pool sleep'); - start = +new Date(); - console.log( - await Promise.all([ - p.execute('select sleep(2.5)'), - p.execute('select sleep(2.5)') - ]) - ); - console.log('after 2+3 parallel pool sleep'); - end = +new Date(); - console.log(end - start); - await p.end(); -} - -test() - .then(() => { - console.log('done'); - }) - .catch(err => { - console.log('error!', err); - throw err; - }); diff --git a/examples/promise-co-await/co.js b/examples/promise-co-await/co.js deleted file mode 100644 index fdae08b19b..0000000000 --- a/examples/promise-co-await/co.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -const mysql = require('mysql2/promise'); -const co = require('co'); - -co(function*() { - const c = yield mysql.createConnection({ - port: 3306, - user: 'root', - namedPlaceholders: true - }); - const rows = yield c.query('show databases'); - console.log(rows); - console.log(yield c.execute('select 1+:toAdd as qqq', { toAdd: 10 })); - yield c.end(); -}) - .then(function() { - console.log('done'); - }) - .catch(function(err) { - console.log(err); - throw err; - }); diff --git a/examples/promise-co-await/package.json b/examples/promise-co-await/package.json deleted file mode 100644 index 38f35dc103..0000000000 --- a/examples/promise-co-await/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "promise-co-await", - "version": "1.0.0", - "description": "", - "main": "await.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "dependencies": { - "babel-cli": "^6.9.0" - } -} diff --git a/examples/server.js b/examples/server.js deleted file mode 100644 index bea9c05a7b..0000000000 --- a/examples/server.js +++ /dev/null @@ -1,68 +0,0 @@ -'use strict'; - -const mysql = require('mysql2'); -const flags = require('mysql2/lib/constants/client.js'); -const auth = require('mysql2/lib/auth_41.js'); - -function authenticate(params, cb) { - console.log(params); - const doubleSha = auth.doubleSha1('pass123'); - const isValid = auth.verifyToken( - params.authPluginData1, - params.authPluginData2, - params.authToken, - doubleSha - ); - if (isValid) { - cb(null); - } else { - // for list of codes lib/constants/errors.js - cb(null, { message: 'wrong password dude', code: 1045 }); - } -} - -const server = mysql.createServer(); -server.listen(3333); -server.on('connection', conn => { - // we can deny connection here: - // conn.writeError({ message: 'secret', code: 123 }); - // conn.close(); - conn.serverHandshake({ - protocolVersion: 10, - serverVersion: '5.6.10', // 'node.js rocks', - connectionId: 1234, - statusFlags: 2, - characterSet: 8, - // capabilityFlags: 0xffffff, - // capabilityFlags: -2113931265, - capabilityFlags: 2181036031, - authCallback: authenticate - }); - - conn.on('field_list', (table, fields) => { - console.log('FIELD LIST:', table, fields); - conn.writeEof(); - }); - - conn.on('query', query => { - conn.writeColumns([ - { - catalog: 'def', - schema: 'test', - table: 'test_table', - orgTable: 'test_table', - name: 'beta', - orgName: 'beta', - characterSet: 33, - columnLength: 384, - columnType: 253, - flags: 0, - decimals: 0 - } - ]); - conn.writeTextRow(['test тест テスト փորձարկում পরীক্ষা kiểm tra ']); - conn.writeTextRow(['ტესტი પરીક્ષણ מבחן פּרובירן اختبار परीक्षण']); - conn.writeEof(); - conn.close(); - }); -}); diff --git a/examples/simple-select.js b/examples/simple-select.js deleted file mode 100644 index c38352b44b..0000000000 --- a/examples/simple-select.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -// get the client -const mysql = require('mysql2'); - -// create the connection to database -const connection = mysql.createConnection({ - host: 'localhost', - user: 'root', - database: 'test' -}); - -// simple query -connection.query( - 'SELECT * FROM `table` WHERE `name` = "Page" AND `age` > 45', - (err, results, fields) => { - console.log(results); // results contains rows returned by server - console.log(fields); // fields contains extra meta data about results, if available - } -); - -// with placeholder -connection.query( - 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', - ['Page', 45], - (err, results) => { - console.log(results); - } -); diff --git a/examples/ssl/rds-ssl.js b/examples/ssl/rds-ssl.js deleted file mode 100644 index 9138c53976..0000000000 --- a/examples/ssl/rds-ssl.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -const mysql = require('mysql2'); - -const conn = mysql.createConnection({ - user: process.env.USER, - password: process.env.PASSWORD, - database: process.env.DB, - host: process.env.HOST, - port: 3306, - ssl: 'Amazon RDS' -}); - -conn.query("show status like 'Ssl_cipher'", function(err, res) { - console.log(err, res); - conn.end(); -}); diff --git a/examples/ssl/select-over-ssl.js b/examples/ssl/select-over-ssl.js deleted file mode 100644 index 13d8de3b32..0000000000 --- a/examples/ssl/select-over-ssl.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const mysql = require('mysql2'); - -const conn = mysql.createConnection({ - user: 'root', - password: '', - database: 'test', - host: '127.0.0.1', - port: '3306', - ssl: { - // key: fs.readFileSync('./certs/client-key.pem'), - // cert: fs.readFileSync('./certs/client-cert.pem') - ca: fs.readFileSync('./certs/ca-cert.pem') - } -}); - -conn.query('select 1+1 as test', function(err, res) { - console.log(res); - conn.query('select repeat("a", 100) as test', function(err, res) { - console.log(res); - }); -}); diff --git a/examples/typescript/basic-custom-class.ts b/examples/typescript/basic-custom-class.ts deleted file mode 100644 index dc9da8f4dd..0000000000 --- a/examples/typescript/basic-custom-class.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * The types are explicity for learning purpose - */ - -import { - createPool, - PoolOptions, - Pool, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - -interface User extends RowDataPacket { - id: number; - name: string; -} - -class MySQL { - private conn: Pool; - private credentials: PoolOptions; - - constructor(credentials: PoolOptions) { - this.credentials = credentials; - this.conn = createPool(this.credentials); - } - - /** A random method to simulate a step before to get the class methods */ - private ensureConnection() { - if (!this?.conn) this.conn = createPool(this.credentials); - } - - /** For `SELECT` and `SHOW` */ - get queryRows() { - this.ensureConnection(); - return this.conn.query.bind(this.conn); - } - - /** For `SELECT` and `SHOW` with `rowAsArray` as `true` */ - get queryRowsAsArray() { - this.ensureConnection(); - return this.conn.query.bind(this.conn); - } - - /** For `INSERT`, `UPDATE`, etc. */ - get queryResult() { - this.ensureConnection(); - return this.conn.query.bind(this.conn); - } - - /** For multiple `INSERT`, `UPDATE`, etc. with `multipleStatements` as `true` */ - get queryResults() { - this.ensureConnection(); - return this.conn.query.bind(this.conn); - } - - /** For `SELECT` and `SHOW` */ - get executeRows() { - this.ensureConnection(); - return this.conn.execute.bind(this.conn); - } - - /** For `SELECT` and `SHOW` with `rowAsArray` as `true` */ - get executeRowsAsArray() { - this.ensureConnection(); - return this.conn.execute.bind(this.conn); - } - - /** For `INSERT`, `UPDATE`, etc. */ - get executeResult() { - this.ensureConnection(); - return this.conn.execute.bind(this.conn); - } - - /** For multiple `INSERT`, `UPDATE`, etc. with `multipleStatements` as `true` */ - get executeResults() { - this.ensureConnection(); - return this.conn.execute.bind(this.conn); - } - - /** Expose the Pool Connection */ - get connection() { - return this.conn; - } -} - -(async () => { - const access: PoolOptions = { - host: '', - user: '', - password: '', - database: '', - }; - - const mysql = new MySQL(access); - - /** Deleting the `users` table, if it exists */ - await mysql.queryResult('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await mysql.queryResult( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await mysql.executeResult( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Getting users */ - const [users] = await mysql.queryRows( - 'SELECT * FROM `users` ORDER BY `name` ASC;', - ); - - users.forEach((user: User) => { - console.log('-----------'); - console.log('id: ', user.id); - console.log('name:', user.name); - }); - - await mysql.connection.end(); -})(); - -/** Output - * - * Inserted: 4 - * ----------- - * id: 4 - * name: Gween - * ----------- - * id: 2 - * name: John - * ----------- - * id: 1 - * name: Josh - * ----------- - * id: 3 - * name: Marie - */ diff --git a/examples/typescript/procedure-call-packet-row-as-array.ts b/examples/typescript/procedure-call-packet-row-as-array.ts deleted file mode 100644 index a6ffd8bd78..0000000000 --- a/examples/typescript/procedure-call-packet-row-as-array.ts +++ /dev/null @@ -1,113 +0,0 @@ -/** - * The types are explicity for learning purpose - * By extending the `RowDataPacket`, you can use your Interface in `query` and `execute` - */ - -import mysql, { - ConnectionOptions, - ProcedureCallPacket, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - -interface User extends RowDataPacket { - /** id */ - 0: number; - /** name */ - 1: string; -} - -const isResultSetHeader = (data: unknown): data is ResultSetHeader => { - if (!data || typeof data !== 'object') return false; - - const keys = [ - 'fieldCount', - 'affectedRows', - 'insertId', - 'info', - 'serverStatus', - 'warningStatus', - 'changedRows', - ]; - - return keys.every((key) => key in data); -}; - -(async () => { - const access: ConnectionOptions = { - host: '', - user: '', - password: '', - database: '', - rowsAsArray: true, - }; - - const conn = await mysql.createConnection(access); - - /** Deleting the `users` table, if it exists */ - await conn.query('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await conn.query( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await conn.execute( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Deleting the `getUsers` procedure, if it exists */ - await conn.query('DROP PROCEDURE IF EXISTS getUsers'); - - /** Creating a procedure to get the users */ - await conn.query(` - CREATE PROCEDURE getUsers() - BEGIN - SELECT * FROM users ORDER BY name ASC; - END - `); - - /** Getting users */ - const [procedureResult] = await conn.query>( - 'CALL getUsers()', - ); - - procedureResult.forEach((users) => { - /** By perform a `SELECT` or `SHOW`, The last item of `procedureResult` always be a `ResultSetHeader` */ - if (isResultSetHeader(users)) { - console.log('----------------'); - console.log('Affected Rows:', users.affectedRows); - } else { - users.forEach((user) => { - console.log('----------------'); - console.log('id: ', user[0]); - console.log('name:', user[1]); - }); - } - }); - - await conn.end(); -})(); - -/** Output - * - * Inserted: 4 - * ---------------- - * id: 4 - * name: Gween - * ---------------- - * id: 2 - * name: John - * ---------------- - * id: 1 - * name: Josh - * ---------------- - * id: 3 - * name: Marie - * ---------------- - * Affected Rows: 0 - */ diff --git a/examples/typescript/procedure-call-packet.ts b/examples/typescript/procedure-call-packet.ts deleted file mode 100644 index b2b2db290a..0000000000 --- a/examples/typescript/procedure-call-packet.ts +++ /dev/null @@ -1,110 +0,0 @@ -/** - * The types are explicity for learning purpose - * By extending the `RowDataPacket`, you can use your Interface in `query` and `execute` - */ - -import mysql, { - ConnectionOptions, - ProcedureCallPacket, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - -interface User extends RowDataPacket { - id: number; - name: string; -} - -const isResultSetHeader = (data: unknown): data is ResultSetHeader => { - if (!data || typeof data !== 'object') return false; - - const keys = [ - 'fieldCount', - 'affectedRows', - 'insertId', - 'info', - 'serverStatus', - 'warningStatus', - 'changedRows', - ]; - - return keys.every((key) => key in data); -}; - -(async () => { - const access: ConnectionOptions = { - host: '', - user: '', - password: '', - database: '', - }; - - const conn = await mysql.createConnection(access); - - /** Deleting the `users` table, if it exists */ - await conn.query('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await conn.query( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await conn.execute( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Deleting the `getUsers` procedure, if it exists */ - await conn.query('DROP PROCEDURE IF EXISTS getUsers'); - - /** Creating a procedure to get the users */ - await conn.query(` - CREATE PROCEDURE getUsers() - BEGIN - SELECT * FROM users ORDER BY name ASC; - END - `); - - /** Getting users */ - const [procedureResult] = await conn.query>( - 'CALL getUsers()', - ); - - procedureResult.forEach((users) => { - /** By perform a `SELECT` or `SHOW`, The last item of `procedureResult` always be a `ResultSetHeader` */ - if (isResultSetHeader(users)) { - console.log('----------------'); - console.log('Affected Rows:', users.affectedRows); - } else { - users.forEach((user) => { - console.log('----------------'); - console.log('id: ', user.id); - console.log('name:', user.name); - }); - } - }); - - await conn.end(); -})(); - -/** Output - * - * Inserted: 4 - * ---------------- - * id: 4 - * name: Gween - * ---------------- - * id: 2 - * name: John - * ---------------- - * id: 1 - * name: Josh - * ---------------- - * id: 3 - * name: Marie - * ---------------- - * Affected Rows: 0 - */ diff --git a/examples/typescript/row-data-packet-multi-statements.ts b/examples/typescript/row-data-packet-multi-statements.ts deleted file mode 100644 index 75b870d93a..0000000000 --- a/examples/typescript/row-data-packet-multi-statements.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * The types are explicity for learning purpose - * By extending the `RowDataPacket`, you can use your Interface in `query` and `execute` - */ - -import mysql, { - ConnectionOptions, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - -interface User extends RowDataPacket { - id: number; - name: string; -} - -(async () => { - const access: ConnectionOptions = { - host: '', - user: '', - password: '', - database: '', - multipleStatements: true, - }; - - const conn = await mysql.createConnection(access); - - /** Deleting the `users` table, if it exists */ - await conn.query('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await conn.query( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await conn.execute( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Getting users */ - const [rows] = await conn.query( - [ - 'SELECT * FROM `users` ORDER BY `name` ASC LIMIT 2;', - 'SELECT * FROM `users` ORDER BY `name` ASC LIMIT 2 OFFSET 2;', - ].join(' '), - ); - - rows.forEach((users) => { - users.forEach((user) => { - console.log('-----------'); - console.log('id: ', user.id); - console.log('name:', user.name); - }); - }); - - await conn.end(); -})(); - -/** Output - * - * Inserted: 4 - * ----------- - * id: 4 - * name: Gween - * ----------- - * id: 2 - * name: John - * ----------- - * id: 1 - * name: Josh - * ----------- - * id: 3 - * name: Marie - */ diff --git a/examples/typescript/row-data-packet-row-as-array-multi-statements.ts b/examples/typescript/row-data-packet-row-as-array-multi-statements.ts deleted file mode 100644 index f544971be8..0000000000 --- a/examples/typescript/row-data-packet-row-as-array-multi-statements.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * The types are explicity for learning purpose - * By extending the `RowDataPacket`, you can use your Interface in `query` and `execute` - */ - -import mysql, { - ConnectionOptions, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - - interface User extends RowDataPacket { - /** id */ - 0: number; - /** name */ - 1: string; - } - -(async () => { - const access: ConnectionOptions = { - host: '', - user: '', - password: '', - database: '', - multipleStatements: true, - rowsAsArray: true, - }; - - const conn = await mysql.createConnection(access); - - /** Deleting the `users` table, if it exists */ - await conn.query('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await conn.query( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await conn.execute( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Getting users */ - const [rows] = await conn.query( - [ - 'SELECT * FROM `users` ORDER BY `name` ASC LIMIT 2;', - 'SELECT * FROM `users` ORDER BY `name` ASC LIMIT 2 OFFSET 2;', - ].join(' '), - ); - - rows.forEach((users) => { - users.forEach((user) => { - console.log('-----------'); - console.log('id: ', user[0]); - console.log('name:', user[1]); - }); - }); - - await conn.end(); -})(); - -/** Output - * - * Inserted: 4 - * ----------- - * id: 4 - * name: Gween - * ----------- - * id: 2 - * name: John - * ----------- - * id: 1 - * name: Josh - * ----------- - * id: 3 - * name: Marie - */ diff --git a/examples/typescript/row-data-packet-row-as-array.ts b/examples/typescript/row-data-packet-row-as-array.ts deleted file mode 100644 index a9abe2c83e..0000000000 --- a/examples/typescript/row-data-packet-row-as-array.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The types are explicity for learning purpose - * By extending the `RowDataPacket`, you can use your Interface in `query` and `execute` - */ - -import mysql, { - ConnectionOptions, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - - interface User extends RowDataPacket { - /** id */ - 0: number; - /** name */ - 1: string; - } - -(async () => { - const access: ConnectionOptions = { - host: '', - user: '', - password: '', - database: '', - rowsAsArray: true, - }; - - const conn = await mysql.createConnection(access); - - /** Deleting the `users` table, if it exists */ - await conn.query('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await conn.query( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await conn.execute( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Getting users */ - const [users] = await conn.query( - 'SELECT * FROM `users` ORDER BY `name` ASC;', - ); - - users.forEach((user) => { - console.log('-----------'); - console.log('id: ', user[0]); - console.log('name:', user[1]); - }); - - await conn.end(); -})(); - -/** Output - * - * Inserted: 4 - * ----------- - * id: 4 - * name: Gween - * ----------- - * id: 2 - * name: John - * ----------- - * id: 1 - * name: Josh - * ----------- - * id: 3 - * name: Marie - */ diff --git a/examples/typescript/row-data-packet.ts b/examples/typescript/row-data-packet.ts deleted file mode 100644 index fbf23c6261..0000000000 --- a/examples/typescript/row-data-packet.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The types are explicity for learning purpose - * By extending the `RowDataPacket`, you can use your Interface in `query` and `execute` - */ - -import mysql, { - ConnectionOptions, - ResultSetHeader, - RowDataPacket, -} from 'mysql2/promise'; - -interface User extends RowDataPacket { - id: number; - name: string; -} - -(async () => { - const access: ConnectionOptions = { - host: '', - user: '', - password: '', - database: '', - }; - - const conn = await mysql.createConnection(access); - - /** Deleting the `users` table, if it exists */ - await conn.query('DROP TABLE IF EXISTS `users`;'); - - /** Creating a minimal user table */ - await conn.query( - 'CREATE TABLE `users` (`id` INT(11) AUTO_INCREMENT, `name` VARCHAR(50), PRIMARY KEY (`id`));', - ); - - /** Inserting some users */ - const [inserted] = await conn.execute( - 'INSERT INTO `users`(`name`) VALUES(?), (?), (?), (?);', - ['Josh', 'John', 'Marie', 'Gween'], - ); - - console.log('Inserted:', inserted.affectedRows); - - /** Getting users */ - const [users] = await conn.query( - 'SELECT * FROM `users` ORDER BY `name` ASC;', - ); - - users.forEach((user) => { - console.log('-----------'); - console.log('id: ', user.id); - console.log('name:', user.name); - }); - - await conn.end(); -})(); - -/** Output - * - * Inserted: 4 - * ----------- - * id: 4 - * name: Gween - * ----------- - * id: 2 - * name: John - * ----------- - * id: 1 - * name: Josh - * ----------- - * id: 3 - * name: Marie - */ diff --git a/lib/commands/query.js b/lib/commands/query.js index 8d643bdc4b..0de52ff05a 100644 --- a/lib/commands/query.js +++ b/lib/commands/query.js @@ -37,7 +37,7 @@ class Query extends Command { then() { const err = - "You have tried to call .then(), .catch(), or invoked await on the result of query that is not a promise, which is a programming error. Try calling con.promise().query(), or require('mysql2/promise') instead of 'mysql2' for a promise-compatible version of the query interface. To learn how to use async/await or Promises check out documentation at https://www.npmjs.com/package/mysql2#using-promise-wrapper, or the mysql2 documentation at https://github.com/sidorares/node-mysql2/tree/master/documentation/en/Promise-Wrapper.md"; + "You have tried to call .then(), .catch(), or invoked await on the result of query that is not a promise, which is a programming error. Try calling con.promise().query(), or require('mysql2/promise') instead of 'mysql2' for a promise-compatible version of the query interface. To learn how to use async/await or Promises check out documentation at https://sidorares.github.io/node-mysql2/docs#using-promise-wrapper, or the mysql2 documentation at https://sidorares.github.io/node-mysql2/docs/documentation/promise-wrapper"; // eslint-disable-next-line console.log(err); throw new Error(err); diff --git a/package.json b/package.json index 015dd36e88..de9fb9b6fb 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,18 @@ "version": "3.6.5", "description": "fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS", "main": "index.js", - "directories": { - "example": "examples" - }, "typings": "typings/mysql/index", "scripts": { "lint": "npm run lint:docs && npm run lint:code", "lint:code": "eslint index.js promise.js index.d.ts promise.d.ts \"typings/**/*.ts\" \"lib/**/*.js\" \"test/**/*.{js,ts}\" \"benchmarks/**/*.js\"", - "lint:docs": "eslint Contributing.md README.md \"documentation/**/*.md\" \"examples/*.js\"", + "lint:docs": "eslint Contributing.md README.md", "test": "node ./test/run.js", "test:builtin-node-runner": "NODE_V8_COVERAGE=./coverage node --test --experimental-test-coverage test/builtin-runner", "test:tsc-build": "cd \"test/tsc-build\" && npx tsc -p \"tsconfig.json\"", "coverage-test": "c8 -r cobertura -r lcov -r text node ./test/run.js", "benchmark": "node ./benchmarks/benchmark.js", - "prettier": "prettier --single-quote --trailing-comma none --write \"{lib,examples,test}/**/*.js\"", - "prettier:docs": "prettier --single-quote --trailing-comma none --write README.md documentation/*", + "prettier": "prettier --single-quote --trailing-comma none --write \"{lib,test}/**/*.js\"", + "prettier:docs": "prettier --single-quote --trailing-comma none --write README.md", "precommit": "lint-staged", "eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check", "wait-port": "wait-on" @@ -32,6 +29,7 @@ "type": "git", "url": "https://github.com/sidorares/node-mysql2" }, + "homepage": "https://sidorares.github.io/node-mysql2/docs", "keywords": [ "mysql", "client", diff --git a/test/common.js b/test/common.js index 9e9d057e9e..8d5321962c 100644 --- a/test/common.js +++ b/test/common.js @@ -16,7 +16,7 @@ if (process.env.MYSQL_USE_TLS === '1') { config.ssl = { rejectUnauthorized: false, ca: fs.readFileSync( - path.join(__dirname, '../examples/ssl/certs/ca.pem'), + path.join(__dirname, '../test/fixtures/ssl/certs/ca.pem'), 'utf-8' ) }; diff --git a/examples/custom-conf/config-file.cnf b/test/fixtures/custom-conf/config-file.cnf similarity index 100% rename from examples/custom-conf/config-file.cnf rename to test/fixtures/custom-conf/config-file.cnf diff --git a/examples/ssl/certs/ca-key.pem b/test/fixtures/ssl/certs/ca-key.pem similarity index 100% rename from examples/ssl/certs/ca-key.pem rename to test/fixtures/ssl/certs/ca-key.pem diff --git a/examples/ssl/certs/ca.pem b/test/fixtures/ssl/certs/ca.pem similarity index 100% rename from examples/ssl/certs/ca.pem rename to test/fixtures/ssl/certs/ca.pem diff --git a/examples/ssl/certs/client-key.pem b/test/fixtures/ssl/certs/client-key.pem similarity index 100% rename from examples/ssl/certs/client-key.pem rename to test/fixtures/ssl/certs/client-key.pem diff --git a/examples/ssl/certs/mkcerts.sh b/test/fixtures/ssl/certs/mkcerts.sh similarity index 100% rename from examples/ssl/certs/mkcerts.sh rename to test/fixtures/ssl/certs/mkcerts.sh diff --git a/examples/ssl/certs/server-cert.pem b/test/fixtures/ssl/certs/server-cert.pem similarity index 100% rename from examples/ssl/certs/server-cert.pem rename to test/fixtures/ssl/certs/server-cert.pem diff --git a/examples/ssl/certs/server-key.pem b/test/fixtures/ssl/certs/server-key.pem similarity index 100% rename from examples/ssl/certs/server-key.pem rename to test/fixtures/ssl/certs/server-key.pem diff --git a/examples/ssl/certs/server-req.pem b/test/fixtures/ssl/certs/server-req.pem similarity index 100% rename from examples/ssl/certs/server-req.pem rename to test/fixtures/ssl/certs/server-req.pem diff --git a/examples/ssl/client-flags.sh b/test/fixtures/ssl/client-flags.sh similarity index 100% rename from examples/ssl/client-flags.sh rename to test/fixtures/ssl/client-flags.sh diff --git a/website/README.md b/website/README.md index a168c13464..b3cadb1dfa 100644 --- a/website/README.md +++ b/website/README.md @@ -1,25 +1,8 @@ # Website -This website is built using [Docusaurus 3](https://docusaurus.io/), a modern static website generator. +This [website](https://sidorares.github.io/node-mysql2/docs) is built using [Docusaurus 3](https://docusaurus.io/), a modern static website generator. -### Installation +# Contributing -```bash -npm i -``` - -### Local Development - -```bash -npm start -``` - -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. - -### Build - -```bash -npm run build -``` - -This command generates static content into the `build` directory and can be served using any static contents hosting service. +Want to improve something in **MySQL2 Documentation Website**? +Please visit the [Website Contributing Guidelines](https://sidorares.github.io/node-mysql2/docs) for detailed instruction on how to get started.