'use strict'
|
|
const url = require('url')
|
const qs = require('querystring')
|
|
const IGNORE_KEYS = ['stream']
|
const oror = function () {
|
for (let i = 0, l = arguments.length; i < l; i++) {
|
if (arguments[i] !== null && arguments[i] !== undefined) {
|
return arguments[i]
|
}
|
}
|
}
|
|
const parseConnectionURI = function (uri) {
|
const parsed = url.parse(uri)
|
let instance
|
let user
|
let password
|
|
const path = parsed.pathname.substr(1).split('/')
|
if (path.length > 1) {
|
instance = path.shift()
|
}
|
|
if (parsed.auth) {
|
parsed.auth = parsed.auth.split(':')
|
user = parsed.auth.shift()
|
password = parsed.auth.join(':')
|
}
|
|
const port = parsed.port ? `,${parsed.port}` : (instance ? `\\${instance}` : '')
|
const object = {
|
server: `${parsed.hostname}${port}`,
|
uid: user || '',
|
pwd: password || '',
|
database: path[0]
|
}
|
|
if (parsed.query) {
|
const query = qs.parse(parsed.query)
|
for (const key in query) {
|
if (Object.prototype.hasOwnProperty.call(query, key)) {
|
const value = query[key]
|
if (key === 'domain') {
|
object.uid = `${value}\\${object.uid}`
|
} else {
|
object[key] = value
|
}
|
}
|
}
|
}
|
|
Object.defineProperty(object, 'toString', {
|
value () {
|
const out = []
|
for (const key in this) {
|
if (IGNORE_KEYS.indexOf(key) === -1) {
|
out.push(`${key}={${this[key]}}`)
|
}
|
}
|
return out.join(';')
|
}
|
})
|
|
return object
|
}
|
|
const parseConnectionString = function (string) {
|
let cursor = 0
|
let parsing = 'name'
|
let param = null
|
let buffer = ''
|
let quotes = null
|
const parsed = {}
|
const original = {}
|
|
Object.defineProperty(parsed, '__original__', { value: original })
|
Object.defineProperty(parsed, 'toString', {
|
value () {
|
const out = []
|
for (const key in this) {
|
if (IGNORE_KEYS.indexOf(key) === -1) {
|
const esc = original[key].escape || ['', '']
|
out.push(`${original[key].name}=${esc[0] || ''}${this[key]}${esc[1] || ''}`)
|
}
|
}
|
return out.join(';')
|
}
|
})
|
|
while (cursor < string.length) {
|
const char = string.charAt(cursor)
|
switch (char) {
|
case '=':
|
if (parsing === 'name') {
|
buffer = buffer.trim()
|
param = buffer.toLowerCase()
|
original[param] = { name: buffer }
|
parsing = 'value'
|
buffer = ''
|
} else {
|
buffer += char
|
}
|
break
|
|
case '\'': case '"':
|
if (parsing === 'value') {
|
if (!buffer.trim().length) {
|
// value is wrapped in qotes
|
original[param].escape = [char, char]
|
quotes = char
|
buffer = ''
|
} else if (quotes) {
|
if (char === quotes) {
|
// found same char as used for wrapping quotes
|
if (char === string.charAt(cursor + 1)) {
|
// escaped quote
|
buffer += char
|
cursor++
|
} else {
|
// end of value
|
parsed[param] = buffer
|
param = null
|
parsing = null
|
buffer = ''
|
quotes = null
|
}
|
} else {
|
buffer += char
|
}
|
} else {
|
buffer += char
|
}
|
} else {
|
throw new Error('Invalid connection string.')
|
}
|
break
|
|
case '{':
|
if (parsing === 'value') {
|
if (buffer.trim().length === 0) {
|
// value is wrapped in qotes
|
original[param].escape = ['{', '}']
|
quotes = '{}'
|
buffer = ''
|
} else {
|
buffer += char
|
}
|
} else {
|
throw new Error('Invalid connection string.')
|
}
|
break
|
|
case '}':
|
if (parsing === 'value') {
|
if (quotes === '{}') {
|
// end of value
|
parsed[param] = buffer
|
param = null
|
parsing = null
|
buffer = ''
|
quotes = null
|
} else {
|
buffer += char
|
}
|
} else {
|
throw new Error('Invalid connection string.')
|
}
|
break
|
|
case ';':
|
if (parsing === 'value') {
|
if (quotes) {
|
buffer += char
|
} else {
|
// end of value
|
parsed[param] = buffer
|
param = null
|
parsing = 'name'
|
buffer = ''
|
}
|
} else {
|
buffer = ''
|
parsing = 'name'
|
}
|
break
|
|
default:
|
buffer += char
|
}
|
|
cursor++
|
}
|
|
if (parsing === 'value') {
|
// end of value
|
parsed[param] = buffer
|
}
|
|
return parsed
|
}
|
|
const resolveConnectionString = function (string, driver) {
|
const parsed = /^(mssql|tedious|msnodesql|tds):\/\//i.test(string) ? parseConnectionURI(string) : parseConnectionString(string)
|
const stream = (parsed.stream || '').toLowerCase()
|
const encrypt = (parsed.encrypt || '').toLowerCase()
|
|
if (driver === 'msnodesqlv8') {
|
parsed.driver = 'SQL Server Native Client 11.0'
|
|
if (parsed.__original__) {
|
parsed.__original__.driver = { name: 'Driver', escape: ['{', '}'] }
|
}
|
|
return { connectionString: parsed.toString() }
|
}
|
|
let user = parsed.uid || parsed.uid || parsed['user id']
|
let server = parsed.server || parsed.address || parsed.addr || parsed['data source'] || parsed['network address']
|
|
const config = {
|
password: oror(parsed.pwd, parsed.password),
|
database: oror(parsed.database, parsed['initial catalog']),
|
connectionTimeout: oror(parsed.connectionTimeout, parsed.timeout, parsed['connect timeout'], parsed['connection timeout']),
|
requestTimeout: oror(parsed.requestTimeout, parsed['request timeout']),
|
stream: stream === 'true' || stream === 'yes' || stream === '1',
|
options: {
|
readOnlyIntent: parsed.applicationintent && parsed.applicationintent.toLowerCase() === 'readonly',
|
encrypt: encrypt === 'true' || encrypt === 'yes' || encrypt === '1'
|
}
|
}
|
|
if (parsed.useUTC != null) {
|
const utc = parsed.useUTC.toLowerCase()
|
config.options.useUTC = utc === 'true' || utc === 'yes' || utc === '1'
|
}
|
if (config.connectionTimeout != null) {
|
config.connectionTimeout = parseInt(config.connectionTimeout, 10) * 1000
|
}
|
if (config.requestTimeout != null) {
|
config.requestTimeout = parseInt(config.requestTimeout, 10)
|
}
|
|
if (parsed.multisubnetfailover != null) {
|
config.options.multiSubnetFailover = parsed.multisubnetfailover.toLowerCase() === 'true'
|
}
|
|
if (/^(.*)\\(.*)$/.exec(user)) {
|
config.domain = RegExp.$1
|
user = RegExp.$2
|
}
|
|
if (server) {
|
server = server.trim()
|
|
if (/^np:/i.test(server)) {
|
throw new Error('Connection via Named Pipes is not supported.')
|
}
|
|
if (/^tcp:/i.test(server)) {
|
server = server.substr(4)
|
}
|
|
if (/^(.*)\\(.*)$/.exec(server)) {
|
server = RegExp.$1
|
config.options.instanceName = RegExp.$2
|
}
|
|
if (/^(.*),(.*)$/.exec(server)) {
|
server = RegExp.$1.trim()
|
config.port = parseInt(RegExp.$2.trim(), 10)
|
}
|
|
if (server === '.' || server === '(.)' || server.toLowerCase() === '(localdb)' || server.toLowerCase() === '(local)') {
|
server = 'localhost'
|
}
|
}
|
|
config.user = user
|
config.server = server
|
return config
|
}
|
|
module.exports = {
|
parse: parseConnectionString,
|
resolve: resolveConnectionString
|
}
|