Kura
Early Access: Documentation is actively evolving and may change without notice.

Raw SQL

Execute raw PostgreSQL queries when you need the full escape hatch.

The raw SQL API lives on kura.db.raw.

Use raw SQL when you need:

  • complex joins or CTEs
  • COPY, LISTEN, NOTIFY, or other PostgreSQL-specific features
  • advanced update expressions that the builder does not model yet
  • framework-specific SQL that does not map cleanly to the chainable builder

For normal gameplay scripts, prefer the builder and generated schema refs first.

Builder strings are not raw SQL

Plain string table and column names in kura.db.select(), insert(), update(), delete(), and op.* are treated as quoted SQL identifiers for compatibility. They are not interpreted as raw SQL fragments, expressions, or aliases.

PostgreSQL uses $1, $2, $3 placeholders, not ?.

Result shape

Raw SQL results come back with PostgreSQL column names unless you alias them yourself in SQL. If you want Lua field-name remapping from created_at to createdAt, use the schema-aware builder instead.

query

Returns all matching rows as a table.

local rows = kura.db.raw.query.await(
  'SELECT * FROM "public"."users" WHERE job = $1',
  { 'police' }
)

single

Returns the first row, or nil if no rows match.

local user = kura.db.raw.single.await(
  'SELECT * FROM "public"."users" WHERE id = $1',
  { userId }
)

scalar

Returns a single value from the first row.

local count = kura.db.raw.scalar.await('SELECT COUNT(*) FROM "public"."users"')

update

Executes an update and returns the number of affected rows.

local changed = kura.db.raw.update.await(
  'UPDATE "public"."users" SET last_seen_at = NOW() WHERE id = $1',
  { userId }
)

insert

Executes an insert and returns the inserted id. KuraDB automatically appends RETURNING id for simple inserts without an existing RETURNING clause.

local id = kura.db.raw.insert.await(
  'INSERT INTO "public"."users" (license) VALUES ($1)',
  { license }
)

execute

Execute the same statement with multiple parameter sets:

local results = kura.db.raw.execute.await(
  'UPDATE "public"."users" SET cash = cash + $1 WHERE id = $2',
  {
    { 100, 1 },
    { 250, 2 },
  }
)

batch

The fast path for repeated prepared statements. Uses PostgreSQL pipeline mode for high throughput.

local rows = kura.db.raw.batch.await(
  'SELECT id FROM "public"."users" WHERE license = $1 LIMIT 1',
  {
    { 'license:1' },
    { 'license:2' },
  },
  { prepare = true }
)

insertMany

Multi-row insert optimized for bulk operations:

local ids = kura.db.raw.insertMany.await('users', {
  { license = 'license:1', cash = 500 },
  { license = 'license:2', cash = 750 },
}, {
  returning = 'id',
})

insertMany is the recommended bulk-insert API today. There is not yet a builder-native valuesMany() helper.

Pub/Sub

PostgreSQL LISTEN / NOTIFY is exposed directly.

listen

local subscription = kura.db.raw.listen.await('player_banned', function(payload)
  local data = json.decode(payload)
  DropPlayer(data.serverId, 'Banned by admin panel')
end)

notify

kura.db.raw.notify.await('player_banned', json.encode({ serverId = 1 }))

unlisten

kura.db.raw.unlisten.await(subscription.id)

COPY

Bulk ingest and export using PostgreSQL's COPY protocol.

copyFrom

kura.db.raw.copyFrom.await(
  'COPY users (license, cash) FROM STDIN WITH (FORMAT csv)',
  {
    'license:1,500\n',
    'license:2,750\n',
  }
)

copyTo

local csv = kura.db.raw.copyTo.await(
  'COPY users (license, cash) TO STDOUT WITH (FORMAT csv)'
)

On this page