/db.go

https://github.com/jollheef/out-of-tree · Go · 335 lines · 281 code · 50 blank · 4 comment · 64 complexity · 687b3408ae7cfb3cbd51cc7f92acce2d MD5 · raw file

  1. // Copyright 2019 Mikhail Klementev. All rights reserved.
  2. // Use of this source code is governed by a AGPLv3 license
  3. // (or later) that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "database/sql"
  7. "fmt"
  8. "strconv"
  9. "time"
  10. _ "github.com/mattn/go-sqlite3"
  11. "code.dumpstack.io/tools/out-of-tree/config"
  12. "code.dumpstack.io/tools/out-of-tree/qemu"
  13. )
  14. // Change on ANY database update
  15. const currentDatabaseVersion = 2
  16. const versionField = "db_version"
  17. type logEntry struct {
  18. ID int
  19. Tag string
  20. Timestamp time.Time
  21. qemu.System
  22. config.Artifact
  23. config.KernelInfo
  24. phasesResult
  25. }
  26. func createLogTable(db *sql.DB) (err error) {
  27. _, err = db.Exec(`
  28. CREATE TABLE IF NOT EXISTS log (
  29. id INTEGER PRIMARY KEY,
  30. time DATETIME DEFAULT CURRENT_TIMESTAMP,
  31. tag TEXT,
  32. name TEXT,
  33. type TEXT,
  34. distro_type TEXT,
  35. distro_release TEXT,
  36. kernel_release TEXT,
  37. build_output TEXT,
  38. build_ok BOOLEAN,
  39. run_output TEXT,
  40. run_ok BOOLEAN,
  41. test_output TEXT,
  42. test_ok BOOLEAN,
  43. qemu_stdout TEXT,
  44. qemu_stderr TEXT,
  45. kernel_panic BOOLEAN,
  46. timeout_kill BOOLEAN
  47. )`)
  48. return
  49. }
  50. func createMetadataTable(db *sql.DB) (err error) {
  51. _, err = db.Exec(`
  52. CREATE TABLE IF NOT EXISTS metadata (
  53. id INTEGER PRIMARY KEY,
  54. key TEXT UNIQUE,
  55. value TEXT
  56. )`)
  57. return
  58. }
  59. func metaChkValue(db *sql.DB, key string) (exist bool, err error) {
  60. sql := "SELECT EXISTS(SELECT id FROM metadata WHERE key = $1)"
  61. stmt, err := db.Prepare(sql)
  62. if err != nil {
  63. return
  64. }
  65. defer stmt.Close()
  66. err = stmt.QueryRow(key).Scan(&exist)
  67. return
  68. }
  69. func metaGetValue(db *sql.DB, key string) (value string, err error) {
  70. stmt, err := db.Prepare("SELECT value FROM metadata " +
  71. "WHERE key = $1")
  72. if err != nil {
  73. return
  74. }
  75. defer stmt.Close()
  76. err = stmt.QueryRow(key).Scan(&value)
  77. return
  78. }
  79. func metaSetValue(db *sql.DB, key, value string) (err error) {
  80. stmt, err := db.Prepare("INSERT OR REPLACE INTO metadata " +
  81. "(key, value) VALUES ($1, $2)")
  82. if err != nil {
  83. return
  84. }
  85. defer stmt.Close()
  86. _, err = stmt.Exec(key, value)
  87. return
  88. }
  89. func getVersion(db *sql.DB) (version int, err error) {
  90. s, err := metaGetValue(db, versionField)
  91. if err != nil {
  92. return
  93. }
  94. version, err = strconv.Atoi(s)
  95. return
  96. }
  97. func addToLog(db *sql.DB, q *qemu.System, ka config.Artifact,
  98. ki config.KernelInfo, res *phasesResult, tag string) (err error) {
  99. stmt, err := db.Prepare("INSERT INTO log (name, type, tag, " +
  100. "distro_type, distro_release, kernel_release, " +
  101. "build_output, build_ok, " +
  102. "run_output, run_ok, " +
  103. "test_output, test_ok, " +
  104. "qemu_stdout, qemu_stderr, " +
  105. "kernel_panic, timeout_kill) " +
  106. "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, " +
  107. "$10, $11, $12, $13, $14, $15, $16);")
  108. if err != nil {
  109. return
  110. }
  111. defer stmt.Close()
  112. _, err = stmt.Exec(
  113. ka.Name, ka.Type, tag,
  114. ki.DistroType, ki.DistroRelease, ki.KernelRelease,
  115. res.Build.Output, res.Build.Ok,
  116. res.Run.Output, res.Run.Ok,
  117. res.Test.Output, res.Test.Ok,
  118. q.Stdout, q.Stderr,
  119. q.KernelPanic, q.KilledByTimeout,
  120. )
  121. if err != nil {
  122. return
  123. }
  124. return
  125. }
  126. func getAllLogs(db *sql.DB, tag string, num int) (les []logEntry, err error) {
  127. stmt, err := db.Prepare("SELECT id, time, name, type, tag, " +
  128. "distro_type, distro_release, kernel_release, " +
  129. "build_ok, run_ok, test_ok, kernel_panic, " +
  130. "timeout_kill FROM log ORDER BY datetime(time) DESC " +
  131. "LIMIT $1")
  132. if err != nil {
  133. return
  134. }
  135. defer stmt.Close()
  136. rows, err := stmt.Query(num)
  137. if err != nil {
  138. return
  139. }
  140. defer rows.Close()
  141. for rows.Next() {
  142. le := logEntry{}
  143. err = rows.Scan(&le.ID, &le.Timestamp,
  144. &le.Name, &le.Type, &le.Tag,
  145. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  146. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  147. &le.KernelPanic, &le.KilledByTimeout,
  148. )
  149. if err != nil {
  150. return
  151. }
  152. if tag == "" || tag == le.Tag {
  153. les = append(les, le)
  154. }
  155. }
  156. return
  157. }
  158. func getAllArtifactLogs(db *sql.DB, tag string, num int, ka config.Artifact) (
  159. les []logEntry, err error) {
  160. stmt, err := db.Prepare("SELECT id, time, name, type, tag, " +
  161. "distro_type, distro_release, kernel_release, " +
  162. "build_ok, run_ok, test_ok, kernel_panic, " +
  163. "timeout_kill FROM log WHERE name=$1 AND type=$2 " +
  164. "ORDER BY datetime(time) DESC LIMIT $3")
  165. if err != nil {
  166. return
  167. }
  168. defer stmt.Close()
  169. rows, err := stmt.Query(ka.Name, ka.Type, num)
  170. if err != nil {
  171. return
  172. }
  173. defer rows.Close()
  174. for rows.Next() {
  175. le := logEntry{}
  176. err = rows.Scan(&le.ID, &le.Timestamp,
  177. &le.Name, &le.Type, &le.Tag,
  178. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  179. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  180. &le.KernelPanic, &le.KilledByTimeout,
  181. )
  182. if err != nil {
  183. return
  184. }
  185. if tag == "" || tag == le.Tag {
  186. les = append(les, le)
  187. }
  188. }
  189. return
  190. }
  191. func getLogByID(db *sql.DB, id int) (le logEntry, err error) {
  192. stmt, err := db.Prepare("SELECT id, time, name, type, tag, " +
  193. "distro_type, distro_release, kernel_release, " +
  194. "build_ok, run_ok, test_ok, " +
  195. "build_output, run_output, test_output, " +
  196. "qemu_stdout, qemu_stderr, " +
  197. "kernel_panic, timeout_kill " +
  198. "FROM log WHERE id=$1")
  199. if err != nil {
  200. return
  201. }
  202. defer stmt.Close()
  203. err = stmt.QueryRow(id).Scan(&le.ID, &le.Timestamp,
  204. &le.Name, &le.Type, &le.Tag,
  205. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  206. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  207. &le.Build.Output, &le.Run.Output, &le.Test.Output,
  208. &le.Stdout, &le.Stderr,
  209. &le.KernelPanic, &le.KilledByTimeout,
  210. )
  211. return
  212. }
  213. func getLastLog(db *sql.DB) (le logEntry, err error) {
  214. err = db.QueryRow("SELECT MAX(id), time, name, type, tag, "+
  215. "distro_type, distro_release, kernel_release, "+
  216. "build_ok, run_ok, test_ok, "+
  217. "build_output, run_output, test_output, "+
  218. "qemu_stdout, qemu_stderr, "+
  219. "kernel_panic, timeout_kill "+
  220. "FROM log").Scan(&le.ID, &le.Timestamp,
  221. &le.Name, &le.Type, &le.Tag,
  222. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  223. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  224. &le.Build.Output, &le.Run.Output, &le.Test.Output,
  225. &le.Stdout, &le.Stderr,
  226. &le.KernelPanic, &le.KilledByTimeout,
  227. )
  228. return
  229. }
  230. func createSchema(db *sql.DB) (err error) {
  231. err = createMetadataTable(db)
  232. if err != nil {
  233. return
  234. }
  235. err = createLogTable(db)
  236. if err != nil {
  237. return
  238. }
  239. return
  240. }
  241. func openDatabase(path string) (db *sql.DB, err error) {
  242. db, err = sql.Open("sqlite3", path)
  243. if err != nil {
  244. return
  245. }
  246. db.SetMaxOpenConns(1)
  247. exists, _ := metaChkValue(db, versionField)
  248. if !exists {
  249. err = createSchema(db)
  250. if err != nil {
  251. return
  252. }
  253. err = metaSetValue(db, versionField,
  254. strconv.Itoa(currentDatabaseVersion))
  255. return
  256. }
  257. version, err := getVersion(db)
  258. if err != nil {
  259. return
  260. }
  261. if version == 1 {
  262. _, err = db.Exec(`ALTER TABLE log ADD tag TEXT`)
  263. if err != nil {
  264. return
  265. }
  266. err = metaSetValue(db, versionField, "2")
  267. if err != nil {
  268. return
  269. }
  270. version = 2
  271. }
  272. if version != currentDatabaseVersion {
  273. err = fmt.Errorf("Database is not supported (%d instead of %d)",
  274. version, currentDatabaseVersion)
  275. return
  276. }
  277. return
  278. }