Node.js 用の PostgreSQL インターフェイス
このライブラリは、node-postgres の上に構築され、次の機能を追加します。
2015 年の開始時点では、このライブラリは基本ドライバーに Promise を追加するだけだったので、 pg-promise
名前が付けられました。そして、元の名前は保たれたまま、ライブラリの機能は大幅に拡張され、Promise はそのほんの一部にすぎなくなりました。
ここと StackOverflow で無料サポートを行っています。
このプロジェクトを支援したい場合は、ビットコインを受け取ります: 1yki7MXMkuDw8qqe5icVdh1GJZSQSzKZp
以下の章の使用法では、知っておくべき基本事項が説明されています。また、公式ドキュメントで入門し、他のすべてのリソースへのリンクが提供されています。
新しい号や PR を開く前に、寄稿ノートをお読みください。
公式ドキュメントの手順に従って Database オブジェクトを作成すると、以下に記載されているメソッドにアクセスできるようになります。
ライブラリのすべてのクエリ メソッドは、汎用メソッド クエリに基づいています。
通常、クエリの実行には結果固有の派生メソッドのみを使用する必要があります。これらのメソッドはすべて、クエリが返すと予想されるデータ行数に応じて名前が付けられているため、クエリごとに適切なメソッド (なし、1、 oneOrNone、多く、 manyOrNone = 任意。メソッド名とクエリの影響を受ける行数を混同しないでください。これはまったく無関係です。
結果固有のメソッドに依存することで、予期しない数のデータ行が自動的に拒否される (エラーとして扱われる) ことからコードを保護できます。
多くの場合必要になる特定のメソッドもいくつかあります。
このプロトコルは、イベント拡張を介して完全にカスタマイズ/拡張可能です。
重要:
最初から理解しておくべき最も重要なメソッドは、タスクと tx/txIf です (「タスクとトランザクション」を参照)。メソッド クエリについて説明されているように、接続を取得して解放するため、複数のクエリを同時に実行するのには適していません。このため、接続を悪用するコードの作成を避けるために、「クエリの連鎖」を必ず読んでください。
Learn by Example は、例に基づいた初心者向けのチュートリアルです。
このライブラリには、高性能の値エスケープ、柔軟性、拡張性を提供する埋め込みクエリ形式エンジンが付属しています。初期化オプション内のオプションpgFormatting
で完全にオプトアウトしない限り、すべてのクエリ メソッドでデフォルトで使用されます。
内部で使用されるすべての書式設定メソッドは書式設定名前空間から利用できるため、必要に応じて直接使用することもできます。主なメソッドは format で、クエリをフォーマットするためにすべてのクエリ メソッドで使用されます。
変数の書式設定構文は、渡される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' )
ただし、 Array
やObject
などの型はパラメーターの解釈方法を変更するため、これは、 number
、 bigint
、 string
、 boolean
、 Date
、およびnull
型に対してのみ機能します。このため、あいまいさを避けるために、配列内のインデックス変数を渡す方が安全であると推奨されます。
クエリ メソッドがオブジェクトとして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 テンプレート文字列内では予約済みの${}
構文を決して使用しないでください。ES6 テンプレート文字列には PostgreSQL の値をフォーマットする方法の知識がないからです。 ES6 テンプレート文字列内では、 $()
、 $<>
、 $[]
または$//
4 つの代替文字列のうち 1 つだけを使用する必要があります。一般に、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 (カスタム タイプ フォーマット) の二重構文をサポートしています。
ライブラリは常に最初にシンボリック CTF をチェックし、そのような構文が使用されていない場合のみ、明示的 CTF をチェックします。
toPostgres
関数を実装する値/オブジェクトは、カスタム書式設定タイプとして扱われます。次に、この関数が呼び出されて実際の値を取得し、 this
コンテキスト経由でオブジェクトを渡し、さらに単一のパラメーターとして渡します ( toPostgres
が ES6 アロー関数の場合)。
const obj = {
toPostgres ( self ) {
// self = this = obj
// return a value that needs proper escaping
}
}
toPostgres
関数は、独自のtoPostgres
関数を持つ別のオブジェクトを含むあらゆるものを返すことができます。つまり、ネストされたカスタム タイプがサポートされています。
toPostgres
から返される値は、オブジェクトに true 値に設定されたプロパティrawType
が含まれていない限り、その JavaScript タイプに従ってエスケープされます。この場合、戻り値は事前にフォーマットされているとみなされ、Raw Text として直接挿入されます。
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' )
} ;
それ以外は、明示的 CTF とまったく同じように機能しますが、オブジェクトの署名は変更されません。
その意味がわからない場合は、ES6 Symbol API と一意のプロパティ名の使用法を読んでください。しかし、要するに、シンボル プロパティはfor(name in obj)
を介して列挙されません。つまり、シンボル プロパティは通常 JavaScript 内では表示されず、特定の API Object.getOwnPropertySymbols
を介してのみ表示されます。
外部 SQL ファイルを (QueryFile 経由で) 使用すると、次のような多くの利点があります。
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}
ライブラリのすべてのクエリ メソッドは、QueryFile 型をquery
パラメータとして受け入れることができます。 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