.netおよびgit-hookを使用してSQLコードを検証する

こんにちは、Habr!



ごく最近、当社は次のハッカソンを主催しました。 そして、そのフレームワーク内で、自分自身と他の開発者の両方にとって有用なことをするために、 より興味深い時間つぶしたかったのです。 選択は、コンパイラが実行できないさまざまなルールと、Code Reviewを実行する人がスキップできるルールについてテストする一種のSQLコードバリデータに基づいています。 単純な「クエリの最後にGOを追加する」から始まり、より複雑な「テーブルの代わりにビューを使用する」で終わる、多くのこのようなルールを考え出すことができます。 そして最も重要なことは、このバリデーターはそれを使用する開発者に時間を追加するべきではありません。 簡単に言えば、開発者のアクションに関係なく、どこかで自動的に検証する必要があります。



歴史的に、実稼働に入る前のすべてのsqlコード(つまり、メインデータベースで実行される)は、GITリポジトリに格納され、開発者から直接取得されます(当然、コードレビュー後)。 そのため、このリポジトリにgit-hookを追加して、sqlコードを検証し、それが有効でない場合、改訂のためにコミットが開発者に返されるというアイデアが生まれました。 想像するのが少し難しく、描きやすい:









Gitフック



そのため、とりあえず(必要に応じて)コミットをキャンセルする方法を見つけるために、私はgit-hooksに向かって掘り下げることにしました。 つまり、git-hookとは何ですか。これは、リポジトリの状態の変更に関連する特定のイベントが発生するたびに実行される一種のbashスクリプトです。 このようなスクリプトには、クライアントとサーバーの2種類があります。 はい、そしてスクリプトのような多くのイベントがあります。 詳細はこちらをご覧ください

私たちの目的では、git-hook“ update”(サーバー側スクリプト)が最適です。 サーバー上のブランチの状態を更新する前、実際には、サーバーに送信された変更が適用される前に開始されます。 そして重要なことに、そのようなスクリプトの終了コードがゼロ以外の場合、変更は破棄されます。 スクリプトには3つの入力パラメーターがあります。





このような一連のパラメーターがあると、まず最初に、スクリプトの2番目と3番目のパラメーターを使用して、変更されたファイルのリストを取得できます。



git diff --name-only --diff-filter=[AMCR] $2 $3
      
      





ファイルのリストを受け取ったら、git-showコマンドを使用して各ファイルのコンテンツにアクセスする必要があります。 そして、コンテンツ自体を持って、それをチェックしてみることができます。 以下は、git-hook updateスクリプトと2つのヘルパースクリプトのリストです。



 #!/bin/sh ex=0 okResult="OK" originalPath='/git/test.git/' #   git printf "---- 'Sql checker' hook ----" listOfFiles=$(git diff --name-only --diff-filter=[AMCR] $2 $3) #   for changedFile in $listOfFiles do printf "checking:$changedFile" fullFilePath="$3:$changedFile" git-show $fullFilePath >tmp_sql result=$($originalPath/hooks/check-sql tmp_sql) #  if [ "$result" != $okResult ] then res=${result//|/\\n } printf " $res\n" #  ex=1 else printf "ok!\n" fi done printf "---- Done ----" exit $ex
      
      





check-sqlスクリプトは、外部コードに対してリクエストを行い、sqlコードでファイルをチェックし、 soapの形式で取得した結果も解析します。



 #!/bin/sh soap-template $1 tmp_soap wget -qO- --post-file=tmp_soap --header="Content-type: text/xml; charset=utf-8" 127.0.0.1/check-sql.asmx | gawk -v re="<CheckFileResult.*>(.*)/CheckFileResult>" '{match($0,re,RMATCH); print RMATCH[1]}'
      
      





そして、sql-codeがsoapリクエストを作成するsoap-templateスクリプトの後:



 #!/bin/sh encodedFile=$(base64 $1) echo '<?xml version="1.0" encoding="utf-8"?>' >$2 echo '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSc ma" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' >> $2 echo ' <soap:Body>' >>$2 echo ' <CheckFile xmlns="http://tempuri.org/">' >>$2 echo " <base64>$encodedFile</base64>" >>$2 echo ' </CheckFile>' >>$2 echo ' </soap:Body>' >>$2 echo '</soap:Envelope>' >>$2
      
      





そのため、bashスクリプトでSQLコードチェックを行うほど強くないため、お気に入りの.Net環境に配置することにしました。



.Netパーツ



git-hookと.Netの間の通信ブリッジで、.net Webサービスを選択しました。したがって、sqlファイルの内容を含むパラメーターを使用してこのサービスにリクエストを行うのは、git-hookスクリプトの「check-sql」スクリプトです。



 [WebMethod] public string CheckFile(string base64) { var errors = new List<SqlCheckError>(); string result = OkResult; try { //  sql- string sqlScript = Encoding.Default.GetString(Convert.FromBase64String(base64)); //    ISqlCheck var containerProvider = new SqlCheckUnityProvider(); //    var checker = new SqlCheckProcess(containerProvider); errors.AddRange(checker.CheckSql(sqlScript)); } catch (Exception e) { Log(e); // return OkResult; //      ,       . } //  ( )     git-hook if (errors.Count > 0) { result = string.Join("|", errors.Select(error => string.Format("{1}: {0}", error.Message, error.Type))); } return result; }
      
      





すでに.net環境があり、検証用のsql-codeを提供してくれたので、多くのことを思いつくことができます。 このような検証用の小さなフレームワークを作成しましたが、すべてのコードをここにリストすることはあまり適切ではないと思います。 近い将来、githubに投稿します。 一言で言えば、すべてがこのようなシンプルなインターフェイスに基づいています。



 public interface ISqlCheck { bool CheckSql(string sqlCode); SqlCheckError GetError(string sqlCode); }
      
      





このインターフェイスは検証ルールです。 Unity Containerと組み合わせてこのインターフェイスの実装を多数持つことで、一般的な検証に適用可能な一連のルールを制御できます。 また、.netにSQLパーサー( TSql100Parser )が組み込まれているため、ほとんどすべてのルールの検証を実装できます。



練習する



このシンプルなシステムはすでに当社で運用され始めており、いくつかの大きなメリットについて結論を出すのは時期尚早ですが、機能を拡張するための潜在的に大きなマージンがあるため、このメリットは害よりもはるかに大きいと確信できます。

結論として、このシステムを使用する場合、コンソールログを表示します。



 $ git push origin master Counting objects: 7, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 347 bytes, done. Total 4 (delta 2), reused 0 (delta 0) remote: remote: ---- 'Sql checker' hook ---- remote: remote: checking: test/create_sp.sql remote: Error: Missed 'GO' statement after stored procedure. remote: remote: ---- Done ---- remote: remote: error: hook declined to update refs/heads/master To ssh://user@git.local/git/test.git ! [remote rejected] master -> master (hook declined) error: failed to push some refs to 'ssh://user@git.local/git/test.git'
      
      





最後から2行目からわかるように、変更可能なSQLスクリプトのエラーが原因で、システムによってコミットが拒否されました。



実際、この投稿について伝えたかったのはこれだけです。

ご清聴ありがとうございました。 すべての開発に便利!



All Articles