-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathdb_table.go
More file actions
137 lines (110 loc) · 3.8 KB
/
db_table.go
File metadata and controls
137 lines (110 loc) · 3.8 KB
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
package core
import (
"database/sql"
"fmt"
"github.com/pocketbase/dbx"
)
// TableColumns returns all column names of a single table by its name.
func (app *BaseApp) TableColumns(tableName string) ([]string, error) {
columns := []string{}
err := app.ConcurrentDB().NewQuery("SELECT name FROM PRAGMA_TABLE_INFO({:tableName})").
Bind(dbx.Params{"tableName": tableName}).
Column(&columns)
return columns, err
}
type TableInfoRow struct {
// the `db:"pk"` tag has special semantic so we cannot rename
// the original field without specifying a custom mapper
PK int
Index int `db:"cid"`
Name string `db:"name"`
Type string `db:"type"`
NotNull bool `db:"notnull"`
DefaultValue sql.NullString `db:"dflt_value"`
}
// TableInfo returns the "table_info" pragma result for the specified table.
func (app *BaseApp) TableInfo(tableName string) ([]*TableInfoRow, error) {
info := []*TableInfoRow{}
err := app.ConcurrentDB().NewQuery("SELECT * FROM PRAGMA_TABLE_INFO({:tableName})").
Bind(dbx.Params{"tableName": tableName}).
All(&info)
if err != nil {
return nil, err
}
// mattn/go-sqlite3 doesn't throw an error on invalid or missing table
// so we additionally have to check whether the loaded info result is nonempty
if len(info) == 0 {
return nil, fmt.Errorf("empty table info probably due to invalid or missing table %s", tableName)
}
return info, nil
}
// TableIndexes returns a name grouped map with all non empty index of the specified table.
//
// Note: This method doesn't return an error on nonexisting table.
func (app *BaseApp) TableIndexes(tableName string) (map[string]string, error) {
indexes := []struct {
Name string
Sql string
}{}
err := app.ConcurrentDB().Select("name", "sql").
From("sqlite_master").
AndWhere(dbx.NewExp("sql is not null")).
AndWhere(dbx.HashExp{
"type": "index",
"tbl_name": tableName,
}).
All(&indexes)
if err != nil {
return nil, err
}
result := make(map[string]string, len(indexes))
for _, idx := range indexes {
result[idx.Name] = idx.Sql
}
return result, nil
}
// DeleteTable drops the specified table.
//
// This method is a no-op if a table with the provided name doesn't exist.
//
// NB! Be aware that this method is vulnerable to SQL injection and the
// "dangerousTableName" argument must come only from trusted input!
func (app *BaseApp) DeleteTable(dangerousTableName string) error {
_, err := app.NonconcurrentDB().NewQuery(fmt.Sprintf(
"DROP TABLE IF EXISTS {{%s}}",
dangerousTableName,
)).Execute()
return err
}
// HasTable checks if a table (or view) with the provided name exists (case insensitive).
// in the data.db.
func (app *BaseApp) HasTable(tableName string) bool {
return app.hasTable(app.ConcurrentDB(), tableName)
}
// AuxHasTable checks if a table (or view) with the provided name exists (case insensitive)
// in the auixiliary.db.
func (app *BaseApp) AuxHasTable(tableName string) bool {
return app.hasTable(app.AuxConcurrentDB(), tableName)
}
func (app *BaseApp) hasTable(db dbx.Builder, tableName string) bool {
var exists int
err := db.Select("(1)").
From("sqlite_schema").
AndWhere(dbx.HashExp{"type": []any{"table", "view"}}).
AndWhere(dbx.NewExp("LOWER([[name]])=LOWER({:tableName})", dbx.Params{"tableName": tableName})).
Limit(1).
Row(&exists)
return err == nil && exists > 0
}
// Vacuum executes VACUUM on the data.db in order to reclaim unused data db disk space.
func (app *BaseApp) Vacuum() error {
return app.vacuum(app.NonconcurrentDB())
}
// AuxVacuum executes VACUUM on the auxiliary.db in order to reclaim unused auxiliary db disk space.
func (app *BaseApp) AuxVacuum() error {
return app.vacuum(app.AuxNonconcurrentDB())
}
func (app *BaseApp) vacuum(db dbx.Builder) error {
_, err := db.NewQuery("VACUUM").Execute()
return err
}