db.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. package main
  2. import (
  3. "database/sql"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "time"
  10. _ "github.com/mattn/go-sqlite3"
  11. )
  12. // Make sure the go-sqlite3 is here. Keep the db related things together.
  13. type DBData struct {
  14. DB *sql.DB
  15. User string
  16. LockValue string
  17. }
  18. func (db *DBData) Open(filename string) {
  19. var err error
  20. db.DB, err = sql.Open("sqlite3", filename)
  21. if err != nil {
  22. log.Panicln("Error Opening Sqlite3:", err)
  23. }
  24. // Create the tables (if needed)
  25. db.Create()
  26. }
  27. func (db *DBData) Close() {
  28. db.DB.Close()
  29. }
  30. const LOCK_USERNAME = ""
  31. const LOCK_SETTING = "LOCK"
  32. const LOCK_CLEAR = "0"
  33. func (db *DBData) Create() {
  34. _, err := db.DB.Exec(
  35. "CREATE TABLE IF NOT EXISTS settings(username TEXT, setting TEXT, value TEXT, PRIMARY KEY(username, setting));")
  36. if err != nil {
  37. log.Panicln(err)
  38. }
  39. _, err = db.DB.Exec(
  40. "CREATE TABLE IF NOT EXISTS scores ( username TEXT, `when` INTEGER, date INTEGER, hand INTEGER, won INTEGER, score INTEGER, PRIMARY KEY(username, date, hand));")
  41. if err != nil {
  42. log.Panicln(err)
  43. }
  44. _, err = db.DB.Exec(
  45. "CREATE TABLE IF NOT EXISTS monthly ( month INTEGER, username TEXT, days INTEGER, hands_won INTEGER, score INTEGER, PRIMARY KEY(month, username) );")
  46. if err != nil {
  47. log.Panicln(err)
  48. }
  49. // Make sure our settings LOCK value exists
  50. db.DB.Exec(
  51. "INSERT INTO settings(username, setting, value) VALUES(?,?,?);",
  52. LOCK_USERNAME, LOCK_SETTING, LOCK_CLEAR)
  53. // An error here is OK. It means the setting already exists.
  54. /*
  55. if err != nil {
  56. log.Printf("LOCK %#v\n", err)
  57. }
  58. */
  59. }
  60. func (db *DBData) Lock(timeout int) bool {
  61. var now time.Time = time.Now()
  62. var value string = fmt.Sprintf("%d,%d", os.Getpid(), now.Unix())
  63. RetryLock:
  64. _, err := db.DB.Exec("UPDATE settings SET value=? WHERE username=? AND setting=? AND value=?;",
  65. value, LOCK_USERNAME, LOCK_SETTING, LOCK_CLEAR)
  66. if err != nil {
  67. log.Printf("Lock(%s) : %s\n", value, err)
  68. }
  69. row := db.DB.QueryRow("SELECT value FROM settings WHERE username=? AND setting=?;",
  70. LOCK_USERNAME, LOCK_SETTING)
  71. var result string
  72. err = row.Scan(&result)
  73. if err != nil {
  74. log.Println("Lock Scan", err)
  75. }
  76. // I need to do further checking here -- to make sure the lock isn't stale.
  77. if result == value {
  78. // Success! We've got the lock!
  79. log.Printf("Lock [%s]\n", value)
  80. db.LockValue = value
  81. return true
  82. }
  83. // We failed. There's already a lock present. Check if it is stale.
  84. var lockSet string
  85. var pos int
  86. var howOld time.Duration
  87. pos = strings.Index(result, ",")
  88. if pos != -1 {
  89. lockSet = result[pos+1:]
  90. var unixtime int64
  91. unixtime, err = strconv.ParseInt(lockSet, 10, 64)
  92. if err == nil {
  93. var lockTime time.Time = time.Unix(unixtime, 0)
  94. howOld = now.Sub(lockTime)
  95. log.Printf("Lock %s [%0.2f seconds old]\n", result, howOld.Seconds())
  96. if howOld.Seconds() > 60 {
  97. // Lock expired
  98. log.Printf("Lock %s [%0.2f seconds old] -- Expiring\n", result, howOld.Seconds())
  99. db.DB.Exec("UPDATE settings SET value=? WHERE username=? AND setting=? AND value=?;",
  100. LOCK_CLEAR, LOCK_USERNAME, LOCK_SETTING, result)
  101. goto RetryLock
  102. }
  103. }
  104. } else {
  105. log.Printf("Can't find , in [%s]\n", result)
  106. }
  107. time.Sleep(time.Duration(50) * time.Millisecond)
  108. howOld = time.Until(now) // now.Sub(time.Now())
  109. if int(-howOld.Seconds()) > timeout {
  110. return false
  111. }
  112. goto RetryLock
  113. }
  114. func (db *DBData) Unlock() bool {
  115. if db.LockValue == "" {
  116. // Nothing to unlock -- we didn't get the lock.
  117. return true
  118. }
  119. _, err := db.DB.Exec("UPDATE settings SET value=? WHERE username=? AND setting=? AND value=?;",
  120. LOCK_CLEAR, LOCK_USERNAME, LOCK_SETTING, db.LockValue)
  121. if err != nil {
  122. log.Printf("Unlock(%s) : %s\n", db.LockValue, err)
  123. }
  124. row := db.DB.QueryRow("SELECT value FROM settings WHERE username=? AND setting=?;",
  125. LOCK_USERNAME, LOCK_SETTING)
  126. var result string
  127. err = row.Scan(&result)
  128. if err != nil {
  129. log.Println("Unlock Scan", err)
  130. }
  131. if result != LOCK_CLEAR {
  132. log.Printf("Unlock Failed (LockValue %s) Result %s (should be %s)\n", db.LockValue, result, LOCK_CLEAR)
  133. } else {
  134. log.Println("Unlocked.")
  135. }
  136. return result == LOCK_CLEAR
  137. }
  138. func (db *DBData) GetSetting(setting string, ifMissing string) string {
  139. row := db.DB.QueryRow("SELECT value FROM settings WHERE username=? AND setting=?;",
  140. db.User, setting)
  141. var value string
  142. // log.Printf("row: %#v\n", row)
  143. err := row.Scan(&value)
  144. if err != nil {
  145. return ifMissing
  146. }
  147. return value
  148. }
  149. func (db *DBData) SetSetting(setting string, value string) {
  150. _, err := db.DB.Exec("REPLACE INTO settings(username, setting, value) VALUES(?,?,?);",
  151. db.User, setting, value)
  152. if err != nil {
  153. log.Panicln("Query SetSetting", err)
  154. }
  155. // log.Printf("SetSetting %s %s = %s\n", db.User, setting, value)
  156. }
  157. func (db *DBData) SaveScore(when int64, date int64, hand int, won int, score int) {
  158. _, err := db.DB.Exec("INSERT INTO scores(username, `when`, date, hand, won, score) VALUES(?,?,?,?,?,?);",
  159. db.User, when, date, hand, won, score)
  160. if err != nil {
  161. log.Println("SaveScore", err)
  162. // When we play a hand we've already played:
  163. // panic: SaveScore UNIQUE constraint failed: scores.username, scores.date, scores.hand
  164. }
  165. }
  166. func (db *DBData) HandsPlayedOnDay(day int64) int {
  167. row := db.DB.QueryRow("SELECT COUNT(*) FROM scores WHERE username=? AND date=?;",
  168. db.User, day)
  169. var value int
  170. err := row.Scan(&value)
  171. if err != nil {
  172. return 0
  173. }
  174. return value
  175. }
  176. /*
  177. WhenPlayed = GetPlayed (as far as I can tell...)
  178. */
  179. func (db *DBData) WhenPlayed() map[int64]int {
  180. var result map[int64]int = make(map[int64]int)
  181. rows, err := db.DB.Query("SELECT date, COUNT(hand) FROM scores WHERE username=? GROUP BY date;",
  182. db.User)
  183. if err != nil {
  184. log.Println("GetPlayed", err)
  185. return result
  186. }
  187. defer rows.Close()
  188. for rows.Next() {
  189. var date int64
  190. var hands int
  191. if err := rows.Scan(&date, &hands); err != nil {
  192. log.Println("GetPlayed Scan", err)
  193. return result
  194. }
  195. result[date] = hands
  196. }
  197. if err = rows.Err(); err != nil {
  198. log.Println("GetPlayed rows.Err", err)
  199. }
  200. return result
  201. }
  202. type MonthUser struct {
  203. Date int64
  204. Username string
  205. }
  206. type MonthStats struct {
  207. Days int
  208. Hands_Won int
  209. Score int
  210. }
  211. func (db *DBData) ExpireScores(month_first_unix int64) {
  212. // step 1: acquire lock
  213. l := db.Lock(5)
  214. if l {
  215. // log.Println("Locked")
  216. defer db.Unlock()
  217. } else {
  218. log.Println("Lock failed")
  219. return
  220. }
  221. var Monthly map[MonthUser]MonthStats = make(map[MonthUser]MonthStats)
  222. rows, err := db.DB.Query("SELECT date, username, SUM(score), SUM(won) FROM scores WHERE date < ? GROUP BY date, username ORDER BY date, SUM(score) DESC;",
  223. month_first_unix)
  224. if err != nil {
  225. log.Println("ExpireScores", err)
  226. return
  227. }
  228. defer rows.Close()
  229. for rows.Next() {
  230. var mu MonthUser
  231. var score int
  232. var won int
  233. if err := rows.Scan(&mu.Date, &mu.Username, &score, &won); err != nil {
  234. log.Println("ExpireScores Scan", err)
  235. return
  236. }
  237. FirstOfMonthDateUnix(&mu.Date)
  238. if mumap, found := Monthly[mu]; found {
  239. // entry found
  240. mumap.Days++
  241. mumap.Score += score
  242. mumap.Hands_Won += won
  243. Monthly[mu] = mumap
  244. } else {
  245. // new entry
  246. var ms MonthStats = MonthStats{Days: 1,
  247. Score: score,
  248. Hands_Won: won,
  249. }
  250. Monthly[mu] = ms
  251. }
  252. }
  253. if len(Monthly) == 0 {
  254. // Nothing to do
  255. return
  256. }
  257. // Begin transaction
  258. tx, err := db.DB.Begin()
  259. if err != nil {
  260. log.Println("ExpireScores Begin", err)
  261. return
  262. }
  263. tx.Exec(
  264. "DELETE FROM scores WHERE date < ?;",
  265. month_first_unix)
  266. for mu, ms := range Monthly {
  267. _, err := db.DB.Exec("INSERT INTO monthly(month, username, days, hands_won, score) VALUES(?,?,?,?,?);",
  268. mu.Date, mu.Username, ms.Days, ms.Hands_Won, ms.Score)
  269. if err != nil {
  270. log.Println("ExpireScores Insert", err)
  271. tx.Rollback()
  272. return
  273. }
  274. }
  275. // End transaction / Commit
  276. err = tx.Commit()
  277. if err != nil {
  278. log.Println("ExpireScores Commit", err)
  279. }
  280. }
  281. type ScoresDetails struct {
  282. User string
  283. Date int64
  284. Hand int
  285. Won int
  286. Score int
  287. }
  288. func (db *DBData) GetScoresOnDay(date int64) []ScoresDetails {
  289. var result []ScoresDetails
  290. rows, err := db.DB.Query("SELECT username, date, hand, won, score FROM SCORES WHERE date=? ORDER BY username, hand;",
  291. date)
  292. if err != nil {
  293. log.Println("GetScoresOnDay", date, err)
  294. return result
  295. }
  296. defer rows.Close()
  297. for rows.Next() {
  298. var sd ScoresDetails
  299. if err := rows.Scan(&sd.User, &sd.Date, &sd.Hand, &sd.Won, &sd.Score); err != nil {
  300. log.Println("GetScoresOnDay Scan", err)
  301. return result
  302. }
  303. result = append(result, sd)
  304. }
  305. if err = rows.Err(); err != nil {
  306. log.Println("GetScoresOnDay rows.Err", err)
  307. }
  308. return result
  309. }
  310. type ScoresData struct {
  311. Date int64
  312. User string
  313. Score int
  314. Won int
  315. }
  316. func (db *DBData) GetScores(limit int) []ScoresData {
  317. var result []ScoresData
  318. rows, err := db.DB.Query("SELECT date, username, SUM(score), SUM(won) FROM SCORES GROUP BY date, username ORDER BY SUM(score) DESC LIMIT ?;",
  319. limit)
  320. if err != nil {
  321. log.Println("GetScores", err)
  322. return result
  323. }
  324. defer rows.Close()
  325. for rows.Next() {
  326. var sd ScoresData
  327. if err := rows.Scan(&sd.Date, &sd.User, &sd.Score, &sd.Won); err != nil {
  328. log.Println("GetScores Scan", err)
  329. return result
  330. }
  331. result = append(result, sd)
  332. }
  333. if err = rows.Err(); err != nil {
  334. log.Println("GetScores rows.Err", err)
  335. }
  336. return result
  337. }
  338. type MonthlyData struct {
  339. Date int64
  340. User string
  341. Days int
  342. Hands_Won int
  343. Score int
  344. }
  345. func (db *DBData) GetMonthlyScores(limit int) []MonthlyData {
  346. var result []MonthlyData
  347. rows, err := db.DB.Query("SELECT month, username, days, hands_won, score FROM monthly ORDER BY score DESC LIMIT ?;",
  348. limit)
  349. if err != nil {
  350. log.Println("GetMonthlyScores", err)
  351. return result
  352. }
  353. defer rows.Close()
  354. for rows.Next() {
  355. var md MonthlyData
  356. if err := rows.Scan(&md.Date, &md.User, &md.Days, &md.Hands_Won, &md.Score); err != nil {
  357. log.Println("GetMonthlyScores Scan", err)
  358. return result
  359. }
  360. result = append(result, md)
  361. }
  362. if err = rows.Err(); err != nil {
  363. log.Println("GetMonthlyScores rows.Err", err)
  364. }
  365. return result
  366. }