From 7ae8e12dc89e2cf96e84870da4f0f386c9dbb5c6 Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 20 Aug 2025 10:11:14 +0300 Subject: [PATCH] add new method --- internal/server/sv1/db_sqlite.go | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/internal/server/sv1/db_sqlite.go b/internal/server/sv1/db_sqlite.go index 4305a31..fa7cd68 100644 --- a/internal/server/sv1/db_sqlite.go +++ b/internal/server/sv1/db_sqlite.go @@ -82,6 +82,7 @@ func loadDBMod(llog *slog.Logger, sid string) func(*lua.LState) int { L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{ "exec": dbExec, "query": dbQuery, + "query_row": dbQueryRow, "close": dbClose, })) @@ -213,6 +214,102 @@ func dbExec(L *lua.LState) int { return 2 } +func dbQueryRow(L *lua.LState) int { + ud := L.CheckUserData(1) + conn, ok := ud.Value.(*DBConnection) + if !ok { + L.Push(lua.LNil) + L.Push(lua.LString("invalid database connection")) + return 2 + } + + query := L.CheckString(2) + + var args []any + if L.GetTop() >= 3 { + params := L.CheckTable(3) + params.ForEach(func(k lua.LValue, v lua.LValue) { + args = append(args, ConvertLuaTypesToGolang(v)) + }) + } + + if conn.log { + conn.logger.Info("DB QueryRow", + slog.String("query", query), + slog.Any("params", args)) + } + + mtx := getDBMutex(conn.dbPath) + mtx.RLock() + defer mtx.RUnlock() + + db, err := sql.Open("sqlite", conn.dbPath+"?_busy_timeout=5000&_journal_mode=WAL&_sync=NORMAL&_cache_size=-10000") + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(err.Error())) + return 2 + } + defer db.Close() + + row := db.QueryRow(query, args...) + + columns := []string{} + stmt, err := db.Prepare(query) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("prepare failed: %v", err))) + return 2 + } + defer stmt.Close() + rows, err := stmt.Query(args...) + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("query failed: %v", err))) + return 2 + } + defer rows.Close() + cols, err := rows.Columns() + if err != nil { + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("get columns failed: %v", err))) + return 2 + } + for _, c := range cols { + columns = append(columns, c) + } + + colCount := len(columns) + values := make([]any, colCount) + valuePtrs := make([]any, colCount) + for i := range columns { + valuePtrs[i] = &values[i] + } + + err = row.Scan(valuePtrs...) + if err != nil { + if err == sql.ErrNoRows { + L.Push(lua.LNil) + return 1 + } + L.Push(lua.LNil) + L.Push(lua.LString(fmt.Sprintf("scan failed: %v", err))) + return 2 + } + + rowTable := L.NewTable() + for i, col := range columns { + val := values[i] + if val == nil { + L.SetField(rowTable, col, lua.LNil) + } else { + L.SetField(rowTable, col, ConvertGolangTypesToLua(L, val)) + } + } + + L.Push(rowTable) + return 1 +} + func dbQuery(L *lua.LState) int { ud := L.CheckUserData(1) conn, ok := ud.Value.(*DBConnection)