sql
前回の選挙で、ゼロ票確認なんて言う名誉が有る事を知った。22日の早朝にチャレンジしようと したら、台風に阻まれてあえなく撤退。また、次回、早急にやってください。(一回選挙をやると、600だか700億円かかるそうだから、頻繁には出来ません。)
勝てると思った時に合戦を仕掛けるのが、喧嘩の必勝法。特権と言うか専権事項が親分には 有るから、覇者に有利に出来ている。これを潰したがってる所もあるね。 でも、そんな美味しい権利をむざむざ放棄するなんて事は、天地がひっくり返ってもないだろう。
大体、合戦前には、どことどこが同盟を結ぶとか、新たに徒党を立ち上げるとかやるよね。 あの、厚化粧の大年増(と目パチクリの老人が言ってた)も、子分を沢山用意して、ひと山当てようと頑張ってたし。
開票と言う合戦結果判定で、TV局が持ち切り。統計とかAIとか引っ張り出してきて、騒がしい事。 そんなの、3分でけりがつくようなスマートな方法は無いものかね。JRAは昔から3分以内に 決着が付くようにしてるぞ。
女房は、この騒がしい報道を避けるため、1週間ぐらい前から録画をため込んで、見ないように 我慢してた。日曜夜と月曜日の楽しみとか。そうだよな、それが正解。
オイラーは、分厚い『ペンタゴンの頭脳』なんて本を読んでいる。戦争屋が使う道具を開発する 部署。昔は、ARPAと呼ばれていたけど、今はDARPAとなってる。
ここで開発された有名なものとして、ステルス戦闘機、ドローン、GPSとか有るな。最近では、 AIを搭載した自立型殺人ロボット。あと、みんながお世話になってるインターネット。
銀河間コンピュータ・ネットワークのアイデアを思い付いた、リックライダーがその覚書を ペンタゴンに送った。それから10年、彼はそこを離れたが、後任に夢想家のサザーランドと テイラーが指名された。
テイラーはこの提案を各所に紹介して、大いなる賛同を得る。自信を得たテイラーは、予算を 付けてくれるように、局長に交渉に行く。
が、局長は、コンピュータが攻撃されたらネットがダウンしちゃうんじゃ、戦略的に使えんよと 疑問を投げる。テイラーは咄嗟に、そうならないようネットワークに冗長化を組み入れますと 返答。幾らかかる? 100万ドルです。難しいのかね? もうやり方は分かってますから。 宜しい、予算を付けよう。こうして、わずか20分で予算を確保。
開発が始まり、西海岸の大学や研究所が繋がった。(どこかの国と違い、大学も軍に協力するのが伝統)やがて東海岸にも繋がった。 多くのコンピュータを接続するには、プロトコルが必要。DARPAのプログラム技術者カーンがIPにまとめた。そして、サーフと協力して、上位プロトコルTCPも策定した。TCP/IPの誕生。
そのうちに、軍用ネットと切り離して、民間に公開された。なんたって、税金を使っての開発 だから、それを解放する使命が有ったんですな。GPSもそうだね。じっくりと楽しめましたよ。
qhs
前回お世話になったpdf解析のページの著者は、Haskellが得意との事。これはきっと、色々な スクリプトを書かれているに違いないと思って、色々読ませて頂いた。そしたら、面白い記事が 掲載されていた。
CSVファイルをSQLのクエリで集計できるqコマンドをHaskellで実装してみました!
面白いな。csvファイルをDatabaseに突っ込んで、それをSQLコマンドで拾い出すとか合計を 求めるとか、ファイルを結合して集計するとか。unixコマンドではちょいと実現出来そうも ない事が、SQLの力を借りて出来るようになる。これはもう試してみる鹿。
stackが入っていれば、簡単にコマンドを構築出来るよとの事。
Process exited with code: ExitFailure 1 Logs have been written to: /tmp/qhs/.stack-work/logs/sqlite-0.5.5.log [1 of 2] Compiling Main ( /tmp/stack4275/sqlite-0.5.5/Setup.hs, /tmp/stack4275/sqlite-0.5.5/.stack-work/dist/x86_64-linux-nopie/Cabal-1.24.2.0/setup/Main.o ) [2 of 2] Compiling StackSetupShim ( /home/sakae/.stack/setup-exe-src/setup-shim-mPHDZzAJ.hs, /tmp/stack4275/sqlite-0.5.5/.stack-work/dist/x86_64-linux-nopie/Cabal-1.24.2.0/setup/StackSetupShim.o ) Linking /tmp/stack4275/sqlite-0.5.5/.stack-work/dist/x86_64-linux-nopie/Cabal-1.24.2.0/setup/setup ... Configuring sqlite-0.5.5... setup: Missing dependency on a foreign library: * Missing C library: sqlite3 This problem can usually be solved by installing the system package that provides this library (you may need the "-dev" version). If the library is already installed but in a non-standard location then you can use the flags --extra-include-dirs= and --extra-lib-dirs= to specify where it is.
でも、見事にエラーだ。メッセージを見たら、またlinuxのいじめだよ。全く困ったものだ。stack様からの助言によればdevバージョンが足りないのでしょうとな。devって事は、Debian系か。
Fedora系はdevelだったな。Fedoraの変化の 激しさに嫌気が差して、Centを入れていたんだけど、こちらは余りパッケージも充実していな かったので、さよならしておいたよ。
deb9:~$ sudo apt install libsqlite3-dev
これをやったら、無事に出来たみたい。ちょっと試運転。血圧の測定データで試す。
deb9:bld$ qhs 'SELECT c1,c2 from am.dat' | head -2 16011504 121 16011604 131 deb9:bld$ qhs "SELECT avg(c2) FROM pm.dat" 119.128571428571
悪乗りして、標準偏差は求められないのですか? SQLite入門や SQLite が認識できる SQL を調べてみたけど、そんなのネェー。 (と、小島よしお風)ひょっとして、ボラクル方言には、有ったりして。
deb9:qhs$ ps -awxl | qhs -H -O "SELECT UID, total(RSS) FROM - group by UID" UID total(RSS) 0 181004.0 100 4140.0 105 3016.0 106 4120.0 110 101172.0 112 3468.0 1000 80392.0
total関数が有ったので、試してみた。誰がメモリーを一番使ってるか一目瞭然。
deb9:qhs$ id 110 uid=110(lightdm) gid=113(lightdm) groups=113(lightdm)
ソース拝見
動いたね、よかったねで終わってしまっては、もったいないので鑑賞したい。初め、/tmpの 下で、gitして動作を確かめて、永久保存版という事でHOMEDIRに移しておいた。そこで、ghci とかを起動すると、よう分からんエラーが発生。
これはきっと、生まれ故郷を内蔵してるに違いない。移動しちゃったんで、過疎ってて、何も ないぞと嘆いているんだろう(現実的に怖い)。元の生まれ故郷に移動。
deb9:~$ cd /tmp/qhs/src/ deb9:src$ stack ghci Main.hs : Using main module: 1. Package `qhs' component exe:qhs with main-is file: /tmp/qhs/src/Main.hs GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help [1 of 6] Compiling SQL ( /tmp/qhs/src/SQL.hs, interpreted ) [2 of 6] Compiling Paths_qhs ( /tmp/qhs/.stack-work/dist/x86_64-linux-nopie/Cabal-1.24.2.0/build/autogen/Paths_qhs.hs, interpreted ) [3 of 6] Compiling Parser ( /tmp/qhs/src/Parser.hs, interpreted ) [4 of 6] Compiling Option ( /tmp/qhs/src/Option.hs, interpreted ) [5 of 6] Compiling File ( /tmp/qhs/src/File.hs, interpreted ) [6 of 6] Compiling Main ( /tmp/qhs/src/Main.hs, interpreted ) Ok, modules loaded: File, Main, Option, Parser, Paths_qhs, SQL. Loaded GHCi configuration from /tmp/ghci11048/ghci-script
ああ、きっと2番目のやつが、生まれ故郷をしっかり覚えている、鮭 の役割を果たしているの だな。これなら理屈が分かるけど、鮭は長旅をして、きちんと生まれ故郷に戻ってくる。 どうやって戻ってこれるのだろう? 今度、鮭に聞いてみたいぞ。
*Main> :bro main :: IO () runCommand :: Option.Option -> IO () runQuery :: Option.Option -> SQLite.SQLiteHandle -> (String, Parser.TableNameMap) -> IO () fetchQuery :: Option.Option -> IO String parseQuery :: String -> IO (Maybe (String, Parser.TableNameMap)) readFilesCreateTables :: Option.Option -> SQLite.SQLiteHandle -> Parser.TableNameMap -> IO () createTable :: SQLite.SQLiteHandle -> String -> String -> [String] -> [[String]] -> IO ()
もっと沢山のものから出来上がっているかと思ったら、意外に少ないなあ。
*Main> :i Option.Option data Option.Option = Option.Option {Option.skipHeader :: Bool, Option.outputHeader :: Bool, Option.delimiter :: Maybe String, Option.tabDelimited :: Bool, Option.outputDelimiter :: Maybe String, Option.tabDelimitedOutput :: Bool, Option.keepLeadingWhiteSpace :: Bool, Option.gzipped :: Bool, Option.queryFile :: Maybe String, Option.query :: Maybe String} -- Defined at /tmp/qhs/src/Option.hs:10:1
これの使用例が、下記になるんだな。今も昔もオプションの解析とその取り回しは面倒。 C語のmain関数なんて、ほとんどが、オプションの解析に費やされている。
deb9:~$ qhs -h qhs - SQL queries on CSV and TSV files Usage: qhs [-H|--skip-header] [-O|--output-header] [-d|--delimiter DELIMITER] [-t|--tab-delimited] [-D|--output-delimiter OUTPUT_DELIMITER] [-T|--tab-delimited-output] [-k|--keep-leading-whitespace] [-z|--gzipped] [-q|--query-filename QUERY_FILENAME] [QUERY] Available options: -h,--help Show this help text -v,--version Show the version of the command. -H,--skip-header Skip the header row. -O,--output-header Output the header line. -d,--delimiter DELIMITER Field delimiter. If not specified, automatically detected. -t,--tab-delimited Same as -d $'\t'. -D,--output-delimiter OUTPUT_DELIMITER Field delimiter for output. If not specified, the argument of -d DELIMITER is used. -T,--tab-delimited-output Same as -D $'\t'. -k,--keep-leading-whitespace Keep leading whitespace in values. The leading whitespaces are stripped off by default. -z,--gzipped Assuming the gzipped input. -q,--query-filename QUERY_FILENAME Read query from the provided filename.
そして、下記は、名前を分かりやすくしてるんだな。
*Main> :i Parser.TableNameMap type Parser.TableNameMap = Map.Map String String -- Defined at /tmp/qhs/src/Parser.hs:13:1 *Main> :i SQLite.SQLiteHandle newtype SQLite.SQLiteHandle = Database.SQLite.SQLiteHandle (GHC.ForeignPtr.ForeignPtr ()) -- Defined in ‘Database.SQLite’
ForeignPtrを扱う時に、/usr/include/sqlite3.h が、必要なんだけど、意図的に入れて やらないといけない。/usr/lib/x86_64-linux-gnu/libsqlite3.so.0しか入っていなくて、GHCは 使い方が分からず、文句を垂れたんだな。
test
testも頑張って、書かれたそうなので、試してみます。
deb9:qhs$ stack test qhs-0.1.3: test (suite: spec) File readFromFile should read from a test file should read from a gzipped file : detectSplitter should detect the column splitter space should detect the column splitter comma : Main qhs should be executed correctly: basic should be executed correctly: columns : SQL open, close should not throw exception createTable, insertRow, execute should create a table should take care of null values in a number column can create a table when name and column names contain spaces Finished in 0.3170 seconds 66 examples, 0 failures
これが、sh版のテストスクリプト。
deb9:tests$ cat where.sh qhs -H "SELECT foo,baz FROM basic.csv WHERE baz <> 'b2'" deb9:tests$ sh where.sh a0 a2 c0 c2 deb9:tests$ cat where.out a0 a2 c0 c2
これらを駆動する、MainSpec.hs等が用意されてる。ちなみに、Main部では、
describe "qhs" $ do let tests = [ "basic", "columns", "stdin", "header", "where", "tab", "tab2", "count", "is_null", "not_null", "output_header", "spaces", "output_delimiter", "tab_delimited_output", "multiline", "query_file", "empty_query", "empty_query_file", "file_spaces", "gzip", "gzip_stdin", "avg", "sum", "avg_sum", "seq", "group", "group_sum", "concat", "join", "invalid", "version" ] forM_ tests $ \test -> do :
こんな具合になってた。
このHSpecは、RSpecにヒントを得て作られたそうなので、 スはスペックのス 【第 1 回】 RSpec の概要と、RSpec on Railsをみてみた。あの元気な方が、一生懸命に旗振りされてたな。
何となく、英語を読んでる気になって、実はrubyのメソッドの寄せ集めだったと言う、Web屋 さんに敷居を低くした設計だった。昔々、コボルと言う、事務屋さんがしり込みしないプログラミング言語が有ったけど(今も現役)それが、元祖だな。
detectSplitterSpec :: Spec detectSplitterSpec = describe "detectSplitter" $ do it "should detect the column splitter space" $ do let (headLine, secondLine) = ("c0 c1 c2 c3 c4", "0 1 2 3 4") detectSplitter headLine secondLine ' ' `shouldBe` True detectSplitter headLine secondLine '\t' `shouldBe` True :
確かに、doが上手い具合にアクセントになってて、英語っぽく読めるな。(と、偉そうに言ってみるテスト)でも、慣れた人には、まどろっこしい感じがするぞ。
doc
-- | The words after these words are possibly table names. isTableNamePrefix :: String -> Bool isTableNamePrefix xs = map toUpper xs `elem` ["FROM", "JOIN"]
ソースを眺めていると、上記のような見慣れないコメントに遭遇した。これって、あの大御所が 提言した文芸的プログラミングの流れを汲むもの? コードと説明原稿が分離しちゃってると 説明書の更新が、なおざりになる。よって、説明とコードを一緒に書こうよというあれ。
そんなの、*.lhs だろ。あちらは、文書が主体で、コードはその中に紛れ込ませる方式。 コードの冒頭に、'> ' を書いて、区別するんだった。
今回のコメントマーク '-- | ' は、これ大事だから、htmlで見られるように抽出してねマーク なのかなあ? ええい、やってみれ。haddockってコマンドで、htmlを作ってくれるらしい。
deb9:qhs$ stack haddock : Installing executable(s) in /tmp/qhs/.stack-work/install/x86_64-linux-nopie/lts-8.10/8.0.2/bin Completed 28 action(s). Updating Haddock index for snapshot packages in /home/sakae/.stack/snapshots/x86_64-linux-nopie/lts-8.10/8.0.2/doc
随分時間がかかったけど、ドキュメントのhtmlが生成された。
deb9:doc$ pwd /home/sakae/.stack/snapshots/x86_64-linux-nopie/lts-8.10/8.0.2/doc deb9:doc$ server Serving HTTP on 0.0.0.0 port 8080 ... :
そこに移動して、Python製のサーバーを起動。ブラウザーみると、残念ながらqhsの説明は 収録されていなかった。でも、下記のように、お取り寄せしたモジュールの説明が出て きたぞ。
Database Database.SQLite3 Database.SQLite3.Bindings Database.SQLite3.Bindings.Types Database.SQLite3.Direct
後は、これを頼りに、Webに載るコメントの書き方を調べればいいんだな。
上に出てきたDatabaseの階層は、direct-sqlite-2.3.21に有ったんで、ソースを取り寄せた。
Database.SQLite3のexecPrint付近をソースと対比させてみると
-- * Simple query execution -- | <http://sqlite.org/c3ref/exec.html> : -- | Like 'exec', but print result rows to 'System.IO.stdout'. -- -- This is mainly for convenience when experimenting in GHCi. -- The output format may change in the future. execPrint :: Database -> Text -> IO () execPrint !db !sql = interruptibly db $ execWithCallback db sql $ \_count _colnames -> T.putStrLn . showValues :
説明文とコメントが一致したね。
パッケージの決定
自分が使うパッケージを決定するとしたら、どういう手順を踏めば良いのだろう。 思いつくまま挙げてみると、まずはぐぐるして先駆者がどんなのを使ってるか調べる。 キーワードは、haskell sqlite3 ぐらいかな。 検索期間は、足が速い事を考慮して1年以内ぐらいかな。それ以内と言うと1月に なっちゃうんだけど、オイラーとしては半年ぐらいのリミットも欲しいぞ。
HDBCパッケージを使う人やシンプルパッケージを使う人がいて、何を選んだらよいか 迷う。
で、供給元で確認してみる。
Package search で、sqlite3で探して、DLsで人気の程を知る。persisetent-sqlite, yst, direct-sqlite, HDBC-sqlite3 あたりが人気の的のようだ。
これらの候補から絞りこむんだな。ystはYAMLだとか関係なさそうな感じ。HDBCはやりたい事 が出来ない感じ。一番人気のpersisetent-sqlite は何か偏りがありそう。消去法で行ったら、 direct-sqliteが残った。(ええ、正解を知ってるというバイアスがかかってるのは承知 してますよ)
ここから、Database.SQLite を選んでいるのが分からないなあ。元祖だから?
追跡
qhsがどんな風にコンパイルしてるか、追ってみる。
deb9:qhs$ stack build : direct-sqlite-2.3.21: download direct-sqlite-2.3.21: configure direct-sqlite-2.3.21: build : direct-sqlite-2.3.21: copy/register : sqlite-simple-0.4.13.0: download sqlite-simple-0.4.13.0: configure sqlite-simple-0.4.13.0: build : sqlite-0.5.5: download sqlite-simple-0.4.13.0: copy/register : sqlite-0.5.5: configure sqlite-0.5.5: build : sqlite-0.5.5: copy/register
qhs.cabalにdirect-sqliteが無いのに、勝手にインストールされてる。