Node.js용 PostgreSQL 인터페이스
node-postgres 위에 구축된 이 라이브러리는 다음을 추가합니다:
2015년 처음 시작되었을 때 이 라이브러리는 기본 드라이버에만 Promise를 추가했기 때문에 이름은 pg-promise
입니다. 원래 이름은 유지되었지만 라이브러리의 기능은 크게 확장되었으며 이제 약속은 라이브러리의 아주 작은 부분에 불과합니다.
여기와 StackOverflow에서 무료 지원을 제공합니다.
그리고 이 프로젝트를 돕고 싶다면 비트코인 1yki7MXMkuDw8qqe5icVdh1GJZSQSzKZp
수락할 수 있습니다.
아래 장 사용법에서는 알아야 할 기본 사항을 설명하고, 공식 문서에서는 시작하는 방법과 기타 모든 리소스에 대한 링크를 제공합니다.
새 호나 PR을 열기 전에 기여 노트를 읽어보세요.
공식 문서의 단계에 따라 데이터베이스 개체를 생성하면 아래에 설명된 메서드에 액세스할 수 있습니다.
라이브러리의 모든 쿼리 방법은 일반 방법 쿼리를 기반으로 합니다.
일반적으로 쿼리를 실행하기 위해 파생된 결과별 메서드만 사용해야 합니다. 이 메서드는 모두 쿼리에서 반환할 것으로 예상되는 데이터 행 수에 따라 이름이 지정되므로 각 쿼리에 대해 올바른 메서드(없음, 하나, oneOrNone, 많음, ManyOrNone = 모두. 쿼리의 영향을 받는 행 수에 대한 메소드 이름을 혼동하지 마십시오. 이는 전혀 관련이 없습니다.
결과별 방법을 사용하면 예상치 못한 수의 데이터 행으로부터 코드를 보호하여 자동으로 거부됩니다(오류로 처리).
또한 자주 필요한 몇 가지 구체적인 방법도 있습니다.
프로토콜은 이벤트 확장을 통해 완전히 사용자 정의/확장 가능합니다.
중요한:
처음부터 이해해야 할 가장 중요한 방법은 task와 tx/txIf입니다(Tasks and Transactions 참조). 메서드 쿼리에 대해 설명된 대로 연결을 획득하고 해제하므로 한 번에 여러 쿼리를 실행하는 데 적합하지 않습니다. 이러한 이유로 연결을 오용하는 코드 작성을 방지하려면 Chaining Queries를 꼭 읽어야 합니다.
Learn by 예제는 예제를 기반으로 한 초보자용 튜토리얼입니다.
이 라이브러리에는 고성능 값 이스케이프, 유연성 및 확장성을 제공하는 쿼리 형식화 엔진이 내장되어 있습니다. 초기화 옵션 내의 pgFormatting
옵션을 통해 완전히 선택 해제하지 않는 한 모든 쿼리 방법에서 기본적으로 사용됩니다.
내부적으로 사용되는 모든 서식 지정 방법은 서식 지정 네임스페이스에서 사용할 수 있으므로 필요할 때 직접 사용할 수도 있습니다. 주요 방법은 모든 쿼리 방법에서 쿼리 형식을 지정하는 데 사용되는 형식입니다.
변수의 형식 지정 구문은 전달된 values
의 유형에 따라 결정됩니다.
values
배열이거나 단일 기본 유형인 경우 인덱스 변수.values
객체( Array
또는 null
제외)인 경우 명명된 매개변수입니다.주의: 쿼리를 생성하기 위해 ES6 템플릿 문자열이나 수동 연결을 사용하지 마십시오. 둘 다 쿼리가 쉽게 깨질 수 있기 때문입니다! 이 라이브러리의 형식 지정 엔진만이 PostgreSQL의 변수 값을 적절하게 이스케이프하는 방법을 알고 있습니다.
가장 간단한(전통적인) 형식은 $1, $2, ...
구문을 사용하여 값 배열의 인덱스( $1
~ $100000
)를 기반으로 쿼리 문자열에 값을 삽입합니다.
await db . any ( 'SELECT * FROM product WHERE price BETWEEN $1 AND $2' , [ 1 , 10 ] )
형식 지정 엔진은 $1
변수만 사용하는 쿼리에 대해 단일 값 매개변수화도 지원합니다.
await db . any ( 'SELECT * FROM users WHERE name = $1' , 'John' )
그러나 이는 number
, bigint
, string
, boolean
, Date
및 null
유형에만 작동합니다. Array
및 Object
와 같은 유형은 매개변수가 해석되는 방식을 변경하기 때문입니다. 그렇기 때문에 모호함을 피하기 위해 배열 내에서 인덱스 변수를 전달하는 것이 더 안전하다고 권장됩니다.
쿼리 메서드가 개체 values
으로 매개 변수화되면 형식 지정 엔진은 쿼리가 명명된 매개 변수 구문 $*propName*
사용할 것으로 예상합니다. 여기서 *
다음 열기-닫기 쌍 중 하나입니다: {}
, ()
, <>
, []
, //
.
// We can use every supported variable syntax at the same time, if needed:
await db . none ( 'INSERT INTO users(first_name, last_name, age) VALUES(${name.first}, $, $/age/)' , {
name : { first : 'John' , last : 'Dow' } ,
age : 30
} ) ;
중요: ES6 템플릿 문자열 내에서 예약된 ${}
구문을 사용하지 마십시오. PostgreSQL의 값 형식을 지정하는 방법에 대한 지식이 없기 때문입니다. ES6 템플릿 문자열 내에서는 $()
, $<>
, $[]
또는 $//
4가지 대안 중 하나만 사용해야 합니다. 일반적으로 SQL에 대한 표준 문자열을 사용하거나 SQL을 외부 파일에 배치해야 합니다. 쿼리 파일을 참조하세요.
유효한 변수 이름은 공개 이름 JavaScript 변수의 구문으로 제한됩니다. 그리고 this
이름은 특별한 의미를 갖습니다. 서식 지정 개체 자체를 나타냅니다(아래 참조).
속성 값 null
및 undefined
는 모두 null
로 형식화되지만 속성이 존재하지 않으면 오류가 발생한다는 점에 유의하세요.
this
참조
this
속성은 JSON 형식의 문자열로 삽입될 서식 지정 개체 자체를 나타냅니다.
await db . none ( 'INSERT INTO documents(id, doc) VALUES(${id}, ${this})' , {
id : 123 ,
body : 'some text'
} )
//=> INSERT INTO documents(id, doc) VALUES(123, '{"id":123,"body":"some text"}')
명명된 매개변수는 모든 깊이의 속성 이름 중첩을 지원합니다.
const obj = {
one : {
two : {
three : {
value1 : 123 ,
value2 : a => {
// a = obj.one.two.three
return 'hello' ;
} ,
value3 : function ( a ) {
// a = this = obj.one.two.three
return 'world' ;
} ,
value4 : {
toPostgres : a => {
// Custom Type Formatting
// a = obj.one.two.three.value4
return a . text ;
} ,
text : 'custom'
}
}
}
}
} ;
await db . one ( 'SELECT ${one.two.three.value1}' , obj ) ; //=> SELECT 123
await db . one ( 'SELECT ${one.two.three.value2}' , obj ) ; //=> SELECT 'hello'
await db . one ( 'SELECT ${one.two.three.value3}' , obj ) ; //=> SELECT 'world'
await db . one ( 'SELECT ${one.two.three.value4}' , obj ) ; //=> SELECT 'custom'
결의안의 성은 다음을 포함하여 무엇이든 될 수 있습니다.
즉, 해결 체인은 무한히 유연하며 제한 없이 재귀를 지원합니다.
그러나 중첩된 매개변수는 도우미 네임스페이스 내에서 지원되지 않습니다.
기본적으로 모든 값은 JavaScript 유형에 따라 형식이 지정됩니다. 필터(또는 수정자) 형식을 변경하면 값 형식이 다르게 지정됩니다.
형식 지정 필터는 일반 쿼리에만 작동하며, 정의에 따라 서버측에서 형식이 지정되기 때문에 preparedStatement 또는 ParameterizedQuery 내에서는 사용할 수 없습니다.
필터는 변수 이름 바로 다음에 인덱스 변수와 명명된 매개변수에 대해 동일한 구문을 사용합니다.
await db . any ( 'SELECT $1:name FROM $2:name' , [ 'price' , 'products' ] )
//=> SELECT "price" FROM "products"
await db . any ( 'SELECT ${column:name} FROM ${table:name}' , {
column : 'price' ,
table : 'products'
} ) ;
//=> SELECT "price" FROM "products"
다음 필터가 지원됩니다.
:name
/ ~
- SQL 이름:alias
- 별칭 필터:raw
/ ^
- 원시 텍스트:value
/ #
- 열린 값:csv
/ :list
- CSV 필터:json
- JSON 필터 변수 이름이 :name
또는 더 짧은 구문 ~
(물결표)로 끝나면 그에 따라 이스케이프되는 SQL 이름 또는 식별자를 나타냅니다.
await db . query ( 'INSERT INTO $1~($2~) VALUES(...)' , [ 'Table Name' , 'Column Name' ] ) ;
//=> INSERT INTO "Table Name"("Column Name") VALUES(...)
await db . query ( 'INSERT INTO $1:name($2:name) VALUES(...)' , [ 'Table Name' , 'Column Name' ] ) ;
//=> INSERT INTO "Table Name"("Column Name") VALUES(...)
일반적으로 SQL 이름 변수는 텍스트 문자열이며 길이는 1자 이상이어야 합니다. 그러나 pg-promise
SQL 이름을 제공할 수 있는 다양한 방법을 지원합니다.
*
(별표)만 포함된 문자열은 자동으로 모든 열로 인식됩니다. await db . query ( 'SELECT $1:name FROM $2:name' , [ '*' , 'table' ] ) ;
//=> SELECT * FROM "table"
await db . query ( 'SELECT ${columns:name} FROM ${table:name}' , {
columns : [ 'column1' , 'column2' ] ,
table : 'table'
} ) ;
//=> SELECT "column1","column2" FROM "table"
const obj = {
one : 1 ,
two : 2
} ;
await db . query ( 'SELECT $1:name FROM $2:name' , [ obj , 'table' ] ) ;
//=> SELECT "one","two" FROM "table"
또한 구문은 서식 지정 개체의 열 이름을 열거하기 위해 this
지원합니다.
const obj = {
one : 1 ,
two : 2
} ;
await db . query ( 'INSERT INTO table(${this:name}) VALUES(${this:csv})' , obj ) ;
//=> INSERT INTO table("one","two") VALUES(1, 2)
SQL 이름 및 식별자에 대해 이러한 유형의 형식을 사용하고 일반 변수 형식을 사용하면 SQL 삽입으로부터 애플리케이션을 보호할 수 있습니다.
as.name 메소드는 형식화를 구현합니다.
별칭은 :name
필터의 더 간단하고 덜 엄격한 버전으로, 텍스트 문자열만 지원합니다. 즉, :name
처럼 *
, this
, 배열 또는 객체를 입력으로 지원하지 않습니다. 그러나 아래 표시된 것처럼 덜 엄격하지만 모든 사용 사례의 99% 이상을 포괄하는 다른 널리 사용되는 사례를 지원합니다.
await db . any ( 'SELECT full_name as $1:alias FROM $2:name' , [ 'name' , 'table' ] ) ;
//=> SELECT full_name as name FROM "table"
.
, 각 부분을 개별적으로 이스케이프하여 자동 합성 SQL 이름을 지원합니다. await db . any ( 'SELECT * FROM $1:alias' , [ 'schemaName.table' ] ) ;
//=> SELECT * FROM "schemaName".table
자세한 내용은 형식 지정을 구현하는 as.alias 메서드를 참조하세요.
변수 이름이 :raw
또는 더 짧은 구문 ^
으로 끝나는 경우 값은 이스케이프 없이 원시 텍스트로 삽입됩니다.
이러한 변수는 이 경우 모호한 의미로 인해 null
또는 undefined
일 수 없으며 해당 값은 오류를 발생시킵니다. Values null/undefined cannot be used as raw text.
const where = pgp . as . format ( 'WHERE price BETWEEN $1 AND $2' , [ 5 , 10 ] ) ; // pre-format WHERE condition
await db . any ( 'SELECT * FROM products $1:raw' , where ) ;
//=> SELECT * FROM products WHERE price BETWEEN 5 AND 10
서식 지정 개체를 원시 JSON 문자열로 삽입하기 위한 특수 구문 this:raw
/ this^
지원됩니다.
경고:
이 필터는 안전하지 않으며 클라이언트 측에서 오는 값에 사용하면 안 됩니다. SQL 주입이 발생할 수 있기 때문입니다.
변수 이름이 :value
또는 더 짧은 구문 #
으로 끝나면 평소와 같이 이스케이프됩니다. 단, 유형이 문자열인 경우에는 후행 따옴표가 추가되지 않습니다.
개방형 값은 기본적으로 코드에서 생성할 필요 없이 외부 SQL 파일에서 완전한 LIKE
/ ILIKE
동적 문을 구성할 수 있습니다.
즉, 코드에서 다음과 같은 필터를 생성할 수 있습니다.
const name = 'John' ;
const filter = '%' + name + '%' ;
그런 다음 일반 문자열 변수로 전달하거나 name
만 전달하고 쿼리에서 공개 값 구문을 사용하여 추가 검색 논리를 추가하도록 할 수 있습니다.
SELECT * FROM table WHERE name LIKE ' %$1:value% ' )
경고:
이 필터는 안전하지 않으며 SQL 주입이 발생할 수 있으므로 클라이언트 측에서 오는 값에 사용해서는 안 됩니다.
as.value 메소드는 형식화를 구현합니다.
변수 이름이 :json
으로 끝나면 명시적 JSON 형식이 값에 적용됩니다.
기본적으로 Date
, Array
, Buffer
, null
또는 Custom-Type(사용자 정의 유형 형식 참조)이 아닌 모든 개체는 자동으로 JSON 형식으로 지정됩니다.
as.json 메서드는 형식 지정을 구현합니다.
변수 이름이 :csv
또는 :list
로 끝나는 경우 각 값은 JavaScript 유형에 따라 형식이 지정된 쉼표로 구분된 값 목록으로 형식이 지정됩니다.
일반적으로 이는 배열인 값에 사용되지만 단일 값에도 작동합니다. 아래 예를 참조하세요.
const ids = [ 1 , 2 , 3 ] ;
await db . any ( 'SELECT * FROM table WHERE id IN ($1:csv)' , [ ids ] )
//=> SELECT * FROM table WHERE id IN (1,2,3)
const ids = [ 1 , 2 , 3 ] ;
await db . any ( 'SELECT * FROM table WHERE id IN ($1:list)' , [ ids ] )
//=> SELECT * FROM table WHERE id IN (1,2,3)
자동 속성 열거 사용:
const obj = { first : 123 , second : 'text' } ;
await db . none ( 'INSERT INTO table($1:name) VALUES($1:csv)' , [ obj ] )
//=> INSERT INTO table("first","second") VALUES(123,'text')
await db . none ( 'INSERT INTO table(${this:name}) VALUES(${this:csv})' , obj )
//=> INSERT INTO table("first","second") VALUES(123,'text')
const obj = { first : 123 , second : 'text' } ;
await db . none ( 'INSERT INTO table($1:name) VALUES($1:list)' , [ obj ] )
//=> INSERT INTO table("first","second") VALUES(123,'text')
await db . none ( 'INSERT INTO table(${this:name}) VALUES(${this:list})' , obj )
//=> INSERT INTO table("first","second") VALUES(123,'text')
as.csv 메소드는 형식화를 구현합니다.
라이브러리는 CTF (Custom Type Formatting)에 대한 이중 구문을 지원합니다.
라이브러리는 항상 먼저 Symbolic CTF를 확인하고, 그러한 구문이 사용되지 않는 경우에만 Explicit CTF를 확인합니다.
toPostgres
함수를 구현하는 모든 값/객체는 사용자 정의 형식 지정 유형으로 처리됩니다. 그런 다음 함수가 호출되어 실제 값을 가져오고 this
컨텍스트를 통해 객체에 전달하고 단일 매개변수로 추가합니다( toPostgres
가 ES6 화살표 함수인 경우).
const obj = {
toPostgres ( self ) {
// self = this = obj
// return a value that needs proper escaping
}
}
toPostgres
함수는 자체 toPostgres
함수가 있는 다른 객체를 포함하여 무엇이든 반환할 수 있습니다. 즉, 중첩된 사용자 정의 유형이 지원됩니다.
toPostgres
에서 반환된 값은 JavaScript 유형에 따라 이스케이프됩니다. 단, 개체에 실제 값으로 설정된 rawType
속성이 포함되어 있지 않은 경우에는 반환된 값이 미리 형식화된 것으로 간주되어 원시 텍스트로 직접 삽입됩니다.
const obj = {
toPostgres ( self ) {
// self = this = obj
// return a pre-formatted value that does not need escaping
} ,
rawType : true // use result from toPostgres directly, as Raw Text
}
아래 예는 좌표에서 ST_MakePoint
자동 형식화하는 클래스를 구현합니다.
class STPoint {
constructor ( x , y ) {
this . x = x ;
this . y = y ;
this . rawType = true ; // no escaping, because we return pre-formatted SQL
}
toPostgres ( self ) {
return pgp . as . format ( 'ST_MakePoint($1, $2)' , [ this . x , this . y ] ) ;
}
}
그리고 그러한 클래스에 대한 고전적인 구문은 훨씬 더 간단합니다.
function STPoint ( x , y ) {
this . rawType = true ; // no escaping, because we return pre-formatted SQL
this . toPostgres = ( ) => pgp . as . format ( 'ST_MakePoint($1, $2)' , [ x , y ] ) ;
}
이 클래스를 사용하면 올바르게 삽입될 형식 지정 값으로 new STPoint(12, 34)
사용할 수 있습니다.
CTF를 사용하여 표준 유형을 재정의할 수도 있습니다.
Date . prototype . toPostgres = a => a . getTime ( ) ;
Explicit CTF와의 유일한 차이점은 toPostgres
및 rawType
ctf 네임스페이스에 정의된 ES6 기호 속성으로 설정한다는 것입니다.
const { toPostgres , rawType } = pgp . as . ctf ; // Global CTF symbols
const obj = {
[ toPostgres ] ( self ) {
// self = this = obj
// return a pre-formatted value that does not need escaping
} ,
[ rawType ] : true // use result from toPostgres directly, as Raw Text
} ;
CTF 기호는 전역적이므로 이 라이브러리와 독립적으로 개체를 구성할 수도 있습니다.
const ctf = {
toPostgres : Symbol . for ( 'ctf.toPostgres' ) ,
rawType : Symbol . for ( 'ctf.rawType' )
} ;
그 외에는 Explicit CTF와 동일하게 작동하지만 객체의 서명을 변경하지 않습니다.
이것이 무엇을 의미하는지 모른다면 ES6 기호 API와 고유한 속성 이름에 대한 사용법을 읽어보세요. 그러나 간단히 말해서 Symbol 속성은 for(name in obj)
통해 열거되지 않습니다. 즉 일반적으로 JavaScript 내에서는 표시되지 않고 특정 API Object.getOwnPropertySymbols
통해서만 표시됩니다.
QueryFile을 통해 외부 SQL 파일을 사용하면 다음과 같은 많은 이점을 얻을 수 있습니다.
debug
옵션).params
), 2단계 SQL 형식화 자동화minify
+ compress
). const { join : joinPath } = require ( 'path' ) ;
// Helper for linking to external query files:
function sql ( file ) {
const fullPath = joinPath ( __dirname , file ) ;
return new pgp . QueryFile ( fullPath , { minify : true } ) ;
}
// Create a QueryFile globally, once per file:
const sqlFindUser = sql ( './sql/findUser.sql' ) ;
db . one ( sqlFindUser , { id : 123 } )
. then ( user => {
console . log ( user ) ;
} )
. catch ( error => {
if ( error instanceof pgp . errors . QueryFileError ) {
// => the error is related to our QueryFile
}
} ) ;
파일 findUser.sql
:
/*
multi-line comments are supported
*/
SELECT name, dob -- single-line comments are supported
FROM Users
WHERE id = ${id}
라이브러리의 모든 쿼리 메소드는 query
매개변수로 QueryFile 유형을 허용할 수 있습니다. QueryFile 유형은 오류를 발생시키지 않으며 쿼리 메서드가 QueryFileError를 사용하여 정상적으로 거부하도록 남겨 둡니다.
인덱스 변수보다 외부 SQL 파일 내에서 명명된 매개변수를 사용하는 것이 좋습니다. SQL을 훨씬 쉽게 읽고 이해할 수 있고 중첩된 명명된 매개변수도 허용하므로 크고 복잡한 SQL 파일의 변수를 네임스페이스로 그룹화할 수 있기 때문입니다. 더욱 쉽게 시각적으로 분리할 수 있습니다.
작업은 여러 쿼리를 실행하기 위한 공유 연결을 나타냅니다.
db . task ( t => {
// execute a chain of queries against the task context, and return the result:
return t . one ( 'SELECT count(*) FROM events WHERE id = $1' , 123 , a => + a . count )
. then ( count => {
if ( count > 0 ) {
return t . any ( 'SELECT * FROM log WHERE event_id = $1' , 123 )
. then ( logs => {
return { count , logs } ;
} )
}
return { count } ;
} ) ;
} )
. then ( data => {
// success, data = either {count} or {count, logs}
} )
. catch ( error => {
// failed
} ) ;
작업은 콜백 함수에 대한 공유 연결 컨텍스트를 제공하고 완료되면 해제되며 한 번에 둘 이상의 쿼리를 실행할 때마다 사용해야 합니다. 작업 사용의 중요성을 이해하려면 쿼리 연결을 참조하세요.
선택적으로 작업에 태그를 지정하고(태그 참조) ES7 비동기 구문을 사용할 수 있습니다.
db . task ( async t => {
const count = await t . one ( 'SELECT count(*) FROM events WHERE id = $1' , 123 , a => + a . count ) ;
if ( count > 0 ) {
const logs = await t . any ( 'SELECT * FROM log WHERE event_id = $1' , 123 ) ;
return { count