-
Notifications
You must be signed in to change notification settings - Fork 15
/
QueryUtil.scala
95 lines (81 loc) · 4.12 KB
/
QueryUtil.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see <http://www.gnu.org/licenses/>.
package org.alephium.explorer.persistence.queries
import scala.annotation.tailrec
import slick.dbio.DBIOAction
import org.alephium.explorer.persistence.DBActionW
import org.alephium.explorer.persistence.schema.CustomSetParameter.paramPlaceholder
object QueryUtil {
/** Maximum number of parameters allowed by Postgres per query.
*
* TODO: This should be passed as a configuration instead of hardcoding it
* and should be passed as parameter to the function using this value.
* */
val postgresDefaultParameterLimit: Short = Short.MaxValue
private val emptyUpdates = DBIOAction.successful(0)
/** Splits update queries into batches limited by the total number parameters allowed per query */
@SuppressWarnings(Array("org.wartremover.warts.DefaultArguments"))
def splitUpdates[R](rows: Iterable[R],
queryRowParams: Int,
queryMaxParams: Short = postgresDefaultParameterLimit)(
queryBuilder: (Iterable[R], String) => DBActionW[Int]): DBActionW[Int] =
splitFoldLeft[R, DBActionW[Int]](initialQuery = emptyUpdates,
queryRowParams = queryRowParams,
queryMaxParams = queryMaxParams,
rows = rows) { (rows, placeholder, action) =>
action andThen queryBuilder(rows, placeholder)
}
/**
* Splits queries into batches limited by the total number parameters allowed per query.
*
* @param rows All query parameters
* @param initialQuery Returned when params are empty
* @param queryRowParams Max number of parameters in a single row of a query. Or the number of '?' in a single row.
* @param queryMaxParams Maximum number of parameters for each query. Eg: [[postgresDefaultParameterLimit]]
* @param foldLeft Given a set of following inputs (Tuple3) returns the next query.
* Similar to foldLeft in a collection type.
* - _1 = Parameter split for current query
* - _2 = Placeholder for current query
* - _3 = Previous query. Used to join with next query.
* @tparam R type of rows
* @tparam Q type of query
* @return A single query of type [[Q]]
*/
def splitFoldLeft[R, Q](rows: Iterable[R],
initialQuery: Q,
queryRowParams: Int,
queryMaxParams: Short)(foldLeft: (Iterable[R], String, Q) => Q): Q = {
//maximum number of rows per query
val maxRows = queryMaxParams / queryRowParams
@tailrec
def build(rowsRemaining: Iterable[R], previousQuery: Q): Q =
if (rowsRemaining.isEmpty) {
previousQuery
} else {
//number of rows for this query
val queryRows = rowsRemaining.size min maxRows
//generate placeholder string
val placeholder = paramPlaceholder(rows = queryRows, columns = queryRowParams)
//thisBatch = rows for this query
//remaining = rows not processed in this query
val (thisBatch, remaining) = rowsRemaining.splitAt(queryRows)
val nextResult = foldLeft(thisBatch, placeholder, previousQuery)
//process remaining
build(rowsRemaining = remaining, previousQuery = nextResult)
}
build(rowsRemaining = rows, previousQuery = initialQuery)
}
}