diff --git a/spinner/lib/src/main/assets/browse.hbs b/spinner/lib/src/main/assets/browse.hbs index ae4dedfd1..8affb72ac 100644 --- a/spinner/lib/src/main/assets/browse.hbs +++ b/spinner/lib/src/main/assets/browse.hbs @@ -23,7 +23,21 @@ {{/if}} {{#if queryResult}} -

{{queryResult.rowCount}} row(s).

+

Viewing rows {{pagingData.startRow}}-{{pagingData.endRow}} of {{pagingData.rowCount}}.

+ + +
+ + + + + + + + +
+ + {{#each queryResult.columns}} @@ -42,6 +56,20 @@ Select a table from above and click 'browse'. {{/if}} - {{> suffix}} +
+ + + + + + + + + + + + + + {{> suffix}} diff --git a/spinner/lib/src/main/java/org/signal/spinner/DatabaseUtil.kt b/spinner/lib/src/main/java/org/signal/spinner/DatabaseUtil.kt index ed49b8ad3..97db8601c 100644 --- a/spinner/lib/src/main/java/org/signal/spinner/DatabaseUtil.kt +++ b/spinner/lib/src/main/java/org/signal/spinner/DatabaseUtil.kt @@ -25,3 +25,13 @@ fun SupportSQLiteDatabase.getIndexes(): Cursor { fun SupportSQLiteDatabase.getTriggers(): Cursor { return this.query("SELECT * FROM sqlite_master WHERE type='trigger' ORDER BY name ASC") } + +fun SupportSQLiteDatabase.getTableRowCount(table: String): Int { + return this.query("SELECT COUNT(*) FROM $table").use { + if (it.moveToFirst()) { + it.getInt(0) + } else { + 0 + } + } +} \ No newline at end of file diff --git a/spinner/lib/src/main/java/org/signal/spinner/SpinnerServer.kt b/spinner/lib/src/main/java/org/signal/spinner/SpinnerServer.kt index fb54f9aac..686df8f89 100644 --- a/spinner/lib/src/main/java/org/signal/spinner/SpinnerServer.kt +++ b/spinner/lib/src/main/java/org/signal/spinner/SpinnerServer.kt @@ -11,7 +11,9 @@ import fi.iki.elonen.NanoHTTPD import org.signal.core.util.ExceptionUtil import org.signal.core.util.logging.Log import java.lang.IllegalArgumentException +import kotlin.math.ceil import kotlin.math.max +import kotlin.math.min /** * The workhorse of this lib. Handles all of our our web routing and response generation. @@ -31,6 +33,7 @@ internal class SpinnerServer( private val handlebars: Handlebars = Handlebars(AssetTemplateLoader(context)).apply { registerHelper("eq", ConditionalHelpers.eq) + registerHelper("neq", ConditionalHelpers.neq) } override fun serve(session: IHTTPSession): Response { @@ -86,7 +89,22 @@ internal class SpinnerServer( private fun postBrowse(dbName: String, db: SupportSQLiteDatabase, session: IHTTPSession): Response { val table: String = session.parameters["table"]?.get(0).toString() - val query = "select * from $table" + val pageSize: Int = session.parameters["pageSize"]?.get(0)?.toInt() ?: 1000 + var pageIndex: Int = session.parameters["pageIndex"]?.get(0)?.toInt() ?: 0 + val action: String? = session.parameters["action"]?.get(0) + + val rowCount = db.getTableRowCount(table) + val pageCount = ceil(rowCount.toFloat() / pageSize.toFloat()).toInt() + + when (action) { + "first" -> pageIndex = 0 + "next" -> pageIndex = min(pageIndex + 1, pageCount - 1) + "previous" -> pageIndex = max(pageIndex - 1, 0) + "last" -> pageIndex = pageCount - 1 + } + + val query = "select * from $table limit $pageSize offset ${pageSize * pageIndex}" + val queryResult = db.query(query).toQueryResult() return renderTemplate( "browse", @@ -96,7 +114,15 @@ internal class SpinnerServer( databases = databases.keys.toList(), tableNames = db.getTableNames(), table = table, - queryResult = db.query(query).toQueryResult() + queryResult = queryResult, + pagingData = PagingData( + rowCount = rowCount, + pageSize = pageSize, + pageIndex = pageIndex, + pageCount = pageCount, + startRow = pageSize * pageIndex, + endRow = min(pageSize * (pageIndex + 1), rowCount) + ) ) ) } @@ -275,7 +301,8 @@ internal class SpinnerServer( val databases: List, val tableNames: List, val table: String? = null, - val queryResult: QueryResult? = null + val queryResult: QueryResult? = null, + val pagingData: PagingData? = null, ) data class QueryPageModel( @@ -308,4 +335,15 @@ internal class SpinnerServer( val name: String, val sql: String ) + + data class PagingData( + val rowCount: Int, + val pageSize: Int, + val pageIndex: Int, + val pageCount: Int, + val firstPage: Boolean = pageIndex == 0, + val lastPage: Boolean = pageIndex == pageCount - 1, + val startRow: Int, + val endRow: Int + ) }