v0.0.125
This commit is contained in:
parent
fe278f7772
commit
9daf71e2ed
195
sq/hasher.go
Normal file
195
sq/hasher.go
Normal file
@ -0,0 +1,195 @@
|
||||
package sq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func HashSqliteSchema(ctx context.Context, schemaStr string) (string, error) {
|
||||
dbdir := os.TempDir()
|
||||
dbfile1 := filepath.Join(dbdir, langext.MustHexUUID()+".sqlite3")
|
||||
|
||||
err := os.MkdirAll(dbdir, os.ModePerm)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", dbfile1, "DELETE", 1000, "true", 1000)
|
||||
|
||||
xdb, err := sqlx.Open("sqlite3", url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
db := NewDB(xdb)
|
||||
|
||||
_, err = db.Exec(ctx, schemaStr, PP{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ss, err := CreateSqliteDatabaseSchemaString(ctx, db)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cs := sha256.Sum256([]byte(ss))
|
||||
|
||||
return hex.EncodeToString(cs[:]), nil
|
||||
}
|
||||
|
||||
func CreateSqliteDatabaseSchemaString(ctx context.Context, db DB) (string, error) {
|
||||
|
||||
type colInfo struct {
|
||||
Name string `db:"name"`
|
||||
Type string `db:"type"`
|
||||
NotNull string `db:"notnull"`
|
||||
Default *string `db:"dflt_value"`
|
||||
PrimaryKey *string `db:"pk"`
|
||||
}
|
||||
|
||||
type idxInfo struct {
|
||||
Name string `json:"name" db:"name"`
|
||||
Unique int `json:"unique" db:"unique"`
|
||||
Origin string `json:"origin" db:"origin"`
|
||||
Patial int `json:"partial" db:"partial"`
|
||||
}
|
||||
|
||||
type fkyInfo struct {
|
||||
TableDest string `json:"table_dest" db:"table"`
|
||||
From string `json:"from" db:"from"`
|
||||
To string `json:"to" db:"to"`
|
||||
OnUpdate string `json:"on_update" db:"on_update"`
|
||||
OnDelete string `json:"on_delete" db:"on_delete"`
|
||||
Match string `json:"match" db:"match"`
|
||||
}
|
||||
|
||||
type tabInfo struct {
|
||||
Name string `json:"name" db:"name"`
|
||||
Type string `json:"type" db:"type"`
|
||||
NumCol int `json:"ncol" db:"ncol"`
|
||||
Strict int `json:"strict" db:"strict"`
|
||||
|
||||
ColumnInfo []colInfo `json:"-"`
|
||||
IndexInfo []idxInfo `json:"-"`
|
||||
FKeyInfo []fkyInfo `json:"-"`
|
||||
}
|
||||
|
||||
rowsTableList, err := db.Query(ctx, "PRAGMA table_list;", PP{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tableList, err := ScanAll[tabInfo](rowsTableList, SModeFast, Unsafe, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
langext.SortBy(tableList, func(v tabInfo) string { return v.Name })
|
||||
|
||||
result := make([]tabInfo, 0)
|
||||
|
||||
for i, tab := range tableList {
|
||||
|
||||
if strings.HasPrefix(tab.Name, "sqlite_") {
|
||||
continue
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
rowsColumnList, err := db.Query(ctx, fmt.Sprintf("PRAGMA table_info(\"%s\");", tab.Name), PP{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
columnList, err := ScanAll[colInfo](rowsColumnList, SModeFast, Unsafe, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
langext.SortBy(columnList, func(v colInfo) string { return v.Name })
|
||||
|
||||
tableList[i].ColumnInfo = columnList
|
||||
}
|
||||
|
||||
{
|
||||
rowsIdxList, err := db.Query(ctx, fmt.Sprintf("PRAGMA index_list(\"%s\");", tab.Name), PP{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
idxList, err := ScanAll[idxInfo](rowsIdxList, SModeFast, Unsafe, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
langext.SortBy(idxList, func(v idxInfo) string { return v.Name })
|
||||
|
||||
tableList[i].IndexInfo = idxList
|
||||
}
|
||||
|
||||
{
|
||||
rowsIdxList, err := db.Query(ctx, fmt.Sprintf("PRAGMA foreign_key_list(\"%s\");", tab.Name), PP{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fkyList, err := ScanAll[fkyInfo](rowsIdxList, SModeFast, Unsafe, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
langext.SortBy(fkyList, func(v fkyInfo) string { return v.From })
|
||||
|
||||
tableList[i].FKeyInfo = fkyList
|
||||
}
|
||||
|
||||
result = append(result, tableList[i])
|
||||
}
|
||||
|
||||
strBuilderResult := ""
|
||||
for _, vTab := range result {
|
||||
jbinTable, err := json.Marshal(vTab)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
strBuilderResult += fmt.Sprintf("#TABLE: %s\n{\n", string(jbinTable))
|
||||
|
||||
for _, vCol := range vTab.ColumnInfo {
|
||||
jbinColumn, err := json.Marshal(vCol)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
strBuilderResult += fmt.Sprintf(" COLUMN: %s\n", string(jbinColumn))
|
||||
}
|
||||
|
||||
for _, vIdx := range vTab.IndexInfo {
|
||||
jbinIndex, err := json.Marshal(vIdx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
strBuilderResult += fmt.Sprintf(" INDEX: %s\n", string(jbinIndex))
|
||||
}
|
||||
|
||||
for _, vFky := range vTab.FKeyInfo {
|
||||
jbinFKey, err := json.Marshal(vFky)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
strBuilderResult += fmt.Sprintf(" FKEY: %s\n", string(jbinFKey))
|
||||
}
|
||||
|
||||
strBuilderResult += "}\n\n"
|
||||
}
|
||||
|
||||
return strBuilderResult, nil
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
package sq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StructScanMode string
|
||||
@ -16,10 +20,79 @@ const (
|
||||
type StructScanSafety string
|
||||
|
||||
const (
|
||||
Safe StructScanSafety = "SAFE"
|
||||
Unsafe StructScanSafety = "UNSAFE"
|
||||
Safe StructScanSafety = "SAFE" // return error for missing fields
|
||||
Unsafe StructScanSafety = "UNSAFE" // ignore missing fields
|
||||
)
|
||||
|
||||
func InsertSingle[TData any](ctx context.Context, q Queryable, tableName string, v TData) (sql.Result, error) {
|
||||
|
||||
rval := reflect.ValueOf(v)
|
||||
rtyp := rval.Type()
|
||||
|
||||
columns := make([]string, 0)
|
||||
params := make([]string, 0)
|
||||
pp := PP{}
|
||||
|
||||
for i := 0; i < rtyp.NumField(); i++ {
|
||||
|
||||
rsfield := rtyp.Field(i)
|
||||
rvfield := rval.Field(i)
|
||||
|
||||
if !rsfield.IsExported() {
|
||||
continue
|
||||
}
|
||||
|
||||
columnName := rsfield.Tag.Get("db")
|
||||
if columnName == "" || columnName == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
paramkey := fmt.Sprintf("_%s", columnName)
|
||||
|
||||
columns = append(columns, "\""+columnName+"\"")
|
||||
params = append(params, ":"+paramkey)
|
||||
pp[paramkey] = rvfield.Interface()
|
||||
|
||||
}
|
||||
|
||||
sqlstr := fmt.Sprintf("INSERT"+" INTO \"%s\" (%s) VALUES (%s)", tableName, strings.Join(columns, ", "), strings.Join(params, ", "))
|
||||
|
||||
sqlr, err := q.Exec(ctx, sqlstr, pp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sqlr, nil
|
||||
}
|
||||
|
||||
func QuerySingle[TData any](ctx context.Context, q Queryable, sql string, pp PP, mode StructScanMode, sec StructScanSafety) (TData, error) {
|
||||
rows, err := q.Query(ctx, sql, pp)
|
||||
if err != nil {
|
||||
return *new(TData), err
|
||||
}
|
||||
|
||||
data, err := ScanSingle[TData](rows, mode, sec, true)
|
||||
if err != nil {
|
||||
return *new(TData), err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func QueryAll[TData any](ctx context.Context, q Queryable, sql string, pp PP, mode StructScanMode, sec StructScanSafety) ([]TData, error) {
|
||||
rows, err := q.Query(ctx, sql, pp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ScanAll[TData](rows, mode, sec, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func ScanSingle[TData any](rows *sqlx.Rows, mode StructScanMode, sec StructScanSafety, close bool) (TData, error) {
|
||||
if rows.Next() {
|
||||
var strscan *StructScanner
|
||||
|
Loading…
Reference in New Issue
Block a user