AthenaのSaved Queriesをコンソール以外から取得する

# はじめに

AWSアカウントをクローズする際にAthenaのバックアップを行ったのがきっかけです。
コンソール上でSaved Queriesをまとめて取得する方法が見つからず、またS3に保存されているわけでも無さそうだったため、備忘録として試した内容を以下にまとめます。  

# Saved Queries
何度も使うクエリに名前と説明を付けて保存しておく機能です。
ワークグループごとに分けて保存できます。
<img width="1223" alt="saved_queries.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66217/ee8555aa-730f-6cdb-7342-63379a13b9cb.png">

# 取得方法  

以下の2つの方法でSaved Queriesを取得します。  

* AWS CLI
* AWS SDK for Python  

### 実行環境
* macOS High Sierra 10.13.6
* aws-cli 1.16.128  
* Python 3.6.8
* botocore 1.15.46

`~/.aws/credentials`にprofileの設定がされていることが前提です。  

## AWS CLIを使う方法

公式のドキュメントを参考にしました。  

* [athena - AWS CLI Command Reference](https://docs.aws.amazon.com/cli/latest/reference/athena/index.html#cli-aws-athena)

### クエリのIDリスト取得

コンソール上からは分かりませんが、各クエリにはIDが付与されています。
クエリを取得するにはこのIDが必要なため、まずは`list-named-queries`コマンドを使ってIDのリストを取得します。  

```:コマンド
aws athena list-named-queries --work-group primary --profile default
```


```:実行結果
{
    "NamedQueryIds": [
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    ]
}
```

### クエリの取得  

`get-named-query`コマンドでIDを指定してクエリを取得します。  

```:コマンド
aws athena get-named-query --named-query-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --profile default
```

```:実行結果
{
    "NamedQuery": {
        "Name": "test query",
        "Description": "test saved query",
        "Database": "test_database",
        "QueryString": "SELECT * FROM \"test_database\".\"test_table\" limit 10",
        "NamedQueryId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "WorkGroup": "primary"
    }
}
```

一度に複数のクエリを取得することはできないため、全てのクエリを取得するにはIDの数だけコマンドを実行する必要があります。

## AWS SDK for Python (boto3)を使う方法

基本的な流れはAWS CLIの場合と同じです。
Boto3のドキュメントを参考にしました。  

* [Athena - Boto 3 Docs 1.12.46 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/athena.html#Athena.Client.list_named_queries)


### クエリのIDリスト取得

`list_named_queries`関数でIDリストを取得します。

```python:
import os
import json
import boto3
import time

from boto3.session import Session
profile = 'default'
session = Session(profile_name=profile)
athena = session.client('athena','ap-northeast-1')

#IDのリスト取得
saved_query_list = athena.list_named_queries(
    WorkGroup='primary',
    MaxResults=50
)
```
#### レスポンスの内容について

基本的ににはAWS CLIと同様のJSON形式で返却されます。  

```:レスポンス形式
{
    'NamedQueryIds': [
        'string',
    ],
    'NextToken': 'string'
}
```
AWS CLIの場合との違いは`NextToken`です。
AWS SDKで一度に取得できるIDの数はリクエストで指定した`MaxResults`の値(最大50)となり、保存されているクエリの数がそれより多い場合はレスポンスに`NextToken`がセットされています。  

### IDリストの続きを取得  
51個目以降(リクエストで`MaxResults=50`を設定した場合)のIDリストを取得するには、`NextToken`を設定して再度リクエストを行います。

```python:
next_saved_query_list = athena.list_named_queries(
    WorkGroup='primary',
    MaxResults=50,
    NextToken=saved_query_list['NextToken']
)
```

更にIDリストが続く場合は同様のリクエストを繰り返します。  

### クエリの取得

`get_named_query`関数にIDを指定してクエリを取得します。  

```python:
saved_query = athena.get_named_query(
    NamedQueryId=saved_query_list['NamedQueryIds'][0]
)
```

AWS CLIの場合と同様の形式でクエリが返却されます。

```:レスポンス形式
{
    'NamedQuery': {
        'Name': 'string',
        'Description': 'string',
        'Database': 'string',
        'QueryString': 'string',
        'NamedQueryId': 'string',
        'WorkGroup': 'string'
    }
}
```

全てのクエリを取得するにはIDの数だけ`get_named_query`関数を実行します。

### 全てのクエリを取得

IDリストの取得、クエリの取得、保存を行うコードをまとめてみました。
ローカルディレクトリにクエリ毎にファイルを分けて保存しています。 

```python:
import os
import json
import boto3
import time
from boto3.session import Session
​
profile = 'default'
session = Session(profile_name=profile)
athena = session.client('athena','ap-northeast-1')
​
# IDリスト取得
query_id_list = []
nextToken = ''
while True:
    if nextToken == '':
        saved_query_list = athena.list_named_queries(
            WorkGroup='primary',
            MaxResults=50
        )
    else:
        saved_query_list = athena.list_named_queries(
            WorkGroup='primary',
            MaxResults=50,
            NextToken=nextToken
        )
        
    tmp_query_id_list = saved_query_list['NamedQueryIds']
    for query_id in tmp_query_id_list:
        query_id_list.append(query_id)
​
    if 'NextToken' in saved_query_list.keys():    #50件以上ある場合
        nextToken = saved_query_list['NextToken']
        time.sleep(1)
    else:
        nextToken=''
        break
​
print('query num:',len(query_id_list))
​
# クエリ取得&保存
for query_id in query_id_list:
    filename = './query/' + query_id + '.json'
    # 取得
    query = athena.get_named_query(
        NamedQueryId=query_id
    )
    # 保存
    with open(filename, mode='w') as outfile:
        outfile.write(json.dumps(query))    
    time.sleep(1)
```

今回は試していませんが、`create_named_query`関数を使えば別のワークグループやAWSアカウントへのクエリの保存もできると思います。

# まとめ

AWS CLIを使う方法とAWS SDKを使う方法の2つを試しました。
どちらも方法もIDリストの取得 → クエリの取得という手順を踏む必要があります。
まとめて取得したい場合や、シェルを書くことに抵抗が無い人以外はSDKを使って作業するのが良いと思います。