Skip to main content

SQL

Persist information and state in a database using SQLite. The server automatically creates and manages a database.db file in the server directory.

To browse the information in the database, you can use SQLite Browser or any other SQLite client.

Parameter Binding​

All SQL methods support parameter binding to prevent SQL injection attacks. Use ? placeholders in your SQL statements and pass the values as additional arguments. Parameters are bound in order and support the following Lua types:

  • string → SQLite TEXT
  • number → SQLite INTEGER or REAL (automatically detected)
  • boolean → SQLite INTEGER (0 or 1)
  • nil → SQLite NULL

Methods​

SQL.Execute(statement: string, ...params): table​

Executes SQL statements that don't return data (INSERT, UPDATE, DELETE, CREATE, etc.). Returns a table with execution details.

Parameters:

  • statement - SQL statement with optional ? parameter placeholders
  • ...params - Values to bind to the ? placeholders in order

Returns a table with:

  • success (boolean) - true if successful, false if failed
  • error (string|nil) - Error message if failed, nil if successful
  • changes (number) - Number of rows affected by the operation
  • last_insert_id (number|nil) - Row ID of last inserted row (INSERT only)
-- Create a table
local result = SQL.Execute([[
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
steam_id TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
level INTEGER DEFAULT 1,
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
)
]])

if result.success then
print("Table created successfully")
else
print("Error:", result.error)
end

-- Insert a new player
local result = SQL.Execute(
"INSERT INTO players (steam_id, name, level) VALUES (?, ?, ?)",
"76561198000000000", "PlayerName", 5
)

if result.success then
print("Player inserted with ID:", result.last_insert_id)
print("Rows affected:", result.changes)
else
print("Insert failed:", result.error)
end

-- Update player level
local result = SQL.Execute(
"UPDATE players SET level = ?, last_seen = CURRENT_TIMESTAMP WHERE steam_id = ?",
10, "76561198000000000"
)

if result.success then
print("Updated", result.changes, "player(s)")
else
print("Update failed:", result.error)
end

SQL.Query(query: string, ...params): table​

Executes SELECT queries and returns the results as a table array.

Parameters:

  • query - SELECT SQL statement with optional ? parameter placeholders
  • ...params - Values to bind to the ? placeholders in order

Returns: Array table where each element is a row table with column names as keys. Returns empty table {} if no results or on error.

Row data types:

  • SQLite INTEGER → Lua number
  • SQLite REAL → Lua number
  • SQLite TEXT → Lua string
  • SQLite BLOB → Lua string (binary data)
  • SQLite NULL → Lua nil
-- Query all players
local players = SQL.Query("SELECT * FROM players ORDER BY level DESC")

for i, player in ipairs(players) do
print(string.format("Player %d: %s (Level %d, ID: %d)",
i, player.name, player.level, player.id))
end

-- Query specific player by Steam ID
local players = SQL.Query(
"SELECT name, level, last_seen FROM players WHERE steam_id = ?",
"76561198000000000"
)

if #players > 0 then
local player = players[1]
print("Found player:", player.name)
print("Level:", player.level)
print("Last seen:", player.last_seen)
else
print("Player not found")
end

-- Query with multiple conditions
local highLevelPlayers = SQL.Query(
"SELECT name, level FROM players WHERE level >= ? AND last_seen > ? ORDER BY level DESC LIMIT ?",
10, "2024-01-01", 5
)

print("Top high-level players:")
for _, player in ipairs(highLevelPlayers) do
print("-", player.name, "(Level " .. player.level .. ")")
end

-- Aggregate queries
local stats = SQL.Query("SELECT COUNT(*) as total_players, AVG(level) as avg_level, MAX(level) as max_level FROM players")
if #stats > 0 then
local stat = stats[1]
print("Total players:", stat.total_players)
print("Average level:", stat.avg_level)
print("Max level:", stat.max_level)
end

Common Patterns​

Error Handling​

local result = SQL.Execute("INSERT INTO players (steam_id, name) VALUES (?, ?)", steamId, playerName)
if not result.success then
print("Database error:", result.error)
return
end

Checking if Records Exist​

local existing = SQL.Query("SELECT id FROM players WHERE steam_id = ?", steamId)
if #existing > 0 then
print("Player already exists with ID:", existing[1].id)
else
print("New player")
end

Transactions (Manual)​

-- Begin transaction
SQL.Execute("BEGIN TRANSACTION")

-- Multiple operations
local result1 = SQL.Execute("INSERT INTO players (steam_id, name) VALUES (?, ?)", steamId, name)
local result2 = SQL.Execute("INSERT INTO player_stats (player_id, kills) VALUES (?, ?)", result1.last_insert_id, 0)

-- Commit or rollback
if result1.success and result2.success then
SQL.Execute("COMMIT")
print("Transaction completed successfully")
else
SQL.Execute("ROLLBACK")
print("Transaction failed, rolled back")
end