ノードを用いたSQLクエリの動的生成js
16831 ワード
発売以来Slonik (ノード用のPostgreSQLクライアント)Stop using Knex.js 記事( TL ; DR ;クエリビルダは、ORM用のブロックを構築するように設計されています;クエリの大部分が静的であるときに値を追加しません).私は多くの質問されている-どのように動的なクエリを生成するのですか?私は実際の例のカップルを共有することによって、これに答えます.
この記事のクエリのすべては実際のビジネスで使用されるクエリです.Applaudience どちらheavily relies on PostgreSQL .
免責事項:(1)すべての例はSQLインジェクションの脅威のみを議論します.認可ロジック(例えば、whitesting列ユーザーはアクセスする権限がある)はこの記事の範囲にありません.( 2 )すべての文はslonik実装にバグがないと仮定します.
クエリロジックがユーザーの入力によって変更されない場合は、SQLクエリを
クエリ入力が値のリストであるとき(例えば、複数の識別子に一致する行を検索するときなど)、使用することができます
クエリ結果がユーザーの入力に依存するカラムを参照した場合、
(注意:ビジネスで使用される実際のクエリではありません.
ユーザーのクエリに応じてアプリケーションが異なる列を返す必要がある場合、ビジネスロジックのスコープにあるすべての列を選択し、必要なカラムの値を選ぶ必要があります
これは、SQLの注入のリスクを減らす(どのくらいのコード生成ロジックを信頼し、静的コードは常に動的なコードよりも安全です). それは1つのエントリだけを生成します
上記と同じだが
(注:ビジネスで使用されている実際のクエリの簡略版)
クエリを生成するための既知のユースケースはありません
クエリに存在する比較述語の演算子が動的であれば、
(注意:ビジネスで使用される実際のクエリではありません).
存在の場合
これらの例は、すべての一般的な動的なSQLの構築シナリオをカバーし、どのようにSlonikother query building methods provided by Slonik . この記事の主な意図は、slonikがクエリの静的部分をそのまま維持するSQLクエリを構築するための安全な抽象化を提供することを示すことでした.
あなたが私の仕事を評価して、Slonikとmany other of my オープンソースのプロジェクトを継続的に改善し、パトロンになることを考えてください.
最後に、私はあなたがカバーするために私をご希望のユースケースのシナリオを逃した、コメントでそれを言及し、私は喜んでそれを含むでしょう.
この記事のクエリのすべては実際のビジネスで使用されるクエリです.Applaudience どちらheavily relies on PostgreSQL .
免責事項:(1)すべての例はSQLインジェクションの脅威のみを議論します.認可ロジック(例えば、whitesting列ユーザーはアクセスする権限がある)はこの記事の範囲にありません.( 2 )すべての文はslonik実装にバグがないと仮定します.
動的値結合を持つ静的クエリ
クエリロジックがユーザーの入力によって変更されない場合は、SQLクエリを
sql
tagged template literal , 例えばsql`
SELECT c1.country_id
FROM cinema_movie_name cmn1
INNER JOIN cinema c1 ON c1.id = cmn1.cinema_id
WHERE cmn1.id = ${cinemaMovieNameId}
`;
あなたがslonikを使っているならばsafe to pass values as template literal placeholders . sql
すべてのプレースホルダトークンを解釈し、最終SQLクエリを構築します.この場合、クエリの唯一の動的部分は値バインディング自体です.したがって、最終的なクエリは以下の通りです.SELECT c1.country_id
FROM cinema_movie_name cmn1
INNER JOIN cinema c1 ON c1.id = cmn1.cinema_id
WHERE cmn1.id = $1
クエリとバインド値は、PostgreSQLに個別に送信されます.SQLインジェクションのリスクはありません.値のリストのバインド
クエリ入力が値のリストであるとき(例えば、複数の識別子に一致する行を検索するときなど)、使用することができます
sql.valueList
, 例えばsql`
SELECT m1.*
FROM movie m1
WHERE m1.id IN (${sql.valueList(movieIds)})
`;
これは動的な値バインディングを持つクエリを生成しますmovieIds
is [1, 2, 3]
PostgreSQLに送られるクエリは次のようになります.SELECT m1.*
FROM movie m1
WHERE m1.id IN ($1, $2, $3)
しかし、これは一般的なパターンにもかかわらず、私はこのパターンを使用することをお勧めしません.代わりにsql.array
, 例えばsql`
SELECT m1.*
FROM movie m1
WHERE m1.id = ANY(${sql.array(movieIds, 'int4')})
`;
これは、入力に基づいて変更されない固定長クエリを生成します.SELECT m1.*
FROM movie m1
WHERE m1.id = ANY($1::"int4"[])
続きを読む sql.array
vs sql.valueList
.動的カラムによるクエリ
クエリ結果がユーザーの入力に依存するカラムを参照した場合、
sql.identifier
これらの列を識別するSQLを生成する例を示します.(注意:ビジネスで使用される実際のクエリではありません.
sql`
SELECT m1.id, ${sql.identifier(['m1', movieTableColumnName])}
FROM movie m1
WHERE
m1.id = ${moveId}
`;
このクエリは、正確に1つの動的に識別された列を選択するクエリを生成します.SQL注入の危険性はありません.すなわち、movieTableColumnName
どうにかして、最悪の事態が起こる可能性があるのは、クエリー攻撃者がm1
無効なカラム識別子値(両方とも危険を運びます;ビジネスロジックはこの記事の範囲にありません)で、エイリアスかExecute質問を実行してください.ユーザーのクエリに応じてアプリケーションが異なる列を返す必要がある場合、ビジネスロジックのスコープにあるすべての列を選択し、必要なカラムの値を選ぶ必要があります
movieTableColumnName
, それから静的クエリを書くほうが良いです.sql`
SELECT
m1.id,
m1.foreign_comscore_id,
m1.foreign_imdb_id,
m1.foreign_metacritic_id
m1.foreign_rottentomatoes_id,
m1.foreign_tmdb_id,
m1.foreign_webedia_id
FROM movie m1
WHERE
m1.id = ${moveId}
`;
後者はすべてのクエリにいくつかの余分なデータを返しますが、いくつかの利点があります.pg_stat_statements
. あなたは、可能な限り少ないクエリをpg_stat_statements
あなたのアプリケーションのスケールとして.複数の動的カラムによる質問
上記と同じだが
sql.identifierList
.動的SQLクエリの入れ子
sql
タグ付きテンプレートリテラルは入れ子にできます.(注:ビジネスで使用されている実際のクエリの簡略版)
const futureEventEventChangeSqlToken = sql`
SELECT
ec1.event_id,
ec1.seat_count,
ec1.seat_sold_count
FROM event_change_future_event_view ec1
`;
sql`
SELECT
event_id,
seat_count,
seat_sold_count
FROM (
${futureEventEventChangeSqlToken}
) AS haystack
WHERE ${paginatedWhereSqlToken}
ORDER BY ${orderSqlToken}
LIMIT ${limitSqlToken}
`
これは、プログラムを越えて一流の市民としてプリバインドされたSQLクエリを渡すことができます.これは意図がテストのためにSQL生成論理を隔離するか、または大きいSQL断片が質問の間で共有されるとき、または、意図が単に1つの場所でコード複雑さの集中を減らすことになっているとき、便利です.動的SQLフラグメントの注入
sql.raw
は、動的なSQLフラグメントを注入するために使用されます.sql`
SELECT ${sql.raw('foo bar baz')}
`
(無効な)クエリへの変換SELECT foo bar baz
前の例とは異なりsql
タグ付きテンプレートsql.raw
安全ではない-それはユーザーの入力を使用して動的なSQLを作成することができます.クエリを生成するための既知のユースケースはありません
sql.raw
それは入れ子に縛られていないsql
式(「動的SQLクエリの入れ子」で記述されている)または他のquery building methods . sql.raw
外部に格納された静的なファイルを実行するための機構として存在します.動的比較述語メンバまたは演算子を使用したクエリ
クエリに存在する比較述語の演算子が動的であれば、
sql.comparisonPredicate
, 例えば(注意:ビジネスで使用される実際のクエリではありません).
sql`
SELECT
c1.id,
c1.nid,
c1.name
FROM cinema c1
WHERE
${sql.comparisonPredicate(
sql`c1.name`,
nameComparisonOperator,
nameComparisonValue
)}
`;
nameComparisonOperator
などの値=
, >
, <
, 仮定nameComparisonOperator
は"="で、結果として得られるクエリは以下のようになります:SELECT
c1.id,
c1.nid,
c1.name
FROM cinema c1
WHERE
c1.name = $1
後者は非常にまれなユースケースです.これはアドバンス検索のために役に立つかもしれませんがsql.booleanExpression
).動的WHERE節メンバーによる質問
存在の場合
WHERE
節メンバーは動的で sql.booleanExpression
.const findCinemas = (root, parameters, context) => {
const booleanExpressions = [
sql`TRUE`,
];
if (parameters.input.query) {
const query = parameters.input.query;
if (query.countryId !== undefined) {
booleanExpressions.push(
sql`c2.id = ${query.countryId}`
);
}
if (query.nid !== undefined) {
booleanExpressions.push(
sql`c1.nid % ${query.nid}`
);
}
if (query.name !== undefined) {
booleanExpressions.push(
sql`c1.name % ${query.name}`
);
}
}
const whereSqlToken = sql.booleanExpression(
booleanExpressions,
'AND'
);
return context.pool.any(sql`
SELECT
c1.id,
c1.nid,
c1.name,
c2.code_alpha_2 country_code,
c2.name country_name
FROM cinema c1
INNER JOIN country c2 ON c2.id = c1.country_id
WHERE ${whereSqlToken}
`);
},
findCinemas
は、GraphSQLリゾルバの実装です.クエリの句は、3つの可能なブール式の組み合わせを使用して構築されます.Slonikの他のクエリビルディングメソッドの場合と同様に、すべての式は入れ子にできます.ブール式のメンバー、あるいはsql
タグ付きテンプレートリテラル.概要
これらの例は、すべての一般的な動的なSQLの構築シナリオをカバーし、どのようにSlonikother query building methods provided by Slonik . この記事の主な意図は、slonikがクエリの静的部分をそのまま維持するSQLクエリを構築するための安全な抽象化を提供することを示すことでした.
あなたが私の仕事を評価して、Slonikとmany other of my オープンソースのプロジェクトを継続的に改善し、パトロンになることを考えてください.
最後に、私はあなたがカバーするために私をご希望のユースケースのシナリオを逃した、コメントでそれを言及し、私は喜んでそれを含むでしょう.
Reference
この問題について(ノードを用いたSQLクエリの動的生成js), 我々は、より多くの情報をここで見つけました https://dev.to/gajus/dynamically-generating-sql-queries-using-node-js-2c1gテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol