| 
									
										
										
										
											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" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-22 23:15:17 +08:00
										 |  |  | // 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{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 	err := app.DB().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.DB().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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-21 21:31:20 +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) { | 
					
						
							| 
									
										
										
										
											2023-03-21 21:31:20 +08:00
										 |  |  | 	indexes := []struct { | 
					
						
							|  |  |  | 		Name string | 
					
						
							|  |  |  | 		Sql  string | 
					
						
							|  |  |  | 	}{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | 	err := app.DB().Select("name", "sql"). | 
					
						
							| 
									
										
										
										
											2023-03-21 21:31:20 +08:00
										 |  |  | 		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.DB().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 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-11-13 06:38:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-08 21:23:58 +08:00
										 |  |  | // HasTable checks if a table (or view) with the provided name exists (case insensitive).
 | 
					
						
							|  |  |  | // in the current app.DB() instance.
 | 
					
						
							|  |  |  | func (app *BaseApp) HasTable(tableName string) bool { | 
					
						
							|  |  |  | 	return app.hasTable(app.DB(), tableName) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AuxHasTable checks if a table (or view) with the provided name exists (case insensitive)
 | 
					
						
							|  |  |  | // in the current app.AuxDB() instance.
 | 
					
						
							|  |  |  | func (app *BaseApp) AuxHasTable(tableName string) bool { | 
					
						
							|  |  |  | 	return app.hasTable(app.AuxDB(), tableName) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (app *BaseApp) hasTable(db dbx.Builder, tableName string) bool { | 
					
						
							|  |  |  | 	var exists bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 00:23:19 +08:00
										 |  |  | // Vacuum executes VACUUM on the current app.DB() instance
 | 
					
						
							|  |  |  | // in order to reclaim unused data db disk space.
 | 
					
						
							|  |  |  | func (app *BaseApp) Vacuum() error { | 
					
						
							|  |  |  | 	return app.vacuum(app.DB()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AuxVacuum executes VACUUM on the current app.AuxDB() instance
 | 
					
						
							|  |  |  | // in order to reclaim unused auxiliary db disk space.
 | 
					
						
							|  |  |  | func (app *BaseApp) AuxVacuum() error { | 
					
						
							|  |  |  | 	return app.vacuum(app.AuxDB()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (app *BaseApp) vacuum(db dbx.Builder) error { | 
					
						
							|  |  |  | 	_, err := db.NewQuery("VACUUM").Execute() | 
					
						
							| 
									
										
										
										
											2022-11-13 06:38:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } |