2026-08-01 01:09:39 -04:00
--[[
DEPRECATED: the SQL helper module is deprecated in its entirety and will be
REMOVED on 2026-08-22. Migrate to oxmysql directly — MySQL.query.await(query,
params) with ? placeholders — which is safer and more flexible than these
wrappers ever were.
]]
2025-02-05 17:42:50 -06:00
SQL = {}
Require ( "lib/MySQL.lua" , "oxmysql" )
2026-08-01 01:09:39 -04:00
local warnedResources = {}
local function warnDeprecated ()
local caller = GetInvokingResource () or GetCurrentResourceName ()
if warnedResources [ caller ] then return end
warnedResources [ caller ] = true
print (( "^3[community_bridge] SQL helpers are deprecated and will be REMOVED on 2026-08-22. '%s' should migrate to oxmysql directly (MySQL.query.await with ? placeholders).^0" ): format ( caller ))
end
-- Identifiers (table/column names) cannot be parameterized, so they are
-- validated against a strict pattern and backtick-quoted instead. Values are
-- never concatenated into queries - they always go through ? placeholders.
local function quoteIdentifier ( name )
assert ( type ( name ) == "string" and name : match ( "^[%a_][%w_]*$" ),
( "SQL: unsafe identifier '%s' - table/column names may only contain letters, digits and underscores" ): format ( tostring ( name )))
return "`" .. name .. "`"
end
--- Builds a parameterized WHERE clause from a table of column = value pairs.
-- Raw WHERE strings are intentionally rejected: they made SQL injection
-- unavoidable whenever a player-influenced value reached these helpers.
local function buildWhere ( where )
assert ( type ( where ) == "table" and next ( where ) ~= nil ,
"SQL: where must be a non-empty table of column = value pairs (raw WHERE strings are no longer accepted)" )
local conditions = {}
local params = {}
for column , value in pairs ( where ) do
table.insert ( conditions , quoteIdentifier ( column ) .. " = ?" )
table.insert ( params , value )
end
return table.concat ( conditions , " AND " ), params
end
2025-06-22 17:40:41 -05:00
--- Creates a table in the database if it does not exist.
-- @param tableName The name of the table to create. Example: {{ name = "identifier", type = "VARCHAR(50)", primary = true }}
-- @param columns A table containing column definitions, where each column is a table with 'name' and 'type'.
---@return nil
2025-05-05 11:22:38 -03:00
2026-08-01 01:09:39 -04:00
---@deprecated Removed on 2026-08-22 — use oxmysql directly.
2025-02-05 17:42:50 -06:00
function SQL . Create ( tableName , columns )
2026-08-01 01:09:39 -04:00
warnDeprecated ()
2025-02-05 17:42:50 -06:00
assert ( MySQL , "Tried using module SQL without MySQL being loaded" )
local columnsList = {}
for i , column in pairs ( columns ) do
2026-08-01 01:09:39 -04:00
assert ( type ( column.type ) == "string" , "SQL: column type must be a string" )
table.insert ( columnsList , string.format ( "%s %s" , quoteIdentifier ( column.name ), column.type ))
2025-02-05 17:42:50 -06:00
end
2025-02-05 19:52:48 -05:00
2025-02-05 17:42:50 -06:00
local query = string.format ( "CREATE TABLE IF NOT EXISTS %s (%s);" ,
2026-08-01 01:09:39 -04:00
quoteIdentifier ( tableName ),
2025-02-05 17:42:50 -06:00
table.concat ( columnsList , ", " )
)
2025-02-05 19:52:48 -05:00
2025-02-05 17:42:50 -06:00
MySQL.query . await ( query )
end
-- insert if not exist otherwise update
2026-08-01 01:09:39 -04:00
---@deprecated Removed on 2026-08-22 — use oxmysql directly.
2025-02-05 17:42:50 -06:00
function SQL . InsertOrUpdate ( tableName , data )
2026-08-01 01:09:39 -04:00
warnDeprecated ()
2025-02-05 17:42:50 -06:00
assert ( MySQL , "Tried using module SQL without MySQL being loaded" )
local columns = {}
2026-08-01 01:09:39 -04:00
local placeholders = {}
2025-02-05 17:42:50 -06:00
local updates = {}
2026-08-01 01:09:39 -04:00
local params = {}
2025-02-05 17:42:50 -06:00
for column , value in pairs ( data ) do
2026-08-01 01:09:39 -04:00
local quoted = quoteIdentifier ( column )
table.insert ( columns , quoted )
table.insert ( placeholders , "?" )
table.insert ( updates , quoted .. " = VALUES(" .. quoted .. ")" ) -- Use VALUES() for update
table.insert ( params , value )
2025-02-05 17:42:50 -06:00
end
local query = string.format (
"INSERT INTO %s (%s) VALUES (%s) ON DUPLICATE KEY UPDATE %s;" ,
2026-08-01 01:09:39 -04:00
quoteIdentifier ( tableName ),
2025-02-05 17:42:50 -06:00
table.concat ( columns , ", " ),
2026-08-01 01:09:39 -04:00
table.concat ( placeholders , ", " ),
2025-02-05 17:42:50 -06:00
table.concat ( updates , ", " )
)
2026-08-01 01:09:39 -04:00
MySQL.query . await ( query , params )
2025-02-05 17:42:50 -06:00
end
2026-08-01 01:09:39 -04:00
--- Selects rows matching the given filters.
-- @param tableName The table to query.
-- @param where Table of column = value pairs, joined with AND. Example: { citizenid = cid }
---@deprecated Removed on 2026-08-22 — use oxmysql directly.
2025-02-05 17:42:50 -06:00
function SQL . Get ( tableName , where )
2026-08-01 01:09:39 -04:00
warnDeprecated ()
2025-02-05 17:42:50 -06:00
assert ( MySQL , "Tried using module SQL without MySQL being loaded" )
2026-08-01 01:09:39 -04:00
local conditions , params = buildWhere ( where )
local query = string.format ( "SELECT * FROM %s WHERE %s;" , quoteIdentifier ( tableName ), conditions )
local result = MySQL.query . await ( query , params )
2025-02-05 17:42:50 -06:00
return result
end
2026-08-01 01:09:39 -04:00
---@deprecated Removed on 2026-08-22 — use oxmysql directly.
2025-02-05 17:42:50 -06:00
function SQL . GetAll ( tableName )
2026-08-01 01:09:39 -04:00
warnDeprecated ()
2025-02-05 17:42:50 -06:00
assert ( MySQL , "Tried using module SQL without MySQL being loaded" )
2026-08-01 01:09:39 -04:00
local query = string.format ( "SELECT * FROM %s;" , quoteIdentifier ( tableName ))
2025-02-05 17:42:50 -06:00
local result = MySQL.query . await ( query )
return result
end
2026-08-01 01:09:39 -04:00
--- Deletes rows matching the given filters.
-- @param tableName The table to delete from.
-- @param where Table of column = value pairs, joined with AND. Example: { citizenid = cid }
---@deprecated Removed on 2026-08-22 — use oxmysql directly.
2025-02-05 17:42:50 -06:00
function SQL . Delete ( tableName , where )
2026-08-01 01:09:39 -04:00
warnDeprecated ()
2025-02-05 17:42:50 -06:00
assert ( MySQL , "Tried using module SQL without MySQL being loaded" )
2026-08-01 01:09:39 -04:00
local conditions , params = buildWhere ( where )
local query = string.format ( "DELETE FROM %s WHERE %s;" , quoteIdentifier ( tableName ), conditions )
MySQL.query . await ( query , params )
2025-02-05 17:42:50 -06:00
end
2025-02-12 08:58:36 -06:00
exports ( 'SQL' , function ()
2026-08-01 01:09:39 -04:00
warnDeprecated ()
2025-02-12 08:58:36 -06:00
return SQL
end )
2025-02-05 17:42:50 -06:00
2025-05-05 11:22:38 -03:00
return SQL