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)
----------------------------------------------------------------------
※以降省略

以上。