mirror of
https://github.com/brianc/node-postgres.git
synced 2025-12-08 20:16:25 +00:00
Document client.escapeIdentifier and client.escapeLiteral (#2954)
* Document client.escapeIdentifier and client.escapeLiteral Per #1978 it seems that these client APIs are undocumented. Added documentation for these functions along with some examples and relevant links. * Fix typos in new docs * Migrate escapeIdentifier and escapeLiteral from Client to PG These are standalone utility functions, they do not need a client instance to function. Changes made: - Refactored escapeIdentifer and escapeLiteral from client class to functions in utils - Update PG to export escapeIdentifier and escapeLiteral - Migrated tests for Client.escapeIdentifier and Client.escapeLiteral to tests for utils - Updated documentation, added a "utilities" page where these helpers are discussed **note** this is a breaking change. Users who used these functions (previously undocumented) on instances of Client, or via Client.prototype. * Export escapeIdentifier and escapeLiteral from PG These are standalone utility functions, they should not depend on a client instance. Changes made: - Refactored escapeIdentifer and escapeLiteral from client class to functions in utils - Re-exported functions on client for backwards compatibility - Update PG to export escapeIdentifier and escapeLiteral - Updated tests to validate the newly exported functions from both entry points - Updated documentation, added a "utilities" page where these helpers are discussed * Ensure escape functions work via Client.prototype Updated changes such that escapeIdentifier and escapeLiteral are usable via the client prototype Updated tests to check for both entry points in client
This commit is contained in:
parent
d63c761be8
commit
249182ea9f
@ -3,5 +3,6 @@
|
||||
"pool": "pg.Pool",
|
||||
"result": "pg.Result",
|
||||
"types": "pg.Types",
|
||||
"cursor": "Cursor"
|
||||
"cursor": "Cursor",
|
||||
"utilities": "Utilities"
|
||||
}
|
||||
|
||||
30
docs/pages/apis/utilities.mdx
Normal file
30
docs/pages/apis/utilities.mdx
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Utilities
|
||||
---
|
||||
import { Alert } from '/components/alert.tsx'
|
||||
|
||||
## Utility Functions
|
||||
### pg.escapeIdentifier
|
||||
|
||||
Escapes a string as a [SQL identifier](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS).
|
||||
|
||||
```js
|
||||
const { escapeIdentifier } = require('pg')
|
||||
const escapedIdentifier = escapeIdentifier('FooIdentifier')
|
||||
console.log(escapedIdentifier) // '"FooIdentifier"'
|
||||
```
|
||||
|
||||
|
||||
### pg.escapeLiteral
|
||||
|
||||
<Alert>
|
||||
**Note**: Instead of manually escaping SQL literals, it is recommended to use parameterized queries. Refer to [parameterized queries](/features/queries#parameterized-query) and the [client.query](/apis/client#clientquery) API for more information.
|
||||
</Alert>
|
||||
|
||||
Escapes a string as a [SQL literal](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS).
|
||||
|
||||
```js
|
||||
const { escapeLiteral } = require('pg')
|
||||
const escapedLiteral = escapeLiteral("hello 'world'")
|
||||
console.log(escapedLiteral) // "'hello ''world'''"
|
||||
```
|
||||
@ -456,35 +456,15 @@ class Client extends EventEmitter {
|
||||
return this._types.getTypeParser(oid, format)
|
||||
}
|
||||
|
||||
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
||||
// escapeIdentifier and escapeLiteral moved to utility functions & exported
|
||||
// on PG
|
||||
// re-exported here for backwards compatibility
|
||||
escapeIdentifier(str) {
|
||||
return '"' + str.replace(/"/g, '""') + '"'
|
||||
return utils.escapeIdentifier(str)
|
||||
}
|
||||
|
||||
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
||||
escapeLiteral(str) {
|
||||
var hasBackslash = false
|
||||
var escaped = "'"
|
||||
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str[i]
|
||||
if (c === "'") {
|
||||
escaped += c + c
|
||||
} else if (c === '\\') {
|
||||
escaped += c + c
|
||||
hasBackslash = true
|
||||
} else {
|
||||
escaped += c
|
||||
}
|
||||
}
|
||||
|
||||
escaped += "'"
|
||||
|
||||
if (hasBackslash === true) {
|
||||
escaped = ' E' + escaped
|
||||
}
|
||||
|
||||
return escaped
|
||||
return utils.escapeLiteral(str)
|
||||
}
|
||||
|
||||
_pulseQueryQueue() {
|
||||
|
||||
@ -5,6 +5,7 @@ var defaults = require('./defaults')
|
||||
var Connection = require('./connection')
|
||||
var Pool = require('pg-pool')
|
||||
const { DatabaseError } = require('pg-protocol')
|
||||
const { escapeIdentifier, escapeLiteral } = require('./utils')
|
||||
|
||||
const poolFactory = (Client) => {
|
||||
return class BoundPool extends Pool {
|
||||
@ -23,6 +24,8 @@ var PG = function (clientConstructor) {
|
||||
this.Connection = Connection
|
||||
this.types = require('pg-types')
|
||||
this.DatabaseError = DatabaseError
|
||||
this.escapeIdentifier = escapeIdentifier
|
||||
this.escapeLiteral = escapeLiteral
|
||||
}
|
||||
|
||||
if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {
|
||||
|
||||
@ -175,6 +175,38 @@ const postgresMd5PasswordHash = function (user, password, salt) {
|
||||
return 'md5' + outer
|
||||
}
|
||||
|
||||
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
||||
const escapeIdentifier = function (str) {
|
||||
return '"' + str.replace(/"/g, '""') + '"'
|
||||
}
|
||||
|
||||
const escapeLiteral = function (str) {
|
||||
var hasBackslash = false
|
||||
var escaped = "'"
|
||||
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str[i]
|
||||
if (c === "'") {
|
||||
escaped += c + c
|
||||
} else if (c === '\\') {
|
||||
escaped += c + c
|
||||
hasBackslash = true
|
||||
} else {
|
||||
escaped += c
|
||||
}
|
||||
}
|
||||
|
||||
escaped += "'"
|
||||
|
||||
if (hasBackslash === true) {
|
||||
escaped = ' E' + escaped
|
||||
}
|
||||
|
||||
return escaped
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
prepareValue: function prepareValueWrapper(value) {
|
||||
// this ensures that extra arguments do not get passed into prepareValue
|
||||
@ -184,4 +216,6 @@ module.exports = {
|
||||
normalizeQueryConfig,
|
||||
postgresMd5PasswordHash,
|
||||
md5,
|
||||
escapeIdentifier,
|
||||
escapeLiteral
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
var helper = require('./test-helper')
|
||||
var utils = require('../../../lib/utils')
|
||||
|
||||
function createClient(callback) {
|
||||
var client = new Client(helper.config)
|
||||
@ -14,6 +15,17 @@ var testLit = function (testName, input, expected) {
|
||||
var actual = client.escapeLiteral(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
|
||||
test('Client.prototype.' + testName, function () {
|
||||
var actual = Client.prototype.escapeLiteral(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
|
||||
|
||||
test('utils.' + testName, function () {
|
||||
var actual = utils.escapeLiteral(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
var testIdent = function (testName, input, expected) {
|
||||
@ -22,6 +34,17 @@ var testIdent = function (testName, input, expected) {
|
||||
var actual = client.escapeIdentifier(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
|
||||
test('Client.prototype.' + testName, function () {
|
||||
var actual = Client.prototype.escapeIdentifier(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
|
||||
|
||||
test('utils.' + testName, function () {
|
||||
var actual = utils.escapeIdentifier(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
testLit('escapeLiteral: no special characters', 'hello world', "'hello world'")
|
||||
|
||||
@ -239,3 +239,56 @@ test('prepareValue: can safely be used to map an array of values including those
|
||||
var out = values.map(utils.prepareValue)
|
||||
assert.deepEqual(out, [1, 'test', 'zomgcustom!'])
|
||||
})
|
||||
|
||||
var testEscapeLiteral = function (testName, input, expected) {
|
||||
test(testName, function () {
|
||||
var actual = utils.escapeLiteral(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
}
|
||||
testEscapeLiteral('escapeLiteral: no special characters', 'hello world', "'hello world'")
|
||||
|
||||
testEscapeLiteral('escapeLiteral: contains double quotes only', 'hello " world', "'hello \" world'")
|
||||
|
||||
testEscapeLiteral('escapeLiteral: contains single quotes only', "hello ' world", "'hello '' world'")
|
||||
|
||||
testEscapeLiteral('escapeLiteral: contains backslashes only', 'hello \\ world', " E'hello \\\\ world'")
|
||||
|
||||
testEscapeLiteral('escapeLiteral: contains single quotes and double quotes', 'hello \' " world', "'hello '' \" world'")
|
||||
|
||||
testEscapeLiteral('escapeLiteral: contains double quotes and backslashes', 'hello \\ " world', " E'hello \\\\ \" world'")
|
||||
|
||||
testEscapeLiteral('escapeLiteral: contains single quotes and backslashes', "hello \\ ' world", " E'hello \\\\ '' world'")
|
||||
|
||||
testEscapeLiteral(
|
||||
'escapeLiteral: contains single quotes, double quotes, and backslashes',
|
||||
'hello \\ \' " world',
|
||||
" E'hello \\\\ '' \" world'"
|
||||
)
|
||||
|
||||
var testEscapeIdentifier = function (testName, input, expected) {
|
||||
test(testName, function () {
|
||||
var actual = utils.escapeIdentifier(input)
|
||||
assert.equal(expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: no special characters', 'hello world', '"hello world"')
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: contains double quotes only', 'hello " world', '"hello "" world"')
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: contains single quotes only', "hello ' world", '"hello \' world"')
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: contains backslashes only', 'hello \\ world', '"hello \\ world"')
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: contains single quotes and double quotes', 'hello \' " world', '"hello \' "" world"')
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: contains double quotes and backslashes', 'hello \\ " world', '"hello \\ "" world"')
|
||||
|
||||
testEscapeIdentifier('escapeIdentifier: contains single quotes and backslashes', "hello \\ ' world", '"hello \\ \' world"')
|
||||
|
||||
testEscapeIdentifier(
|
||||
'escapeIdentifier: contains single quotes, double quotes, and backslashes',
|
||||
'hello \\ \' " world',
|
||||
'"hello \\ \' "" world"'
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user