-- --------------------------------------------------------------------------- -- 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