2023-12-14 20:36:07 +00:00
|
|
|
// Package fileio provides SQL functions to read, write and list files.
|
2023-12-12 01:00:13 +00:00
|
|
|
//
|
|
|
|
|
// https://sqlite.org/src/doc/tip/ext/misc/fileio.c
|
2023-12-11 14:48:15 +00:00
|
|
|
package fileio
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/fs"
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
"github.com/ncruces/go-sqlite3"
|
|
|
|
|
)
|
|
|
|
|
|
2023-12-12 01:00:13 +00:00
|
|
|
// Register registers SQL functions readfile, writefile, lsmode,
|
2024-01-03 00:54:30 +00:00
|
|
|
// and the table-valued function fsdir.
|
2024-07-08 12:06:57 +01:00
|
|
|
func Register(db *sqlite3.Conn) error {
|
|
|
|
|
return RegisterFS(db, nil)
|
2023-12-12 01:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-08-13 03:31:12 +01:00
|
|
|
// RegisterFS registers SQL functions readfile, lsmode,
|
2024-01-03 00:54:30 +00:00
|
|
|
// and the table-valued function fsdir;
|
2023-12-19 00:13:51 +00:00
|
|
|
// fsys will be used to read files and list directories.
|
2024-07-08 12:06:57 +01:00
|
|
|
func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
|
|
|
|
|
var err error
|
2023-12-12 14:06:54 +00:00
|
|
|
if fsys == nil {
|
2024-07-08 12:06:57 +01:00
|
|
|
err = db.CreateFunction("writefile", -1, sqlite3.DIRECTONLY, writefile)
|
2023-12-12 01:00:13 +00:00
|
|
|
}
|
2024-07-08 12:06:57 +01:00
|
|
|
return errors.Join(err,
|
|
|
|
|
db.CreateFunction("readfile", 1, sqlite3.DIRECTONLY, readfile(fsys)),
|
|
|
|
|
db.CreateFunction("lsmode", 1, sqlite3.DETERMINISTIC, lsmode),
|
|
|
|
|
sqlite3.CreateModule(db, "fsdir", nil, func(db *sqlite3.Conn, _, _, _ string, _ ...string) (fsdir, error) {
|
2025-12-19 16:51:50 +00:00
|
|
|
err := db.DeclareVTab(`CREATE TABLE x(name TEXT,mode INT,mtime TIMESTAMP,data BLOB,path HIDDEN,dir HIDDEN)`)
|
2024-10-07 13:22:31 +01:00
|
|
|
if err == nil {
|
|
|
|
|
err = db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
|
|
|
|
|
}
|
2024-07-08 12:06:57 +01:00
|
|
|
return fsdir{fsys}, err
|
|
|
|
|
}))
|
2023-12-11 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func lsmode(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
|
|
|
|
ctx.ResultText(fs.FileMode(arg[0].Int()).String())
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 14:07:43 +00:00
|
|
|
func readfile(fsys fs.FS) sqlite3.ScalarFunction {
|
2023-12-12 01:00:13 +00:00
|
|
|
return func(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
|
|
|
|
var err error
|
|
|
|
|
var data []byte
|
|
|
|
|
|
2023-12-12 14:06:54 +00:00
|
|
|
if fsys != nil {
|
|
|
|
|
data, err = fs.ReadFile(fsys, arg[0].Text())
|
2023-12-12 01:00:13 +00:00
|
|
|
} else {
|
|
|
|
|
data, err = os.ReadFile(arg[0].Text())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch {
|
|
|
|
|
case err == nil:
|
|
|
|
|
ctx.ResultBlob(data)
|
|
|
|
|
case !errors.Is(err, fs.ErrNotExist):
|
2024-07-26 13:29:24 +01:00
|
|
|
ctx.ResultError(fmt.Errorf("readfile: %w", err)) // notest
|
2023-12-12 01:00:13 +00:00
|
|
|
}
|
2023-12-11 14:48:15 +00:00
|
|
|
}
|
|
|
|
|
}
|