Files
redl-fivem-rp/server-code/data/resources/[rp]/rp_db/lib/db.lua
T
redlandClaude Fable 5 80c1d75d2f Part 1 final build: Los Santos RP written on camera by the agent
rp_core (sessions, scrypt auth, self-written MySQL-wire driver rp_db),
county-records NUI: residency-file auth, identity-record character
intake, survey-grid spawn, procedural night-city loading screen.
Secrets replaced with .example files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 19:27:48 +00:00

88 lines
2.8 KiB
Lua

-- ---------------------------------------------------------------------------
-- DB - Lua front end for rp_db.
--
-- Loaded *into* the consuming resource with:
-- server_scripts { '@rp_db/lib/db.lua', 'server/whatever.lua' }
--
-- so that Citizen.Await suspends the caller's own coroutine instead of trying
-- to yield across a cross-resource export call.
--
-- Every function comes in two flavours:
-- DB.Query(sql, params) -> rows, err (blocking, inside a thread)
-- DB.QueryAsync(sql, params, cb) -> cb(rows, err) (callback, anywhere)
--
-- Values are always passed as `?` parameters. Never build SQL by concatenation.
-- Use DB.NULL where you need a literal SQL NULL (Lua nil cannot survive a table).
-- ---------------------------------------------------------------------------
DB = {}
DB.NULL = 'RP_NULL'
local backend = exports.rp_db
local function async(method)
return function(sql, params, cb)
backend[method](backend, sql, params or {}, cb)
end
end
local function blocking(method)
return function(sql, params)
local p = promise.new()
backend[method](backend, sql, params or {}, function(result, err)
-- wrapped: a promise resolved with nil would never wake the coroutine
p:resolve({ result = result, err = err })
end)
local r = Citizen.Await(p)
if r.err then return nil, r.err end
return r.result
end
end
--- Returns all rows as an array of tables (empty table when nothing matched).
DB.Query = blocking('query')
--- Returns the first row as a table, or nil.
DB.Single = blocking('single')
--- Returns the first column of the first row, or nil.
DB.Scalar = blocking('scalar')
--- Returns the AUTO_INCREMENT id produced by an INSERT.
DB.Insert = blocking('insert')
--- Returns the number of rows changed by an UPDATE/DELETE.
DB.Update = blocking('update')
DB.QueryAsync = async('query')
DB.SingleAsync = async('single')
DB.ScalarAsync = async('scalar')
DB.InsertAsync = async('insert')
DB.UpdateAsync = async('update')
--- All statements commit together or none of them do.
--- statements: { { query = 'UPDATE ..', values = { 1, 2 } }, ... }
function DB.Transaction(statements)
local p = promise.new()
backend:transaction(statements, function(ok, err)
p:resolve({ ok = ok, err = err })
end)
local r = Citizen.Await(p)
if not r.ok then return false, r.err end
return true
end
function DB.TransactionAsync(statements, cb)
backend:transaction(statements, cb)
end
--- Blocks until the connection pool has at least one live connection.
--- Returns false if the database never came up within `timeout` ms.
function DB.WaitReady(timeout)
local waited, step = 0, 100
timeout = timeout or 30000
while not backend:isReady() do
if waited >= timeout then return false end
Citizen.Wait(step)
waited = waited + step
end
return true
end