pysparkでSparkSQLの文法チェックをする
ETL処理などをSpark SQLで行う場合に、
SQLを設定ファイルに持たせておくケースは、よくあると思います。
このような設定ファイルに書いてあるSQLが文法的に正しいかどうかを、
単体テストレベルでざっとチェックできると、
実サーバでの動作確認から手戻りが減らせると思い、方法を調べてみました。
# 本来はダミーデータなどを作成して、
# ローカルの環境で、実際にクエリを実行・テストすべきではありますが。
やりたいこと
前提として、次のようなyamlファイルにSQLが書いてあるとします。
queries.yaml
---
queries:
- id: ok
query: select * from hoge
- id: ng
query: selects * from hoge
やりたいことは、
このファイルのSQLが文法的に正しいかどうかを、単体テストで確認することです。
単体テストの実装
Apache SparkのSparkSessionクラスに、
クエリをparseしている処理があったので、その部分を参考にします。
sqlメソッドの処理です。
SparkSession | Spark - GitHub
https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/SparkSession.scala
pysparkは動くように設定出来ているものとして、
以下のようなテストコードを用意します。
tests/test_query_check.py
import unittest
import yaml
from pyspark.sql import SparkSession
class TestCases(unittest.TestCase):
def setUp(self) -> None:
self.spark = SparkSession.builder.getOrCreate()
def test_sql_check(self):
with open('./queries.yaml') as f:
yaml_obj = yaml.load(f)
errs = []
for q in yaml_obj['queries']:
try:
self.spark._jsparkSession.sessionState().sqlParser().parsePlan(q['query'])
except Exception as ex:
print('Query Parse Error: id = {}'.format(q['id']))
print(ex)
errs.append((q['id'], ex))
self.assertFalse(
len(errs),
'Query parse failures. Errors: {}'.format(errs)
)
実際にunittestを実行してみると、以下のようにFAILEDになります。
% python -m unittest tests/test_query_check.py
Query Parse Error: id = ng
mismatched input 'selects' expecting {'(', 'ADD', 'ALTER', 'ANALYZE', 'CACHE', 'CLEAR', 'COMMENT', 'COMMIT', 'CREATE', 'DELETE', 'DESC', 'DESCRIBE', 'DFS', 'DROP', 'EXPLAIN', 'EXPORT', 'FROM', 'GRANT', 'IMPORT', 'INSERT', 'LIST', 'LOAD', 'LOCK', 'MAP', 'MERGE', 'MSCK', 'REDUCE', 'REFRESH', 'REPLACE', 'RESET', 'REVOKE', 'ROLLBACK', 'SELECT', 'SET', 'SHOW', 'START', 'TABLE', 'TRUNCATE', 'UNCACHE', 'UNLOCK', 'UPDATE', 'USE', 'VALUES', 'WITH'}(line 1, pos 0)
== SQL ==
selects * from hoge
^^^
F
======================================================================
FAIL: test_sql_check (tests.test_query_check.TestCases)
----------------------------------------------------------------------
※以降省略
以上。