万年素人からHackerへの道

万年素人がHackerになれるまで殴り書きするぜ。

Boo Language Advent Calendar 2012 9日目 Title:「Sqlite+Boo連携! in Unity」

URL: http://atnd.org/events/34622

SqliteとBoo連携をUnityでやってる人はいないんじゃないんでしょうか?
てことで書きます。まぁ、Unityでsqliteやってる人もあまり見ない。
ていうかBooは更にすくないかも?

まず、Boo公式のドキュメントを読む(Copyright 2003-2006)
URL: http://boo.codehaus.org/SQLite+Database

念のため書くが、

print myRow[myColumn])

の最後の「)」カッコはバグ

「Mono.Data.SqliteClient.dll」を使いましょうとコメントで書いてある。

//Requires Mono.Data.SqliteClient.dll which is included with Mono.

探すとこの日本語のBlog(2007-02-23)にたどり着いた。
URL: http://d.hatena.ne.jp/atsushieno/20070223

「これまで使われていたMono.Data.SqliteClient.dllは、vladが2002年にやっつけたのが起源で(彼は今でこそfirefox3の主要開発者だが、昔はXimianインターンだった)、その後ずっとメンテナーフリーの状態だった。で、最近になってここで開発されているSystem.Data.SqliteClientが出来が良いらしい、ということで、これをMarek(ASP.NETの方)がmono向けに移植しているようだ。」

このBlogを鵜呑みにして解釈すると、「Mono.Data.SqliteClient.dll」は最近になってより良い「System.Data.SqliteClient」になったということだろう。

Boo公式のドキュメントが2003-2006と書かれてたので、最高でも2006年の情報なので、2007-02-23のこのBlogの方が新しい(5年前・・・むしろ6年弱前)であろう。

なので、
URL: http://sourceforge.net/projects/sqlite-dotnet2/
から「Mono.Data.Sqlite.dll」をダウンロードした。
Last Updateが「2010-12-10」なので、2年前だが・・。

やりかたとしては、先ほどの「Mono.Data.Sqlite.dll」を「Plugins」というフォルダに突っ込んだ。こうするとUnityがDLL認識してくれる。

さらに、公式BlogのBooコードをUnityらしく記載してみた。
※Awakeに書いて、printをDebug.Log("String")にした

このコードをMain Cameraにアタッチしたよ。
・SqliteTest

import System
import System.Data from System.Data
import Mono.Data.Sqlite

class SqliteTest (MonoBehaviour):

    def Awake ():
        Debug.Log('If this test works, you should get:')
        Debug.Log('Data 1: 5')
        Debug.Log('Data 2: Mono')
        Debug.Log('create SqliteConnection...')
        dbcon as SqliteConnection = SqliteConnection()
        connectionString as string = 'URI=file:SqliteTest.db'
        Debug.Log('setting ConnectionString using: ' + connectionString)
        dbcon.ConnectionString = connectionString
        Debug.Log('open the connection...')
        dbcon.Open()
        Debug.Log('create SqliteCommand to CREATE TABLE MONO_TEST')
        dbcmd as SqliteCommand = SqliteCommand()
        dbcmd.Connection = dbcon
        dbcmd.CommandText = 'CREATE TABLE MONO_TEST ( ' + 'NID INT, ' + 'NDESC TEXT )'
        Debug.Log('execute command...')
        dbcmd.ExecuteNonQuery()
        Debug.Log('set and execute command to INSERT INTO MONO_TEST')
        dbcmd.CommandText = 'INSERT INTO MONO_TEST  ' + '(NID, NDESC )' + 'VALUES(5,\'Mono\')'
        dbcmd.ExecuteNonQuery()
        Debug.Log('set command to SELECT FROM MONO_TEST')
        dbcmd.CommandText = 'SELECT * FROM MONO_TEST'
        reader as SqliteDataReader
        Debug.Log('execute reader...')
        reader = dbcmd.ExecuteReader()
        Debug.Log('read and display data...')
        while reader.Read():
            Debug.Log('Data 1: ' + reader[0].ToString())
            Debug.Log('Data 2: ' + reader[1].ToString())

        Debug.Log('read and display data using DataAdapter...')
        adapter as SqliteDataAdapter = SqliteDataAdapter('SELECT * FROM MONO_TEST', connectionString)
        dataset as DataSet = DataSet()
        adapter.Fill(dataset)
        for myTable as DataTable in dataset.Tables:
            for myRow as DataRow in myTable.Rows:
                for myColumn as DataColumn in myTable.Columns:
                    Debug.Log(myRow[myColumn])

        Debug.Log('clean up...')
        dataset.Dispose()
        adapter.Dispose()
        reader.Close()
        dbcmd.Dispose()
        dbcon.Close()
        Debug.Log('Done.')

しかし、

adapter.Fill(dataset)

の行で怒られた!!

エラーは↓

EntryPointNotFoundException: sqlite3_column_origin_name
Mono.Data.Sqlite.SQLite3.ColumnOriginalName (Mono.Data.Sqlite.SqliteStatement stmt, Int32 index)
Mono.Data.Sqlite.SqliteDataReader.GetSchemaTable (Boolean wantUniqueInfo, Boolean wantDefaultValue)
Mono.Data.Sqlite.SqliteDataReader.GetSchemaTable ()
System.Data.Common.DataAdapter.BuildSchema (IDataReader reader, System.Data.DataTable table, SchemaType schemaType, MissingSchemaAction missingSchAction, MissingMappingAction missingMapAction, System.Data.Common.DataTableMappingCollection dtMapping)
System.Data.Common.DataAdapter.BuildSchema (IDataReader reader, System.Data.DataTable table, SchemaType schemaType)
System.Data.Common.DataAdapter.FillTable (System.Data.DataTable dataTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords, System.Int32& counter)
System.Data.Common.DataAdapter.FillInternal (System.Data.DataSet dataSet, System.String srcTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords)
System.Data.Common.DataAdapter.Fill (System.Data.DataSet dataSet, System.String srcTable, IDataReader dataReader, Int32 startRecord, Int32 maxRecords)
System.Data.Common.DbDataAdapter.Fill (System.Data.DataSet dataSet, Int32 startRecord, Int32 maxRecords, System.String srcTable, IDbCommand command, CommandBehavior behavior)
System.Data.Common.DbDataAdapter.Fill (System.Data.DataSet dataSet)
SqliteTest.Awake () (at Assets/SqliteTest.boo:40)

ちなみに、DBファイル自体は「SqliteTest.db」ファイルがプロジェクトディレクトリ直下に作られてます。
※Unityエディタ上の「Project」直下では無いですよ!



ちなみに2度実行すると

dbcmd.CommandText = 'CREATE TABLE MONO_TEST ( ' + 'NID INT, ' + 'NDESC TEXT )'

の行で、MONO_TESTテーブルを生成しているため、すでにMONO_TESTテーブルが有るよと怒られるので注意!
※僕は面倒なので「SqliteTest.db」ファイルを削除しましたw

SqliteException: SQLite error
table MONO_TEST already exists


先ほどのエラーの話に戻ります、
「adapter.Fill(dataset)」の行でエラーになるからコメントアウトしてみたら、

        for myTable as DataTable in dataset.Tables:
            for myRow as DataRow in myTable.Rows:
                for myColumn as DataColumn in myTable.Columns:
                    Debug.Log(myRow[myColumn])

が表示できませんでした、「dataset」が空だからあたりまえか・・。
解決策が分かれば、Advent Calenderに書きます!
NHibernate連携も書きたいですね!

では、次回の方!僕かしら?