Files
Baradb/tests/join_tests.nim
T
dimgigov 652ed1b477 feat: add ID generators, fix 6 forum-discovered deficiencies
ID Generators:
- AUTO_INCREMENT for INTEGER columns
- SERIAL / BIGSERIAL as syntactic sugar
- gen_random_uuid() / uuid() for UUID v4 generation
- nextval() / currval() for sequence support
- snowflake_id(node_id) for distributed 64-bit IDs
- RETURNING clause for INSERT

Deficiency Fixes:
- Comma join: FROM t1, t2 now works as implicit CROSS JOIN
- DEFAULT in restoreSchema: defaults survive server restart
- Duplicate column names: t.id instead of id in joins
- Empty result sets: always send column metadata to client
- GROUP BY non-agg columns: return first row value instead of empty
- Aggregate column names: count(*) instead of count()

Bug fix: PK uniqueness check now uses correct key format
2026-05-16 02:57:30 +03:00

111 lines
4.3 KiB
Nim

import std/unittest
import std/os
import std/strutils
import std/times
import std/monotimes
import std/tables
import barabadb/core/types
import barabadb/query/executor as qexec
import barabadb/query/parser
import barabadb/query/ast
import barabadb/storage/lsm
proc execSql(ctx: qexec.ExecutionContext, sql: string): qexec.ExecResult =
qexec.executeQuery(ctx, parse(sql))
# ---------------------------------------------------------------------------
suite "JOIN execution":
var db: LSMTree
var ctx: qexec.ExecutionContext
var testDir: string
setup:
testDir = getTempDir() / "baradb_join_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
createDir(testDir)
db = newLSMTree(testDir)
ctx = qexec.newExecutionContext(db)
discard execSql(ctx, "CREATE TABLE users (id INT, name TEXT)")
discard execSql(ctx, "CREATE TABLE orders (id INT, user_id INT, total REAL)")
discard execSql(ctx, "INSERT INTO users (id, name) VALUES (1, 'Alice')")
discard execSql(ctx, "INSERT INTO users (id, name) VALUES (2, 'Bob')")
discard execSql(ctx, "INSERT INTO orders (id, user_id, total) VALUES (10, 1, 99.5)")
discard execSql(ctx, "INSERT INTO orders (id, user_id, total) VALUES (20, 1, 23.0)")
discard execSql(ctx, "INSERT INTO orders (id, user_id, total) VALUES (30, 3, 150.0)")
teardown:
removeDir(testDir)
test "INNER JOIN returns matching rows only":
let r = execSql(ctx, "SELECT * FROM users u JOIN orders o ON u.id = o.user_id")
check r.rows.len == 2
check r.rows[0]["name"] == "Alice"
check r.rows[0]["total"] == "99.5"
test "LEFT JOIN keeps unmatched left rows":
let r = execSql(ctx, "SELECT * FROM users u LEFT JOIN orders o ON u.id = o.user_id")
check r.rows.len == 3
check r.rows[0]["name"] == "Alice"
check r.rows[1]["name"] == "Alice"
check r.rows[2]["name"] == "Bob"
check r.rows[2]["total"] == "" # NULL represented as empty string
test "RIGHT JOIN keeps unmatched right rows":
let r = execSql(ctx, "SELECT * FROM users u RIGHT JOIN orders o ON u.id = o.user_id")
check r.rows.len == 3
check r.rows[2]["id"] == "30"
check r.rows[2]["name"] == "" # NULL
check r.rows[2]["total"] == "150.0"
test "FULL JOIN keeps all rows":
let r = execSql(ctx, "SELECT * FROM users u FULL JOIN orders o ON u.id = o.user_id")
check r.rows.len == 4
test "CROSS JOIN cartesian product":
let r = execSql(ctx, "SELECT * FROM users u CROSS JOIN orders o")
check r.rows.len == 6
test "aliased column projection":
let r = execSql(ctx, "SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id")
check r.rows.len == 2
check r.rows[0]["u.name"] == "Alice"
check r.rows[0]["o.total"] == "99.5"
check "id" notin r.rows[0]
test "count after FULL JOIN":
let r = execSql(ctx, "SELECT COUNT(*) AS cnt FROM users u FULL JOIN orders o ON u.id = o.user_id")
check r.rows.len == 1
check r.rows[0]["cnt"] == "4"
test "count after CROSS JOIN":
let r = execSql(ctx, "SELECT COUNT(*) AS cnt FROM users u CROSS JOIN orders o")
check r.rows[0]["cnt"] == "6"
test "LATERAL JOIN with correlated subquery":
let r = execSql(ctx,
"SELECT u.name, recent.total FROM users u JOIN LATERAL (SELECT o.total FROM orders o WHERE o.user_id = u.id ORDER BY o.total DESC LIMIT 1) AS recent ON 1=1")
check r.rows.len == 1
check r.rows[0]["u.name"] == "Alice"
check r.rows[0]["recent.total"] == "99.5"
test "LATERAL JOIN returns no rows when subquery empty":
let r = execSql(ctx,
"SELECT u.name, x.total FROM users u JOIN LATERAL (SELECT o.total FROM orders o WHERE o.user_id = u.id AND o.total > 1000) AS x ON 1=1")
check r.rows.len == 0
test "LEFT LATERAL JOIN keeps unmatched rows":
let r = execSql(ctx,
"SELECT u.name, x.total FROM users u LEFT JOIN LATERAL (SELECT o.total FROM orders o WHERE o.user_id = u.id ORDER BY o.total DESC LIMIT 1) AS x ON 1=1")
check r.rows.len == 2
# Alice has match (99.5), Bob has no orders -> NULL
var foundBob = false
for row in r.rows:
if row["u.name"] == "Bob":
check row["x.total"] == ""
foundBob = true
check foundBob
test "CROSS JOIN LATERAL":
let r = execSql(ctx,
"SELECT u.name, x.total FROM users u CROSS JOIN LATERAL (SELECT o.total FROM orders o WHERE o.user_id = u.id) AS x")
check r.rows.len == 2 # Alice has 2 orders, Bob has 0