[BigQuery API(V2)] Javaでデータセットのルーティンを生成・更新・削除する

BigQuery API V2(Java)を使用して、
BigQueryデータセットのルーティンを生成・更新・削除する方法
についてご紹介します。

**ルーティンとは**
処理を事前に定義しておいて、後から呼び出せるようにしておくためのコンポーネントです。
BigQueryでは、以下のリソースをルーティンと定義付けています。
|リソース|概要|
|---|---|
|リモート関数を含むユーザー定義関数（UDF）|SQL または JavaScript で記述するステートメント群|
|ストアドプロシージャ|SQLで記述するステートメント群|
|テーブル関数(TFV)|戻り値にテーブルを返すユーザー定義関数|

データセットのルーティンを生成・更新・削除するには、
データセットに対し適切なIAM権限が付与されている必要があります。

権限付与に関しては
[⧉[BigQuery API(V2)] JavaでBigQueryを使う(2.権限付与)](https://qiita.com/doran/items/8b7ba29672527426e88d#2-権限付与)
を参照ください。

:::note info
APIを利用する環境の準備から始める場合や、コードを実行する際は、
[⧉[BigQuery API(V2)] JavaでBigQueryを使う](https://qiita.com/doran/items/8b7ba29672527426e88d)
を参照ください。
:::

|No||目次|
|---|---|---|
|1||[ルーティンを生成](#1-ルーティンを生成)|
||1|[スコープ](#11-スコープ)|
||2|[実行](#12-実行)|
||3|[レスポンスの内容](#13-レスポンスの内容)|
|2||[ルーティンを更新](#2-ルーティンを更新)|
||1|[スコープ](#21-スコープ)|
||2|[実行](#22-実行)|
||3|[レスポンスの内容](#23-レスポンスの内容)|
|3||[ルーティンを削除](#3-ルーティンを削除)|
||1|[スコープ](#31-スコープ)|
||2|[実行](#32-実行)|
||3|[レスポンスの内容](#33-レスポンスの内容)|

## 1. ルーティンを生成
指定したデータセットにルーティンを作成します。

### 1.1. スコープ
このAPIを実行するには、以下のいずれかのスコープを指定してください。
```
BigqueryScopes.BIGQUERY
BigqueryScopes.CLOUD_PLATFORM
```
[⧉[BigQuery API(V2)] JavaでBigQueryを使う(3.2. BigQueryインスタンスを取得)](https://qiita.com/doran/items/8b7ba29672527426e88d#32-bigqueryインスタンスを取得)
でスコープを指定してください。

### 1.2. 実行
APIを通すことが目的なのでルーティンについての説明はここでは省きます。

プロジェクトIDとデータセットIDは2度ずつ設定していますが、
設定しないとエラーになります。

```java
public static void main(String[] args) throws Exception{
    Bigquery bigquery = getBigquery();
    Bigquery.Routines routines = bigquery.routines();
    
    RoutineReference reference = new RoutineReference();
    reference.setProjectId("プロジェクトID");
    reference.setDatasetId("データセットID");
    reference.setRoutineId("これから生成するルーティンID");
    
    Routine content = new Routine();
    content.setRoutineReference(reference);
    content.setRoutineType("SCALAR_FUNCTION");
    content.setDefinitionBody("x * 3");
    Argument argument = new Argument();
    argument.setName("test");
    argument.setArgumentKind("ANY_TYPE");
    content.setArguments(Arrays.asList(argument));
    
    Bigquery.Routines.Insert insert = routines.insert("プロジェクトID","データセットID",content);
    
    Routine res = insert.execute();
    System.out.println(res);
}
```
#### 1.2.1. HTTPリクエスト
POST:　https:\//bigquery.googleapis.com/bigquery/v2/projects/{プロジェクトID}/datasets/{データセットID}/routines
が実行されます。

#### 1.2.2. クエリパラメータ
クエリパラメータはありません。

#### 1.2.3. リクエストボディ
Routineのsetメソッドにより、リクエストボディを追加できます。

|メソッド|引数|説明|
|---|---|---|
|setRoutineReference|[RoutineReference](#routinereference)|【必須】ルーティンのリファレンス|
|setRoutineType|[RoutineType(enum)](#routinetype)|【必須】ルーティンの種類|
|setDefinitionBody|String|【必須】ルーティンの本体<br>【routineType="TABLE_VALUED_FUNCTION"の場合】AS句の式<br>【language="SQL"の場合】括弧内の部分文字列<br>【language="JAVASCRIPT"の場合】AS句で評価される文字列|
|setLanguage|[Language(enum)](#language)|ルーティンの言語<br><br>remoteFunctionOptionsの設定がない場合は"SQL"で固定。"SQL"以外にする場合は、remoteFunctionOptionsの設定が必須|
|setArguments|List\<[Argument](#argument)\>|引数情報|
|setReturnType|[StandardSqlDataType](#standardsqldatatype)|【language!="SQL"の場合は必須】<br>ルーティンのデータ型情報|
|setReturnTableType|[StandardSqlTableType](#standardsqltabletype)|ルーティンタイプ情報|
|setImportedLibraries|List\<String\>|インポートされたJavaScriptライブラリのパス|
|setDescription|String|ルーティンの説明|
|setDeterminismLevel|[DeterminismLevel(enum)](#determinismlevel)|JavaScriptUDFの決定レベル|
|setStrictMode|Boolean|多くの一般的なエラーを検出するか|
|setRemoteFunctionOptions|[RemoteFunctionOptions](#remotefunctionoptions)|リモート機能固有のオプション|
|setSparkOptions|[SparkOptions](#sparkoptions)|Spark固有のオプション|
|setDataGovernanceType|[DataGovernanceType(enum)](#datagovernancetype)|データガバナンスタイプ|

#### RoutineReference
|メソッド|引数|説明|
|---|---|---|
|setProjectId|String|【必須】プロジェクトID|
|setDatasetId|String|【必須】データセットID|
|setRoutineId|String|【必須】ルーティンID|

#### Argument
|メソッド|引数|説明|
|---|---|---|
|setName|String|引数の名前|
|setArgumentKind|[ArgumentKind(enum)](#argumentkind)|引数の種類|
|setMode|[Mode(enum)](#mode)|引数の入出力モード|
|setDataType|[StandardSqlDataType](#standardsqldatatype)|【argumentKind!="ANY_TYPE"の場合は必須】<br>引数のデータ型情報|

#### StandardSqlTableType
|メソッド|引数|説明|
|---|---|---|
|setColumns|List\<[StandardSqlField](#standardsqlfield)\>|このテーブルの列の種類|

#### RemoteFunctionOptions
|メソッド|引数|説明|
|---|---|---|
|setEndpoint|String|ユーザー提供のリモートサービスのエンドポイント|
|setConnection|String|リモートサービスにリクエストを送信するための認証情報を保持するユーザー指定の接続オブジェクトの完全修飾名|
|setUserDefinedContext|Map\<String,String\>|ユーザー定義コンテキスト|
|setMaxBatchingRows|Long|リモートサービスに送信される各バッチの最大行数|

#### SparkOptions
|メソッド|引数|説明|
|---|---|---|
|setConnection|String|Spark接続オブジェクトの完全修飾名|
|setRuntimeVersion|String|ランタイムバージョン|
|setContainerImage|String|ランタイム環境のカスタムコンテナーイメージ|
|setProperties|Map\<String,String\>|構成プロパティ|
|setMainFileUri|String|SparkアプリケーションのメインファイルURI|
|setPyFileUris|List\<String\>|PySparkアプリケーションのPYTHONPATHに配置されるPythonファイル|
|setJarUris|List\<String\>|ドライバーおよびエグゼキューターのCLASSPATHに含めるJARファイル|
|setFileUris|List\<String\>|各エグゼキュータの作業ディレクトリに配置されるファイル|
|setArchiveUris|List\<String\>|各エグゼキュータの作業ディレクトリに抽出されるアーカイブファイル|
|setMainClass|String|jarUris内のクラスの完全修飾名|

#### StandardSqlField
|メソッド|引数|説明|
|---|---|---|
|setName|String|名前|
|setType|[StandardSqlDataType](#standardsqldatatype)|パラメータのタイプ|

#### StandardSqlDataType
|メソッド|引数|説明|
|---|---|---|
|setTypeKind|[TypeKind(enum)](#typekind)|【必須】トップレベルのタイプ|
|setArrayElementType|[StandardSqlDataType](#standardsqldatatype)|【getTypeKind()が"ARRAY"の場合】<br>配列の要素の型|
|setStructType|[StandardSqlStructType](#standardsqlstructtype)|【getTypeKind()が"STRUCT"の場合】<br>構造体のフィールド|

#### StandardSqlStructType
|メソッド|引数|説明|
|---|---|---|
|setFields|List\<[StandardSqlField](#standardsqlfield)\>|構造体のフィールド|

#### TypeKind
|定義値|内容|
|---|---|
|TYPE_KIND_UNSPECIFIED|無効なタイプ|
|STRING|文字列|
|BYTES|バイト(base64文字列としてエンコード)|
|INT64|8byte整数(10進数の文字列としてエンコード)|
|NUMERIC|16byte整数(10進数の文字列としてエンコード)|
|BIGNUMERIC|32byte整数(10進数の文字列としてエンコード)|
|BOOL|真偽値|
|FLOAT64|浮動小数点数(数値または文字列「NaN」、「Infinity」、「-Infinity」としてエンコード|
|TIMESTAMP|タイムスタンプ|
|DATE|日付|
|TIME|時間|
|DATETIME|日付時刻|
|GEOGRAPHY|地理空間データ(WKTとしてエンコード)|
|ARRAY|配列型(Type.array_typeと一致するタイプのリストとしてエンコード)|
|STRUCT|構造体型(Type.struct_type[i]型のフィールドを持つリストとしてエンコード)|
|JSON|JSON文字列(tabledata.listが使用)|

#### RoutineType
|定義値|内容|
|---|---|
|ROUTINE_TYPE_UNSPECIFIED|デフォルト値|
|SCALAR_FUNCTION|リモート関数を含むユーザー定義関数（UDF)|
|PROCEDURE|ストアドプロシージャ|
|TABLE_VALUED_FUNCTION|テーブル関数|

#### Language
|定義値|内容|
|---|---|
|LANGUAGE_UNSPECIFIED|デフォルト値|
|SQL|SQL言語|
|JAVASCRIPT|JavaScript言語|
|PYTHON|Python言語|
|JAVA|Java言語|
|SCALA|スカラ言語|

#### ArgumentKind
|定義値|内容|
|---|---|
|ARGUMENT_KIND_UNSPECIFIED|デフォルト値|
|FIXED_TYPE|引数は完全に指定された型の変数|
|ANY_TYPE|引数は構造体や配列を含む任意の型|

#### Mode
|定義値|内容|
|---|---|
|MODE_UNSPECIFIED|デフォルト値|
|IN|引数は入力専用|
|OUT|引数は出力専用|
|INOUT|引数は入力でもあり出力でもある|

#### DeterminismLevel
|定義値|内容|
|---|---|
|DETERMINISM_LEVEL_UNSPECIFIED|未指定|
|DETERMINISTIC|UDFは決定的|
|NOT_DETERMINISTIC|UDFは決定論的ではない|

#### DataGovernanceType
|定義値|内容|
|---|---|
|DATA_GOVERNANCE_TYPE_UNSPECIFIED|未指定|
|DATA_MASKING|データマスキング|

### 1.3. レスポンスの内容
#### Routine
[⧉[BigQuery API(V2)] Javaでデータセットのルーティン情報を取得する(Routine)](https://qiita.com/doran/items/a4aa20982398d70ae394#routine)
と同じです。

## 2. ルーティンを更新
指定したルーティンを更新します。

### 2.1. スコープ
このAPIを実行するには、以下のいずれかのスコープを指定してください。
```
BigqueryScopes.BIGQUERY
BigqueryScopes.CLOUD_PLATFORM
```
[⧉[BigQuery API(V2)] JavaでBigQueryを使う(3.2. BigQueryインスタンスを取得)](https://qiita.com/doran/items/8b7ba29672527426e88d#32-bigqueryインスタンスを取得)
でスコープを指定してください。

### 2.2. 実行
ルーティンIDは、RoutineReferenceのgetRoutineId()で取得できる値を使用します。
[⧉[BigQuery API(V2)] Javaでデータセットのルーティン情報を取得する(RoutineReference)](https://qiita.com/doran/items/a4aa20982398d70ae394#routinereference)
を参照ください。

必須項目はルーティン生成時と同じです。

プロジェクトIDとデータセットID、ルーティンIDは2度ずつ設定していますが、
設定しないとエラーになります。

```java
public static void main(String[] args) throws Exception{
    Bigquery bigquery = getBigquery();
    Bigquery.Routines routines = bigquery.routines();
    
    RoutineReference reference = new RoutineReference();
    reference.setProjectId("プロジェクトID");
    reference.setDatasetId("データセットID");
    reference.setRoutineId("ルーティンID");
    
    Routine content = new Routine();
    content.setRoutineReference(reference);
    content.setRoutineType("SCALAR_FUNCTION");
    content.setDefinitionBody("x * 10");  //ここだけを変えました
    Argument argument = new Argument();
    argument.setName("test");
    argument.setArgumentKind("ANY_TYPE");
    content.setArguments(Arrays.asList(argument));
    
    Bigquery.Routines.Update update = routines.update("プロジェクトID","データセットID","ルーティンID",content);
    
    Routine res = update.execute();
    System.out.println(res);
}
```
#### 2.2.1. HTTPリクエスト
PUT:　https:\//bigquery.googleapis.com/bigquery/v2/projects/{プロジェクトID}/datasets/{データセットID}/routines/{ルーティンID}
が実行されます。

#### 2.2.2. クエリパラメータ
リクエストパラメータはありません。

#### 2.2.3. リクエストボディ
Routineのsetメソッドにより、リクエストボディを追加できます。

[1.2.3. リクエストボディ](#123-リクエストボディ)と同じです。

### 2.3. レスポンスの内容
#### Routine
[⧉[BigQuery API(V2)] Javaでデータセットのルーティン情報を取得する(Routine)](https://qiita.com/doran/items/a4aa20982398d70ae394#routine)
と同じです。

## 3. ルーティンを削除
指定したルーティンを削除します。

### 3.1. スコープ
このAPIを実行するには、以下のいずれかのスコープを指定してください。
```
BigqueryScopes.BIGQUERY
BigqueryScopes.CLOUD_PLATFORM
```
[⧉[BigQuery API(V2)] JavaでBigQueryを使う(3.2. BigQueryインスタンスを取得)](https://qiita.com/doran/items/8b7ba29672527426e88d#32-bigqueryインスタンスを取得)
でスコープを指定してください。

### 3.2. 実行
ルーティンIDは、RoutineReferenceのgetRoutineId()で取得できる値を使用します。
[⧉[BigQuery API(V2)] Javaでデータセットのルーティン情報を取得する(RoutineReference)](https://qiita.com/doran/items/a4aa20982398d70ae394#routinereference)
を参照ください。

```java
public static void main(String[] args) throws Exception{
    Bigquery bigquery = getBigquery();
    Bigquery.Routines routines = bigquery.routines();
    
    Bigquery.Routines.Delete delete = routines.delete("プロジェクトID","データセットID","ルーティンID");
    
    delete.execute();
}
```
#### 3.2.1. HTTPリクエスト
DELETE:　https:\//bigquery.googleapis.com/bigquery/v2/projects/{プロジェクトID}/datasets/{データセットID}/routines/{ルーティンID}
が実行されます。

#### 3.2.2. クエリパラメータ
リクエストパラメータはありません。

#### 3.2.3. リクエストボディ
リクエストボディはありません。

### 3.3. レスポンスの内容
レスポンスはありません。
削除に失敗した場合は例外が発生します。

<br>
<br>
おしまい。。