f7d4961125
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
- SET var = value / current_setting('var') for session-scoped variables
- current_user / current_role SQL keywords with auth bridge
- server.nim + httpserver.nim populate ExecutionContext.currentUser/currentRole
- RLS policies can reference current_setting('app.tenant_id') for tenant isolation
- Fixed evalExpr to propagate ctx recursively (fixes current_user in sub-expressions)
- Fixed GROUPING SETS execution (lowerSelect checks selGroupingSetsKind)
- Fixed FTS CREATE INDEX docId mismatch (hash of tableName.$key)
- Fixed all test suites to use isolated temp directories
- Added 5 multi-tenant tests (355 total, all green)
- Updated docs: PLAN_SQL_ADVANCED.md, baraql.md, changelog.md
14 KiB
14 KiB
BaraQL - Query Language Reference
BaraQL is a SQL-compatible query language with extensions for graph, vector, and document operations.
Data Types
| Type | Description | Example |
|---|---|---|
null |
Null value | null |
bool |
Boolean | true, false |
int8 |
8-bit signed integer | 127 |
int16 |
16-bit signed integer | 32767 |
int32 |
32-bit signed integer | 2147483647 |
int64 |
64-bit signed integer | 9223372036854775807 |
float32 |
32-bit float | 3.14 |
float64 |
64-bit float | 3.14159265359 |
str |
UTF-8 string | 'hello' |
bytes |
Raw bytes | 0xDEADBEEF |
array<T> |
Homogeneous array | [1, 2, 3] |
vector |
Float32 vector | [0.1, 0.2, 0.3] |
vector(n) |
Fixed-dimension float32 vector (SQL) | VECTOR(768) |
object |
Key-value object | {"a": 1} |
datetime |
ISO 8601 timestamp | '2025-01-15T10:30:00Z' |
uuid |
UUID v4 | '550e8400-e29b-41d4-a716-446655440000' |
json |
JSON document | {"key": "value"} |
jsonb |
Binary JSON (validated) | {"key": "value"} |
Basic Queries
SELECT
-- All columns
SELECT * FROM users;
-- Specific columns
SELECT name, age FROM users;
-- Aliases
SELECT name AS full_name, age AS years FROM users;
-- DISTINCT
SELECT DISTINCT department FROM employees;
-- LIMIT and OFFSET
SELECT * FROM users LIMIT 10 OFFSET 20;
WHERE
-- Comparison operators
SELECT * FROM users WHERE age > 18;
SELECT * FROM users WHERE age >= 18 AND age <= 65;
SELECT * FROM users WHERE name = 'Alice';
SELECT * FROM users WHERE name != 'Bob';
-- Range
SELECT * FROM users WHERE age BETWEEN 18 AND 65;
-- Set membership
SELECT * FROM users WHERE department IN ('Engineering', 'Sales');
-- Pattern matching
SELECT * FROM users WHERE name LIKE 'A%';
SELECT * FROM users WHERE name ILIKE 'alice'; -- Case-insensitive
-- NULL checks
SELECT * FROM users WHERE email IS NOT NULL;
-- Logical operators
SELECT * FROM users WHERE age > 18 AND (department = 'Engineering' OR department = 'Sales');
ORDER BY
-- Ascending (default)
SELECT * FROM users ORDER BY age;
-- Descending
SELECT * FROM users ORDER BY age DESC;
-- Multiple columns
SELECT * FROM users ORDER BY department ASC, age DESC;
INSERT
-- Single row
INSERT users { name := 'Alice', age := 30 };
-- With explicit type
INSERT User { name := 'Alice', age := 30 };
-- Multiple rows
INSERT users {
{ name := 'Alice', age := 30 },
{ name := 'Bob', age := 25 }
};
UPDATE
-- Update all rows
UPDATE users SET status = 'active';
-- Conditional update
UPDATE users SET age = 31 WHERE name = 'Alice';
-- Update multiple columns
UPDATE users SET age = 32, status = 'premium' WHERE name = 'Alice';
DELETE
-- Delete all rows
DELETE FROM users;
-- Conditional delete
DELETE FROM users WHERE age < 18;
Aggregates and Grouping
Aggregate Functions
| Function | Description |
|---|---|
count(*) |
Count all rows |
count(column) |
Count non-NULL values |
sum(column) |
Sum of values |
avg(column) |
Average |
min(column) |
Minimum value |
max(column) |
Maximum value |
stddev(column) |
Standard deviation |
variance(column) |
Variance |
GROUP BY
SELECT department, count(*) as emp_count, avg(salary) as avg_salary
FROM employees
GROUP BY department;
-- With HAVING
SELECT department, count(*) as emp_count
FROM employees
GROUP BY department
HAVING count(*) > 5;
-- Multiple groupings
SELECT department, role, count(*), avg(salary)
FROM employees
GROUP BY department, role;
JOINs
-- INNER JOIN
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- LEFT JOIN
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- RIGHT JOIN
SELECT u.name, o.total
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- FULL JOIN
SELECT u.name, o.total
FROM users u
FULL JOIN orders o ON u.id = o.user_id;
-- CROSS JOIN
SELECT u.name, p.name
FROM users u
CROSS JOIN products p;
-- Multiple JOINs
SELECT u.name, o.id, p.name
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN products p ON o.product_id = p.id;
-- Self JOIN
SELECT e.name, m.name as manager
FROM employees e
JOIN employees m ON e.manager_id = m.id;
CTEs (Common Table Expressions)
-- Single CTE
WITH active_users AS (
SELECT * FROM users WHERE active = true
)
SELECT * FROM active_users;
-- Multiple CTEs
WITH
recent AS (
SELECT * FROM orders WHERE date > '2025-01-01'
),
totals AS (
SELECT user_id, sum(amount) as total FROM recent GROUP BY user_id
)
SELECT u.name, t.total
FROM users u
JOIN totals t ON u.id = t.user_id;
-- Recursive CTE
WITH RECURSIVE subordinates AS (
SELECT id, name, manager_id FROM employees WHERE name = 'CEO'
UNION ALL
SELECT e.id, e.name, e.manager_id
FROM employees e
JOIN subordinates s ON e.manager_id = s.id
)
SELECT * FROM subordinates;
Subqueries
-- Subquery in SELECT
SELECT name, (SELECT count(*) FROM orders WHERE user_id = u.id) as order_count
FROM users u;
-- Subquery in FROM
SELECT * FROM (SELECT id, name FROM users WHERE active = true) AS active;
-- Subquery in WHERE (IN)
SELECT name FROM users WHERE id IN (SELECT user_id FROM orders);
-- Subquery in WHERE (EXISTS)
SELECT name FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);
-- Correlated subquery
SELECT name FROM users u
WHERE age > (SELECT avg(age) FROM users WHERE department = u.department);
CASE Expressions
SELECT name,
CASE
WHEN age < 13 THEN 'child'
WHEN age < 20 THEN 'teenager'
WHEN age < 65 THEN 'adult'
ELSE 'senior'
END AS category
FROM users;
-- Simple CASE
SELECT name,
CASE department
WHEN 'Engineering' THEN 'Tech'
WHEN 'Sales' THEN 'Revenue'
ELSE 'Other'
END AS division
FROM employees;
Set Operations
-- UNION (distinct)
SELECT name FROM customers
UNION
SELECT name FROM suppliers;
-- UNION ALL (with duplicates)
SELECT name FROM customers
UNION ALL
SELECT name FROM suppliers;
-- INTERSECT
SELECT name FROM customers
INTERSECT
SELECT name FROM suppliers;
-- EXCEPT
SELECT name FROM customers
EXCEPT
SELECT name FROM suppliers;
Schema Definition
CREATE TYPE
CREATE TYPE Person {
name: str,
age: int32
};
-- With required fields
CREATE TYPE User {
email: str REQUIRED,
name: str,
age: int32,
created_at: datetime DEFAULT now()
};
-- With links
CREATE TYPE Movie {
title: str,
year: int32,
director: Person
};
-- With computed properties
CREATE TYPE Employee {
name: str,
base_salary: float64,
bonus: float64,
total_compensation: float64 COMPUTED (base_salary + bonus)
};
Inheritance
CREATE TYPE Animal {
name: str
};
CREATE TYPE Dog EXTENDING Animal {
breed: str
};
CREATE TYPE Cat EXTENDING Animal {
indoor: bool
};
Indexes
CREATE INDEX idx_users_name ON users(name);
CREATE UNIQUE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_age ON users(age) USING btree;
CREATE INDEX idx_vectors ON items(embedding) USING hnsw;
DROP
DROP TYPE User;
DROP INDEX idx_users_name;
JSON Path Operators
-- Extract JSON field as JSON
SELECT data->'name' FROM users;
-- Extract JSON field as text
SELECT data->>'name' FROM users;
Full-Text Search (SQL)
-- Create FTS index with BM25
CREATE INDEX idx_fts ON articles(body) USING FTS;
-- Search with BM25 ranking
SELECT * FROM articles WHERE body @@ 'machine learning';
Point-in-Time Recovery
RECOVER TO TIMESTAMP '2026-05-07T12:00:00';
Vector Search (SQL)
Creating Vector Columns
CREATE TABLE items (
id INT PRIMARY KEY,
embedding VECTOR(768)
);
Inserting Vectors
INSERT INTO items (id, embedding) VALUES (1, '[0.1, 0.2, 0.3, 0.4]');
Distance Functions
-- Cosine distance (0 = identical, 2 = opposite)
SELECT id, cosine_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
FROM items;
-- Euclidean / L2 distance
SELECT id, euclidean_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
FROM items;
-- L2 distance with <-> operator
SELECT id, embedding <-> '[0.1, 0.2, 0.3, 0.4]' AS dist
FROM items;
-- Inner product (negative dot product)
SELECT id, inner_product(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
FROM items;
-- Manhattan / L1 distance
SELECT id, l1_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
FROM items;
Nearest Neighbor Search
-- Top-10 nearest neighbors by cosine distance
SELECT id FROM items
ORDER BY cosine_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') ASC
LIMIT 10;
-- Top-5 nearest neighbors by Euclidean distance
SELECT id FROM items
ORDER BY embedding <-> '[0.1, 0.2, 0.3, 0.4]'
LIMIT 5;
-- With metadata filter
SELECT id FROM items
WHERE category = 'tech'
ORDER BY cosine_distance(embedding, '[0.1, 0.2, 0.3, 0.4]')
LIMIT 5;
Vector Indexes
-- Create HNSW index for approximate nearest neighbor search
CREATE INDEX idx_items_vec ON items(embedding) USING hnsw;
-- Supported index methods: hnsw, ivfpq
Graph Patterns
-- Find friends of Alice
MATCH (p:Person)-[:KNOWS]->(friend:Person)
WHERE p.name = 'Alice'
RETURN friend.name;
-- Find shortest path
MATCH path = shortestPath((a:Person)-[:KNOWS*1..5]->(b:Person))
WHERE a.name = 'Alice' AND b.name = 'Bob'
RETURN path;
-- Find all relationships
MATCH (p:Person)-[r]->(other)
WHERE p.name = 'Alice'
RETURN type(r), other.name;
-- Multiple hops
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)
WHERE a.name = 'Alice'
RETURN c.name;
-- With aggregates
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name, count(friend) as friend_count
ORDER BY friend_count DESC;
Full-Text Search
-- Basic search
SELECT * FROM articles
WHERE MATCH(title, body) AGAINST('database programming');
-- With relevance score
SELECT title, relevance()
FROM articles
WHERE MATCH(title, body) AGAINST('Nim language')
ORDER BY relevance() DESC;
-- Boolean mode
SELECT * FROM articles
WHERE MATCH(title, body) AGAINST('+Nim -Python' IN BOOLEAN MODE);
-- Fuzzy search
SELECT * FROM articles
WHERE MATCH(title) AGAINST('programing' WITH FUZZINESS 2);
Transactions
BEGIN;
INSERT users { name := 'Alice', age := 30 };
INSERT orders { user_id := last_insert_id(), total := 100 };
COMMIT;
-- With savepoint
BEGIN;
INSERT users { name := 'Bob', age := 25 };
SAVEPOINT sp1;
INSERT orders { user_id := last_insert_id(), total := 200 };
-- Oops, rollback to savepoint
ROLLBACK TO sp1;
COMMIT;
User-Defined Functions
-- Register a UDF
CREATE FUNCTION greet(name str) -> str {
RETURN 'Hello, ' || name || '!';
};
-- Use it
SELECT greet(name) FROM users;
-- Built-in functions
SELECT abs(-5), sqrt(16), lower('HELLO'), len('test');
Query Hints
-- Force index usage
SELECT /*+ USE_INDEX(idx_users_age) */ * FROM users WHERE age > 18;
-- Force approximate vector search
SELECT /*+ APPROXIMATE */ * FROM vectors
ORDER BY cosine_distance(embedding, [...])
LIMIT 10;
-- Parallel execution
SELECT /*+ PARALLEL(4) */ * FROM large_table;
Window Functions
-- Ranking functions
SELECT
name,
department,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
FROM employees;
-- Value functions
SELECT
name,
salary,
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
FROM employees;
-- Distribution functions
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
Frame Specifications
-- ROWS frame
SUM(salary) OVER (
PARTITION BY department
ORDER BY hire_date
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
)
-- RANGE frame
SUM(salary) OVER (
PARTITION BY department
ORDER BY hire_date
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
Multi-Tenant ERP
BaraDB supports running multiple companies (tenants) in a single database instance, using Row-Level Security (RLS) combined with session variables.
Session Variables
SET app.tenant_id = 'company-123';
SELECT current_setting('app.tenant_id') AS tenant;
Current User / Role
SELECT current_user AS me, current_role AS my_role;
RLS Tenant Isolation
-- Enable RLS on a table
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
-- Create a policy that filters by tenant
CREATE POLICY tenant_isolation ON invoices
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
-- Each session only sees its own data
SET app.tenant_id = 'company-a';
SELECT * FROM invoices; -- only company-a rows
Why Multi-Tenant?
- One instance, many tenants — no need to run 100 separate databases
- JSONB documents — schema-flexible storage, easy to add fields per tenant
- RLS guarantees isolation — the database enforces tenant boundaries, not just the application
Supported Keywords
| Category | Keywords |
|---|---|
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
| CTEs | WITH, RECURSIVE, AS |
| Case | CASE, WHEN, THEN, ELSE, END |
| Transaction | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
| Graph | MATCH, RETURN, WHERE, shortestPath, type |
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
| Vector | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
| JSON | ->, ->> |
| FTS | @@ (BM25 match) |
| Recovery | RECOVER TO TIMESTAMP |
| Functions | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
| Session | SET, current_setting, current_user, current_role |
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
| Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |