【BigQuery】standardSQLのLanguage Serverを公開した（VSCode拡張機能だけどVimもOK）

## 最新情報
* `Version 0.0.27`
    * 2022/08/03までの[リリースノート](https://cloud.google.com/bigquery/docs/release-notes)に対応
    * qualify周りのサイレント修正に対応

## 概要
先日公開した **[BigQuery Extension for VSCode](https://github.com/dr666m1/bq-extension-vscode)** というVSCode拡張機能の紹介。ほとんどの機能はLanguage Serverとして実装されており、Vimなど他のエディタからも利用可能。legacySQLは守備範囲外で、standardSQLに特化させた。

## 機能
:::note
VSCodeから拡張機能をインストールするだけでは動作しない機能もある。
全ての機能を使うには後述のセットアップを済ませること。
:::

### 補完
列名や関数名の補完ができる（現状、列名の補完はSELECT文のみで機能する）。
`` ` ``を入力するとプロジェクト名・データセット名・テーブル名の順で補完される。

![ezgif.com-gif-maker (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/2162dfaf-d9f7-e7c6-8b55-c25a7cc82cdc.gif)

関数については、使い方を確認のために簡単な例文も表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/e2a07e32-76e7-45b7-f5ff-4e955bded791.png)


### フォーマット
以前作った[prettier-plugin-bq](https://github.com/dr666m1/prettier-plugin-bq)[^prettier]によるフォーマットが利用可能（`Shift` + `Alt` + `F`もしくはコマンドパレットから実行）。

[^prettier]: [過去記事](https://qiita.com/dr666m1/items/6bdac6f17d3530152bb9)で使い方などを紹介したが、この記事の拡張機能から利用する場合はこの記事のセットアップだけ済ませればよい。

![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/a33f44c9-21fa-2c0c-9245-bf52edcf6c61.gif)

### テーブル定義の表示
カーソルをテーブル名に合わせると、列名やデータ型を表示する。現状、プロジェクト名からテーブル名までが`` ` ``で囲まれていないと動作しない。

![ezgif.com-gif-maker (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/ff5edfbc-de5d-9db9-068c-197f4353315b.gif)

### Dry Run
変更を保存する度にDry Runを実行し、処理されるデータ量を右下（ステータスバー）に表示する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/07389f7d-dfff-6233-717f-6b0e92b059ac.png)

VSCode以外のエディタでも表示されるが、表示形式はクライアント側の実装による。後ほど紹介するVim & coc.nvimの場合は以下のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/9e11f4c2-3df8-0ad6-b996-f145140cfe2a.png)

### エラー検知
上記Dry Runのタイミングでエラーがあれば下線を引き、BigQueryからのメッセージを表示（下図の`CURRNETTIMESTAMP`）。それ以外のタイミングでも明らかに文法的な間違いがあれば下線を引き、構文解析器のエラーメッセージを表示（下図の`;;;;;`。エラーメッセージは改善の余地あり）。

![ezgif.com-gif-maker (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/d64cd9ff-9478-1e80-ad09-cc3e16ad5e2a.gif)


### 言語特有の設定
VSCodeで通常ののSQLを編集すると`` ` ``が自動で閉じられない、`#`以降がコメントとして認識されないといった問題があるので調整。これはLanguage Serverとしての機能ではないため、Vimなど他のエディタから利用する場合は無関係。

**Before**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/8f947408-a617-7d33-a5b9-67168c2325c2.png)

**After**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/8a262357-cfe0-bb8f-0d06-570520b193e5.png)

## セットアップ
ℹ️Windowsの場合、WSLの利用を推奨（[このあたり](https://docs.microsoft.com/ja-jp/windows/wsl/tutorials/wsl-vscode)が参考になるかと）

### 手順
1. [Google Cloud SDK](https://cloud.google.com/sdk/docs/install)をインストール
1. `gcloud auth login` `gcloud auth application-default login`コマンドを実行
1. sqlite3をインストール[^sqlite]
1. VSCodeでこの拡張機能をインストール（`BigQuery Extension for VSCode`で検索）
1. VSCodeでファイルを開く（拡張子は`.bq`もしくは`.bigquery`）
1. コマンドパレットから`BQExtensionVSCode: UpdateCache`を実行

[^sqlite]: ターミナルで`which sqlite3`を実行して何らかのPATHが返ってくればOK。WindowsでもMacでも動くようにするにはコンパイルがネックになるため、あえて[node-sqlite3](https://github.com/mapbox/node-sqlite3)を使わなかった。

### 補足
`BQExtensionVSCode: UpdateCache`は、テーブル名などを補完するために必要な情報を取得し、ローカルのsqlite3に保存するコマンド。[INFROMATION_SCHEMA](https://cloud.google.com/bigquery/docs/information-schema-intro)に対してクエリを実行するため、**最低10MBのデータ処理が何回か走る**。

このデータ処理量を節約するため、クエリに登場しないデータセットは無視される。例えば以下のファイルを開いた状態で`BQExtensionVSCode: UpdateCache`を実行すると、`datasetname1`と`datasetname2`についての情報が取得される。一方で、BigQuery上に`datasetname3`というデータセットが存在したとしても、その情報は取得しない。

```sql:xxx.bq
SELECT *
FROM
  `projectname.datasetname1.tablename`
  INNER JOIN `projectname.datasetname2.talbename` USING(column1)
```

## おまけ
### 拡張機能を.sqlでも有効にする
デフォルトでは拡張子が`.bq`か`.bigquery`の場合のみ拡張機能が有効になる[^extension]。もし`.sql`でも拡張機能を有効にしたいなら、以下の設定を`settings.json`に記載する。

```json:setting.json
{
  "files.associations": {
    "*.sql": "bigquery"
  }
}
```

[^extension]: BigQuery以外のSQLを書いているのに、保存する度Dry Runが走って煩わしい。。。ということが起きないよう配慮した。

### Vimからの利用
理論上は**Language Serverに対応していればどのエディタからでも利用可能**。ここではVimを例に手順を紹介する。npmは利用可能な前提。

#### coc.nvimのインストール
まずLSPクライアントとして[coc.nvim](https://github.com/neoclide/coc.nvim)をインストールする[^coc]。名前に反して（Neovimだけでなく）Vimでも使えるらしい。[vim-plug](https://github.com/junegunn/vim-plug)を導入済みなら`.vimrc`や`init.vim`に以下を追記して`:PlugInstall`するだけ。もしインストールに詰まったら[Wiki](https://github.com/neoclide/coc.nvim/wiki/Install-coc.nvim)を確認すること。

```vim:.vimrc
call plug#begin('~/.vim/plugged')
" ... other plugins
Plug 'neoclide/coc.nvim', {'branch': 'release'}
" ... other plugins
call plug#end()
```

[^coc]: これも本当はcoc.nvimである必要はなく、好きなLSPクライアントを使ってよい。ただし、設定方法はそれぞれ異なるだろうから要確認。

#### Language Serverのインストール
以下のコマンドを実行する。npmパッケージとして公開していないため、GitHubリポジトリをcloneするところから。

```bash
git clone https://github.com/dr666m1/bq-extension-vscode.git
cd bq-extension-vscode
npm install
npm run compile
cd server
npm install -g .
```

#### coc.nvimの設定
`coc-settings.json`に以下を追記。ファイルがどこにあるかわからなくても`:CocConfig`で開けるはず。filetypesは`:echo &filetype`したら表示されるもので、ここではsqlとbqを指定している（詳述しないが、拡張子と一対一で対応する概念ではない。ドキュメントは[こちら](https://vim-jp.org/vimdoc-ja/filetype.html)）。

```json:coc-settings.json
{
  "languageserver": {
    "bigquery": {
      "command": "bq-language-server",
      "args": ["--stdio"],
      "filetypes": ["sql", "bigquery"],
      "settings": {
        "bqExtensionVSCode": {
          "diagnostic": {
            "forVSCode": false
          }
        }
      }
    }
  }
}
```

#### UpdateCache
filetypeがsqlかbigqueryのファイルを開いたら、`:call CocRequestAsync("bigquery", "bq/updateCache")`でテーブル定義などの情報を取得する。

#### map
`` ` ``で閉じ括弧も入力されるよう、以下を`.vimrc`や`init.vim`に追記するのがおすすめ。ちゃんと閉じないと構文解析に支障が出る。

```vim
inoremap ` `<left>`
```

以上でセットアップ完了。下図のように補完も効くはず。

![ezgif.com-gif-maker (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/80f51f64-71c3-e21f-46ec-a979e2a95eaf.gif)

## 今後
今後は以下を進める予定。

* コードのリファクタリング
* ~~[構文解析器](https://github.com/dr666m1/bq2cst)の改善（多少エラーがあっても補完が効くようにする。一方でエラー検知は充実させる）~~ 構文解析器ではなくLanguage Server側の調整で、``SELECT * FROM `tablename` WHERE ;``のような構文エラー状態からでも補完が効くように。
* 補完機能を充実させる（現状、列名補完がSELECT文でしか効かない）
* ~~ホバーメッセージの充実（まだテーブル定義だけなのでそれ以外も）~~
* ~~INFROMATION_SCHEMAに対するクエリの削減（`bq ls`コマンドで代用できる部分はないか？）~~
* スニペット機能の実装

ほかにもバグがあれば随時直すので、何かあれば教えてください。