forked from lazyhacker/go-links
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdb.go
170 lines (138 loc) · 3.54 KB
/
db.go
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package main
import (
"context"
"database/sql"
"fmt"
"log"
_ "modernc.org/sqlite"
)
var db *sql.DB
// openDatabase returns the database handle. It checks that the database
// schema exists and create it if it doesn't.
func openDatabase(dbPath string) (*sql.DB, error) {
var err error
if db, err = sql.Open("sqlite", dbPath); err != nil {
return nil, err
}
if _, err = db.Exec("PRAGMA journal_mode=WAL"); err != nil {
log.Printf("Error setting WAL mode. %v", err)
return nil, err
}
// Check to see if the schema already exists and create it if it isn't.
if _, err = db.ExecContext(
context.Background(),
`CREATE TABLE IF NOT EXISTS links (
keyword TEXT PRIMARY KEY,
URL TEXT NOT NULL,
owner TEXT NOT NULL,
created DATETIME DEFAULT CURRENT_TIMESTAMP
)`,
); err != nil {
return nil, err
}
if _, err = db.ExecContext(
context.Background(),
`CREATE TABLE IF NOT EXISTS count (
keyword TEXT PRIMARY KEY,
count INTEGER DEFAULT 0
)`,
); err != nil {
return nil, err
}
// Creating schema for sessions.
if _, err = db.ExecContext(
context.Background(),
`CREATE TABLE IF NOT EXISTS sessions (
token TEXT PRIMARY KEY,
data BLOB NOT NULL,
expiry REAL NOT NULL)`,
); err != nil {
return nil, err
}
if _, err = db.ExecContext(
context.Background(),
`CREATE INDEX IF NOT EXISTS sessions_expiry_idx ON sessions(expiry)`,
); err != nil {
return nil, err
}
return db, nil
}
// getUrlByKeyword takes in a keyword and returns the redirect URL.
func getUrlByKeyword(kw string) (string, error) {
row := db.QueryRowContext(
context.Background(),
"SELECT URL from links where keyword=?",
kw,
)
var path string
if err := row.Scan(&path); err != nil {
return "", err
}
return path, nil
}
// insertOrUpdateLink uses SQLite's upsert to insert a record if it is a
// new keyword or update an existing record if it already exists.
func insertOrUpdateLink(kw, url, owner string) error {
if len(kw) == 0 || len(url) == 0 || len(owner) == 0 {
return fmt.Errorf("Keyword, URL and Owners can not be blank!")
}
sql := `INSERT INTO links(keyword, URL, owner) VALUES (?, ?, ?)
on CONFLICT (keyword) DO
UPDATE
SET
URL = excluded.URL,
owner = excluded.owner
WHERE owner = ?
`
if _, err := db.Exec(sql, kw, url, owner, owner); err != nil {
return fmt.Errorf("error while inserting. %v", err)
}
return nil
}
func deleteLink(kw, user string) error {
if _, err := db.Exec("DELETE FROM links WHERE keyword = ? AND owner = ?", kw, user); err != nil {
return fmt.Errorf("unable to delete row. %v", err)
}
return nil
}
func allLinks() ([]link, error) {
var links []link
rows, err := db.QueryContext(
context.Background(),
"SELECT links.keyword, links.URL, links.owner, COALESCE(count.count, 0) as views FROM links LEFT JOIN count on links.keyword = count.keyword ORDER BY created desc")
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var l link
if err := rows.Scan(&l.Keyword, &l.Url, &l.Owner, &l.Views); err != nil {
return nil, err
}
links = append(links, l)
}
return links, nil
}
func updateCounts(i map[string]int) error {
var tx *sql.Tx
var err error
tx, err = db.BeginTx(context.Background(), nil)
if err != nil {
return err
}
sql := `INSERT INTO count(keyword, count) VALUES (?,?)
on CONFLICT (keyword) DO
UPDATE
SET
count = count + ?
WHERE
keyword = ?
`
for k, v := range i {
if _, err := tx.Exec(sql, k, v, v, k); err != nil {
tx.Rollback()
return fmt.Errorf("error updating count for %v. %v", k, err)
}
}
return tx.Commit()
}