Commit iniziale
This commit is contained in:
643
node_modules/mssql/lib/base/connection-pool.js
generated
vendored
Normal file
643
node_modules/mssql/lib/base/connection-pool.js
generated
vendored
Normal file
@@ -0,0 +1,643 @@
|
||||
'use strict'
|
||||
|
||||
const { EventEmitter } = require('node:events')
|
||||
const debug = require('debug')('mssql:base')
|
||||
const { parseSqlConnectionString } = require('@tediousjs/connection-string')
|
||||
const tarn = require('tarn')
|
||||
const { IDS } = require('../utils')
|
||||
const ConnectionError = require('../error/connection-error')
|
||||
const shared = require('../shared')
|
||||
const clone = require('rfdc/default')
|
||||
const { MSSQLError } = require('../error')
|
||||
|
||||
/**
|
||||
* Class ConnectionPool.
|
||||
*
|
||||
* Internally, each `Connection` instance is a separate pool of TDS connections. Once you create a new `Request`/`Transaction`/`Prepared Statement`, a new TDS connection is acquired from the pool and reserved for desired action. Once the action is complete, connection is released back to the pool.
|
||||
*
|
||||
* @property {Boolean} connected If true, connection is established.
|
||||
* @property {Boolean} connecting If true, connection is being established.
|
||||
*
|
||||
* @fires ConnectionPool#connect
|
||||
* @fires ConnectionPool#close
|
||||
*/
|
||||
|
||||
class ConnectionPool extends EventEmitter {
|
||||
/**
|
||||
* Create new Connection.
|
||||
*
|
||||
* @param {Object|String} config Connection configuration object or connection string.
|
||||
* @param {basicCallback} [callback] A callback which is called after connection has established, or an error has occurred.
|
||||
*/
|
||||
|
||||
constructor (config, callback) {
|
||||
super()
|
||||
|
||||
IDS.add(this, 'ConnectionPool')
|
||||
debug('pool(%d): created', IDS.get(this))
|
||||
|
||||
this._connectStack = []
|
||||
this._closeStack = []
|
||||
|
||||
this._connected = false
|
||||
this._connecting = false
|
||||
this._healthy = false
|
||||
|
||||
if (typeof config === 'string') {
|
||||
try {
|
||||
this.config = this.constructor.parseConnectionString(config)
|
||||
} catch (ex) {
|
||||
if (typeof callback === 'function') {
|
||||
return setImmediate(callback, ex)
|
||||
}
|
||||
throw ex
|
||||
}
|
||||
} else {
|
||||
this.config = clone(config)
|
||||
}
|
||||
|
||||
// set defaults
|
||||
this.config.port = this.config.port || 1433
|
||||
this.config.options = this.config.options || {}
|
||||
this.config.stream = this.config.stream || false
|
||||
this.config.parseJSON = this.config.parseJSON || false
|
||||
this.config.arrayRowMode = this.config.arrayRowMode || false
|
||||
this.config.validateConnection = 'validateConnection' in this.config ? this.config.validateConnection : true
|
||||
|
||||
const namedServer = /^(.*)\\(.*)$/.exec(this.config.server)
|
||||
if (namedServer) {
|
||||
this.config.server = namedServer[1]
|
||||
this.config.options.instanceName = namedServer[2]
|
||||
}
|
||||
|
||||
if (typeof this.config.options.useColumnNames !== 'undefined' && this.config.options.useColumnNames !== true) {
|
||||
const ex = new MSSQLError('Invalid options `useColumnNames`, use `arrayRowMode` instead')
|
||||
if (typeof callback === 'function') {
|
||||
return setImmediate(callback, ex)
|
||||
}
|
||||
throw ex
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this.connect(callback)
|
||||
}
|
||||
}
|
||||
|
||||
get connected () {
|
||||
return this._connected
|
||||
}
|
||||
|
||||
get connecting () {
|
||||
return this._connecting
|
||||
}
|
||||
|
||||
get healthy () {
|
||||
return this._healthy
|
||||
}
|
||||
|
||||
static parseConnectionString (connectionString) {
|
||||
return this._parseConnectionString(connectionString)
|
||||
}
|
||||
|
||||
static _parseAuthenticationType (type, entries) {
|
||||
switch (type.toLowerCase()) {
|
||||
case 'active directory integrated':
|
||||
if (entries.includes('token')) {
|
||||
return 'azure-active-directory-access-token'
|
||||
} else if (['client id', 'client secret', 'tenant id'].every(entry => entries.includes(entry))) {
|
||||
return 'azure-active-directory-service-principal-secret'
|
||||
} else if (['client id', 'msi endpoint', 'msi secret'].every(entry => entries.includes(entry))) {
|
||||
return 'azure-active-directory-msi-app-service'
|
||||
} else if (['client id', 'msi endpoint'].every(entry => entries.includes(entry))) {
|
||||
return 'azure-active-directory-msi-vm'
|
||||
}
|
||||
return 'azure-active-directory-default'
|
||||
case 'active directory password':
|
||||
return 'azure-active-directory-password'
|
||||
case 'ntlm':
|
||||
return 'ntlm'
|
||||
default:
|
||||
return 'default'
|
||||
}
|
||||
}
|
||||
|
||||
static _parseConnectionString (connectionString) {
|
||||
const parsed = parseSqlConnectionString(connectionString, true, true)
|
||||
return Object.entries(parsed).reduce((config, [key, value]) => {
|
||||
switch (key) {
|
||||
case 'application name':
|
||||
break
|
||||
case 'applicationintent':
|
||||
Object.assign(config.options, {
|
||||
readOnlyIntent: value === 'readonly'
|
||||
})
|
||||
break
|
||||
case 'asynchronous processing':
|
||||
break
|
||||
case 'attachdbfilename':
|
||||
break
|
||||
case 'authentication':
|
||||
Object.assign(config, {
|
||||
authentication_type: this._parseAuthenticationType(value, Object.keys(parsed))
|
||||
})
|
||||
break
|
||||
case 'column encryption setting':
|
||||
break
|
||||
case 'connection timeout':
|
||||
Object.assign(config, {
|
||||
connectionTimeout: value * 1000
|
||||
})
|
||||
break
|
||||
case 'connection lifetime':
|
||||
break
|
||||
case 'connectretrycount':
|
||||
break
|
||||
case 'connectretryinterval':
|
||||
Object.assign(config.options, {
|
||||
connectionRetryInterval: value * 1000
|
||||
})
|
||||
break
|
||||
case 'context connection':
|
||||
break
|
||||
case 'client id':
|
||||
Object.assign(config, {
|
||||
clientId: value
|
||||
})
|
||||
break
|
||||
case 'client secret':
|
||||
Object.assign(config, {
|
||||
clientSecret: value
|
||||
})
|
||||
break
|
||||
case 'current language':
|
||||
Object.assign(config.options, {
|
||||
language: value
|
||||
})
|
||||
break
|
||||
case 'data source':
|
||||
{
|
||||
let server = value
|
||||
let instanceName
|
||||
let port = 1433
|
||||
if (/^np:/i.test(server)) {
|
||||
throw new Error('Connection via Named Pipes is not supported.')
|
||||
}
|
||||
if (/^tcp:/i.test(server)) {
|
||||
server = server.substr(4)
|
||||
}
|
||||
const namedServerParts = /^(.*)\\(.*)$/.exec(server)
|
||||
if (namedServerParts) {
|
||||
server = namedServerParts[1].trim()
|
||||
instanceName = namedServerParts[2].trim()
|
||||
}
|
||||
const serverParts = /^(.*),(.*)$/.exec(server)
|
||||
if (serverParts) {
|
||||
server = serverParts[1].trim()
|
||||
port = parseInt(serverParts[2].trim(), 10)
|
||||
} else {
|
||||
const instanceParts = /^(.*),(.*)$/.exec(instanceName)
|
||||
if (instanceParts) {
|
||||
instanceName = instanceParts[1].trim()
|
||||
port = parseInt(instanceParts[2].trim(), 10)
|
||||
}
|
||||
}
|
||||
if (server === '.' || server === '(.)' || server.toLowerCase() === '(localdb)' || server.toLowerCase() === '(local)') {
|
||||
server = 'localhost'
|
||||
}
|
||||
Object.assign(config, {
|
||||
port,
|
||||
server
|
||||
})
|
||||
if (instanceName) {
|
||||
Object.assign(config.options, {
|
||||
instanceName
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'encrypt':
|
||||
Object.assign(config.options, {
|
||||
encrypt: !!value
|
||||
})
|
||||
break
|
||||
case 'enlist':
|
||||
break
|
||||
case 'failover partner':
|
||||
break
|
||||
case 'initial catalog':
|
||||
Object.assign(config, {
|
||||
database: value
|
||||
})
|
||||
break
|
||||
case 'integrated security':
|
||||
break
|
||||
case 'max pool size':
|
||||
Object.assign(config.pool, {
|
||||
max: value
|
||||
})
|
||||
break
|
||||
case 'min pool size':
|
||||
Object.assign(config.pool, {
|
||||
min: value
|
||||
})
|
||||
break
|
||||
case 'msi endpoint':
|
||||
Object.assign(config, {
|
||||
msiEndpoint: value
|
||||
})
|
||||
break
|
||||
case 'msi secret':
|
||||
Object.assign(config, {
|
||||
msiSecret: value
|
||||
})
|
||||
break
|
||||
case 'multipleactiveresultsets':
|
||||
break
|
||||
case 'multisubnetfailover':
|
||||
Object.assign(config.options, {
|
||||
multiSubnetFailover: value
|
||||
})
|
||||
break
|
||||
case 'network library':
|
||||
break
|
||||
case 'packet size':
|
||||
Object.assign(config.options, {
|
||||
packetSize: value
|
||||
})
|
||||
break
|
||||
case 'password':
|
||||
Object.assign(config, {
|
||||
password: value
|
||||
})
|
||||
break
|
||||
case 'persist security info':
|
||||
break
|
||||
case 'poolblockingperiod':
|
||||
break
|
||||
case 'pooling':
|
||||
break
|
||||
case 'replication':
|
||||
break
|
||||
case 'tenant id':
|
||||
Object.assign(config, {
|
||||
tenantId: value
|
||||
})
|
||||
break
|
||||
case 'token':
|
||||
Object.assign(config, {
|
||||
token: value
|
||||
})
|
||||
break
|
||||
case 'transaction binding':
|
||||
Object.assign(config.options, {
|
||||
enableImplicitTransactions: value.toLowerCase() === 'implicit unbind'
|
||||
})
|
||||
break
|
||||
case 'transparentnetworkipresolution':
|
||||
break
|
||||
case 'trustservercertificate':
|
||||
Object.assign(config.options, {
|
||||
trustServerCertificate: value
|
||||
})
|
||||
break
|
||||
case 'type system version':
|
||||
break
|
||||
case 'user id': {
|
||||
let user = value
|
||||
let domain
|
||||
const domainUser = /^(.*)\\(.*)$/.exec(user)
|
||||
if (domainUser) {
|
||||
domain = domainUser[1]
|
||||
user = domainUser[2]
|
||||
}
|
||||
if (domain) {
|
||||
Object.assign(config, {
|
||||
domain
|
||||
})
|
||||
}
|
||||
if (user) {
|
||||
Object.assign(config, {
|
||||
user
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'user instance':
|
||||
break
|
||||
case 'workstation id':
|
||||
Object.assign(config.options, {
|
||||
workstationId: value
|
||||
})
|
||||
break
|
||||
case 'request timeout':
|
||||
Object.assign(config, {
|
||||
requestTimeout: parseInt(value, 10)
|
||||
})
|
||||
break
|
||||
case 'stream':
|
||||
Object.assign(config, {
|
||||
stream: !!value
|
||||
})
|
||||
break
|
||||
case 'useutc':
|
||||
Object.assign(config.options, {
|
||||
useUTC: !!value
|
||||
})
|
||||
break
|
||||
case 'parsejson':
|
||||
Object.assign(config, {
|
||||
parseJSON: !!value
|
||||
})
|
||||
break
|
||||
}
|
||||
return config
|
||||
}, { options: {}, pool: {} })
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire connection from this connection pool.
|
||||
*
|
||||
* @param {ConnectionPool|Transaction|PreparedStatement} requester Requester.
|
||||
* @param {acquireCallback} [callback] A callback which is called after connection has been acquired, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {ConnectionPool|Promise}
|
||||
*/
|
||||
|
||||
acquire (requester, callback) {
|
||||
const acquirePromise = shared.Promise.resolve(this._acquire()).catch(err => {
|
||||
this.emit('error', err)
|
||||
throw err
|
||||
})
|
||||
if (typeof callback === 'function') {
|
||||
acquirePromise.then(connection => callback(null, connection, this.config)).catch(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
return acquirePromise
|
||||
}
|
||||
|
||||
_acquire () {
|
||||
if (!this.pool) {
|
||||
return shared.Promise.reject(new ConnectionError('Connection not yet open.', 'ENOTOPEN'))
|
||||
} else if (this.pool.destroyed) {
|
||||
return shared.Promise.reject(new ConnectionError('Connection is closing', 'ENOTOPEN'))
|
||||
}
|
||||
|
||||
return this.pool.acquire().promise
|
||||
}
|
||||
|
||||
/**
|
||||
* Release connection back to the pool.
|
||||
*
|
||||
* @param {Connection} connection Previously acquired connection.
|
||||
* @return {ConnectionPool}
|
||||
*/
|
||||
|
||||
release (connection) {
|
||||
debug('connection(%d): released', IDS.get(connection))
|
||||
|
||||
if (this.pool) {
|
||||
this.pool.release(connection)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection pool with one active connection. This one initial connection serves as a probe to find out whether the configuration is valid.
|
||||
*
|
||||
* @param {basicCallback} [callback] A callback which is called after connection has established, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {ConnectionPool|Promise}
|
||||
*/
|
||||
|
||||
connect (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
this._connect(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
return this._connect(err => {
|
||||
if (err) return reject(err)
|
||||
resolve(this)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {basicCallback} callback
|
||||
*/
|
||||
|
||||
_connect (callback) {
|
||||
if (this._connected) {
|
||||
debug('pool(%d): already connected, executing connect callback immediately', IDS.get(this))
|
||||
return setImmediate(callback, null, this)
|
||||
}
|
||||
|
||||
this._connectStack.push(callback)
|
||||
|
||||
if (this._connecting) {
|
||||
return
|
||||
}
|
||||
|
||||
this._connecting = true
|
||||
debug('pool(%d): connecting', IDS.get(this))
|
||||
|
||||
// create one test connection to check if everything is ok
|
||||
this._poolCreate().then((connection) => {
|
||||
debug('pool(%d): connected', IDS.get(this))
|
||||
this._healthy = true
|
||||
|
||||
return this._poolDestroy(connection).then(() => {
|
||||
// prepare pool
|
||||
this.pool = new tarn.Pool(
|
||||
Object.assign({
|
||||
create: () => this._poolCreate()
|
||||
.then(connection => {
|
||||
this._healthy = true
|
||||
return connection
|
||||
})
|
||||
.catch(err => {
|
||||
if (this.pool.numUsed() + this.pool.numFree() <= 0) {
|
||||
this._healthy = false
|
||||
}
|
||||
throw err
|
||||
}),
|
||||
validate: this._poolValidate.bind(this),
|
||||
destroy: this._poolDestroy.bind(this),
|
||||
max: 10,
|
||||
min: 0,
|
||||
idleTimeoutMillis: 30000,
|
||||
propagateCreateError: true
|
||||
}, this.config.pool)
|
||||
)
|
||||
|
||||
this._connecting = false
|
||||
this._connected = true
|
||||
})
|
||||
}).then(() => {
|
||||
this._connectStack.forEach((cb) => {
|
||||
setImmediate(cb, null, this)
|
||||
})
|
||||
}).catch(err => {
|
||||
this._connecting = false
|
||||
this._connectStack.forEach((cb) => {
|
||||
setImmediate(cb, err)
|
||||
})
|
||||
}).then(() => {
|
||||
this._connectStack = []
|
||||
})
|
||||
}
|
||||
|
||||
get size () {
|
||||
return this.pool.numFree() + this.pool.numUsed() + this.pool.numPendingCreates()
|
||||
}
|
||||
|
||||
get available () {
|
||||
return this.pool.numFree()
|
||||
}
|
||||
|
||||
get pending () {
|
||||
return this.pool.numPendingAcquires()
|
||||
}
|
||||
|
||||
get borrowed () {
|
||||
return this.pool.numUsed()
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all active connections in the pool.
|
||||
*
|
||||
* @param {basicCallback} [callback] A callback which is called after connection has closed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {ConnectionPool|Promise}
|
||||
*/
|
||||
|
||||
close (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
this._close(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._close(err => {
|
||||
if (err) return reject(err)
|
||||
resolve(this)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {basicCallback} callback
|
||||
*/
|
||||
|
||||
_close (callback) {
|
||||
// we don't allow pools in a connecting state to be closed because it means there are far too many
|
||||
// edge cases to deal with
|
||||
if (this._connecting) {
|
||||
debug('pool(%d): close called while connecting', IDS.get(this))
|
||||
setImmediate(callback, new ConnectionError('Cannot close a pool while it is connecting'))
|
||||
}
|
||||
|
||||
if (!this.pool) {
|
||||
debug('pool(%d): already closed, executing close callback immediately', IDS.get(this))
|
||||
return setImmediate(callback, null)
|
||||
}
|
||||
|
||||
this._closeStack.push(callback)
|
||||
|
||||
if (this.pool.destroyed) return
|
||||
|
||||
this._connecting = this._connected = this._healthy = false
|
||||
|
||||
this.pool.destroy().then(() => {
|
||||
debug('pool(%d): pool closed, removing pool reference and executing close callbacks', IDS.get(this))
|
||||
this.pool = null
|
||||
this._closeStack.forEach(cb => {
|
||||
setImmediate(cb, null)
|
||||
})
|
||||
}).catch(err => {
|
||||
this.pool = null
|
||||
this._closeStack.forEach(cb => {
|
||||
setImmediate(cb, err)
|
||||
})
|
||||
}).then(() => {
|
||||
this._closeStack = []
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new request using this connection.
|
||||
*
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
request () {
|
||||
return new shared.driver.Request(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new transaction using this connection.
|
||||
*
|
||||
* @return {Transaction}
|
||||
*/
|
||||
|
||||
transaction () {
|
||||
return new shared.driver.Transaction(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query using this connection from a tagged template string.
|
||||
*
|
||||
* @variation 1
|
||||
* @param {Array} strings Array of string literals.
|
||||
* @param {...*} keys Values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute the SQL command.
|
||||
*
|
||||
* @variation 2
|
||||
* @param {String} command T-SQL command to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
query () {
|
||||
if (typeof arguments[0] === 'string') { return new shared.driver.Request(this).query(arguments[0], arguments[1]) }
|
||||
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
|
||||
return new shared.driver.Request(this)._template(strings, values, 'query')
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new batch using this connection from a tagged template string.
|
||||
*
|
||||
* @variation 1
|
||||
* @param {Array} strings Array of string literals.
|
||||
* @param {...*} keys Values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute the SQL command.
|
||||
*
|
||||
* @variation 2
|
||||
* @param {String} command T-SQL command to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
batch () {
|
||||
if (typeof arguments[0] === 'string') { return new shared.driver.Request(this).batch(arguments[0], arguments[1]) }
|
||||
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
|
||||
return new shared.driver.Request(this)._template(strings, values, 'batch')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConnectionPool
|
||||
138
node_modules/mssql/lib/base/index.js
generated
vendored
Normal file
138
node_modules/mssql/lib/base/index.js
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
'use strict'
|
||||
|
||||
const ConnectionPool = require('./connection-pool')
|
||||
const PreparedStatement = require('./prepared-statement')
|
||||
const Request = require('./request')
|
||||
const Transaction = require('./transaction')
|
||||
const { ConnectionError, TransactionError, RequestError, PreparedStatementError, MSSQLError } = require('../error')
|
||||
const shared = require('../shared')
|
||||
const Table = require('../table')
|
||||
const ISOLATION_LEVEL = require('../isolationlevel')
|
||||
const { TYPES } = require('../datatypes')
|
||||
const { connect, close, on, off, removeListener, query, batch } = require('../global-connection')
|
||||
|
||||
module.exports = {
|
||||
ConnectionPool,
|
||||
Transaction,
|
||||
Request,
|
||||
PreparedStatement,
|
||||
ConnectionError,
|
||||
TransactionError,
|
||||
RequestError,
|
||||
PreparedStatementError,
|
||||
MSSQLError,
|
||||
driver: shared.driver,
|
||||
exports: {
|
||||
ConnectionError,
|
||||
TransactionError,
|
||||
RequestError,
|
||||
PreparedStatementError,
|
||||
MSSQLError,
|
||||
Table,
|
||||
ISOLATION_LEVEL,
|
||||
TYPES,
|
||||
MAX: 65535, // (1 << 16) - 1
|
||||
map: shared.map,
|
||||
getTypeByValue: shared.getTypeByValue,
|
||||
connect,
|
||||
close,
|
||||
on,
|
||||
removeListener,
|
||||
off,
|
||||
query,
|
||||
batch
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(module.exports, 'Promise', {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
return shared.Promise
|
||||
},
|
||||
set: (value) => {
|
||||
shared.Promise = value
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(module.exports, 'valueHandler', {
|
||||
enumerable: true,
|
||||
value: shared.valueHandler,
|
||||
writable: false,
|
||||
configurable: false
|
||||
})
|
||||
|
||||
for (const key in TYPES) {
|
||||
const value = TYPES[key]
|
||||
module.exports.exports[key] = value
|
||||
module.exports.exports[key.toUpperCase()] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* @callback Request~requestCallback
|
||||
* @param {Error} err Error on error, otherwise null.
|
||||
* @param {Object} [result] Request result.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback Request~bulkCallback
|
||||
* @param {Error} err Error on error, otherwise null.
|
||||
* @param {Number} [rowsAffected] Number of affected rows.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback basicCallback
|
||||
* @param {Error} err Error on error, otherwise null.
|
||||
* @param {Connection} [connection] Acquired connection.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback acquireCallback
|
||||
* @param {Error} err Error on error, otherwise null.
|
||||
* @param {Connection} [connection] Acquired connection.
|
||||
* @param {Object} [config] Connection config
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched after connection has established.
|
||||
* @event ConnectionPool#connect
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched after connection has closed a pool (by calling close).
|
||||
* @event ConnectionPool#close
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched when transaction begin.
|
||||
* @event Transaction#begin
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched on successful commit.
|
||||
* @event Transaction#commit
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched on successful rollback.
|
||||
* @event Transaction#rollback
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched when metadata for new recordset are parsed.
|
||||
* @event Request#recordset
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched when new row is parsed.
|
||||
* @event Request#row
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched when request is complete.
|
||||
* @event Request#done
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dispatched on error.
|
||||
* @event Request#error
|
||||
*/
|
||||
384
node_modules/mssql/lib/base/prepared-statement.js
generated
vendored
Normal file
384
node_modules/mssql/lib/base/prepared-statement.js
generated
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')('mssql:base')
|
||||
const { EventEmitter } = require('node:events')
|
||||
const { IDS, objectHasProperty } = require('../utils')
|
||||
const globalConnection = require('../global-connection')
|
||||
const { TransactionError, PreparedStatementError } = require('../error')
|
||||
const shared = require('../shared')
|
||||
const { TYPES, declare } = require('../datatypes')
|
||||
|
||||
/**
|
||||
* Class PreparedStatement.
|
||||
*
|
||||
* IMPORTANT: Rememeber that each prepared statement means one reserved connection from the pool. Don't forget to unprepare a prepared statement!
|
||||
*
|
||||
* @property {String} statement Prepared SQL statement.
|
||||
*/
|
||||
|
||||
class PreparedStatement extends EventEmitter {
|
||||
/**
|
||||
* Creates a new Prepared Statement.
|
||||
*
|
||||
* @param {ConnectionPool|Transaction} [holder]
|
||||
*/
|
||||
|
||||
constructor (parent) {
|
||||
super()
|
||||
|
||||
IDS.add(this, 'PreparedStatement')
|
||||
debug('ps(%d): created', IDS.get(this))
|
||||
|
||||
this.parent = parent || globalConnection.pool
|
||||
this._handle = 0
|
||||
this.prepared = false
|
||||
this.parameters = {}
|
||||
}
|
||||
|
||||
get config () {
|
||||
return this.parent.config
|
||||
}
|
||||
|
||||
get connected () {
|
||||
return this.parent.connected
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire connection from connection pool.
|
||||
*
|
||||
* @param {Request} request Request.
|
||||
* @param {ConnectionPool~acquireCallback} [callback] A callback which is called after connection has established, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {PreparedStatement|Promise}
|
||||
*/
|
||||
|
||||
acquire (request, callback) {
|
||||
if (!this._acquiredConnection) {
|
||||
setImmediate(callback, new PreparedStatementError('Statement is not prepared. Call prepare() first.', 'ENOTPREPARED'))
|
||||
return this
|
||||
}
|
||||
|
||||
if (this._activeRequest) {
|
||||
setImmediate(callback, new TransactionError("Can't acquire connection for the request. There is another request in progress.", 'EREQINPROG'))
|
||||
return this
|
||||
}
|
||||
|
||||
this._activeRequest = request
|
||||
setImmediate(callback, null, this._acquiredConnection, this._acquiredConfig)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Release connection back to the pool.
|
||||
*
|
||||
* @param {Connection} connection Previously acquired connection.
|
||||
* @return {PreparedStatement}
|
||||
*/
|
||||
|
||||
release (connection) {
|
||||
if (connection === this._acquiredConnection) {
|
||||
this._activeRequest = null
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an input parameter to the prepared statement.
|
||||
*
|
||||
* @param {String} name Name of the input parameter without @ char.
|
||||
* @param {*} type SQL data type of input parameter.
|
||||
* @return {PreparedStatement}
|
||||
*/
|
||||
|
||||
input (name, type) {
|
||||
if (/--| |\/\*|\*\/|'/.test(name)) {
|
||||
throw new PreparedStatementError(`SQL injection warning for param '${name}'`, 'EINJECT')
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
throw new PreparedStatementError('Invalid number of arguments. 2 arguments expected.', 'EARGS')
|
||||
}
|
||||
|
||||
if (type instanceof Function) {
|
||||
type = type()
|
||||
}
|
||||
|
||||
if (objectHasProperty(this.parameters, name)) {
|
||||
throw new PreparedStatementError(`The parameter name ${name} has already been declared. Parameter names must be unique`, 'EDUPEPARAM')
|
||||
}
|
||||
|
||||
this.parameters[name] = {
|
||||
name,
|
||||
type: type.type,
|
||||
io: 1,
|
||||
length: type.length,
|
||||
scale: type.scale,
|
||||
precision: type.precision,
|
||||
tvpType: type.tvpType
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace an input parameter on the request.
|
||||
*
|
||||
* @param {String} name Name of the input parameter without @ char.
|
||||
* @param {*} [type] SQL data type of input parameter. If you omit type, module automaticaly decide which SQL data type should be used based on JS data type.
|
||||
* @param {*} value Input parameter value. `undefined` and `NaN` values are automatically converted to `null` values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
replaceInput (name, type, value) {
|
||||
delete this.parameters[name]
|
||||
|
||||
return this.input(name, type, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an output parameter to the prepared statement.
|
||||
*
|
||||
* @param {String} name Name of the output parameter without @ char.
|
||||
* @param {*} type SQL data type of output parameter.
|
||||
* @return {PreparedStatement}
|
||||
*/
|
||||
|
||||
output (name, type) {
|
||||
if (/--| |\/\*|\*\/|'/.test(name)) {
|
||||
throw new PreparedStatementError(`SQL injection warning for param '${name}'`, 'EINJECT')
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
throw new PreparedStatementError('Invalid number of arguments. 2 arguments expected.', 'EARGS')
|
||||
}
|
||||
|
||||
if (type instanceof Function) type = type()
|
||||
|
||||
if (objectHasProperty(this.parameters, name)) {
|
||||
throw new PreparedStatementError(`The parameter name ${name} has already been declared. Parameter names must be unique`, 'EDUPEPARAM')
|
||||
}
|
||||
|
||||
this.parameters[name] = {
|
||||
name,
|
||||
type: type.type,
|
||||
io: 2,
|
||||
length: type.length,
|
||||
scale: type.scale,
|
||||
precision: type.precision
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace an output parameter on the request.
|
||||
*
|
||||
* @param {String} name Name of the output parameter without @ char.
|
||||
* @param {*} type SQL data type of output parameter.
|
||||
* @return {PreparedStatement}
|
||||
*/
|
||||
|
||||
replaceOutput (name, type) {
|
||||
delete this.parameters[name]
|
||||
|
||||
return this.output(name, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a statement.
|
||||
*
|
||||
* @param {String} statement SQL statement to prepare.
|
||||
* @param {basicCallback} [callback] A callback which is called after preparation has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {PreparedStatement|Promise}
|
||||
*/
|
||||
|
||||
prepare (statement, callback) {
|
||||
if (typeof callback === 'function') {
|
||||
this._prepare(statement, callback)
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._prepare(statement, err => {
|
||||
if (err) return reject(err)
|
||||
resolve(this)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {String} statement
|
||||
* @param {basicCallback} callback
|
||||
*/
|
||||
|
||||
_prepare (statement, callback) {
|
||||
debug('ps(%d): prepare', IDS.get(this))
|
||||
|
||||
if (typeof statement === 'function') {
|
||||
callback = statement
|
||||
statement = undefined
|
||||
}
|
||||
|
||||
if (this.prepared) {
|
||||
return setImmediate(callback, new PreparedStatementError('Statement is already prepared.', 'EALREADYPREPARED'))
|
||||
}
|
||||
|
||||
this.statement = statement || this.statement
|
||||
|
||||
this.parent.acquire(this, (err, connection, config) => {
|
||||
if (err) return callback(err)
|
||||
|
||||
this._acquiredConnection = connection
|
||||
this._acquiredConfig = config
|
||||
|
||||
const req = new shared.driver.Request(this)
|
||||
req.stream = false
|
||||
req.output('handle', TYPES.Int)
|
||||
req.input('params', TYPES.NVarChar, ((() => {
|
||||
const result = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
result.push(`@${name} ${declare(param.type, param)}${param.io === 2 ? ' output' : ''}`)
|
||||
}
|
||||
return result
|
||||
})()).join(','))
|
||||
req.input('stmt', TYPES.NVarChar, this.statement)
|
||||
req.execute('sp_prepare', (err, result) => {
|
||||
if (err) {
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
debug('ps(%d): prepared', IDS.get(this))
|
||||
|
||||
this._handle = result.output.handle
|
||||
this.prepared = true
|
||||
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a prepared statement.
|
||||
*
|
||||
* @param {Object} values An object whose names correspond to the names of parameters that were added to the prepared statement before it was prepared.
|
||||
* @param {basicCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
execute (values, callback) {
|
||||
if (this.stream || (typeof callback === 'function')) {
|
||||
return this._execute(values, callback)
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._execute(values, (err, recordset) => {
|
||||
if (err) return reject(err)
|
||||
resolve(recordset)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} values
|
||||
* @param {basicCallback} callback
|
||||
*/
|
||||
|
||||
_execute (values, callback) {
|
||||
const req = new shared.driver.Request(this)
|
||||
req.stream = this.stream
|
||||
req.arrayRowMode = this.arrayRowMode
|
||||
req.input('handle', TYPES.Int, this._handle)
|
||||
|
||||
// copy parameters with new values
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
req.parameters[name] = {
|
||||
name,
|
||||
type: param.type,
|
||||
io: param.io,
|
||||
value: values[name],
|
||||
length: param.length,
|
||||
scale: param.scale,
|
||||
precision: param.precision
|
||||
}
|
||||
}
|
||||
|
||||
req.execute('sp_execute', (err, result) => {
|
||||
if (err) return callback(err)
|
||||
|
||||
callback(null, result)
|
||||
})
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
/**
|
||||
* Unprepare a prepared statement.
|
||||
*
|
||||
* @param {basicCallback} [callback] A callback which is called after unpreparation has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {PreparedStatement|Promise}
|
||||
*/
|
||||
|
||||
unprepare (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
this._unprepare(callback)
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._unprepare(err => {
|
||||
if (err) return reject(err)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {basicCallback} callback
|
||||
*/
|
||||
|
||||
_unprepare (callback) {
|
||||
debug('ps(%d): unprepare', IDS.get(this))
|
||||
|
||||
if (!this.prepared) {
|
||||
return setImmediate(callback, new PreparedStatementError('Statement is not prepared. Call prepare() first.', 'ENOTPREPARED'))
|
||||
}
|
||||
|
||||
if (this._activeRequest) {
|
||||
return setImmediate(callback, new TransactionError("Can't unprepare the statement. There is a request in progress.", 'EREQINPROG'))
|
||||
}
|
||||
|
||||
const req = new shared.driver.Request(this)
|
||||
req.stream = false
|
||||
req.input('handle', TYPES.Int, this._handle)
|
||||
req.execute('sp_unprepare', err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
this._handle = 0
|
||||
this.prepared = false
|
||||
|
||||
debug('ps(%d): unprepared', IDS.get(this))
|
||||
|
||||
return callback(null)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PreparedStatement
|
||||
642
node_modules/mssql/lib/base/request.js
generated
vendored
Normal file
642
node_modules/mssql/lib/base/request.js
generated
vendored
Normal file
@@ -0,0 +1,642 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')('mssql:base')
|
||||
const { EventEmitter } = require('node:events')
|
||||
const { Readable } = require('node:stream')
|
||||
const { IDS, objectHasProperty } = require('../utils')
|
||||
const globalConnection = require('../global-connection')
|
||||
const { RequestError, ConnectionError } = require('../error')
|
||||
const { TYPES } = require('../datatypes')
|
||||
const shared = require('../shared')
|
||||
|
||||
/**
|
||||
* Class Request.
|
||||
*
|
||||
* @property {Transaction} transaction Reference to transaction when request was created in transaction.
|
||||
* @property {*} parameters Collection of input and output parameters.
|
||||
* @property {Boolean} canceled `true` if request was canceled.
|
||||
*
|
||||
* @fires Request#recordset
|
||||
* @fires Request#row
|
||||
* @fires Request#done
|
||||
* @fires Request#error
|
||||
*/
|
||||
|
||||
class Request extends EventEmitter {
|
||||
/**
|
||||
* Create new Request.
|
||||
*
|
||||
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} parent If omitted, global connection is used instead.
|
||||
*/
|
||||
|
||||
constructor (parent) {
|
||||
super()
|
||||
|
||||
IDS.add(this, 'Request')
|
||||
debug('request(%d): created', IDS.get(this))
|
||||
|
||||
this.canceled = false
|
||||
this._paused = false
|
||||
this.parent = parent || globalConnection.pool
|
||||
this.parameters = {}
|
||||
this.stream = null
|
||||
this.arrayRowMode = null
|
||||
}
|
||||
|
||||
get paused () {
|
||||
return this._paused
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sql string and set input parameters from tagged template string.
|
||||
*
|
||||
* @param {Template literal} template
|
||||
* @return {String}
|
||||
*/
|
||||
template () {
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
return this._template(strings, values)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch request from tagged template string.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} strings
|
||||
* @param {Array} values
|
||||
* @param {String} [method] If provided, method is automatically called with serialized command on this object.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
_template (strings, values, method) {
|
||||
const command = [strings[0]]
|
||||
|
||||
for (let index = 0; index < values.length; index++) {
|
||||
const value = values[index]
|
||||
// if value is an array, prepare each items as it's own comma separated parameter
|
||||
if (Array.isArray(value)) {
|
||||
for (let parameterIndex = 0; parameterIndex < value.length; parameterIndex++) {
|
||||
this.input(`param${index + 1}_${parameterIndex}`, value[parameterIndex])
|
||||
command.push(`@param${index + 1}_${parameterIndex}`)
|
||||
if (parameterIndex < value.length - 1) {
|
||||
command.push(', ')
|
||||
}
|
||||
}
|
||||
command.push(strings[index + 1])
|
||||
} else {
|
||||
this.input(`param${index + 1}`, value)
|
||||
command.push(`@param${index + 1}`, strings[index + 1])
|
||||
}
|
||||
}
|
||||
|
||||
if (method) {
|
||||
return this[method](command.join(''))
|
||||
} else {
|
||||
return command.join('')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an input parameter to the request.
|
||||
*
|
||||
* @param {String} name Name of the input parameter without @ char.
|
||||
* @param {*} [type] SQL data type of input parameter. If you omit type, module automaticaly decide which SQL data type should be used based on JS data type.
|
||||
* @param {*} value Input parameter value. `undefined` and `NaN` values are automatically converted to `null` values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
input (name, type, value) {
|
||||
if (/--| |\/\*|\*\/|'/.test(name)) {
|
||||
throw new RequestError(`SQL injection warning for param '${name}'`, 'EINJECT')
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
throw new RequestError('Invalid number of arguments. At least 2 arguments expected.', 'EARGS')
|
||||
} else if (arguments.length === 2) {
|
||||
value = type
|
||||
type = shared.getTypeByValue(value)
|
||||
}
|
||||
|
||||
// support for custom data types
|
||||
if (value && typeof value.valueOf === 'function' && !(value instanceof Date)) value = value.valueOf()
|
||||
|
||||
if (value === undefined) value = null // undefined to null
|
||||
if (typeof value === 'number' && isNaN(value)) value = null // NaN to null
|
||||
if (type instanceof Function) type = type()
|
||||
|
||||
if (objectHasProperty(this.parameters, name)) {
|
||||
throw new RequestError(`The parameter name ${name} has already been declared. Parameter names must be unique`, 'EDUPEPARAM')
|
||||
}
|
||||
|
||||
this.parameters[name] = {
|
||||
name,
|
||||
type: type.type,
|
||||
io: 1,
|
||||
value,
|
||||
length: type.length,
|
||||
scale: type.scale,
|
||||
precision: type.precision,
|
||||
tvpType: type.tvpType
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace an input parameter on the request.
|
||||
*
|
||||
* @param {String} name Name of the input parameter without @ char.
|
||||
* @param {*} [type] SQL data type of input parameter. If you omit type, module automaticaly decide which SQL data type should be used based on JS data type.
|
||||
* @param {*} value Input parameter value. `undefined` and `NaN` values are automatically converted to `null` values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
replaceInput (name, type, value) {
|
||||
delete this.parameters[name]
|
||||
|
||||
return this.input(name, type, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an output parameter to the request.
|
||||
*
|
||||
* @param {String} name Name of the output parameter without @ char.
|
||||
* @param {*} type SQL data type of output parameter.
|
||||
* @param {*} [value] Output parameter value initial value. `undefined` and `NaN` values are automatically converted to `null` values. Optional.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
output (name, type, value) {
|
||||
if (!type) { type = TYPES.NVarChar }
|
||||
|
||||
if (/--| |\/\*|\*\/|'/.test(name)) {
|
||||
throw new RequestError(`SQL injection warning for param '${name}'`, 'EINJECT')
|
||||
}
|
||||
|
||||
if ((type === TYPES.Text) || (type === TYPES.NText) || (type === TYPES.Image)) {
|
||||
throw new RequestError('Deprecated types (Text, NText, Image) are not supported as OUTPUT parameters.', 'EDEPRECATED')
|
||||
}
|
||||
|
||||
// support for custom data types
|
||||
if (value && typeof value.valueOf === 'function' && !(value instanceof Date)) value = value.valueOf()
|
||||
|
||||
if (value === undefined) value = null // undefined to null
|
||||
if (typeof value === 'number' && isNaN(value)) value = null // NaN to null
|
||||
if (type instanceof Function) type = type()
|
||||
|
||||
if (objectHasProperty(this.parameters, name)) {
|
||||
throw new RequestError(`The parameter name ${name} has already been declared. Parameter names must be unique`, 'EDUPEPARAM')
|
||||
}
|
||||
|
||||
this.parameters[name] = {
|
||||
name,
|
||||
type: type.type,
|
||||
io: 2,
|
||||
value,
|
||||
length: type.length,
|
||||
scale: type.scale,
|
||||
precision: type.precision
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace an output parameter on the request.
|
||||
*
|
||||
* @param {String} name Name of the output parameter without @ char.
|
||||
* @param {*} type SQL data type of output parameter.
|
||||
* @param {*} [value] Output parameter value initial value. `undefined` and `NaN` values are automatically converted to `null` values. Optional.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
replaceOutput (name, type, value) {
|
||||
delete this.parameters[name]
|
||||
|
||||
return this.output(name, type, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the SQL batch.
|
||||
*
|
||||
* @param {String} batch T-SQL batch to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
batch (batch, callback) {
|
||||
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
||||
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
||||
this.rowsAffected = 0
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this._batch(batch, (err, recordsets, output, rowsAffected) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
err = null
|
||||
|
||||
this.emit('done', {
|
||||
output,
|
||||
rowsAffected
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return callback(err)
|
||||
callback(null, {
|
||||
recordsets,
|
||||
recordset: recordsets && recordsets[0],
|
||||
output,
|
||||
rowsAffected
|
||||
})
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
// Check is method was called as tagged template
|
||||
if (typeof batch === 'object') {
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
batch = this._template(strings, values)
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._batch(batch, (err, recordsets, output, rowsAffected) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
err = null
|
||||
|
||||
this.emit('done', {
|
||||
output,
|
||||
rowsAffected
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return reject(err)
|
||||
resolve({
|
||||
recordsets,
|
||||
recordset: recordsets && recordsets[0],
|
||||
output,
|
||||
rowsAffected
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {String} batch
|
||||
* @param {Request~requestCallback} callback
|
||||
*/
|
||||
|
||||
_batch (batch, callback) {
|
||||
if (!this.parent) {
|
||||
return setImmediate(callback, new RequestError('No connection is specified for that request.', 'ENOCONN'))
|
||||
}
|
||||
|
||||
if (!this.parent.connected) {
|
||||
return setImmediate(callback, new ConnectionError('Connection is closed.', 'ECONNCLOSED'))
|
||||
}
|
||||
|
||||
this.canceled = false
|
||||
setImmediate(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk load.
|
||||
*
|
||||
* @param {Table} table SQL table.
|
||||
* @param {object} [options] Options to be passed to the underlying driver (tedious only).
|
||||
* @param {Request~bulkCallback} [callback] A callback which is called after bulk load has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
bulk (table, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options
|
||||
options = {}
|
||||
} else if (typeof options === 'undefined') {
|
||||
options = {}
|
||||
}
|
||||
|
||||
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
||||
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
||||
|
||||
if (this.stream || typeof callback === 'function') {
|
||||
this._bulk(table, options, (err, rowsAffected) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
return this.emit('done', {
|
||||
rowsAffected
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return callback(err)
|
||||
callback(null, {
|
||||
rowsAffected
|
||||
})
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._bulk(table, options, (err, rowsAffected) => {
|
||||
if (err) return reject(err)
|
||||
resolve({
|
||||
rowsAffected
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Table} table
|
||||
* @param {object} options
|
||||
* @param {Request~bulkCallback} callback
|
||||
*/
|
||||
|
||||
_bulk (table, options, callback) {
|
||||
if (!this.parent) {
|
||||
return setImmediate(callback, new RequestError('No connection is specified for that request.', 'ENOCONN'))
|
||||
}
|
||||
|
||||
if (!this.parent.connected) {
|
||||
return setImmediate(callback, new ConnectionError('Connection is closed.', 'ECONNCLOSED'))
|
||||
}
|
||||
|
||||
this.canceled = false
|
||||
setImmediate(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap original request in a Readable stream that supports back pressure and return.
|
||||
* It also sets request to `stream` mode and pulls all rows from all recordsets to a given stream.
|
||||
*
|
||||
* @param {Object} streamOptions - optional options to configure the readable stream with like highWaterMark
|
||||
* @return {Stream}
|
||||
*/
|
||||
toReadableStream (streamOptions = {}) {
|
||||
this.stream = true
|
||||
this.pause()
|
||||
const readableStream = new Readable({
|
||||
...streamOptions,
|
||||
objectMode: true,
|
||||
read: (/* size */) => {
|
||||
this.resume()
|
||||
}
|
||||
})
|
||||
this.on('row', (row) => {
|
||||
if (!readableStream.push(row)) {
|
||||
this.pause()
|
||||
}
|
||||
})
|
||||
this.on('error', (error) => {
|
||||
readableStream.emit('error', error)
|
||||
})
|
||||
this.on('done', () => {
|
||||
readableStream.push(null)
|
||||
})
|
||||
return readableStream
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap original request in a Readable stream that supports back pressure and pipe to the Writable stream.
|
||||
* It also sets request to `stream` mode and pulls all rows from all recordsets to a given stream.
|
||||
*
|
||||
* @param {Stream} stream Stream to pipe data into.
|
||||
* @return {Stream}
|
||||
*/
|
||||
pipe (writableStream) {
|
||||
const readableStream = this.toReadableStream()
|
||||
return readableStream.pipe(writableStream)
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the SQL command.
|
||||
*
|
||||
* @param {String} command T-SQL command to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
query (command, callback) {
|
||||
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
||||
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
||||
this.rowsAffected = 0
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this._query(command, (err, recordsets, output, rowsAffected, columns) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
err = null
|
||||
|
||||
this.emit('done', {
|
||||
output,
|
||||
rowsAffected
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return callback(err)
|
||||
const result = {
|
||||
recordsets,
|
||||
recordset: recordsets && recordsets[0],
|
||||
output,
|
||||
rowsAffected
|
||||
}
|
||||
if (this.arrayRowMode) result.columns = columns
|
||||
callback(null, result)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
// Check is method was called as tagged template
|
||||
if (typeof command === 'object') {
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
command = this._template(strings, values)
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._query(command, (err, recordsets, output, rowsAffected, columns) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
err = null
|
||||
|
||||
this.emit('done', {
|
||||
output,
|
||||
rowsAffected
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return reject(err)
|
||||
const result = {
|
||||
recordsets,
|
||||
recordset: recordsets && recordsets[0],
|
||||
output,
|
||||
rowsAffected
|
||||
}
|
||||
if (this.arrayRowMode) result.columns = columns
|
||||
resolve(result)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {String} command
|
||||
* @param {Request~bulkCallback} callback
|
||||
*/
|
||||
|
||||
_query (command, callback) {
|
||||
if (!this.parent) {
|
||||
return setImmediate(callback, new RequestError('No connection is specified for that request.', 'ENOCONN'))
|
||||
}
|
||||
|
||||
if (!this.parent.connected) {
|
||||
return setImmediate(callback, new ConnectionError('Connection is closed.', 'ECONNCLOSED'))
|
||||
}
|
||||
|
||||
this.canceled = false
|
||||
setImmediate(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a stored procedure.
|
||||
*
|
||||
* @param {String} procedure Name of the stored procedure to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
execute (command, callback) {
|
||||
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
||||
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
||||
this.rowsAffected = 0
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this._execute(command, (err, recordsets, output, returnValue, rowsAffected, columns) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
err = null
|
||||
|
||||
this.emit('done', {
|
||||
output,
|
||||
rowsAffected,
|
||||
returnValue
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return callback(err)
|
||||
const result = {
|
||||
recordsets,
|
||||
recordset: recordsets && recordsets[0],
|
||||
output,
|
||||
rowsAffected,
|
||||
returnValue
|
||||
}
|
||||
if (this.arrayRowMode) result.columns = columns
|
||||
callback(null, result)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._execute(command, (err, recordsets, output, returnValue, rowsAffected, columns) => {
|
||||
if (this.stream) {
|
||||
if (err) this.emit('error', err)
|
||||
err = null
|
||||
|
||||
this.emit('done', {
|
||||
output,
|
||||
rowsAffected,
|
||||
returnValue
|
||||
})
|
||||
}
|
||||
|
||||
if (err) return reject(err)
|
||||
const result = {
|
||||
recordsets,
|
||||
recordset: recordsets && recordsets[0],
|
||||
output,
|
||||
rowsAffected,
|
||||
returnValue
|
||||
}
|
||||
if (this.arrayRowMode) result.columns = columns
|
||||
resolve(result)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {String} procedure
|
||||
* @param {Request~bulkCallback} callback
|
||||
*/
|
||||
|
||||
_execute (procedure, callback) {
|
||||
if (!this.parent) {
|
||||
return setImmediate(callback, new RequestError('No connection is specified for that request.', 'ENOCONN'))
|
||||
}
|
||||
|
||||
if (!this.parent.connected) {
|
||||
return setImmediate(callback, new ConnectionError('Connection is closed.', 'ECONNCLOSED'))
|
||||
}
|
||||
|
||||
this.canceled = false
|
||||
setImmediate(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel currently executed request.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
cancel () {
|
||||
this._cancel()
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
_cancel () {
|
||||
this.canceled = true
|
||||
}
|
||||
|
||||
pause () {
|
||||
if (this.stream) {
|
||||
this._pause()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_pause () {
|
||||
this._paused = true
|
||||
}
|
||||
|
||||
resume () {
|
||||
if (this.stream) {
|
||||
this._resume()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_resume () {
|
||||
this._paused = false
|
||||
}
|
||||
|
||||
_setCurrentRequest (request) {
|
||||
this._currentRequest = request
|
||||
if (this._paused) {
|
||||
this.pause()
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Request
|
||||
265
node_modules/mssql/lib/base/transaction.js
generated
vendored
Normal file
265
node_modules/mssql/lib/base/transaction.js
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')('mssql:base')
|
||||
const { EventEmitter } = require('node:events')
|
||||
const { IDS } = require('../utils')
|
||||
const globalConnection = require('../global-connection')
|
||||
const { TransactionError } = require('../error')
|
||||
const shared = require('../shared')
|
||||
const ISOLATION_LEVEL = require('../isolationlevel')
|
||||
|
||||
/**
|
||||
* Class Transaction.
|
||||
*
|
||||
* @property {Number} isolationLevel Controls the locking and row versioning behavior of TSQL statements issued by a connection. READ_COMMITTED by default.
|
||||
* @property {String} name Transaction name. Empty string by default.
|
||||
*
|
||||
* @fires Transaction#begin
|
||||
* @fires Transaction#commit
|
||||
* @fires Transaction#rollback
|
||||
*/
|
||||
|
||||
class Transaction extends EventEmitter {
|
||||
/**
|
||||
* Create new Transaction.
|
||||
*
|
||||
* @param {Connection} [parent] If ommited, global connection is used instead.
|
||||
*/
|
||||
|
||||
constructor (parent) {
|
||||
super()
|
||||
|
||||
IDS.add(this, 'Transaction')
|
||||
debug('transaction(%d): created', IDS.get(this))
|
||||
|
||||
this.parent = parent || globalConnection.pool
|
||||
this.isolationLevel = Transaction.defaultIsolationLevel
|
||||
this.name = ''
|
||||
}
|
||||
|
||||
get config () {
|
||||
return this.parent.config
|
||||
}
|
||||
|
||||
get connected () {
|
||||
return this.parent.connected
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire connection from connection pool.
|
||||
*
|
||||
* @param {Request} request Request.
|
||||
* @param {ConnectionPool~acquireCallback} [callback] A callback which is called after connection has established, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Transaction|Promise}
|
||||
*/
|
||||
|
||||
acquire (request, callback) {
|
||||
if (!this._acquiredConnection) {
|
||||
setImmediate(callback, new TransactionError('Transaction has not begun. Call begin() first.', 'ENOTBEGUN'))
|
||||
return this
|
||||
}
|
||||
|
||||
if (this._activeRequest) {
|
||||
setImmediate(callback, new TransactionError("Can't acquire connection for the request. There is another request in progress.", 'EREQINPROG'))
|
||||
return this
|
||||
}
|
||||
|
||||
this._activeRequest = request
|
||||
setImmediate(callback, null, this._acquiredConnection, this._acquiredConfig)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Release connection back to the pool.
|
||||
*
|
||||
* @param {Connection} connection Previously acquired connection.
|
||||
* @return {Transaction}
|
||||
*/
|
||||
|
||||
release (connection) {
|
||||
if (connection === this._acquiredConnection) {
|
||||
this._activeRequest = null
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a transaction.
|
||||
*
|
||||
* @param {Number} [isolationLevel] Controls the locking and row versioning behavior of TSQL statements issued by a connection.
|
||||
* @param {basicCallback} [callback] A callback which is called after transaction has began, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Transaction|Promise}
|
||||
*/
|
||||
|
||||
begin (isolationLevel, callback) {
|
||||
if (isolationLevel instanceof Function) {
|
||||
callback = isolationLevel
|
||||
isolationLevel = undefined
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this._begin(isolationLevel, err => {
|
||||
if (!err) {
|
||||
this.emit('begin')
|
||||
}
|
||||
callback(err)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._begin(isolationLevel, err => {
|
||||
if (err) return reject(err)
|
||||
this.emit('begin')
|
||||
resolve(this)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Number} [isolationLevel]
|
||||
* @param {basicCallback} [callback]
|
||||
* @return {Transaction}
|
||||
*/
|
||||
|
||||
_begin (isolationLevel, callback) {
|
||||
if (this._acquiredConnection) {
|
||||
return setImmediate(callback, new TransactionError('Transaction has already begun.', 'EALREADYBEGUN'))
|
||||
}
|
||||
|
||||
this._aborted = false
|
||||
this._rollbackRequested = false
|
||||
if (isolationLevel) {
|
||||
if (Object.keys(ISOLATION_LEVEL).some(key => {
|
||||
return ISOLATION_LEVEL[key] === isolationLevel
|
||||
})) {
|
||||
this.isolationLevel = isolationLevel
|
||||
} else {
|
||||
throw new TransactionError('Invalid isolation level.')
|
||||
}
|
||||
}
|
||||
|
||||
setImmediate(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit a transaction.
|
||||
*
|
||||
* @param {basicCallback} [callback] A callback which is called after transaction has commited, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Transaction|Promise}
|
||||
*/
|
||||
|
||||
commit (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
this._commit(err => {
|
||||
if (!err) {
|
||||
this.emit('commit')
|
||||
}
|
||||
callback(err)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this._commit(err => {
|
||||
if (err) return reject(err)
|
||||
this.emit('commit')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {basicCallback} [callback]
|
||||
* @return {Transaction}
|
||||
*/
|
||||
|
||||
_commit (callback) {
|
||||
if (this._aborted) {
|
||||
return setImmediate(callback, new TransactionError('Transaction has been aborted.', 'EABORT'))
|
||||
}
|
||||
|
||||
if (!this._acquiredConnection) {
|
||||
return setImmediate(callback, new TransactionError('Transaction has not begun. Call begin() first.', 'ENOTBEGUN'))
|
||||
}
|
||||
|
||||
if (this._activeRequest) {
|
||||
return setImmediate(callback, new TransactionError("Can't commit transaction. There is a request in progress.", 'EREQINPROG'))
|
||||
}
|
||||
|
||||
setImmediate(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new request using this transaction.
|
||||
*
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
request () {
|
||||
return new shared.driver.Request(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback a transaction.
|
||||
*
|
||||
* @param {basicCallback} [callback] A callback which is called after transaction has rolled back, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Transaction|Promise}
|
||||
*/
|
||||
|
||||
rollback (callback) {
|
||||
if (typeof callback === 'function') {
|
||||
this._rollback(err => {
|
||||
if (!err) {
|
||||
this.emit('rollback', this._aborted)
|
||||
}
|
||||
callback(err)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
return this._rollback(err => {
|
||||
if (err) return reject(err)
|
||||
this.emit('rollback', this._aborted)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {basicCallback} [callback]
|
||||
* @return {Transaction}
|
||||
*/
|
||||
|
||||
_rollback (callback) {
|
||||
if (this._aborted) {
|
||||
return setImmediate(callback, new TransactionError('Transaction has been aborted.', 'EABORT'))
|
||||
}
|
||||
|
||||
if (!this._acquiredConnection) {
|
||||
return setImmediate(callback, new TransactionError('Transaction has not begun. Call begin() first.', 'ENOTBEGUN'))
|
||||
}
|
||||
|
||||
if (this._activeRequest) {
|
||||
return setImmediate(callback, new TransactionError("Can't rollback transaction. There is a request in progress.", 'EREQINPROG'))
|
||||
}
|
||||
|
||||
this._rollbackRequested = true
|
||||
|
||||
setImmediate(callback)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default isolation level used for any transactions that don't explicitly specify an isolation level.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
Transaction.defaultIsolationLevel = ISOLATION_LEVEL.READ_COMMITTED
|
||||
|
||||
module.exports = Transaction
|
||||
193
node_modules/mssql/lib/datatypes.js
generated
vendored
Normal file
193
node_modules/mssql/lib/datatypes.js
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
'use strict'
|
||||
const objectHasProperty = require('./utils').objectHasProperty
|
||||
const inspect = Symbol.for('nodejs.util.inspect.custom')
|
||||
|
||||
const TYPES = {
|
||||
VarChar (length) {
|
||||
return { type: TYPES.VarChar, length }
|
||||
},
|
||||
NVarChar (length) {
|
||||
return { type: TYPES.NVarChar, length }
|
||||
},
|
||||
Text () {
|
||||
return { type: TYPES.Text }
|
||||
},
|
||||
Int () {
|
||||
return { type: TYPES.Int }
|
||||
},
|
||||
BigInt () {
|
||||
return { type: TYPES.BigInt }
|
||||
},
|
||||
TinyInt () {
|
||||
return { type: TYPES.TinyInt }
|
||||
},
|
||||
SmallInt () {
|
||||
return { type: TYPES.SmallInt }
|
||||
},
|
||||
Bit () {
|
||||
return { type: TYPES.Bit }
|
||||
},
|
||||
Float () {
|
||||
return { type: TYPES.Float }
|
||||
},
|
||||
Numeric (precision, scale) {
|
||||
return { type: TYPES.Numeric, precision, scale }
|
||||
},
|
||||
Decimal (precision, scale) {
|
||||
return { type: TYPES.Decimal, precision, scale }
|
||||
},
|
||||
Real () {
|
||||
return { type: TYPES.Real }
|
||||
},
|
||||
Date () {
|
||||
return { type: TYPES.Date }
|
||||
},
|
||||
DateTime () {
|
||||
return { type: TYPES.DateTime }
|
||||
},
|
||||
DateTime2 (scale) {
|
||||
return { type: TYPES.DateTime2, scale }
|
||||
},
|
||||
DateTimeOffset (scale) {
|
||||
return { type: TYPES.DateTimeOffset, scale }
|
||||
},
|
||||
SmallDateTime () {
|
||||
return { type: TYPES.SmallDateTime }
|
||||
},
|
||||
Time (scale) {
|
||||
return { type: TYPES.Time, scale }
|
||||
},
|
||||
UniqueIdentifier () {
|
||||
return { type: TYPES.UniqueIdentifier }
|
||||
},
|
||||
SmallMoney () {
|
||||
return { type: TYPES.SmallMoney }
|
||||
},
|
||||
Money () {
|
||||
return { type: TYPES.Money }
|
||||
},
|
||||
Binary (length) {
|
||||
return { type: TYPES.Binary, length }
|
||||
},
|
||||
VarBinary (length) {
|
||||
return { type: TYPES.VarBinary, length }
|
||||
},
|
||||
Image () {
|
||||
return { type: TYPES.Image }
|
||||
},
|
||||
Xml () {
|
||||
return { type: TYPES.Xml }
|
||||
},
|
||||
Char (length) {
|
||||
return { type: TYPES.Char, length }
|
||||
},
|
||||
NChar (length) {
|
||||
return { type: TYPES.NChar, length }
|
||||
},
|
||||
NText () {
|
||||
return { type: TYPES.NText }
|
||||
},
|
||||
TVP (tvpType) {
|
||||
return { type: TYPES.TVP, tvpType }
|
||||
},
|
||||
UDT () {
|
||||
return { type: TYPES.UDT }
|
||||
},
|
||||
Geography () {
|
||||
return { type: TYPES.Geography }
|
||||
},
|
||||
Geometry () {
|
||||
return { type: TYPES.Geometry }
|
||||
},
|
||||
Variant () {
|
||||
return { type: TYPES.Variant }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.TYPES = TYPES
|
||||
module.exports.DECLARATIONS = {}
|
||||
|
||||
const zero = function (value, length) {
|
||||
if (length == null) length = 2
|
||||
|
||||
value = String(value)
|
||||
if (value.length < length) {
|
||||
for (let i = 1; i <= length - value.length; i++) {
|
||||
value = `0${value}`
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
for (const key in TYPES) {
|
||||
if (objectHasProperty(TYPES, key)) {
|
||||
const value = TYPES[key]
|
||||
value.declaration = key.toLowerCase()
|
||||
module.exports.DECLARATIONS[value.declaration] = value;
|
||||
|
||||
((key, value) => {
|
||||
value[inspect] = () => `[sql.${key}]`
|
||||
})(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.declare = (type, options) => {
|
||||
switch (type) {
|
||||
case TYPES.VarChar: case TYPES.VarBinary:
|
||||
return `${type.declaration} (${options.length > 8000 ? 'MAX' : (options.length == null ? 'MAX' : options.length)})`
|
||||
case TYPES.NVarChar:
|
||||
return `${type.declaration} (${options.length > 4000 ? 'MAX' : (options.length == null ? 'MAX' : options.length)})`
|
||||
case TYPES.Char: case TYPES.NChar: case TYPES.Binary:
|
||||
return `${type.declaration} (${options.length == null ? 1 : options.length})`
|
||||
case TYPES.Decimal: case TYPES.Numeric:
|
||||
return `${type.declaration} (${options.precision == null ? 18 : options.precision}, ${options.scale == null ? 0 : options.scale})`
|
||||
case TYPES.Time: case TYPES.DateTime2: case TYPES.DateTimeOffset:
|
||||
return `${type.declaration} (${options.scale == null ? 7 : options.scale})`
|
||||
case TYPES.TVP:
|
||||
return `${options.tvpType} readonly`
|
||||
default:
|
||||
return type.declaration
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.cast = (value, type, options) => {
|
||||
if (value == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return `N'${value.replace(/'/g, '\'\'')}'`
|
||||
|
||||
case 'number':
|
||||
case 'bigint':
|
||||
return value
|
||||
|
||||
case 'boolean':
|
||||
return value ? 1 : 0
|
||||
|
||||
case 'object':
|
||||
if (value instanceof Date) {
|
||||
let ns = value.getUTCMilliseconds() / 1000
|
||||
if (value.nanosecondDelta != null) {
|
||||
ns += value.nanosecondDelta
|
||||
}
|
||||
const scale = options.scale == null ? 7 : options.scale
|
||||
|
||||
if (scale > 0) {
|
||||
ns = String(ns).substr(1, scale + 1)
|
||||
} else {
|
||||
ns = ''
|
||||
}
|
||||
|
||||
return `N'${value.getUTCFullYear()}-${zero(value.getUTCMonth() + 1)}-${zero(value.getUTCDate())} ${zero(value.getUTCHours())}:${zero(value.getUTCMinutes())}:${zero(value.getUTCSeconds())}${ns}'`
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
return `0x${value.toString('hex')}`
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
24
node_modules/mssql/lib/error/connection-error.js
generated
vendored
Normal file
24
node_modules/mssql/lib/error/connection-error.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
const MSSQLError = require('./mssql-error')
|
||||
|
||||
/**
|
||||
* Class ConnectionError.
|
||||
*/
|
||||
|
||||
class ConnectionError extends MSSQLError {
|
||||
/**
|
||||
* Creates a new ConnectionError.
|
||||
*
|
||||
* @param {String} message Error message.
|
||||
* @param {String} [code] Error code.
|
||||
*/
|
||||
|
||||
constructor (message, code) {
|
||||
super(message, code)
|
||||
|
||||
this.name = 'ConnectionError'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConnectionError
|
||||
15
node_modules/mssql/lib/error/index.js
generated
vendored
Normal file
15
node_modules/mssql/lib/error/index.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict'
|
||||
|
||||
const ConnectionError = require('./connection-error')
|
||||
const MSSQLError = require('./mssql-error')
|
||||
const PreparedStatementError = require('./prepared-statement-error')
|
||||
const RequestError = require('./request-error')
|
||||
const TransactionError = require('./transaction-error')
|
||||
|
||||
module.exports = {
|
||||
ConnectionError,
|
||||
MSSQLError,
|
||||
PreparedStatementError,
|
||||
RequestError,
|
||||
TransactionError
|
||||
}
|
||||
27
node_modules/mssql/lib/error/mssql-error.js
generated
vendored
Normal file
27
node_modules/mssql/lib/error/mssql-error.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict'
|
||||
|
||||
class MSSQLError extends Error {
|
||||
/**
|
||||
* Creates a new ConnectionError.
|
||||
*
|
||||
* @param {String} message Error message.
|
||||
* @param {String} [code] Error code.
|
||||
*/
|
||||
|
||||
constructor (message, code) {
|
||||
if (message instanceof Error) {
|
||||
super(message.message)
|
||||
this.code = message.code || code
|
||||
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
Object.defineProperty(this, 'originalError', { enumerable: true, value: message })
|
||||
} else {
|
||||
super(message)
|
||||
this.code = code
|
||||
}
|
||||
|
||||
this.name = 'MSSQLError'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MSSQLError
|
||||
24
node_modules/mssql/lib/error/prepared-statement-error.js
generated
vendored
Normal file
24
node_modules/mssql/lib/error/prepared-statement-error.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
const MSSQLError = require('./mssql-error')
|
||||
|
||||
/**
|
||||
* Class PreparedStatementError.
|
||||
*/
|
||||
|
||||
class PreparedStatementError extends MSSQLError {
|
||||
/**
|
||||
* Creates a new PreparedStatementError.
|
||||
*
|
||||
* @param {String} message Error message.
|
||||
* @param {String} [code] Error code.
|
||||
*/
|
||||
|
||||
constructor (message, code) {
|
||||
super(message, code)
|
||||
|
||||
this.name = 'PreparedStatementError'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PreparedStatementError
|
||||
53
node_modules/mssql/lib/error/request-error.js
generated
vendored
Normal file
53
node_modules/mssql/lib/error/request-error.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
const MSSQLError = require('./mssql-error')
|
||||
|
||||
/**
|
||||
* Class RequestError.
|
||||
*
|
||||
* @property {String} number Error number.
|
||||
* @property {Number} lineNumber Line number.
|
||||
* @property {String} state Error state.
|
||||
* @property {String} class Error class.
|
||||
* @property {String} serverName Server name.
|
||||
* @property {String} procName Procedure name.
|
||||
*/
|
||||
|
||||
class RequestError extends MSSQLError {
|
||||
/**
|
||||
* Creates a new RequestError.
|
||||
*
|
||||
* @param {String} message Error message.
|
||||
* @param {String} [code] Error code.
|
||||
*/
|
||||
|
||||
constructor (message, code) {
|
||||
super(message, code)
|
||||
if (message instanceof Error) {
|
||||
if (message.info) {
|
||||
this.number = message.info.number || message.code // err.code is returned by msnodesql driver
|
||||
this.lineNumber = message.info.lineNumber
|
||||
this.state = message.info.state || message.sqlstate // err.sqlstate is returned by msnodesql driver
|
||||
this.class = message.info.class
|
||||
this.serverName = message.info.serverName
|
||||
this.procName = message.info.procName
|
||||
} else {
|
||||
// Use err attributes returned by msnodesql driver
|
||||
this.number = message.code
|
||||
this.lineNumber = message.lineNumber
|
||||
this.state = message.sqlstate
|
||||
this.class = message.severity
|
||||
this.serverName = message.serverName
|
||||
this.procName = message.procName
|
||||
}
|
||||
}
|
||||
|
||||
this.name = 'RequestError'
|
||||
const parsedMessage = (/^\[Microsoft\]\[SQL Server Native Client 11\.0\](?:\[SQL Server\])?([\s\S]*)$/).exec(this.message)
|
||||
if (parsedMessage) {
|
||||
this.message = parsedMessage[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RequestError
|
||||
24
node_modules/mssql/lib/error/transaction-error.js
generated
vendored
Normal file
24
node_modules/mssql/lib/error/transaction-error.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
const MSSQLError = require('./mssql-error')
|
||||
|
||||
/**
|
||||
* Class TransactionError.
|
||||
*/
|
||||
|
||||
class TransactionError extends MSSQLError {
|
||||
/**
|
||||
* Creates a new TransactionError.
|
||||
*
|
||||
* @param {String} message Error message.
|
||||
* @param {String} [code] Error code.
|
||||
*/
|
||||
|
||||
constructor (message, code) {
|
||||
super(message, code)
|
||||
|
||||
this.name = 'TransactionError'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TransactionError
|
||||
193
node_modules/mssql/lib/global-connection.js
generated
vendored
Normal file
193
node_modules/mssql/lib/global-connection.js
generated
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
'use strict'
|
||||
|
||||
const shared = require('./shared')
|
||||
|
||||
let globalConnection = null
|
||||
const globalConnectionHandlers = {}
|
||||
|
||||
/**
|
||||
* Open global connection pool.
|
||||
*
|
||||
* @param {Object|String} config Connection configuration object or connection string.
|
||||
* @param {basicCallback} [callback] A callback which is called after connection has established, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Promise.<ConnectionPool>}
|
||||
*/
|
||||
|
||||
function connect (config, callback) {
|
||||
if (!globalConnection) {
|
||||
globalConnection = new shared.driver.ConnectionPool(config)
|
||||
|
||||
for (const event in globalConnectionHandlers) {
|
||||
for (let i = 0, l = globalConnectionHandlers[event].length; i < l; i++) {
|
||||
globalConnection.on(event, globalConnectionHandlers[event][i])
|
||||
}
|
||||
}
|
||||
|
||||
const ogClose = globalConnection.close
|
||||
|
||||
const globalClose = function (callback) {
|
||||
// remove event handlers from the global connection
|
||||
for (const event in globalConnectionHandlers) {
|
||||
for (let i = 0, l = globalConnectionHandlers[event].length; i < l; i++) {
|
||||
this.removeListener(event, globalConnectionHandlers[event][i])
|
||||
}
|
||||
}
|
||||
|
||||
// attach error handler to prevent process crash in case of error
|
||||
this.on('error', err => {
|
||||
if (globalConnectionHandlers.error) {
|
||||
for (let i = 0, l = globalConnectionHandlers.error.length; i < l; i++) {
|
||||
globalConnectionHandlers.error[i].call(this, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
globalConnection = null
|
||||
return ogClose.call(this, callback)
|
||||
}
|
||||
|
||||
globalConnection.close = globalClose.bind(globalConnection)
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
return globalConnection.connect((err, connection) => {
|
||||
if (err) {
|
||||
globalConnection = null
|
||||
}
|
||||
callback(err, connection)
|
||||
})
|
||||
}
|
||||
return globalConnection.connect().catch((err) => {
|
||||
globalConnection = null
|
||||
return shared.Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all active connections in the global pool.
|
||||
*
|
||||
* @param {basicCallback} [callback] A callback which is called after connection has closed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {ConnectionPool|Promise}
|
||||
*/
|
||||
|
||||
function close (callback) {
|
||||
if (globalConnection) {
|
||||
const gc = globalConnection
|
||||
globalConnection = null
|
||||
return gc.close(callback)
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
setImmediate(callback)
|
||||
return null
|
||||
}
|
||||
|
||||
return new shared.Promise((resolve) => {
|
||||
resolve(globalConnection)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach event handler to global connection pool.
|
||||
*
|
||||
* @param {String} event Event name.
|
||||
* @param {Function} handler Event handler.
|
||||
* @return {ConnectionPool}
|
||||
*/
|
||||
|
||||
function on (event, handler) {
|
||||
if (!globalConnectionHandlers[event]) globalConnectionHandlers[event] = []
|
||||
globalConnectionHandlers[event].push(handler)
|
||||
|
||||
if (globalConnection) globalConnection.on(event, handler)
|
||||
return globalConnection
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach event handler from global connection.
|
||||
*
|
||||
* @param {String} event Event name.
|
||||
* @param {Function} handler Event handler.
|
||||
* @return {ConnectionPool}
|
||||
*/
|
||||
|
||||
function removeListener (event, handler) {
|
||||
if (!globalConnectionHandlers[event]) return globalConnection
|
||||
const index = globalConnectionHandlers[event].indexOf(handler)
|
||||
if (index === -1) return globalConnection
|
||||
globalConnectionHandlers[event].splice(index, 1)
|
||||
if (globalConnectionHandlers[event].length === 0) globalConnectionHandlers[event] = undefined
|
||||
|
||||
if (globalConnection) globalConnection.removeListener(event, handler)
|
||||
return globalConnection
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query using global connection from a tagged template string.
|
||||
*
|
||||
* @variation 1
|
||||
* @param {Array|String} strings Array of string literals or sql command.
|
||||
* @param {...*} keys Values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute the SQL command.
|
||||
*
|
||||
* @variation 2
|
||||
* @param {String} command T-SQL command to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
function query () {
|
||||
if (typeof arguments[0] === 'string') { return new shared.driver.Request().query(arguments[0], arguments[1]) }
|
||||
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
|
||||
return new shared.driver.Request()._template(strings, values, 'query')
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new batch using global connection from a tagged template string.
|
||||
*
|
||||
* @variation 1
|
||||
* @param {Array} strings Array of string literals.
|
||||
* @param {...*} keys Values.
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Execute the SQL command.
|
||||
*
|
||||
* @variation 2
|
||||
* @param {String} command T-SQL command to be executed.
|
||||
* @param {Request~requestCallback} [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise.
|
||||
* @return {Request|Promise}
|
||||
*/
|
||||
|
||||
function batch () {
|
||||
if (typeof arguments[0] === 'string') { return new shared.driver.Request().batch(arguments[0], arguments[1]) }
|
||||
|
||||
const values = Array.prototype.slice.call(arguments)
|
||||
const strings = values.shift()
|
||||
|
||||
return new shared.driver.Request()._template(strings, values, 'batch')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
batch,
|
||||
close,
|
||||
connect,
|
||||
off: removeListener,
|
||||
on,
|
||||
query,
|
||||
removeListener
|
||||
}
|
||||
|
||||
Object.defineProperty(module.exports, 'pool', {
|
||||
get: () => {
|
||||
return globalConnection
|
||||
},
|
||||
set: () => {}
|
||||
})
|
||||
9
node_modules/mssql/lib/isolationlevel.js
generated
vendored
Normal file
9
node_modules/mssql/lib/isolationlevel.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
READ_UNCOMMITTED: 0x01,
|
||||
READ_COMMITTED: 0x02,
|
||||
REPEATABLE_READ: 0x03,
|
||||
SERIALIZABLE: 0x04,
|
||||
SNAPSHOT: 0x05
|
||||
}
|
||||
84
node_modules/mssql/lib/msnodesqlv8/connection-pool.js
generated
vendored
Normal file
84
node_modules/mssql/lib/msnodesqlv8/connection-pool.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
'use strict'
|
||||
|
||||
const msnodesql = require('msnodesqlv8')
|
||||
const debug = require('debug')('mssql:msv8')
|
||||
const BaseConnectionPool = require('../base/connection-pool')
|
||||
const { IDS, INCREMENT } = require('../utils')
|
||||
const shared = require('../shared')
|
||||
const ConnectionError = require('../error/connection-error')
|
||||
const { platform } = require('node:os')
|
||||
const { buildConnectionString } = require('@tediousjs/connection-string')
|
||||
|
||||
const CONNECTION_DRIVER = ['darwin', 'linux'].includes(platform()) ? 'ODBC Driver 17 for SQL Server' : 'SQL Server Native Client 11.0'
|
||||
|
||||
class ConnectionPool extends BaseConnectionPool {
|
||||
_poolCreate () {
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
this.config.requestTimeout = this.config.requestTimeout ?? this.config.timeout ?? 15000
|
||||
|
||||
const cfg = {
|
||||
conn_str: this.config.connectionString,
|
||||
conn_timeout: (this.config.connectionTimeout ?? this.config.timeout ?? 15000) / 1000
|
||||
}
|
||||
|
||||
if (!this.config.connectionString) {
|
||||
cfg.conn_str = buildConnectionString({
|
||||
Driver: CONNECTION_DRIVER,
|
||||
Server: this.config.options.instanceName ? `${this.config.server}\\${this.config.options.instanceName}` : `${this.config.server},${this.config.port}`,
|
||||
Database: this.config.database,
|
||||
Uid: this.config.user,
|
||||
Pwd: this.config.password,
|
||||
Trusted_Connection: !!this.config.options.trustedConnection,
|
||||
Encrypt: !!this.config.options.encrypt
|
||||
})
|
||||
}
|
||||
|
||||
const connedtionId = INCREMENT.Connection++
|
||||
debug('pool(%d): connection #%d created', IDS.get(this), connedtionId)
|
||||
debug('connection(%d): establishing', connedtionId)
|
||||
|
||||
if (typeof this.config.beforeConnect === 'function') {
|
||||
this.config.beforeConnect(cfg)
|
||||
}
|
||||
|
||||
msnodesql.open(cfg, (err, tds) => {
|
||||
if (err) {
|
||||
err = new ConnectionError(err)
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
IDS.add(tds, 'Connection', connedtionId)
|
||||
tds.setUseUTC(this.config.options.useUTC)
|
||||
debug('connection(%d): established', IDS.get(tds))
|
||||
resolve(tds)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_poolValidate (tds) {
|
||||
if (tds && !tds.hasError) {
|
||||
return !this.config.validateConnection || new shared.Promise((resolve) => {
|
||||
tds.query('SELECT 1;', (err) => {
|
||||
resolve(!err)
|
||||
})
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_poolDestroy (tds) {
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
if (!tds) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
debug('connection(%d): destroying', IDS.get(tds))
|
||||
tds.close(() => {
|
||||
debug('connection(%d): destroyed', IDS.get(tds))
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConnectionPool
|
||||
35
node_modules/mssql/lib/msnodesqlv8/index.js
generated
vendored
Normal file
35
node_modules/mssql/lib/msnodesqlv8/index.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const base = require('../base')
|
||||
const ConnectionPool = require('./connection-pool')
|
||||
const Transaction = require('./transaction')
|
||||
const Request = require('./request')
|
||||
|
||||
module.exports = Object.assign({
|
||||
ConnectionPool,
|
||||
Transaction,
|
||||
Request,
|
||||
PreparedStatement: base.PreparedStatement
|
||||
}, base.exports)
|
||||
|
||||
Object.defineProperty(module.exports, 'Promise', {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
return base.Promise
|
||||
},
|
||||
set: (value) => {
|
||||
base.Promise = value
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(module.exports, 'valueHandler', {
|
||||
enumerable: true,
|
||||
value: base.valueHandler,
|
||||
writable: false,
|
||||
configurable: false
|
||||
})
|
||||
|
||||
base.driver.name = 'msnodesqlv8'
|
||||
base.driver.ConnectionPool = ConnectionPool
|
||||
base.driver.Transaction = Transaction
|
||||
base.driver.Request = Request
|
||||
769
node_modules/mssql/lib/msnodesqlv8/request.js
generated
vendored
Normal file
769
node_modules/mssql/lib/msnodesqlv8/request.js
generated
vendored
Normal file
@@ -0,0 +1,769 @@
|
||||
'use strict'
|
||||
|
||||
const msnodesql = require('msnodesqlv8')
|
||||
const debug = require('debug')('mssql:msv8')
|
||||
const BaseRequest = require('../base/request')
|
||||
const RequestError = require('../error/request-error')
|
||||
const { IDS, objectHasProperty } = require('../utils')
|
||||
const { TYPES, DECLARATIONS, declare } = require('../datatypes')
|
||||
const { PARSERS: UDT } = require('../udt')
|
||||
const Table = require('../table')
|
||||
const { valueHandler } = require('../shared')
|
||||
|
||||
const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B'
|
||||
const XML_COLUMN_ID = 'XML_F52E2B61-18A1-11d1-B105-00805F49916B'
|
||||
const EMPTY_BUFFER = Buffer.alloc(0)
|
||||
|
||||
const castParameter = function (value, type) {
|
||||
if (value == null) {
|
||||
if ((type === TYPES.Binary) || (type === TYPES.VarBinary) || (type === TYPES.Image)) {
|
||||
// msnodesql has some problems with NULL values in those types, so we need to replace it with empty buffer
|
||||
return EMPTY_BUFFER
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TYPES.VarChar:
|
||||
case TYPES.NVarChar:
|
||||
case TYPES.Char:
|
||||
case TYPES.NChar:
|
||||
case TYPES.Xml:
|
||||
case TYPES.Text:
|
||||
case TYPES.NText:
|
||||
if ((typeof value !== 'string') && !(value instanceof String)) {
|
||||
value = value.toString()
|
||||
}
|
||||
break
|
||||
|
||||
case TYPES.Int:
|
||||
case TYPES.TinyInt:
|
||||
case TYPES.BigInt:
|
||||
case TYPES.SmallInt:
|
||||
if ((typeof value !== 'number') && !(value instanceof Number)) {
|
||||
value = parseInt(value)
|
||||
if (isNaN(value)) { value = null }
|
||||
}
|
||||
break
|
||||
|
||||
case TYPES.Float:
|
||||
case TYPES.Real:
|
||||
case TYPES.Decimal:
|
||||
case TYPES.Numeric:
|
||||
case TYPES.SmallMoney:
|
||||
case TYPES.Money:
|
||||
if ((typeof value !== 'number') && !(value instanceof Number)) {
|
||||
value = parseFloat(value)
|
||||
if (isNaN(value)) { value = null }
|
||||
}
|
||||
break
|
||||
|
||||
case TYPES.Bit:
|
||||
if ((typeof value !== 'boolean') && !(value instanceof Boolean)) {
|
||||
value = Boolean(value)
|
||||
}
|
||||
break
|
||||
|
||||
case TYPES.DateTime:
|
||||
case TYPES.SmallDateTime:
|
||||
case TYPES.DateTimeOffset:
|
||||
case TYPES.Date:
|
||||
if (!(value instanceof Date)) {
|
||||
value = new Date(value)
|
||||
}
|
||||
break
|
||||
|
||||
case TYPES.Binary:
|
||||
case TYPES.VarBinary:
|
||||
case TYPES.Image:
|
||||
if (!(value instanceof Buffer)) {
|
||||
value = Buffer.from(value.toString())
|
||||
}
|
||||
break
|
||||
case TYPES.TVP:
|
||||
value = msnodesql.TvpFromTable(value)
|
||||
break
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
const createColumns = function (metadata, arrayRowMode) {
|
||||
let out = {}
|
||||
if (arrayRowMode) out = []
|
||||
for (let index = 0, length = metadata.length; index < length; index++) {
|
||||
const column = metadata[index]
|
||||
const colName = column.name
|
||||
const outColumn = {
|
||||
index,
|
||||
name: column.name,
|
||||
length: column.size,
|
||||
type: DECLARATIONS[column.sqlType],
|
||||
nullable: column.nullable
|
||||
}
|
||||
|
||||
if (column.udtType != null) {
|
||||
outColumn.udt = {
|
||||
name: column.udtType
|
||||
}
|
||||
|
||||
if (DECLARATIONS[column.udtType]) {
|
||||
outColumn.type = DECLARATIONS[column.udtType]
|
||||
}
|
||||
}
|
||||
if (arrayRowMode) {
|
||||
out.push(outColumn)
|
||||
} else {
|
||||
out[colName] = outColumn
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
const valueCorrection = function (value, metadata) {
|
||||
const type = metadata && objectHasProperty(metadata, 'sqlType') && objectHasProperty(DECLARATIONS, metadata.sqlType)
|
||||
? DECLARATIONS[metadata.sqlType]
|
||||
: null
|
||||
if (type && valueHandler.has(type)) {
|
||||
return valueHandler.get(type)(value)
|
||||
} else if ((metadata.sqlType === 'time') && (value != null)) {
|
||||
value.setFullYear(1970)
|
||||
return value
|
||||
} else if ((metadata.sqlType === 'udt') && (value != null)) {
|
||||
if (UDT[metadata.udtType]) {
|
||||
return UDT[metadata.udtType](value)
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
class Request extends BaseRequest {
|
||||
_batch (batch, callback) {
|
||||
this._isBatch = true
|
||||
this._query(batch, callback)
|
||||
}
|
||||
|
||||
_bulk (table, options, callback) {
|
||||
super._bulk(table, options, err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
try {
|
||||
table._makeBulk()
|
||||
} catch (e) {
|
||||
return callback(new RequestError(e, 'EREQUEST'))
|
||||
}
|
||||
|
||||
if (!table.name) {
|
||||
setImmediate(callback, new RequestError('Table name must be specified for bulk insert.', 'ENAME'))
|
||||
}
|
||||
|
||||
if (table.name.charAt(0) === '@') {
|
||||
setImmediate(callback, new RequestError("You can't use table variables for bulk insert.", 'ENAME'))
|
||||
}
|
||||
|
||||
this.parent.acquire(this, (err, connection) => {
|
||||
let hasReturned = false
|
||||
if (!err) {
|
||||
debug('connection(%d): borrowed to request #%d', IDS.get(connection), IDS.get(this))
|
||||
|
||||
if (this.canceled) {
|
||||
debug('request(%d): canceled', IDS.get(this))
|
||||
this.parent.release(connection)
|
||||
return callback(new RequestError('Canceled.', 'ECANCEL'))
|
||||
}
|
||||
|
||||
const done = (err, rowCount) => {
|
||||
if (hasReturned) {
|
||||
return
|
||||
}
|
||||
|
||||
hasReturned = true
|
||||
|
||||
if (err) {
|
||||
if ((typeof err.sqlstate === 'string') && (err.sqlstate.toLowerCase() === '08s01')) {
|
||||
connection.hasError = true
|
||||
}
|
||||
|
||||
err = new RequestError(err)
|
||||
err.code = 'EREQUEST'
|
||||
}
|
||||
|
||||
this.parent.release(connection)
|
||||
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else {
|
||||
callback(null, table.rows.length)
|
||||
}
|
||||
}
|
||||
|
||||
const go = () => {
|
||||
const tm = connection.tableMgr()
|
||||
return tm.bind(table.path.replace(/\[|\]/g, ''), mgr => {
|
||||
if (mgr.columns.length === 0) {
|
||||
return done(new RequestError('Table was not found on the server.', 'ENAME'))
|
||||
}
|
||||
|
||||
const rows = []
|
||||
for (const row of Array.from(table.rows)) {
|
||||
const item = {}
|
||||
for (let index = 0; index < table.columns.length; index++) {
|
||||
const col = table.columns[index]
|
||||
item[col.name] = row[index]
|
||||
}
|
||||
|
||||
rows.push(item)
|
||||
}
|
||||
|
||||
mgr.insertRows(rows, done)
|
||||
})
|
||||
}
|
||||
|
||||
if (table.create) {
|
||||
let objectid
|
||||
if (table.temporary) {
|
||||
objectid = `tempdb..[${table.name}]`
|
||||
} else {
|
||||
objectid = table.path
|
||||
}
|
||||
|
||||
return connection.queryRaw(`if object_id('${objectid.replace(/'/g, '\'\'')}') is null ${table.declare()}`, function (err) {
|
||||
if (err) { return done(err) }
|
||||
go()
|
||||
})
|
||||
} else {
|
||||
go()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_query (command, callback) {
|
||||
super._query(command, err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
if (command.length === 0) {
|
||||
return callback(null, [])
|
||||
}
|
||||
|
||||
const recordsets = []
|
||||
const recordsetcolumns = []
|
||||
const errors = []
|
||||
const errorHandlers = {}
|
||||
const output = {}
|
||||
const rowsAffected = []
|
||||
|
||||
let hasReturned = false
|
||||
let row = null
|
||||
let columns = null
|
||||
let recordset = null
|
||||
let handleOutput = false
|
||||
let isChunkedRecordset = false
|
||||
let chunksBuffer = null
|
||||
|
||||
const handleError = (req, connection, info, moreErrors) => {
|
||||
const doReturn = !moreErrors
|
||||
if ((typeof info.sqlstate === 'string') && (info.sqlstate.toLowerCase() === '08s01')) {
|
||||
connection.hasError = true
|
||||
}
|
||||
|
||||
const err = new RequestError(info, 'EREQUEST')
|
||||
err.code = 'EREQUEST'
|
||||
|
||||
if (this.stream) {
|
||||
this.emit('error', err)
|
||||
} else {
|
||||
if (doReturn && !hasReturned) {
|
||||
if (req) {
|
||||
for (const event in errorHandlers) {
|
||||
req.removeListener(event, errorHandlers[event])
|
||||
}
|
||||
}
|
||||
if (connection) {
|
||||
this.parent.release(connection)
|
||||
delete this._cancel
|
||||
|
||||
debug('request(%d): failed', IDS.get(this), err)
|
||||
}
|
||||
|
||||
let previous
|
||||
if (errors.length) {
|
||||
previous = errors.pop()
|
||||
if (!err.precedingErrors) {
|
||||
err.precedingErrors = []
|
||||
}
|
||||
err.precedingErrors.push(previous)
|
||||
}
|
||||
|
||||
hasReturned = true
|
||||
callback(err)
|
||||
}
|
||||
}
|
||||
|
||||
// we must collect errors even in stream mode
|
||||
errors.push(err)
|
||||
}
|
||||
|
||||
// nested = function is called by this.execute
|
||||
|
||||
if (!this._nested) {
|
||||
const input = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
input.push(`@${param.name} ${declare(param.type, param)}`)
|
||||
}
|
||||
|
||||
const sets = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 1) {
|
||||
sets.push(`set @${param.name}=?`)
|
||||
}
|
||||
}
|
||||
|
||||
const output = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2) {
|
||||
output.push(`@${param.name} as '${param.name}'`)
|
||||
}
|
||||
}
|
||||
|
||||
if (input.length) command = `declare ${input.join(',')};${sets.join(';')};${command};`
|
||||
if (output.length) {
|
||||
command += `select ${output.join(',')};`
|
||||
handleOutput = true
|
||||
}
|
||||
}
|
||||
|
||||
this.parent.acquire(this, (err, connection, config) => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('connection(%d): borrowed to request #%d', IDS.get(connection), IDS.get(this))
|
||||
|
||||
if (this.canceled) {
|
||||
debug('request(%d): canceled', IDS.get(this))
|
||||
this.parent.release(connection)
|
||||
return callback(new RequestError('Canceled.', 'ECANCEL'))
|
||||
}
|
||||
|
||||
const params = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 1 || (param.io === 2 && param.value)) {
|
||||
params.push(castParameter(param.value, param.type))
|
||||
}
|
||||
}
|
||||
|
||||
debug('request(%d): query', IDS.get(this), command)
|
||||
|
||||
const req = connection.queryRaw({
|
||||
query_str: command,
|
||||
query_timeout: config.requestTimeout / 1000 // msnodesqlv8 timeouts are in seconds (<1 second not supported)
|
||||
}, params)
|
||||
|
||||
this._setCurrentRequest(req)
|
||||
|
||||
this._cancel = () => {
|
||||
debug('request(%d): cancel', IDS.get(this))
|
||||
req.cancelQuery(err => {
|
||||
if (err) debug('request(%d): failed to cancel', IDS.get(this), err)
|
||||
// this fixes an issue where paused connections don't emit a done event
|
||||
try {
|
||||
if (req.isPaused()) req.emit('done')
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
req.on('meta', metadata => {
|
||||
if (row) {
|
||||
if (isChunkedRecordset) {
|
||||
const concatenatedChunks = chunksBuffer.join('')
|
||||
if ((columns[0].name === JSON_COLUMN_ID) && (config.parseJSON === true)) {
|
||||
try {
|
||||
if (concatenatedChunks === '') {
|
||||
row = null
|
||||
} else {
|
||||
row = JSON.parse(concatenatedChunks)
|
||||
}
|
||||
if (!this.stream) { recordsets[recordsets.length - 1][0] = row }
|
||||
} catch (ex) {
|
||||
row = null
|
||||
const ex2 = new RequestError(`Failed to parse incoming JSON. ${ex.message}`, 'EJSON')
|
||||
|
||||
if (this.stream) {
|
||||
this.emit('error', ex2)
|
||||
} else {
|
||||
console.error(ex2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
row[columns[0].name] = concatenatedChunks
|
||||
}
|
||||
|
||||
chunksBuffer = null
|
||||
if (row && row.___return___ == null) {
|
||||
// row with ___return___ col is the last row
|
||||
if (this.stream && !this.paused) this.emit('row', row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row = null
|
||||
columns = metadata
|
||||
recordset = []
|
||||
|
||||
Object.defineProperty(recordset, 'columns', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: createColumns(metadata, this.arrayRowMode)
|
||||
})
|
||||
|
||||
Object.defineProperty(recordset, 'toTable', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value (name) { return Table.fromRecordset(this, name) }
|
||||
})
|
||||
|
||||
isChunkedRecordset = false
|
||||
if ((metadata.length === 1) && (metadata[0].name === JSON_COLUMN_ID || metadata[0].name === XML_COLUMN_ID)) {
|
||||
isChunkedRecordset = true
|
||||
chunksBuffer = []
|
||||
}
|
||||
|
||||
let hasReturnColumn = false
|
||||
if (recordset.columns.___return___ != null) {
|
||||
hasReturnColumn = true
|
||||
} else if (this.arrayRowMode) {
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
if (columns[i].name === '___return___') {
|
||||
hasReturnColumn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.stream) {
|
||||
if (!hasReturnColumn) {
|
||||
this.emit('recordset', recordset.columns)
|
||||
}
|
||||
} else {
|
||||
recordsets.push(recordset)
|
||||
}
|
||||
if (this.arrayRowMode) recordsetcolumns.push(recordset.columns)
|
||||
})
|
||||
|
||||
req.on('row', rownumber => {
|
||||
if (row && isChunkedRecordset) return
|
||||
|
||||
if (this.arrayRowMode) {
|
||||
row = []
|
||||
} else {
|
||||
row = {}
|
||||
}
|
||||
|
||||
if (!this.stream) recordset.push(row)
|
||||
})
|
||||
|
||||
req.on('column', (idx, data, more) => {
|
||||
if (isChunkedRecordset) {
|
||||
chunksBuffer.push(data)
|
||||
} else {
|
||||
data = valueCorrection(data, columns[idx])
|
||||
|
||||
if (this.arrayRowMode) {
|
||||
row.push(data)
|
||||
} else {
|
||||
const exi = row[columns[idx].name]
|
||||
if (exi != null) {
|
||||
if (exi instanceof Array) {
|
||||
exi.push(data)
|
||||
} else {
|
||||
row[columns[idx].name] = [exi, data]
|
||||
}
|
||||
} else {
|
||||
row[columns[idx].name] = data
|
||||
}
|
||||
}
|
||||
let hasReturnColumn = false
|
||||
if (row && row.___return___ != null) {
|
||||
hasReturnColumn = true
|
||||
} else if (this.arrayRowMode) {
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
if (columns[i].name === '___return___') {
|
||||
hasReturnColumn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasReturnColumn) {
|
||||
if (this.stream && !this.paused && idx === columns.length - 1) {
|
||||
this.emit('row', row)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
req.on('rowcount', rowCount => {
|
||||
rowsAffected.push(rowCount)
|
||||
if (this.stream) {
|
||||
this.emit('rowsaffected', rowCount)
|
||||
}
|
||||
})
|
||||
|
||||
req.on('info', msg => {
|
||||
const parsedMessage = (/^\[Microsoft\]\[SQL Server Native Client 11\.0\](?:\[SQL Server\])?([\s\S]*)$/).exec(msg.message)
|
||||
if (parsedMessage) {
|
||||
msg.message = parsedMessage[1]
|
||||
}
|
||||
|
||||
this.emit('info', {
|
||||
message: msg.message,
|
||||
number: msg.code,
|
||||
state: msg.sqlstate,
|
||||
class: msg.class || 0,
|
||||
lineNumber: msg.lineNumber || 0,
|
||||
serverName: msg.serverName,
|
||||
procName: msg.procName
|
||||
})
|
||||
|
||||
// query terminated
|
||||
if (msg.code === 3621 && !hasReturned) {
|
||||
// if the query has been terminated it's probably best to throw the last meaningful error if there was one
|
||||
// pop it off the errors array so it doesn't get put in twice
|
||||
const error = errors.length > 0 ? errors.pop() : msg
|
||||
handleError(req, connection, error.originalError || error, false)
|
||||
}
|
||||
})
|
||||
|
||||
req.on('error', errorHandlers.error = handleError.bind(null, req, connection))
|
||||
|
||||
req.once('done', () => {
|
||||
if (hasReturned) {
|
||||
return
|
||||
}
|
||||
|
||||
hasReturned = true
|
||||
|
||||
if (!this._nested) {
|
||||
if (row) {
|
||||
if (isChunkedRecordset) {
|
||||
const concatenatedChunks = chunksBuffer.join('')
|
||||
if ((columns[0].name === JSON_COLUMN_ID) && (config.parseJSON === true)) {
|
||||
try {
|
||||
if (concatenatedChunks === '') {
|
||||
row = null
|
||||
} else {
|
||||
row = JSON.parse(concatenatedChunks)
|
||||
}
|
||||
if (!this.stream) { recordsets[recordsets.length - 1][0] = row }
|
||||
} catch (ex) {
|
||||
row = null
|
||||
const ex2 = new RequestError(`Failed to parse incoming JSON. ${ex.message}`, 'EJSON')
|
||||
|
||||
if (this.stream) {
|
||||
this.emit('error', ex2)
|
||||
} else {
|
||||
console.error(ex2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
row[columns[0].name] = concatenatedChunks
|
||||
}
|
||||
|
||||
chunksBuffer = null
|
||||
if (row && row.___return___ == null) {
|
||||
// row with ___return___ col is the last row
|
||||
if (this.stream && !this.paused) { this.emit('row', row) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do we have output parameters to handle?
|
||||
if (handleOutput && recordsets.length) {
|
||||
const last = recordsets.pop()[0]
|
||||
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2) {
|
||||
output[param.name] = last[param.name]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete this._cancel
|
||||
this.parent.release(connection)
|
||||
|
||||
debug('request(%d): completed', IDS.get(this))
|
||||
|
||||
if (this.stream) {
|
||||
callback(null, this._nested ? row : null, output, rowsAffected, recordsetcolumns)
|
||||
} else {
|
||||
callback(null, recordsets, output, rowsAffected, recordsetcolumns)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_execute (procedure, callback) {
|
||||
super._execute(procedure, err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
const params = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2) {
|
||||
params.push(`@${param.name} ${declare(param.type, param)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// set output params w/ values
|
||||
const sets = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2 && param.value) {
|
||||
sets.push(`set @${param.name}=?`)
|
||||
}
|
||||
}
|
||||
|
||||
let cmd = `declare ${['@___return___ int'].concat(params).join(', ')};${sets.join(';')};`
|
||||
cmd += `exec @___return___ = ${procedure} `
|
||||
|
||||
const spp = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
|
||||
if (param.io === 2) {
|
||||
// output parameter
|
||||
spp.push(`@${param.name}=@${param.name} output`)
|
||||
} else {
|
||||
// input parameter
|
||||
spp.push(`@${param.name}=?`)
|
||||
}
|
||||
}
|
||||
|
||||
const params2 = []
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2) {
|
||||
params2.push(`@${param.name} as '${param.name}'`)
|
||||
}
|
||||
}
|
||||
|
||||
cmd += `${spp.join(', ')};`
|
||||
cmd += `select ${['@___return___ as \'___return___\''].concat(params2).join(', ')};`
|
||||
|
||||
this._nested = true
|
||||
|
||||
this._query(cmd, (err, recordsets, output, rowsAffected, recordsetcolumns) => {
|
||||
this._nested = false
|
||||
|
||||
if (err) return callback(err)
|
||||
|
||||
let last, returnValue
|
||||
if (this.stream) {
|
||||
last = recordsets
|
||||
} else {
|
||||
last = recordsets.pop()
|
||||
if (last) last = last[0]
|
||||
}
|
||||
const lastColumns = recordsetcolumns.pop()
|
||||
|
||||
if (last && this.arrayRowMode && lastColumns) {
|
||||
let returnColumnIdx = null
|
||||
const parametersNameToLastIdxDict = {}
|
||||
for (let i = 0; i < lastColumns.length; i++) {
|
||||
if (lastColumns[i].name === '___return___') {
|
||||
returnColumnIdx = i
|
||||
} else if (objectHasProperty(this.parameters, lastColumns[i].name)) {
|
||||
parametersNameToLastIdxDict[lastColumns[i].name] = i
|
||||
}
|
||||
}
|
||||
if (returnColumnIdx != null) {
|
||||
returnValue = last[returnColumnIdx]
|
||||
}
|
||||
for (const name in parametersNameToLastIdxDict) {
|
||||
if (!objectHasProperty(parametersNameToLastIdxDict, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2) {
|
||||
output[param.name] = last[parametersNameToLastIdxDict[name]]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (last && (last.___return___ != null)) {
|
||||
returnValue = last.___return___
|
||||
|
||||
for (const name in this.parameters) {
|
||||
if (!objectHasProperty(this.parameters, name)) {
|
||||
continue
|
||||
}
|
||||
const param = this.parameters[name]
|
||||
if (param.io === 2) {
|
||||
output[param.name] = last[param.name]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.stream) {
|
||||
callback(null, null, output, returnValue, rowsAffected, recordsetcolumns)
|
||||
} else {
|
||||
callback(null, recordsets, output, returnValue, rowsAffected, recordsetcolumns)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_pause () {
|
||||
super._pause()
|
||||
if (this._currentRequest) {
|
||||
this._currentRequest.pauseQuery()
|
||||
}
|
||||
}
|
||||
|
||||
_resume () {
|
||||
super._resume()
|
||||
if (this._currentRequest) {
|
||||
this._currentRequest.resumeQuery()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Request
|
||||
98
node_modules/mssql/lib/msnodesqlv8/transaction.js
generated
vendored
Normal file
98
node_modules/mssql/lib/msnodesqlv8/transaction.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')('mssql:msv8')
|
||||
const BaseTransaction = require('../base/transaction')
|
||||
const { IDS } = require('../utils')
|
||||
const Request = require('./request')
|
||||
const ISOLATION_LEVEL = require('../isolationlevel')
|
||||
const TransactionError = require('../error/transaction-error')
|
||||
|
||||
const isolationLevelDeclaration = function (type) {
|
||||
switch (type) {
|
||||
case ISOLATION_LEVEL.READ_UNCOMMITTED: return 'READ UNCOMMITTED'
|
||||
case ISOLATION_LEVEL.READ_COMMITTED: return 'READ COMMITTED'
|
||||
case ISOLATION_LEVEL.REPEATABLE_READ: return 'REPEATABLE READ'
|
||||
case ISOLATION_LEVEL.SERIALIZABLE: return 'SERIALIZABLE'
|
||||
case ISOLATION_LEVEL.SNAPSHOT: return 'SNAPSHOT'
|
||||
default: throw new TransactionError('Invalid isolation level.')
|
||||
}
|
||||
}
|
||||
|
||||
class Transaction extends BaseTransaction {
|
||||
_begin (isolationLevel, callback) {
|
||||
super._begin(isolationLevel, err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('transaction(%d): begin', IDS.get(this))
|
||||
|
||||
this.parent.acquire(this, (err, connection, config) => {
|
||||
if (err) return callback(err)
|
||||
|
||||
this._acquiredConnection = connection
|
||||
this._acquiredConfig = config
|
||||
|
||||
const req = new Request(this)
|
||||
req.stream = false
|
||||
req.query(`set transaction isolation level ${isolationLevelDeclaration(this.isolationLevel)};begin tran;`, err => {
|
||||
if (err) {
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
debug('transaction(%d): begun', IDS.get(this))
|
||||
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_commit (callback) {
|
||||
super._commit(err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('transaction(%d): commit', IDS.get(this))
|
||||
|
||||
const req = new Request(this)
|
||||
req.stream = false
|
||||
req.query('commit tran', err => {
|
||||
if (err) err = new TransactionError(err)
|
||||
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
|
||||
if (!err) debug('transaction(%d): commited', IDS.get(this))
|
||||
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_rollback (callback) {
|
||||
super._commit(err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('transaction(%d): rollback', IDS.get(this))
|
||||
|
||||
const req = new Request(this)
|
||||
req.stream = false
|
||||
req.query('rollback tran', err => {
|
||||
if (err) err = new TransactionError(err)
|
||||
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
|
||||
if (!err) debug('transaction(%d): rolled back', IDS.get(this))
|
||||
|
||||
callback(null)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transaction
|
||||
120
node_modules/mssql/lib/shared.js
generated
vendored
Normal file
120
node_modules/mssql/lib/shared.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
'use strict'
|
||||
|
||||
const TYPES = require('./datatypes').TYPES
|
||||
const Table = require('./table')
|
||||
|
||||
let PromiseLibrary = Promise
|
||||
const driver = {}
|
||||
const map = []
|
||||
|
||||
/**
|
||||
* Register you own type map.
|
||||
*
|
||||
* @path module.exports.map
|
||||
* @param {*} jstype JS data type.
|
||||
* @param {*} sqltype SQL data type.
|
||||
*/
|
||||
|
||||
map.register = function (jstype, sqltype) {
|
||||
for (let index = 0; index < this.length; index++) {
|
||||
const item = this[index]
|
||||
if (item.js === jstype) {
|
||||
this.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.push({
|
||||
js: jstype,
|
||||
sql: sqltype
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
map.register(String, TYPES.NVarChar)
|
||||
map.register(Number, TYPES.Int)
|
||||
map.register(Boolean, TYPES.Bit)
|
||||
map.register(Date, TYPES.DateTime)
|
||||
map.register(Buffer, TYPES.VarBinary)
|
||||
map.register(Table, TYPES.TVP)
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
const getTypeByValue = function (value) {
|
||||
if ((value === null) || (value === undefined)) { return TYPES.NVarChar }
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
for (const item of Array.from(map)) {
|
||||
if (item.js === String) {
|
||||
return item.sql
|
||||
}
|
||||
}
|
||||
|
||||
return TYPES.NVarChar
|
||||
|
||||
case 'number':
|
||||
if (value % 1 === 0) {
|
||||
if (value < -2147483648 || value > 2147483647) {
|
||||
return TYPES.BigInt
|
||||
} else {
|
||||
return TYPES.Int
|
||||
}
|
||||
} else {
|
||||
return TYPES.Float
|
||||
}
|
||||
|
||||
case 'bigint':
|
||||
if (value < -2147483648n || value > 2147483647n) {
|
||||
return TYPES.BigInt
|
||||
} else {
|
||||
return TYPES.Int
|
||||
}
|
||||
|
||||
case 'boolean':
|
||||
for (const item of Array.from(map)) {
|
||||
if (item.js === Boolean) {
|
||||
return item.sql
|
||||
}
|
||||
}
|
||||
|
||||
return TYPES.Bit
|
||||
|
||||
case 'object':
|
||||
for (const item of Array.from(map)) {
|
||||
if (value instanceof item.js) {
|
||||
return item.sql
|
||||
}
|
||||
}
|
||||
|
||||
return TYPES.NVarChar
|
||||
|
||||
default:
|
||||
return TYPES.NVarChar
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
driver,
|
||||
getTypeByValue,
|
||||
map
|
||||
}
|
||||
|
||||
Object.defineProperty(module.exports, 'Promise', {
|
||||
get: () => {
|
||||
return PromiseLibrary
|
||||
},
|
||||
set: (value) => {
|
||||
PromiseLibrary = value
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(module.exports, 'valueHandler', {
|
||||
enumerable: true,
|
||||
value: new Map(),
|
||||
writable: false,
|
||||
configurable: false
|
||||
})
|
||||
220
node_modules/mssql/lib/table.js
generated
vendored
Normal file
220
node_modules/mssql/lib/table.js
generated
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
'use strict'
|
||||
|
||||
const TYPES = require('./datatypes').TYPES
|
||||
const declareType = require('./datatypes').declare
|
||||
const objectHasProperty = require('./utils').objectHasProperty
|
||||
|
||||
const MAX = 65535 // (1 << 16) - 1
|
||||
const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B'
|
||||
|
||||
function Table (name) {
|
||||
if (name) {
|
||||
const parsed = Table.parseName(name)
|
||||
this.name = parsed.name
|
||||
this.schema = parsed.schema
|
||||
this.database = parsed.database
|
||||
this.path = (this.database ? `[${this.database}].` : '') + (this.schema ? `[${this.schema}].` : '') + `[${this.name}]`
|
||||
this.temporary = this.name.charAt(0) === '#'
|
||||
}
|
||||
|
||||
this.columns = []
|
||||
this.rows = []
|
||||
|
||||
Object.defineProperty(this.columns, 'add', {
|
||||
value (name, column, options) {
|
||||
if (column == null) {
|
||||
throw new Error('Column data type is not defined.')
|
||||
}
|
||||
if (column instanceof Function) {
|
||||
column = column()
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
column.name = name;
|
||||
|
||||
['nullable', 'primary', 'identity', 'readOnly', 'length'].forEach(prop => {
|
||||
if (objectHasProperty(options, prop)) {
|
||||
column[prop] = options[prop]
|
||||
}
|
||||
})
|
||||
|
||||
return this.push(column)
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(this.rows, 'add', {
|
||||
value () {
|
||||
return this.push(Array.prototype.slice.call(arguments))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Object.defineProperty(this.rows, 'clear', {
|
||||
value () {
|
||||
return this.splice(0, this.length)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
@private
|
||||
*/
|
||||
|
||||
Table.prototype._makeBulk = function _makeBulk () {
|
||||
for (let i = 0; i < this.columns.length; i++) {
|
||||
const col = this.columns[i]
|
||||
switch (col.type) {
|
||||
case TYPES.Date:
|
||||
case TYPES.DateTime:
|
||||
case TYPES.DateTime2:
|
||||
for (let j = 0; j < this.rows.length; j++) {
|
||||
const dateValue = this.rows[j][i]
|
||||
if (typeof dateValue === 'string' || typeof dateValue === 'number') {
|
||||
const date = new Date(dateValue)
|
||||
if (isNaN(date.getDate())) {
|
||||
throw new TypeError('Invalid date value passed to bulk rows')
|
||||
}
|
||||
this.rows[j][i] = date
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case TYPES.Xml:
|
||||
col.type = TYPES.NVarChar(MAX).type
|
||||
break
|
||||
|
||||
case TYPES.UDT:
|
||||
case TYPES.Geography:
|
||||
case TYPES.Geometry:
|
||||
col.type = TYPES.VarBinary(MAX).type
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Table.prototype.declare = function declare () {
|
||||
const pkey = this.columns.filter(col => col.primary === true).map(col => `[${col.name}]`)
|
||||
const cols = this.columns.map(col => {
|
||||
const def = [`[${col.name}] ${declareType(col.type, col)}`]
|
||||
|
||||
if (col.nullable === true) {
|
||||
def.push('null')
|
||||
} else if (col.nullable === false) {
|
||||
def.push('not null')
|
||||
}
|
||||
|
||||
if (col.primary === true && pkey.length === 1) {
|
||||
def.push('primary key')
|
||||
}
|
||||
|
||||
return def.join(' ')
|
||||
})
|
||||
|
||||
const constraint = pkey.length > 1 ? `, constraint [PK_${this.temporary ? this.name.substr(1) : this.name}] primary key (${pkey.join(', ')})` : ''
|
||||
return `create table ${this.path} (${cols.join(', ')}${constraint})`
|
||||
}
|
||||
|
||||
Table.fromRecordset = function fromRecordset (recordset, name) {
|
||||
const t = new this(name)
|
||||
|
||||
for (const colName in recordset.columns) {
|
||||
if (objectHasProperty(recordset.columns, colName)) {
|
||||
const col = recordset.columns[colName]
|
||||
|
||||
t.columns.add(colName, {
|
||||
type: col.type,
|
||||
length: col.length,
|
||||
scale: col.scale,
|
||||
precision: col.precision
|
||||
}, {
|
||||
nullable: col.nullable,
|
||||
identity: col.identity,
|
||||
readOnly: col.readOnly
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (t.columns.length === 1 && t.columns[0].name === JSON_COLUMN_ID) {
|
||||
for (let i = 0; i < recordset.length; i++) {
|
||||
t.rows.add(JSON.stringify(recordset[i]))
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < recordset.length; i++) {
|
||||
t.rows.add.apply(t.rows, t.columns.map(col => recordset[i][col.name]))
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
Table.parseName = function parseName (name) {
|
||||
const length = name.length
|
||||
let cursor = -1
|
||||
let buffer = ''
|
||||
let escaped = false
|
||||
const path = []
|
||||
|
||||
while (++cursor < length) {
|
||||
const char = name.charAt(cursor)
|
||||
if (char === '[') {
|
||||
if (escaped) {
|
||||
buffer += char
|
||||
} else {
|
||||
escaped = true
|
||||
}
|
||||
} else if (char === ']') {
|
||||
if (escaped) {
|
||||
escaped = false
|
||||
} else {
|
||||
throw new Error('Invalid table name.')
|
||||
}
|
||||
} else if (char === '.') {
|
||||
if (escaped) {
|
||||
buffer += char
|
||||
} else {
|
||||
path.push(buffer)
|
||||
buffer = ''
|
||||
}
|
||||
} else {
|
||||
buffer += char
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
path.push(buffer)
|
||||
}
|
||||
|
||||
switch (path.length) {
|
||||
case 1:
|
||||
return {
|
||||
name: path[0],
|
||||
schema: null,
|
||||
database: null
|
||||
}
|
||||
|
||||
case 2:
|
||||
return {
|
||||
name: path[1],
|
||||
schema: path[0],
|
||||
database: null
|
||||
}
|
||||
|
||||
case 3:
|
||||
return {
|
||||
name: path[2],
|
||||
schema: path[1],
|
||||
database: path[0]
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error('Invalid table name.')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Table
|
||||
154
node_modules/mssql/lib/tedious/connection-pool.js
generated
vendored
Normal file
154
node_modules/mssql/lib/tedious/connection-pool.js
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
'use strict'
|
||||
|
||||
const tds = require('tedious')
|
||||
const debug = require('debug')('mssql:tedi')
|
||||
const BaseConnectionPool = require('../base/connection-pool')
|
||||
const { IDS } = require('../utils')
|
||||
const shared = require('../shared')
|
||||
const ConnectionError = require('../error/connection-error')
|
||||
|
||||
class ConnectionPool extends BaseConnectionPool {
|
||||
_config () {
|
||||
const cfg = {
|
||||
server: this.config.server,
|
||||
options: Object.assign({
|
||||
encrypt: typeof this.config.encrypt === 'boolean' ? this.config.encrypt : true,
|
||||
trustServerCertificate: typeof this.config.trustServerCertificate === 'boolean' ? this.config.trustServerCertificate : false
|
||||
}, this.config.options),
|
||||
authentication: Object.assign({
|
||||
type: this.config.domain !== undefined ? 'ntlm' : this.config.authentication_type !== undefined ? this.config.authentication_type : 'default',
|
||||
options: Object.entries({
|
||||
userName: this.config.user,
|
||||
password: this.config.password,
|
||||
domain: this.config.domain,
|
||||
clientId: this.config.clientId,
|
||||
clientSecret: this.config.clientSecret,
|
||||
tenantId: this.config.tenantId,
|
||||
token: this.config.token,
|
||||
msiEndpoint: this.config.msiEndpoint,
|
||||
msiSecret: this.config.msiSecret
|
||||
}).reduce((acc, [key, val]) => {
|
||||
if (typeof val !== 'undefined') {
|
||||
return { ...acc, [key]: val }
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}, this.config.authentication)
|
||||
}
|
||||
|
||||
cfg.options.database = cfg.options.database || this.config.database
|
||||
cfg.options.port = cfg.options.port || this.config.port
|
||||
cfg.options.connectTimeout = cfg.options.connectTimeout ?? this.config.connectionTimeout ?? this.config.timeout ?? 15000
|
||||
cfg.options.requestTimeout = cfg.options.requestTimeout ?? this.config.requestTimeout ?? this.config.timeout ?? 15000
|
||||
cfg.options.tdsVersion = cfg.options.tdsVersion || '7_4'
|
||||
cfg.options.rowCollectionOnDone = cfg.options.rowCollectionOnDone || false
|
||||
cfg.options.rowCollectionOnRequestCompletion = cfg.options.rowCollectionOnRequestCompletion || false
|
||||
cfg.options.useColumnNames = cfg.options.useColumnNames || false
|
||||
cfg.options.appName = cfg.options.appName || 'node-mssql'
|
||||
|
||||
// tedious always connect via tcp when port is specified
|
||||
if (cfg.options.instanceName) delete cfg.options.port
|
||||
|
||||
if (isNaN(cfg.options.requestTimeout)) cfg.options.requestTimeout = 15000
|
||||
if (cfg.options.requestTimeout === Infinity || cfg.options.requestTimeout < 0) cfg.options.requestTimeout = 0
|
||||
|
||||
if (!cfg.options.debug && this.config.debug) {
|
||||
cfg.options.debug = {
|
||||
packet: true,
|
||||
token: true,
|
||||
data: true,
|
||||
payload: true
|
||||
}
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
_poolCreate () {
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
const resolveOnce = (v) => {
|
||||
resolve(v)
|
||||
resolve = reject = () => {}
|
||||
}
|
||||
const rejectOnce = (e) => {
|
||||
reject(e)
|
||||
resolve = reject = () => {}
|
||||
}
|
||||
let tedious
|
||||
try {
|
||||
tedious = new tds.Connection(this._config())
|
||||
} catch (err) {
|
||||
rejectOnce(err)
|
||||
return
|
||||
}
|
||||
tedious.connect(err => {
|
||||
if (err) {
|
||||
err = new ConnectionError(err)
|
||||
return rejectOnce(err)
|
||||
}
|
||||
|
||||
debug('connection(%d): established', IDS.get(tedious))
|
||||
this.collation = tedious.databaseCollation
|
||||
resolveOnce(tedious)
|
||||
})
|
||||
IDS.add(tedious, 'Connection')
|
||||
debug('pool(%d): connection #%d created', IDS.get(this), IDS.get(tedious))
|
||||
debug('connection(%d): establishing', IDS.get(tedious))
|
||||
|
||||
tedious.on('end', () => {
|
||||
const err = new ConnectionError('The connection ended without ever completing the connection')
|
||||
rejectOnce(err)
|
||||
})
|
||||
tedious.on('error', err => {
|
||||
if (err.code === 'ESOCKET') {
|
||||
tedious.hasError = true
|
||||
} else {
|
||||
this.emit('error', err)
|
||||
}
|
||||
rejectOnce(err)
|
||||
})
|
||||
|
||||
if (this.config.debug) {
|
||||
tedious.on('debug', this.emit.bind(this, 'debug', tedious))
|
||||
}
|
||||
if (typeof this.config.beforeConnect === 'function') {
|
||||
this.config.beforeConnect(tedious)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_poolValidate (tedious) {
|
||||
if (tedious && !tedious.closed && !tedious.hasError) {
|
||||
return !this.config.validateConnection || new shared.Promise((resolve) => {
|
||||
const req = new tds.Request('SELECT 1;', (err) => {
|
||||
resolve(!err)
|
||||
})
|
||||
tedious.execSql(req)
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_poolDestroy (tedious) {
|
||||
return new shared.Promise((resolve, reject) => {
|
||||
if (!tedious) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
debug('connection(%d): destroying', IDS.get(tedious))
|
||||
|
||||
if (tedious.closed) {
|
||||
debug('connection(%d): already closed', IDS.get(tedious))
|
||||
resolve()
|
||||
} else {
|
||||
tedious.once('end', () => {
|
||||
debug('connection(%d): destroyed', IDS.get(tedious))
|
||||
resolve()
|
||||
})
|
||||
|
||||
tedious.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConnectionPool
|
||||
35
node_modules/mssql/lib/tedious/index.js
generated
vendored
Normal file
35
node_modules/mssql/lib/tedious/index.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const base = require('../base')
|
||||
const ConnectionPool = require('./connection-pool')
|
||||
const Transaction = require('./transaction')
|
||||
const Request = require('./request')
|
||||
|
||||
module.exports = Object.assign({
|
||||
ConnectionPool,
|
||||
Transaction,
|
||||
Request,
|
||||
PreparedStatement: base.PreparedStatement
|
||||
}, base.exports)
|
||||
|
||||
Object.defineProperty(module.exports, 'Promise', {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
return base.Promise
|
||||
},
|
||||
set: (value) => {
|
||||
base.Promise = value
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperty(module.exports, 'valueHandler', {
|
||||
enumerable: true,
|
||||
value: base.valueHandler,
|
||||
writable: false,
|
||||
configurable: false
|
||||
})
|
||||
|
||||
base.driver.name = 'tedious'
|
||||
base.driver.ConnectionPool = ConnectionPool
|
||||
base.driver.Transaction = Transaction
|
||||
base.driver.Request = Request
|
||||
1002
node_modules/mssql/lib/tedious/request.js
generated
vendored
Normal file
1002
node_modules/mssql/lib/tedious/request.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
98
node_modules/mssql/lib/tedious/transaction.js
generated
vendored
Normal file
98
node_modules/mssql/lib/tedious/transaction.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
'use strict'
|
||||
|
||||
const debug = require('debug')('mssql:tedi')
|
||||
const BaseTransaction = require('../base/transaction')
|
||||
const { IDS } = require('../utils')
|
||||
const TransactionError = require('../error/transaction-error')
|
||||
|
||||
class Transaction extends BaseTransaction {
|
||||
constructor (parent) {
|
||||
super(parent)
|
||||
|
||||
this._abort = () => {
|
||||
if (!this._rollbackRequested) {
|
||||
// transaction interrupted because of XACT_ABORT
|
||||
|
||||
const pc = this._acquiredConnection
|
||||
|
||||
// defer releasing so connection can switch from SentClientRequest to LoggedIn state
|
||||
setImmediate(this.parent.release.bind(this.parent), pc)
|
||||
|
||||
this._acquiredConnection.removeListener('rollbackTransaction', this._abort)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
this._aborted = true
|
||||
|
||||
this.emit('rollback', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_begin (isolationLevel, callback) {
|
||||
super._begin(isolationLevel, err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('transaction(%d): begin', IDS.get(this))
|
||||
|
||||
this.parent.acquire(this, (err, connection, config) => {
|
||||
if (err) return callback(err)
|
||||
|
||||
this._acquiredConnection = connection
|
||||
this._acquiredConnection.on('rollbackTransaction', this._abort)
|
||||
this._acquiredConfig = config
|
||||
|
||||
connection.beginTransaction(err => {
|
||||
if (err) err = new TransactionError(err)
|
||||
|
||||
debug('transaction(%d): begun', IDS.get(this))
|
||||
|
||||
callback(err)
|
||||
}, this.name, this.isolationLevel)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_commit (callback) {
|
||||
super._commit(err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('transaction(%d): commit', IDS.get(this))
|
||||
|
||||
this._acquiredConnection.commitTransaction(err => {
|
||||
if (err) err = new TransactionError(err)
|
||||
|
||||
this._acquiredConnection.removeListener('rollbackTransaction', this._abort)
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
|
||||
if (!err) debug('transaction(%d): commited', IDS.get(this))
|
||||
|
||||
callback(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_rollback (callback) {
|
||||
super._rollback(err => {
|
||||
if (err) return callback(err)
|
||||
|
||||
debug('transaction(%d): rollback', IDS.get(this))
|
||||
|
||||
this._acquiredConnection.rollbackTransaction(err => {
|
||||
if (err) err = new TransactionError(err)
|
||||
|
||||
this._acquiredConnection.removeListener('rollbackTransaction', this._abort)
|
||||
this.parent.release(this._acquiredConnection)
|
||||
this._acquiredConnection = null
|
||||
this._acquiredConfig = null
|
||||
|
||||
if (!err) debug('transaction(%d): rolled back', IDS.get(this))
|
||||
|
||||
callback(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transaction
|
||||
320
node_modules/mssql/lib/udt.js
generated
vendored
Normal file
320
node_modules/mssql/lib/udt.js
generated
vendored
Normal file
@@ -0,0 +1,320 @@
|
||||
'use strict'
|
||||
|
||||
/* const FIGURE = {
|
||||
INTERIOR_RING: 0x00,
|
||||
STROKE: 0x01,
|
||||
EXTERIOR_RING: 0x02
|
||||
};
|
||||
|
||||
const FIGURE_V2 = {
|
||||
POINT: 0x00,
|
||||
LINE: 0x01,
|
||||
ARC: 0x02,
|
||||
COMPOSITE_CURVE: 0x03
|
||||
};
|
||||
|
||||
const SHAPE = {
|
||||
POINT: 0x01,
|
||||
LINESTRING: 0x02,
|
||||
POLYGON: 0x03,
|
||||
MULTIPOINT: 0x04,
|
||||
MULTILINESTRING: 0x05,
|
||||
MULTIPOLYGON: 0x06,
|
||||
GEOMETRY_COLLECTION: 0x07
|
||||
};
|
||||
|
||||
const SHAPE_V2 = {
|
||||
POINT: 0x01,
|
||||
LINESTRING: 0x02,
|
||||
POLYGON: 0x03,
|
||||
MULTIPOINT: 0x04,
|
||||
MULTILINESTRING: 0x05,
|
||||
MULTIPOLYGON: 0x06,
|
||||
GEOMETRY_COLLECTION: 0x07,
|
||||
CIRCULAR_STRING: 0x08,
|
||||
COMPOUND_CURVE: 0x09,
|
||||
CURVE_POLYGON: 0x0A,
|
||||
FULL_GLOBE: 0x0B
|
||||
};
|
||||
|
||||
const SEGMENT = {
|
||||
LINE: 0x00,
|
||||
ARC: 0x01,
|
||||
FIRST_LINE: 0x02,
|
||||
FIRST_ARC: 0x03
|
||||
}; */
|
||||
|
||||
class Point {
|
||||
constructor () {
|
||||
this.x = 0
|
||||
this.y = 0
|
||||
this.z = null
|
||||
this.m = null
|
||||
}
|
||||
}
|
||||
|
||||
const parsePoints = (buffer, count, isGeometryPoint) => {
|
||||
// s2.1.5 + s2.1.6
|
||||
// The key distinction for parsing is that a GEOGRAPHY POINT is ordered Lat (y) then Long (x),
|
||||
// while a GEOMETRY POINT is ordered x then y.
|
||||
// Further, there are additional range constraints on GEOGRAPHY POINT that are useful for testing that the coordinate order has not been flipped, such as that Lat must be in the range [-90, +90].
|
||||
|
||||
const points = []
|
||||
if (count < 1) {
|
||||
return points
|
||||
}
|
||||
|
||||
if (isGeometryPoint) {
|
||||
// GEOMETRY POINT (s2.1.6): x then y.
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const point = new Point()
|
||||
points.push(point)
|
||||
point.x = buffer.readDoubleLE(buffer.position)
|
||||
point.y = buffer.readDoubleLE(buffer.position + 8)
|
||||
buffer.position += 16
|
||||
}
|
||||
} else {
|
||||
// GEOGRAPHY POINT (s2.1.5): Lat (y) then Long (x).
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const point = new Point()
|
||||
points.push(point)
|
||||
point.lat = buffer.readDoubleLE(buffer.position)
|
||||
point.lng = buffer.readDoubleLE(buffer.position + 8)
|
||||
|
||||
// For backwards compatibility, preserve the coordinate inversion in x and y.
|
||||
// A future breaking change likely eliminate x and y for geography points in favor of just the lat and lng fields, as they've proven marvelously confusing.
|
||||
// See discussion at: https://github.com/tediousjs/node-mssql/pull/1282#discussion_r677769531
|
||||
point.x = point.lat
|
||||
point.y = point.lng
|
||||
|
||||
buffer.position += 16
|
||||
}
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
|
||||
const parseZ = (buffer, points) => {
|
||||
// s2.1.1 + s.2.1.2
|
||||
|
||||
if (points < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
points.forEach(point => {
|
||||
point.z = buffer.readDoubleLE(buffer.position)
|
||||
buffer.position += 8
|
||||
})
|
||||
}
|
||||
|
||||
const parseM = (buffer, points) => {
|
||||
// s2.1.1 + s.2.1.2
|
||||
|
||||
if (points < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
points.forEach(point => {
|
||||
point.m = buffer.readDoubleLE(buffer.position)
|
||||
buffer.position += 8
|
||||
})
|
||||
}
|
||||
|
||||
const parseFigures = (buffer, count, properties) => {
|
||||
// s2.1.3
|
||||
|
||||
const figures = []
|
||||
if (count < 1) {
|
||||
return figures
|
||||
}
|
||||
|
||||
if (properties.P) {
|
||||
figures.push({
|
||||
attribute: 0x01,
|
||||
pointOffset: 0
|
||||
})
|
||||
} else if (properties.L) {
|
||||
figures.push({
|
||||
attribute: 0x01,
|
||||
pointOffset: 0
|
||||
})
|
||||
} else {
|
||||
for (let i = 1; i <= count; i++) {
|
||||
figures.push({
|
||||
attribute: buffer.readUInt8(buffer.position),
|
||||
pointOffset: buffer.readInt32LE(buffer.position + 1)
|
||||
})
|
||||
|
||||
buffer.position += 5
|
||||
}
|
||||
}
|
||||
|
||||
return figures
|
||||
}
|
||||
|
||||
const parseShapes = (buffer, count, properties) => {
|
||||
// s2.1.4
|
||||
|
||||
const shapes = []
|
||||
if (count < 1) {
|
||||
return shapes
|
||||
}
|
||||
|
||||
if (properties.P) {
|
||||
shapes.push({
|
||||
parentOffset: -1,
|
||||
figureOffset: 0,
|
||||
type: 0x01
|
||||
})
|
||||
} else if (properties.L) {
|
||||
shapes.push({
|
||||
parentOffset: -1,
|
||||
figureOffset: 0,
|
||||
type: 0x02
|
||||
})
|
||||
} else {
|
||||
for (let i = 1; i <= count; i++) {
|
||||
shapes.push({
|
||||
parentOffset: buffer.readInt32LE(buffer.position),
|
||||
figureOffset: buffer.readInt32LE(buffer.position + 4),
|
||||
type: buffer.readUInt8(buffer.position + 8)
|
||||
})
|
||||
|
||||
buffer.position += 9
|
||||
}
|
||||
}
|
||||
|
||||
return shapes
|
||||
}
|
||||
|
||||
const parseSegments = (buffer, count) => {
|
||||
// s2.1.7
|
||||
|
||||
const segments = []
|
||||
if (count < 1) {
|
||||
return segments
|
||||
}
|
||||
|
||||
for (let i = 1; i <= count; i++) {
|
||||
segments.push({ type: buffer.readUInt8(buffer.position) })
|
||||
|
||||
buffer.position++
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
const parseGeography = (buffer, isUsingGeometryPoints) => {
|
||||
// s2.1.1 + s.2.1.2
|
||||
|
||||
const srid = buffer.readInt32LE(0)
|
||||
if (srid === -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
const value = {
|
||||
srid,
|
||||
version: buffer.readUInt8(4)
|
||||
}
|
||||
|
||||
const flags = buffer.readUInt8(5)
|
||||
buffer.position = 6
|
||||
|
||||
// console.log("srid", srid)
|
||||
// console.log("version", version)
|
||||
|
||||
const properties = {
|
||||
Z: (flags & (1 << 0)) > 0,
|
||||
M: (flags & (1 << 1)) > 0,
|
||||
V: (flags & (1 << 2)) > 0,
|
||||
P: (flags & (1 << 3)) > 0,
|
||||
L: (flags & (1 << 4)) > 0
|
||||
}
|
||||
|
||||
if (value.version === 2) {
|
||||
properties.H = (flags & (1 << 3)) > 0
|
||||
}
|
||||
|
||||
// console.log("properties", properties);
|
||||
|
||||
let numberOfPoints
|
||||
if (properties.P) {
|
||||
numberOfPoints = 1
|
||||
} else if (properties.L) {
|
||||
numberOfPoints = 2
|
||||
} else {
|
||||
numberOfPoints = buffer.readUInt32LE(buffer.position)
|
||||
buffer.position += 4
|
||||
}
|
||||
|
||||
// console.log("numberOfPoints", numberOfPoints)
|
||||
|
||||
value.points = parsePoints(buffer, numberOfPoints, isUsingGeometryPoints)
|
||||
|
||||
if (properties.Z) {
|
||||
parseZ(buffer, value.points)
|
||||
}
|
||||
|
||||
if (properties.M) {
|
||||
parseM(buffer, value.points)
|
||||
}
|
||||
|
||||
// console.log("points", points)
|
||||
|
||||
let numberOfFigures
|
||||
if (properties.P) {
|
||||
numberOfFigures = 1
|
||||
} else if (properties.L) {
|
||||
numberOfFigures = 1
|
||||
} else {
|
||||
numberOfFigures = buffer.readUInt32LE(buffer.position)
|
||||
buffer.position += 4
|
||||
}
|
||||
|
||||
// console.log("numberOfFigures", numberOfFigures)
|
||||
|
||||
value.figures = parseFigures(buffer, numberOfFigures, properties)
|
||||
|
||||
// console.log("figures", figures)
|
||||
|
||||
let numberOfShapes
|
||||
if (properties.P) {
|
||||
numberOfShapes = 1
|
||||
} else if (properties.L) {
|
||||
numberOfShapes = 1
|
||||
} else {
|
||||
numberOfShapes = buffer.readUInt32LE(buffer.position)
|
||||
buffer.position += 4
|
||||
}
|
||||
|
||||
// console.log("numberOfShapes", numberOfShapes)
|
||||
|
||||
value.shapes = parseShapes(buffer, numberOfShapes, properties)
|
||||
|
||||
// console.log( "shapes", shapes)
|
||||
|
||||
if (value.version === 2 && buffer.position < buffer.length) {
|
||||
const numberOfSegments = buffer.readUInt32LE(buffer.position)
|
||||
buffer.position += 4
|
||||
|
||||
// console.log("numberOfSegments", numberOfSegments)
|
||||
|
||||
value.segments = parseSegments(buffer, numberOfSegments)
|
||||
|
||||
// console.log("segments", segments)
|
||||
} else {
|
||||
value.segments = []
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
module.exports.PARSERS = {
|
||||
geography (buffer) {
|
||||
return parseGeography(buffer, /* isUsingGeometryPoints: */false)
|
||||
},
|
||||
|
||||
geometry (buffer) {
|
||||
return parseGeography(buffer, /* isUsingGeometryPoints: */true)
|
||||
}
|
||||
}
|
||||
20
node_modules/mssql/lib/utils.js
generated
vendored
Normal file
20
node_modules/mssql/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
const IDS = new WeakMap()
|
||||
const INCREMENT = {
|
||||
Connection: 1,
|
||||
ConnectionPool: 1,
|
||||
Request: 1,
|
||||
Transaction: 1,
|
||||
PreparedStatement: 1
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
objectHasProperty: (object, property) => Object.prototype.hasOwnProperty.call(object, property),
|
||||
INCREMENT,
|
||||
IDS: {
|
||||
get: IDS.get.bind(IDS),
|
||||
add: (object, type, id) => {
|
||||
if (id) return IDS.set(object, id)
|
||||
IDS.set(object, INCREMENT[type]++)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user