Guest User

Untitled

a guest
May 2nd, 2018
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.95 KB | None | 0 0
  1. package com.perkelle.dev.bot.wrappers
  2.  
  3. import com.perkelle.dev.bot.utils.onComplete
  4. import com.zaxxer.hikari.HikariConfig
  5. import com.zaxxer.hikari.HikariDataSource
  6. import kotlinx.coroutines.experimental.async
  7. import kotlinx.coroutines.experimental.launch
  8. import java.sql.*
  9. import java.sql.Date
  10. import java.util.*
  11.  
  12. abstract class SQLWrapper(private val host: String, private val username: String, private val password: String, private val database: String, private val port: Int = 3306) {
  13.  
  14. companion object {
  15. var threadPoolSize = 10
  16.  
  17. var connected = false
  18. lateinit var ds: HikariDataSource
  19. }
  20.  
  21. val ALL = "*"
  22.  
  23. fun login() : Boolean {
  24. return try {
  25. if(connected) return true
  26.  
  27. val timezone = TimeZone.getDefault().id
  28.  
  29. val config = HikariConfig()
  30. config.jdbcUrl = "jdbc:mysql://$host:$port/$database"
  31. config.username = username
  32. config.password = password
  33. config.addDataSourceProperty("autoReconnect", true)
  34. config.addDataSourceProperty("useJDBCCompliantTimezoneShift", true)
  35. config.addDataSourceProperty("useLegacyDatetimeCode", false)
  36. config.addDataSourceProperty("serverTimezone", timezone)
  37. config.addDataSourceProperty("cachePrepStmts", true)
  38. config.addDataSourceProperty("prepStmtCacheSize", 250)
  39. config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048)
  40. config.addDataSourceProperty("maxIdle", 1)
  41. config.maximumPoolSize = threadPoolSize
  42.  
  43. ds = HikariDataSource(config)
  44. connected = true
  45.  
  46. true
  47. } catch(ex: SQLException) {
  48. ex.printStackTrace()
  49. false
  50. }
  51. }
  52.  
  53. abstract fun setup()
  54.  
  55. fun closeConnection() = ds.close()
  56.  
  57. /**
  58. * @param name The name of the table
  59. * @param keys A list of SQLKeys
  60. */
  61. fun createTable(name: String, keys: List<SQLKey>) {
  62. execute("CREATE TABLE IF NOT EXISTS $name (${keys.joinToString(", ", transform = {
  63. "${it.name} " +
  64. it.dataType.name +
  65. "${if(it.length != null) "(${it.length})" else ""} " +
  66. if(it.autoIncrement) " AUTO INCREMENT" else "" +
  67. if(it.primaryKey) " PRIMARY KEY" else "" +
  68. if(it.unique) " UNIQUE" else ""
  69. })});")
  70. }
  71.  
  72. fun selectData(table: String, fields: MutableList<String>, condition: ConditionBuilder? = null, callback: (List<SQLRow>) -> Unit) {
  73. if(fields.contains("*")) throw UnsupportedOperationException("* is unsupported")
  74.  
  75. val injects = mutableListOf<String>()
  76. condition?.inserts?.forEach { injects.add(it.toString()) }
  77.  
  78. executeQuery("SELECT ${fields.joinToString(", ")} FROM $table ${condition?.let { "WHERE ${it.condition}" } ?: "" };", *injects.toTypedArray()) { rs ->
  79. val rows = mutableListOf<SQLRow>()
  80.  
  81. while(rs.next()) {
  82. rows.add(fields.map { it to rs.getString(it) }.toMap())
  83. }
  84.  
  85. callback(rows)
  86. }
  87. }
  88.  
  89. fun count(table: String, condition: ConditionBuilder? = null, callback: (Int) -> Unit) {
  90. val injects = mutableListOf<String>()
  91. condition?.inserts?.forEach { injects.add(it.toString()) }
  92.  
  93. executeQuery("SELECT COUNT(*) FROM $table ${condition?.let { "WHERE ${condition.condition}" } ?: "" };", *injects.toTypedArray()) {
  94. callback(
  95. if(it.next()) it.getInt(1)
  96. else 0
  97. )
  98. }
  99. }
  100.  
  101. fun getColumns(table: String, callback: (List<String>) -> Unit) {
  102. executeQuery("SHOW COLUMNS FROM ?;", table) {
  103. val columns = mutableListOf<String>()
  104. while(it.next())
  105. columns.add(it.getString(1))
  106. }
  107. }
  108.  
  109. /**
  110. * @param table The table name
  111. * @param fields Field name, value
  112. * @param upserts If a unique key is already present, should we update it? Null if you don't want this to happen or there are no unique keys. The list should contain the unique keys.
  113. */
  114. fun insert(table: String, fields: Map<String, Any>, upserts: List<String>? = null) {
  115. fun Any.enclose() =
  116. if(this is Int || this is Long || this is Double || this is Float) "$this"
  117. else "'$this'"
  118.  
  119. val statement = "INSERT INTO $table (" +
  120. "${fields.keys.joinToString(",")}) VALUES(" +
  121. "${"?,".repeat(fields.values.size).substring(0, (fields.values.size * 2) - 1)}) " +
  122. "${upserts?.let { " ON DUPLICATE KEY UPDATE " +
  123. "${upserts.joinToString(",") { "`$it` = ${fields[it]!!.enclose()} " }} " }};"
  124.  
  125. execute(statement, *fields.values.toTypedArray())
  126. }
  127.  
  128. fun remove(table: String, condition: ConditionBuilder? = null) {
  129. val injects = mutableListOf<String>()
  130. condition?.inserts?.forEach { injects.add(it.toString()) }
  131.  
  132. execute("DELETE FROM $table ${condition?.let { " WHERE ${it.condition}" } ?: ""};", *injects.toTypedArray())
  133. }
  134.  
  135. fun update(table: String, fieldToUpdate: String, newValue: Any, condition: ConditionBuilder? = null) {
  136. val injects = mutableListOf<String>()
  137. condition?.inserts?.forEach { injects.add(it.toString()) }
  138.  
  139. execute( "UPDATE $table SET $fieldToUpdate=${newValue as? Number ?: "'$newValue'"} ${condition?.condition};", *injects.toTypedArray())
  140. }
  141.  
  142. /**
  143. * @param name The name of the key
  144. * @param dataType @see SQLDataTypes
  145. * @param length The max length of the data, e.g. VARCHAR(36). Null for thing like TEXT where it doesn't exist
  146. * @param unique Is it a unique key
  147. * @param autoIncrement Is it an auto incremental key (ints only)
  148. */
  149. data class SQLKey(val name: String, val dataType: SQLDataTypes, val length: Int?, val unique: Boolean, val autoIncrement: Boolean = false, val primaryKey: Boolean = false)
  150.  
  151. /**
  152. * @param The key. If you do SELECT * FROM myTable WHERE `NAME` = 'RYAN'; the key will be RYAN
  153. * @param values Column, Value
  154. */
  155.  
  156. /**
  157. * All SQL datatypes
  158. */
  159. enum class SQLDataTypes {
  160. CHAR,
  161. VARCHAR,
  162. TINYTEXT,
  163. TEXT,
  164. MEDIUMTEXT,
  165. LONGTEXT,
  166. TINYINT,
  167. SMALLINT,
  168. MEDIUMINT,
  169. INT,
  170. BIGINT,
  171. FLOAT,
  172. //DOUBLE - Not Supported
  173. //DECIMAL - Not Supported
  174. DATE,
  175. DATETIME,
  176. TIMESTAMP,
  177. TIME,
  178. ENUM,
  179. SET,
  180. BINARY
  181. }
  182.  
  183. class ConditionBuilder {
  184.  
  185. var condition = ""
  186. val inserts = mutableListOf<Any>()
  187.  
  188. fun or(): ConditionBuilder {
  189. condition+=" OR "
  190. return this
  191. }
  192.  
  193. fun and(): ConditionBuilder {
  194. condition+= " AND "
  195. return this
  196. }
  197.  
  198. fun equalTo(comparison: Pair<String, Any>): ConditionBuilder {
  199. condition += "${comparison.first} = ?"
  200.  
  201. inserts.add(comparison.second)
  202. return this
  203. }
  204.  
  205. fun greaterThan(comparison: Pair<String, Int>): ConditionBuilder {
  206. condition += "${comparison.first} > ?"
  207.  
  208. inserts.add(comparison.second)
  209. return this
  210. }
  211.  
  212. fun lessThan(comparison: Pair<String, Int>): ConditionBuilder {
  213. condition+= "${comparison.first} < ?"
  214.  
  215. inserts.add(comparison.second)
  216. return this
  217. }
  218. }
  219.  
  220. private fun execute(toExecute: String, vararg injectValues: Any) {
  221. launch {
  222. val conn = ds.connection
  223. val ps = conn.prepareStatement(toExecute)
  224. ps.injectVariables(injectValues)
  225. ps.execute()
  226. ps.close()
  227. conn.close()
  228. }
  229. }
  230.  
  231. private fun executeQuery(query: String, vararg injectValues: Any, callback: (ResultSet) -> Unit) {
  232. async {
  233. val conn = ds.connection
  234. val ps = conn.prepareStatement(query)
  235. ps.injectVariables(injectValues)
  236. val rs = ps.executeQuery()
  237. return@async Triple(rs, ps, conn)
  238. }.onComplete {
  239. callback(it.first)
  240. it.first.close()
  241. it.second.close()
  242. it.third.close()
  243. }
  244. }
  245.  
  246. private fun PreparedStatement.injectVariables(injectValues: Array<out Any>) {
  247. injectValues.withIndex().map { it.index + 1 to it.value }.forEach { (index, value) ->
  248. when (value) {
  249. is String -> setString(index, value)
  250. is Byte -> setByte(index, value)
  251. is Boolean -> setBoolean(index, value)
  252. is Float -> setFloat(index, value)
  253. is Double -> setDouble(index, value)
  254. is Int -> setInt(index, value)
  255. is Long -> setLong(index, value)
  256. is Short -> setShort(index, value)
  257. is Time -> setTime(index, value)
  258. is Timestamp -> setTimestamp(index, value)
  259. is Date -> setDate(index, value)
  260. }
  261. }
  262. }
  263. }
  264.  
  265. typealias SQLRow = Map<String, String>
Add Comment
Please, Sign In to add comment