ステートメントの実行

ステートメントは 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());
    }
  }
});
Copy

注釈

1回のリクエストの最大ペイロードサイズは16 MB です。

ステートメントパラメーターのバインド

時には、ステートメント内のデータをプレースホルダーに バインド したいことがあります。この方法でステートメントを実行すると、 SQL インジェクション攻撃の防止に役立つため便利です。次のステートメントを考慮します。

connection.execute({
  sqlText: 'SELECT c1 FROM (SELECT 1 AS c1 UNION ALL SELECT 2 AS c1) WHERE c1 = 1;'
});
Copy

次のバインドを使用して同じ結果を達成できます。

connection.execute({
  sqlText: 'SELECT c1 FROM (SELECT :1 AS c1 UNION ALL SELECT :2 AS c1) WHERE c1 = :1;',
  binds: [1, 2]
});
Copy

バインドの ? 構文もサポートされています。

connection.execute({
p
  sqlText: 'SELECT c1 FROM (SELECT ? AS c1 UNION ALL SELECT ? AS c1) WHERE c1 = ?;',
  binds: [1, 2, 1]
});
Copy

注釈

バインドできるデータのサイズ、またはバッチで結合できるデータのサイズには上限があります。詳細については、 クエリテキストサイズの制限 をご参照ください。

SQL ステートメントのバッチの実行(複数ステートメントのサポート)

Node.jsコネクタのバージョン1.6.18以降では、セミコロンで区切られた SQL ステートメントのバッチを送信して、単一のリクエストで実行することができます。

注釈

デフォルトでは、Snowflakeは SQL インジェクション 攻撃から保護するために複数のステートメントで発行されたクエリに対してエラーを返します。複数ステートメント機能により、システムが SQL インジェクションに対して脆弱になるため、慎重に使用する必要があります。 MULTI_STATEMENT_COUNT パラメーターを使用して実行するステートメントの数を指定すると、ステートメントへの追加によるステートメントの注入がより困難になるため、リスクを軽減できます。

クエリ文字列にセミコロンで区切られた複数のステートメントが含まれていることを除いて、複数のステートメントは、単一のステートメントでクエリを実行するのと同じ方法でバッチとして実行できます。複数のステートメントは、並列ではなく順に実行されることに注意してください。 MULTI_STATEMENT_COUNT パラメーターは、バッチに含まれるステートメントの正確な数を指定します。

たとえば、 MULTI_STATEMENT_COUNT=3 を設定する場合は、バッチステートメントには正確に3つのステートメントを含める必要があります。それ以外の数のステートメントを含むバッチステートメントを送信すると、Node.jsドライバーはリクエストを拒否します。 MULTI_STATEMENT_COUNT=0 を設定すると、バッチクエリに任意の数のステートメントを含めることができます。ただし、この値を使用すると、 SQL インジェクション攻撃に対する保護が低下することに注意してください。

このパラメーターは、次のコマンドを使用してセッションレベルで設定するか、クエリを送信するたびに個別に設定できます。

ALTER SESSION SET multi_statement_count = <n>
Copy

セッションレベルで値を設定すると、バッチステートメントを実行するたびに値を設定する必要がなくなります。次の例では、セッションレベルでステートメントの数を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
        }
      }
    }
  });
}
Copy

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
        }
      }
    }
  });
Copy

一括挿入のための配列のバインド

データの配列のバインドは、一括 INSERT 操作でサポートされています。次のように配列の配列を渡します。

connection.execute({
  sqlText: 'INSERT INTO t(c1, c2, c3) values(?, ?, ?)',
  binds: [[1, 'string1', 2.0], [2, 'string2', 4.0], [3, 'string3', 6.0]]
});
Copy

注釈

大きな配列をバインドするとパフォーマンスに影響し、データのサイズが大きすぎてサーバーで処理できない場合は拒否される可能性があります。

ステートメントのキャンセル

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');
  }
});
Copy

リクエストの再送信

ネットワークエラーまたはタイムアウトなどが原因で、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');
      });
    }
});
Copy

コマンドが正常に実行されたという確認を受信しない場合は、以下に示すように保存されたリクエスト 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');
      });
  }
});
Copy

requestIdsqlText を使用したリクエストの再送信を選択する場合は、次の相互作用に注意してください。

  • requestId がすでに存在する場合、つまり以前のリクエストと一致する場合、コマンドは sqlText クエリを無視し、元のコマンドからクエリを再送信します。

  • requestId が存在しない場合、つまり以前のリクエストと一致しない場合、コマンドは sqlText クエリを実行します。