package jp.juggler.subwaytooter.table import android.content.ContentValues import android.database.sqlite.SQLiteDatabase import android.provider.BaseColumns import jp.juggler.subwaytooter.global.appDatabase import jp.juggler.util.data.ColumnMeta import jp.juggler.util.data.TableCompanion import jp.juggler.util.data.put import jp.juggler.util.log.LogCategory object AcctSet : TableCompanion { private val log = LogCategory("AcctSet") override val table = "acct_set" val columnList: ColumnMeta.List = ColumnMeta.List(table, 7).apply { ColumnMeta(this, 0, BaseColumns._ID, "INTEGER PRIMARY KEY", primary = true) createExtra = { arrayOf( "create unique index if not exists ${table}_acct on $table($COL_ACCT)", "create index if not exists ${table}_time on $table($COL_TIME_SAVE)", ) } } private val COL_TIME_SAVE = ColumnMeta(columnList, 0, "time_save", "integer not null") //@who@host ascii文字の大文字小文字は(sqliteにより)同一視される private val COL_ACCT = ColumnMeta(columnList, 0, "acct", "text not null") private val prefix_search_where = "$COL_ACCT like ? escape '$'" private val prefix_search_where_arg = object : ThreadLocal>() { override fun initialValue(): Array { return arrayOfNulls(1) } } override fun onDBCreate(db: SQLiteDatabase) = columnList.onDBCreate(db) override fun onDBUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = columnList.onDBUpgrade(db, oldVersion, newVersion) fun deleteOld(now: Long) { try { // 古いデータを掃除する val expire = now - 86400000L * 365 appDatabase.delete(table, "$COL_TIME_SAVE, offset: Int, length: Int) { try { val cv = ContentValues() cv.put(COL_TIME_SAVE, now) var bOK = false val db = appDatabase db.execSQL("BEGIN TRANSACTION") try { for (i in 0 until length) { val acct = srcList[i + offset] ?: continue cv.put(COL_ACCT, acct) db.replace(table, null, cv) } bOK = true } catch (ex: Throwable) { log.e(ex, "saveList failed.") } if (bOK) { db.execSQL("COMMIT TRANSACTION") } else { db.execSQL("ROLLBACK TRANSACTION") } } catch (ex: Throwable) { log.e(ex, "saveList failed.") } } private fun makePattern(src: String): String { val sb = StringBuilder() var i = 0 val ie = src.length while (i < ie) { val c = src[i] if (c == '%' || c == '_' || c == '$') { sb.append('$') } sb.append(c) ++i } // 前方一致検索にするため、末尾に%をつける sb.append('%') return sb.toString() } fun searchPrefix(prefix: String, limit: Int): ArrayList { try { val where_arg = prefix_search_where_arg.get() ?: arrayOfNulls(1) where_arg[0] = makePattern(prefix) appDatabase.query( table, null, prefix_search_where, where_arg, null, null, "$COL_ACCT asc limit $limit" ).use { cursor -> val dst = ArrayList(cursor.count) val idx_acct = COL_ACCT.getIndex(cursor) while (cursor.moveToNext()) { dst.add(cursor.getString(idx_acct)) } return dst } } catch (ex: Throwable) { log.e(ex, "searchPrefix failed.") } return ArrayList() } }