-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathconnection-parameters.js
More file actions
189 lines (159 loc) · 6.01 KB
/
connection-parameters.js
File metadata and controls
189 lines (159 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
'use strict'
const dns = require('dns')
const defaults = require('./defaults')
const parse = require('pg-connection-string').parse // parses a connection string
const val = function (key, config, envVar) {
if (config[key]) {
return config[key]
}
if (envVar === undefined) {
envVar = process.env['PG' + key.toUpperCase()]
} else if (envVar === false) {
// do nothing ... use false
} else {
envVar = process.env[envVar]
}
return envVar || defaults[key]
}
const readSSLConfigFromEnvironment = function () {
switch (process.env.PGSSLMODE) {
case 'disable':
return false
case 'prefer':
case 'require':
case 'verify-ca':
case 'verify-full':
return true
case 'no-verify':
return { rejectUnauthorized: false }
}
return defaults.ssl
}
// Convert arg to a string, surround in single quotes, and escape single quotes and backslashes
const quoteParamValue = function (value) {
return "'" + ('' + value).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
}
const add = function (params, config, paramName) {
const value = config[paramName]
if (value !== undefined && value !== null) {
params.push(paramName + '=' + quoteParamValue(value))
}
}
class ConnectionParameters {
constructor(config) {
// if a string is passed, it is a raw connection string so we parse it into a config
config = typeof config === 'string' ? parse(config) : config || {}
// if the config has a connectionString defined, parse IT into the config we use
// this will override other default values with what is stored in connectionString
if (config.connectionString) {
config = Object.assign({}, config, parse(config.connectionString))
}
this.user = val('user', config)
this.database = val('database', config)
if (this.database === undefined) {
this.database = this.user
}
const rawPort = val('port', config)
this.port = Array.isArray(rawPort) ? rawPort.map((p) => parseInt(p, 10)) : parseInt(rawPort, 10)
this.host = val('host', config)
const hosts = Array.isArray(this.host) ? this.host : [this.host]
const ports = Array.isArray(this.port) ? this.port : [this.port]
if (ports.length !== 1 && ports.length !== hosts.length) {
throw new Error(`ports must have either 1 entry or the same number of entries as hosts (${hosts.length})`)
}
// "hiding" the password so it doesn't show up in stack traces
// or if the client is console.logged
Object.defineProperty(this, 'password', {
configurable: true,
enumerable: false,
writable: true,
value: val('password', config),
})
this.binary = val('binary', config)
this.options = val('options', config)
this.ssl = typeof config.ssl === 'undefined' ? readSSLConfigFromEnvironment() : config.ssl
if (typeof this.ssl === 'string') {
if (this.ssl === 'true') {
this.ssl = true
}
}
// support passing in ssl=no-verify via connection string
if (this.ssl === 'no-verify') {
this.ssl = { rejectUnauthorized: false }
}
if (this.ssl && this.ssl.key) {
Object.defineProperty(this.ssl, 'key', {
enumerable: false,
})
}
this.client_encoding = val('client_encoding', config)
this.replication = val('replication', config)
// a domain socket begins with '/'
this.isDomainSocket = !(this.host || '').indexOf('/')
this.application_name = val('application_name', config, 'PGAPPNAME')
this.fallback_application_name = val('fallback_application_name', config, false)
this.statement_timeout = val('statement_timeout', config, false)
this.lock_timeout = val('lock_timeout', config, false)
this.idle_in_transaction_session_timeout = val('idle_in_transaction_session_timeout', config, false)
this.query_timeout = val('query_timeout', config, false)
this.targetSessionAttrs = val('targetSessionAttrs', config)
const validTargetSessionAttrs = ['any', 'read-write', 'read-only', 'primary', 'standby', 'prefer-standby']
if (this.targetSessionAttrs && !validTargetSessionAttrs.includes(this.targetSessionAttrs)) {
throw new Error(
`invalid targetSessionAttrs value: "${this.targetSessionAttrs}". Must be one of: ${validTargetSessionAttrs.join(
', '
)}`
)
}
if (config.connectionTimeoutMillis === undefined) {
this.connect_timeout = process.env.PGCONNECT_TIMEOUT || 0
} else {
this.connect_timeout = Math.floor(config.connectionTimeoutMillis / 1000)
}
if (config.keepAlive === false) {
this.keepalives = 0
} else if (config.keepAlive === true) {
this.keepalives = 1
}
if (typeof config.keepAliveInitialDelayMillis === 'number') {
this.keepalives_idle = Math.floor(config.keepAliveInitialDelayMillis / 1000)
}
}
getLibpqConnectionString(cb) {
const params = []
add(params, this, 'user')
add(params, this, 'password')
add(params, this, 'port')
add(params, this, 'application_name')
add(params, this, 'fallback_application_name')
add(params, this, 'connect_timeout')
add(params, this, 'options')
const ssl = typeof this.ssl === 'object' ? this.ssl : this.ssl ? { sslmode: this.ssl } : {}
add(params, ssl, 'sslmode')
add(params, ssl, 'sslca')
add(params, ssl, 'sslkey')
add(params, ssl, 'sslcert')
add(params, ssl, 'sslrootcert')
if (this.database) {
params.push('dbname=' + quoteParamValue(this.database))
}
if (this.replication) {
params.push('replication=' + quoteParamValue(this.replication))
}
if (this.host) {
params.push('host=' + quoteParamValue(this.host))
}
if (this.isDomainSocket) {
return cb(null, params.join(' '))
}
if (this.client_encoding) {
params.push('client_encoding=' + quoteParamValue(this.client_encoding))
}
dns.lookup(this.host, function (err, address) {
if (err) return cb(err, null)
params.push('hostaddr=' + quoteParamValue(address))
return cb(null, params.join(' '))
})
}
}
module.exports = ConnectionParameters