ステートメントの実行¶
ステートメントは connection.execute()
メソッドを呼び出すことで実行できます。 execute()
メソッドは、 SQL テキストと complete
コールバックを指定するために使用できる options
オブジェクトを受け入れます。 complete
コールバックは、ステートメントの実行が終了し、結果を使用する準備ができたときに呼び出されます。
var statement = connection.execute({ sqlText: 'CREATE DATABASE testdb', complete: function(err, stmt, rows) { if (err) { console.error('Failed to execute statement due to the following error: ' + err.message); } else { console.log('Successfully executed statement: ' + stmt.getSqlText()); } } });
注釈
1回のリクエストの最大ペイロードサイズは16 MB です。
クエリを非同期に実行する¶
Snowflake Node.jsドライバーは、非同期クエリ(つまり、クエリが完了する前にユーザーに制御を返すクエリ)をサポートしています。ユーザーはクエリを開始し、ポーリングを使用してクエリがいつ完了したかを判断できます。クエリが完了すると、ユーザーは結果セットを読み取ることができます。
非同期クエリを有効にするには、 connection.execute
メソッドに asyncExec: true
を含めます。
次の例は、 Promise
を使用してクエリを非同期に実行する方法を示しています。
let queryId;
let statement;
// 1. Execute query with asyncExec set to true
await new Promise((resolve) =>
{
connection.execute({
sqlText: 'CALL SYSTEM$WAIT(3, \'SECONDS\')',
asyncExec: true,
complete: async function (err, stmt, rows)
{
queryId = stmt.getQueryId(); // Get the query ID
resolve();
}
});
});
// 2. Get results using the query ID
const statement = await connection.getResultsFromQueryId({ queryId: queryId });
await new Promise((resolve, reject) =>
{
var stream = statement.streamRows();
stream.on('error', function (err)
{
reject(err);
});
stream.on('data', function (row)
{
console.log(row);
});
stream.on('end', function ()
{
resolve();
});
});
また、次の例に示すように、コールバックを使用して非同期クエリを監視することもできます。
connection.execute
メソッドにasyncExec: true
を含めることで、非同期クエリを有効にします。// 1. Execute query with asyncExec set to true connection.execute({ sqlText: 'CALL SYSTEM$WAIT(3, \'SECONDS\')', asyncExec: true, complete: async function (err, stmt, rows) { let queryId = stmt.getQueryId(); // 2. Get results using the query ID connection.getResultsFromQueryId({ queryId: queryId, complete: async function (err, _stmt, rows) { console.log(rows); } }); } });
非同期で実行するために送信されたクエリのステータスを確認します。
let queryId; // 1. Execute query with asyncExec set to true await new Promise((resolve, reject) => { statement = connection.execute({ sqlText: 'CALL SYSTEM$WAIT(3, \'SECONDS\')', asyncExec: true, complete: async function (err, stmt, rows) { queryId = statement.getQueryId(); resolve(); } }); }); // 2. Check query status until it's finished executing const seconds = 2; let status = await connection.getQueryStatus(queryId); while (connection.isStillRunning(status)) { console.log(`Query status is ${status}, timeout for ${seconds} seconds`); await new Promise((resolve) => { setTimeout(() => resolve(), 1000 * seconds); }); status = await connection.getQueryStatus(queryId); } console.log(`Query has finished executing, status is ${status}`);
SQL ステートメントのバッチの実行(複数ステートメントのサポート)¶
Node.jsコネクタのバージョン1.6.18以降では、セミコロンで区切られた SQL ステートメントのバッチを送信して、単一のリクエストで実行することができます。
注釈
1つのクエリで複数のステートメントを実行するには、有効なウェアハウスがセッションで利用可能である必要があります。
デフォルトでは、Snowflakeは SQL インジェクション攻撃から保護するために複数のステートメントで発行されたクエリに対してエラーを返します。1つのクエリで複数のステートメントを実行すると、 SQL インジェクションのリスクが高まります。Snowflakeはこれを控えめに使うことを推奨しています。
MULTI_STATEMENT_COUNT
パラメーターを使用して実行するステートメントの数を指定すると、ステートメントへの追加によるステートメントの注入がより困難になるため、リスクを軽減できます。
この種の攻撃の詳細については、 SQL インジェクション をご参照ください。
クエリ文字列にセミコロンで区切られた複数のステートメントが含まれていることを除いて、複数のステートメントは、単一のステートメントでクエリを実行するのと同じ方法でバッチとして実行できます。複数のステートメントは、並列ではなく順に実行されることに注意してください。 MULTI_STATEMENT_COUNT
パラメーターは、バッチに含まれるステートメントの正確な数を指定します。
たとえば、 MULTI_STATEMENT_COUNT=3
を設定する場合は、バッチステートメントには正確に3つのステートメントを含める必要があります。それ以外の数のステートメントを含むバッチステートメントを送信すると、Node.jsドライバーはリクエストを拒否します。 MULTI_STATEMENT_COUNT=0
を設定すると、バッチクエリに任意の数のステートメントを含めることができます。ただし、この値を使用すると、 SQL インジェクション攻撃に対する保護が低下することに注意してください。
このパラメーターは、次のコマンドを使用してセッションレベルで設定するか、クエリを送信するたびに個別に設定できます。
ALTER SESSION SET multi_statement_count = <n>
セッションレベルで値を設定すると、バッチステートメントを実行するたびに値を設定する必要がなくなります。次の例では、セッションレベルでステートメントの数を3に設定し、3つの SQL ステートメントを実行します。
var statement = connection.execute({ sqlText: "ALTER SESSION SET multi_statement_count=0", complete: function (err, stmt, rows) { if (err) { console.error('1 Failed to execute statement due to the following error: ' + err.message); } else { testMulti(); } } }); function testMulti() { console.log('select bind execute.'); var selectStatement = connection.execute({ sqlText: "create or replace table test(n int); insert into test values(1), (2); select * from test order by n", complete: function (err, stmt, rows) { if (err) { console.error('1 Failed to execute statement due to the following error: ' + err.message); } else { console.log('==== complete'); console.log('==== sqlText=' + stmt.getSqlText()); if(stmt.hasNext()) { stmt.NextResult(); } else { // do something else, e.g. close the connection } } } }); }
connection.execute
関数のパラメーターとして MULTI_STATEMENT_COUNT
を設定することにより、複数ステートメントのクエリを実行するたびに、バッチ内のステートメントの数を設定することもできます。次の例では、バッチのステートメント数を3に設定し、バッチクエリに3つの SQL ステートメントを含めます。
var selectStatement = connection.execute({ sqlText: "CREATE OR REPLACE TABLE test(n int); INSERT INTO test values(1), (2); SELECT * FROM test ORDER BY n", parameters: { MULTI_STATEMENT_COUNT: 3 }, complete: function (err, stmt, rows) { if (err) { console.error('1 Failed to execute statement due to the following error: ' + err.message); } else { console.log('==== complete'); console.log('==== sqlText=' + stmt.getSqlText()); if(stmt.hasNext()) { stmt.NextResult(); } else { // do something else, e.g. close the connection } } } });
ステートメントパラメーターのバインド¶
時には、ステートメント内のデータをプレースホルダーに バインド したいことがあります。この方法でステートメントを実行すると、 SQL インジェクション攻撃の防止に役立つため便利です。次のステートメントを考慮します。
connection.execute({ sqlText: 'SELECT c1 FROM (SELECT 1 AS c1 UNION ALL SELECT 2 AS c1) WHERE c1 = 1;' });
次のバインドを使用して同じ結果を達成できます。
connection.execute({ sqlText: 'SELECT c1 FROM (SELECT :1 AS c1 UNION ALL SELECT :2 AS c1) WHERE c1 = :1;', binds: [1, 2] });
バインドの ?
構文もサポートされています。
connection.execute({ p sqlText: 'SELECT c1 FROM (SELECT ? AS c1 UNION ALL SELECT ? AS c1) WHERE c1 = ?;', binds: [1, 2, 1] });
注釈
バインドできるデータのサイズ、またはバッチで結合できるデータのサイズには上限があります。詳細については、 クエリテキストサイズの制限 をご参照ください。
一括挿入のための配列のバインド¶
データの配列のバインドは、一括 INSERT 操作でサポートされています。次のように配列の配列を渡します。
connection.execute({ sqlText: 'INSERT INTO t(c1, c2, c3) values(?, ?, ?)', binds: [[1, 'string1', 2.0], [2, 'string2', 4.0], [3, 'string3', 6.0]] });
注釈
大きな配列をバインドするとパフォーマンスに影響し、データのサイズが大きすぎてサーバーで処理できない場合は拒否される可能性があります。
ステートメントのキャンセル¶
statement.cancel()
メソッドを呼び出すと、ステートメントをキャンセルできます。
statement.cancel(function(err, stmt) { if (err) { console.error('Unable to abort statement due to the following error: ' + err.message); } else { console.log('Successfully aborted statement'); } });
リクエストの再送信¶
ネットワークエラーまたはタイムアウトなどが原因で、Snowflakeが SQL ステートメントを正常に実行したかどうかが分からない場合は、リクエスト ID を使用して同じステートメントを再送信できます。たとえば、データを追加するために INSERT コマンドを送信したが、確認応答がタイムリーに受信されなかったため、コマンドで何が起こったのかが分からない場合があります。このシナリオでは、コマンドを2回実行してデータの重複が発生する可能性があるため、同じコマンドを新しいコマンドとして実行することは望ましくありません。
SQL ステートメントにリクエスト ID を含めると、データが重複する可能性を回避できます。最初のリクエストからのリクエスト ID を使用してリクエストを再送信すると、最初のリクエストが失敗した場合にのみ、再送信されたコマンドが実行されるようになります。詳細については、 SQL ステートメントの実行リクエストの再送信 をご参照ください。
注釈
リクエスト ID を使用してクエリを再送信するには、リクエスト ID を生成したときと同じ接続を使用する必要があります。別の接続からクエリの結果を取得する場合は、 RESULT_SCAN をご参照ください。
次のコードサンプルは、リクエスト ID を保存して使用し、ステートメントを再送信する方法を示しています。ステートメントを実行すると、 getRequestId()
関数を使用して、送信されたリクエストの ID を取得できます。そうすると、その ID を使用して、後で同じステートメントを実行できます。次の例では、INSERT ステートメントを実行し、そのリクエスト ID を requestId
変数に保存します。
var requestId; connection.execute({ sqlText: 'INSERT INTO testTable VALUES (1);', complete: function (err, stmt, rows) { var stream = stmt.streamRows(); requestId = stmt.getRequestId(); // Retrieves the request ID stream.on('data', function (row) { console.log(row); }); stream.on('end', function (row) { console.log('done'); }); } });
コマンドが正常に実行されたという確認を受信しない場合は、以下に示すように保存されたリクエスト ID を使用してリクエストを再送信できます。
connection.execute({ sqlText: 'INSERT INTO testTable VALUES (1);', // optional requestId: requestId, // Uses the request ID from before complete: function (err, stmt, rows) { var stream = stmt.streamRows(); stream.on('data', function (row) { console.log(row); }); stream.on('end', function (row) { console.log('done'); }); } });
requestId
と sqlText
を使用したリクエストの再送信を選択する場合は、次の相互作用に注意してください。
requestId
がすでに存在する場合、つまり以前のリクエストと一致する場合、コマンドはsqlText
クエリを無視し、元のコマンドからクエリを再送信します。requestId
が存在しない場合、つまり以前のリクエストと一致しない場合、コマンドはsqlText
クエリを実行します。