pocketbase/core/db_table.go

138 lines
3.8 KiB
Go
Raw Normal View History

2024-09-30 00:23:19 +08:00
package core
2022-07-07 05:19:05 +08:00
import (
2024-09-30 00:23:19 +08:00
"database/sql"
2023-02-19 01:33:42 +08:00
"fmt"
2022-07-07 05:19:05 +08:00
"github.com/pocketbase/dbx"
)
// TableColumns returns all column names of a single table by its name.
2024-09-30 00:23:19 +08:00
func (app *BaseApp) TableColumns(tableName string) ([]string, error) {
2022-07-07 05:19:05 +08:00
columns := []string{}
err := app.ConcurrentDB().NewQuery("SELECT name FROM PRAGMA_TABLE_INFO({:tableName})").
2022-07-07 05:19:05 +08:00
Bind(dbx.Params{"tableName": tableName}).
Column(&columns)
return columns, err
}
2024-09-30 00:23:19 +08:00
type TableInfoRow struct {
// the `db:"pk"` tag has special semantic so we cannot rename
// the original field without specifying a custom mapper
PK int
2023-02-19 01:33:42 +08:00
2024-09-30 00:23:19 +08:00
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})").
2023-02-19 01:33:42 +08:00
Bind(dbx.Params{"tableName": tableName}).
All(&info)
2023-02-19 02:31:41 +08:00
if err != nil {
return nil, err
}
2023-02-19 01:33:42 +08:00
2023-02-19 02:31:41 +08:00
// 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
2023-02-19 01:33:42 +08:00
}
// 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.
2024-09-30 00:23:19 +08:00
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
}
2022-07-07 05:19:05 +08:00
// DeleteTable drops the specified table.
2023-02-19 01:33:42 +08:00
//
// This method is a no-op if a table with the provided name doesn't exist.
//
2024-09-30 00:23:19 +08:00
// NB! Be aware that this method is vulnerable to SQL injection and the
2023-02-19 01:33:42 +08:00
// "tableName" argument must come only from trusted input!
2024-09-30 00:23:19 +08:00
func (app *BaseApp) DeleteTable(tableName string) error {
_, err := app.NonconcurrentDB().NewQuery(fmt.Sprintf(
2023-02-19 01:33:42 +08:00
"DROP TABLE IF EXISTS {{%s}}",
tableName,
)).Execute()
2022-07-07 05:19:05 +08:00
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.
2024-09-30 00:23:19 +08:00
func (app *BaseApp) Vacuum() error {
return app.vacuum(app.NonconcurrentDB())
2024-09-30 00:23:19 +08:00
}
// AuxVacuum executes VACUUM on the auxiliary.db in order to reclaim unused auxiliary db disk space.
2024-09-30 00:23:19 +08:00
func (app *BaseApp) AuxVacuum() error {
return app.vacuum(app.AuxNonconcurrentDB())
2024-09-30 00:23:19 +08:00
}
func (app *BaseApp) vacuum(db dbx.Builder) error {
_, err := db.NewQuery("VACUUM").Execute()
return err
}