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
|
package sq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StructScanMode string
|
type StructScanMode string
|
||||||
@ -16,10 +20,79 @@ const (
|
|||||||
type StructScanSafety string
|
type StructScanSafety string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Safe StructScanSafety = "SAFE"
|
Safe StructScanSafety = "SAFE" // return error for missing fields
|
||||||
Unsafe StructScanSafety = "UNSAFE"
|
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) {
|
func ScanSingle[TData any](rows *sqlx.Rows, mode StructScanMode, sec StructScanSafety, close bool) (TData, error) {
|
||||||
if rows.Next() {
|
if rows.Next() {
|
||||||
var strscan *StructScanner
|
var strscan *StructScanner
|
||||||
|
Loading…
Reference in New Issue
Block a user