# Permissions
Source: https://docs.omniql.com/control/permissions
Control access with GRANT and REVOKE
Control database access using DCL (Data Control Language) operations.
## Grant Permissions
```sql theme={}
:GRANT permission ON Entity TO user
```
### Read Permission
```sql theme={}
:GRANT READ ON User TO analyst
```
| Database | Output |
| ---------- | -------------------------------------------------- |
| PostgreSQL | `GRANT SELECT ON users TO analyst` |
| MySQL | `GRANT SELECT ON users.* TO 'analyst'@'localhost'` |
### Write Permission
```sql theme={}
:GRANT WRITE ON Order TO sales_app
```
| Database | Output |
| ---------- | --------------------------------------------- |
| PostgreSQL | `GRANT INSERT, UPDATE ON orders TO sales_app` |
### Delete Permission
```sql theme={}
:GRANT DELETE ON Log TO admin
```
### All Permissions
```sql theme={}
:GRANT ALL ON User TO admin
```
| Database | Output |
| ---------- | ---------------------------------------- |
| PostgreSQL | `GRANT ALL PRIVILEGES ON users TO admin` |
### Multiple Permissions
```sql theme={}
:GRANT READ, WRITE, DELETE ON Order TO app_service
```
### All Tables
```sql theme={}
:GRANT READ ON * TO analyst
```
## Revoke Permissions
```sql theme={}
:REVOKE permission ON Entity FROM user
```
### Single Permission
```sql theme={}
:REVOKE DELETE ON User FROM intern
```
| Database | Output |
| ---------- | ------------------------------------ |
| PostgreSQL | `REVOKE DELETE ON users FROM intern` |
### All Permissions
```sql theme={}
:REVOKE ALL ON User FROM former_employee
```
| Database | Output |
| ---------- | ----------------------------------------------------- |
| PostgreSQL | `REVOKE ALL PRIVILEGES ON users FROM former_employee` |
## User Management
### Create User
```sql theme={}
:CREATE USER john WITH PASSWORD secret123
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------ |
| PostgreSQL | `CREATE USER john WITH PASSWORD 'secret123'` |
| MySQL | `CREATE USER IF NOT EXISTS 'john'@'localhost' IDENTIFIED BY 'secret123'` |
### Alter User Password
```sql theme={}
:ALTER USER john WITH PASSWORD newsecret456
```
| Database | Output |
| ---------- | ------------------------------------------------------------ |
| PostgreSQL | `ALTER USER john WITH PASSWORD 'newsecret456'` |
| MySQL | `ALTER USER 'john'@'localhost' IDENTIFIED BY 'newsecret456'` |
### Drop User
```sql theme={}
:DROP USER john
```
| Database | Output |
| ---------- | ---------------------------------------- |
| PostgreSQL | `DROP USER IF EXISTS john` |
| MySQL | `DROP USER IF EXISTS 'john'@'localhost'` |
## Role Management
### Create Role
```sql theme={}
:CREATE ROLE analyst
```
| Database | Output |
| ---------- | ----------------------------------- |
| PostgreSQL | `CREATE ROLE analyst` |
| MySQL | `CREATE ROLE IF NOT EXISTS analyst` |
### Assign Role to User
```sql theme={}
:ASSIGN ROLE analyst TO john
```
| Database | Output |
| ---------- | --------------------------------------- |
| PostgreSQL | `GRANT analyst TO john` |
| MySQL | `GRANT 'analyst' TO 'john'@'localhost'` |
### Revoke Role from User
```sql theme={}
:REVOKE ROLE analyst FROM john
```
| Database | Output |
| ---------- | ------------------------------------------ |
| PostgreSQL | `REVOKE analyst FROM john` |
| MySQL | `REVOKE 'analyst' FROM 'john'@'localhost'` |
### Drop Role
```sql theme={}
:DROP ROLE analyst
```
| Database | Output |
| ---------- | ----------------------------- |
| PostgreSQL | `DROP ROLE IF EXISTS analyst` |
| MySQL | `DROP ROLE IF EXISTS analyst` |
## Permission Types
| OmniQL | PostgreSQL | MySQL | Description |
| -------- | ---------------- | ---------------- | ----------------- |
| `READ` | `SELECT` | `SELECT` | Read data |
| `WRITE` | `INSERT, UPDATE` | `INSERT, UPDATE` | Create and modify |
| `DELETE` | `DELETE` | `DELETE` | Remove records |
| `ALL` | `ALL PRIVILEGES` | `ALL PRIVILEGES` | Full access |
You can also use native permission names (SELECT, INSERT, UPDATE) directly.
## Complete Examples
### Read-Only Analyst
```sql theme={}
:CREATE ROLE analyst
:GRANT READ ON User TO analyst
:GRANT READ ON Order TO analyst
:GRANT READ ON Product TO analyst
:CREATE USER jane WITH PASSWORD analyst123
:ASSIGN ROLE analyst TO jane
```
### Application Service Account
```sql theme={}
:CREATE ROLE app_service
:GRANT READ, WRITE ON User TO app_service
:GRANT READ, WRITE ON Order TO app_service
:GRANT READ, WRITE ON Product TO app_service
:CREATE USER myapp WITH PASSWORD service456
:ASSIGN ROLE app_service TO myapp
```
### Admin User
```sql theme={}
:CREATE ROLE admin
:GRANT ALL ON User TO admin
:GRANT ALL ON Order TO admin
:GRANT ALL ON Product TO admin
:CREATE USER bob WITH PASSWORD admin789
:ASSIGN ROLE admin TO bob
```
## Database Support
| Feature | PostgreSQL | MySQL | MongoDB |
| ------------------ | ---------- | ----- | ------------ |
| GRANT/REVOKE | Yes | Yes | Via commands |
| CREATE/DROP USER | Yes | Yes | Via commands |
| ALTER USER | Yes | Yes | Via commands |
| CREATE/DROP ROLE | Yes | Yes | Via commands |
| ASSIGN/REVOKE ROLE | Yes | Yes | Via commands |
## MongoDB Note
MongoDB uses role-based access control with built-in roles. OmniQL translates to MongoDB admin commands.
```javascript theme={}
// MongoDB equivalent for CREATE USER
db.createUser({
user: "analyst",
pwd: "secret",
roles: [{ role: "read", db: "myapp" }]
});
```
## Limitations
Not currently supported:
* Column-level permissions
* Schema/database-level permissions
* Sequence permissions
* Role options (SUPERUSER, LOGIN, etc.)
* Multiple tables in single GRANT
## Next Steps
Group operations safely
PostgreSQL specifics
# Transactions
Source: https://docs.omniql.com/control/transactions
Group operations with ACID guarantees
Group multiple operations into atomic units.
## Basic Syntax
```sql theme={}
:BEGIN
:COMMIT
:ROLLBACK
```
## Simple Transaction
```sql theme={}
:BEGIN
:UPDATE Account SET balance = balance - 100 WHERE id = 1
:UPDATE Account SET balance = balance + 100 WHERE id = 2
:COMMIT
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| PostgreSQL | `BEGIN; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT;` |
| MySQL | `START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT;` |
## Rollback
Undo all changes in the transaction.
```sql theme={}
:BEGIN
:UPDATE Account SET balance = balance - 100 WHERE id = 1
:UPDATE Account SET balance = balance + 100 WHERE id = 2
:ROLLBACK
```
## Savepoints
Create checkpoints within a transaction.
```sql theme={}
:BEGIN
:INSERT Order WITH user_id = 1, total = 99.99
:SAVEPOINT order_created
:INSERT OrderItem WITH order_id = 1, product_id = 5, quantity = 2
:INSERT OrderItem WITH order_id = 1, product_id = 8, quantity = 1
:COMMIT
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------------------- |
| PostgreSQL | `BEGIN; INSERT INTO orders ...; SAVEPOINT order_created; INSERT INTO order_items ...; COMMIT;` |
### Rollback to Savepoint
Undo changes back to a savepoint.
```sql theme={}
:ROLLBACK TO order_created
```
### Release Savepoint
Remove a savepoint (keep the changes).
```sql theme={}
:RELEASE SAVEPOINT order_created
```
## Isolation Levels
Control how transactions see each other's changes. Set isolation level before BEGIN.
```sql theme={}
:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
:BEGIN
:GET Product WHERE id = 1
:UPDATE Product SET quantity = quantity - 1 WHERE id = 1
:COMMIT
```
| Level | Description |
| ------------------ | --------------------------------------------------- |
| `READ UNCOMMITTED` | Can see uncommitted changes from other transactions |
| `READ COMMITTED` | Only sees committed changes (PostgreSQL default) |
| `REPEATABLE READ` | Consistent reads within transaction (MySQL default) |
| `SERIALIZABLE` | Full isolation, transactions appear sequential |
| Database | Output |
| ---------- | ---------------------------------------------------------------------- |
| PostgreSQL | `SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN; ...` |
| MySQL | `SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; START TRANSACTION; ...` |
## Complete Examples
### Money Transfer
```sql theme={}
:BEGIN
:UPDATE Account SET balance = balance - 500 WHERE id = 1 AND balance >= 500
:UPDATE Account SET balance = balance + 500 WHERE id = 2
:INSERT TransactionLog WITH from_account = 1, to_account = 2, amount = 500
:COMMIT
```
### Order with Savepoint
```sql theme={}
:BEGIN
:INSERT Order WITH user_id = 42, status = "pending", total = 0
:SAVEPOINT order_created
:INSERT OrderItem WITH order_id = 1, product_id = 5, quantity = 2, price = 29.99
:INSERT OrderItem WITH order_id = 1, product_id = 8, quantity = 1, price = 49.99
:UPDATE Order SET total = 109.97 WHERE id = 1
:UPDATE Product SET quantity = quantity - 2 WHERE id = 5
:UPDATE Product SET quantity = quantity - 1 WHERE id = 8
:COMMIT
```
### High Isolation Transaction
```sql theme={}
:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
:BEGIN
:GET Account WHERE id = 1
:UPDATE Account SET balance = balance - 100 WHERE id = 1
:COMMIT
```
## Database Support
| Feature | PostgreSQL | MySQL | MongoDB |
| --------------------- | ---------- | ----- | ------------------ |
| BEGIN/COMMIT/ROLLBACK | Yes | Yes | Yes (sessions) |
| SAVEPOINT | Yes | Yes | No |
| ROLLBACK TO | Yes | Yes | No |
| RELEASE SAVEPOINT | Yes | Yes | No |
| Isolation Levels | Yes | Yes | Yes (read concern) |
## MongoDB Note
MongoDB supports multi-document transactions in replica sets and sharded clusters (v4.0+). Savepoints are not supported.
```javascript theme={}
// MongoDB equivalent
session.startTransaction();
db.accounts.updateOne({ _id: 1 }, { $inc: { balance: -100 } });
db.accounts.updateOne({ _id: 2 }, { $inc: { balance: 100 } });
session.commitTransaction();
```
## Limitations
Not currently supported:
* BEGIN READ ONLY
* Inline isolation level (BEGIN ISOLATION LEVEL X)
* Nested transactions
## Next Steps
Control access
PostgreSQL specifics
# MongoDB
Source: https://docs.omniql.com/databases/mongodb
Using OmniQL with MongoDB
MongoDB is a document database that stores data in flexible, JSON-like documents.
## Quick Start
```go theme={}
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/omniql-engine/omniql"
)
// Your MongoDB connection
mongoClient, _ := mongo.Connect(context.Background(),
options.Client().ApplyURI("mongodb://localhost:27017"))
db := mongoClient.Database("mydb")
// Wrap with OmniQL
client := oql.WrapMongo(db)
// Query with OmniQL syntax
users, _ := client.Query(":GET User WHERE age > 21")
```
## Type Mappings
| OmniQL | MongoDB |
| ----------- | ------------ |
| `AUTO` | `ObjectId` |
| `BIGAUTO` | `ObjectId` |
| `STRING` | `String` |
| `TEXT` | `String` |
| `CHAR` | `String` |
| `INT` | `Int32` |
| `BIGINT` | `Int64` |
| `SMALLINT` | `Int32` |
| `DECIMAL` | `Decimal128` |
| `NUMERIC` | `Decimal128` |
| `FLOAT` | `Double` |
| `REAL` | `Double` |
| `BOOLEAN` | `Boolean` |
| `BOOL` | `Boolean` |
| `TIMESTAMP` | `Date` |
| `DATETIME` | `Date` |
| `DATE` | `Date` |
| `TIME` | `String` |
| `JSON` | `Object` |
| `JSONB` | `Object` |
| `UUID` | `UUID` |
| `BINARY` | `BinData` |
| `BLOB` | `BinData` |
## Entity Naming
OmniQL entities become collection names (lowercase):
| OmniQL | MongoDB |
| ----------- | ------------ |
| `User` | `users` |
| `Order` | `orders` |
| `OrderItem` | `orderitems` |
## Translation Examples
### CRUD Operations
**GET (find)**
```sql theme={}
:GET User WHERE id = 1
```
```javascript theme={}
db.users.find({ id: 1 })
```
```sql theme={}
:GET User WHERE age > 21 AND status = "active"
```
```javascript theme={}
db.users.find({ age: { $gt: 21 }, status: 'active' })
```
```sql theme={}
:GET id, name, email FROM User WHERE active = true
```
```javascript theme={}
db.users.find({ active: true }, { id: 1, name: 1, email: 1 })
```
**CREATE (insertOne)**
```sql theme={}
:CREATE User WITH name = "John", email = "john@example.com"
```
```javascript theme={}
db.users.insertOne({ name: 'John', email: 'john@example.com' })
```
**BULK INSERT (insertMany)**
```sql theme={}
:BULK INSERT User WITH [name = "Alice", age = 28] [name = "Bob", age = 32]
```
```javascript theme={}
db.users.insertMany([
{ name: 'Alice', age: 28 },
{ name: 'Bob', age: 32 }
])
```
**UPDATE (updateOne)**
```sql theme={}
:UPDATE User SET status = "active" WHERE id = 1
```
```javascript theme={}
db.users.updateOne({ id: 1 }, { $set: { status: 'active' } })
```
**Arithmetic in UPDATE**
```sql theme={}
:UPDATE User SET balance = balance + 100 WHERE id = 1
```
```javascript theme={}
db.users.updateOne({ id: 1 }, { $inc: { balance: 100 } })
```
```sql theme={}
:UPDATE Product SET price = price * 0.9 WHERE category = "sale"
```
```javascript theme={}
db.products.updateOne({ category: 'sale' }, { $mul: { price: 0.9 } })
```
**DELETE (deleteOne)**
```sql theme={}
:DELETE User WHERE id = 1
```
```javascript theme={}
db.users.deleteOne({ id: 1 })
```
**UPSERT**
```sql theme={}
:UPSERT User WITH email = "john@example.com", name = "John" ON email
```
```javascript theme={}
db.users.updateOne(
{ email: 'john@example.com' },
{ $set: { email: 'john@example.com', name: 'John' } },
{ upsert: true }
)
```
**REPLACE (replaceOne)**
```sql theme={}
:REPLACE User WITH id = 1, name = "John", email = "john@example.com"
```
```javascript theme={}
db.users.replaceOne({ id: 1 }, { name: 'John', email: 'john@example.com' })
```
### Filtering (Operators)
| OmniQL | MongoDB |
| ------------- | ----------------- |
| `=` | `$eq` |
| `!=` | `$ne` |
| `>` | `$gt` |
| `>=` | `$gte` |
| `<` | `$lt` |
| `<=` | `$lte` |
| `IN` | `$in` |
| `NOT IN` | `$nin` |
| `LIKE` | `$regex` |
| `IS NULL` | `$eq: null` |
| `IS NOT NULL` | `$ne: null` |
| `AND` | implicit / `$and` |
| `OR` | `$or` |
**Examples:**
```sql theme={}
:GET User WHERE role IN ("admin", "moderator")
```
```javascript theme={}
db.users.find({ role: { $in: ['admin', 'moderator'] } })
```
```sql theme={}
:GET User WHERE age BETWEEN 18 AND 65
```
```javascript theme={}
db.users.find({ age: { $gte: 18, $lte: 65 } })
```
```sql theme={}
:GET User WHERE name LIKE "John%"
```
```javascript theme={}
db.users.find({ name: { $regex: /^John/, $options: 'i' } })
```
```sql theme={}
:GET User WHERE role = "admin" OR role = "moderator"
```
```javascript theme={}
db.users.find({ $or: [{ role: 'admin' }, { role: 'moderator' }] })
```
### Pagination
```sql theme={}
:GET User ORDER BY created_at DESC LIMIT 10 OFFSET 20
```
```javascript theme={}
db.users.find({}).sort({ created_at: -1 }).skip(20).limit(10)
```
### Aggregation
**COUNT**
```sql theme={}
:COUNT * FROM User WHERE active = true
```
```javascript theme={}
db.users.aggregate([
{ $match: { active: true } },
{ $group: { _id: null, result: { $sum: 1 } } }
])
```
**GROUP BY**
```sql theme={}
:COUNT * FROM User GROUP BY status
```
```javascript theme={}
db.users.aggregate([
{ $group: { _id: '$status', result: { $sum: 1 } } }
])
```
**SUM, AVG, MIN, MAX**
```sql theme={}
:SUM total FROM Order WHERE status = "completed"
```
```javascript theme={}
db.orders.aggregate([
{ $match: { status: 'completed' } },
{ $group: { _id: null, result: { $sum: '$total' } } }
])
```
**HAVING**
```sql theme={}
:COUNT * FROM User GROUP BY status HAVING COUNT(*) > 10
```
```javascript theme={}
db.users.aggregate([
{ $group: { _id: '$status', result: { $sum: 1 } } },
{ $match: { result: { $gt: 10 } } }
])
```
### Joins (\$lookup)
OmniQL joins translate to MongoDB's `$lookup` aggregation:
```sql theme={}
:INNER JOIN Order User ON Order.user_id = User.id
```
```javascript theme={}
db.orders.aggregate([
{
$lookup: {
from: 'users',
localField: 'user_id',
foreignField: '_id',
as: 'users_joined'
}
},
{ $unwind: '$users_joined' }
])
```
```sql theme={}
:LEFT JOIN Order User ON Order.user_id = User.id
```
```javascript theme={}
db.orders.aggregate([
{
$lookup: {
from: 'users',
localField: 'user_id',
foreignField: '_id',
as: 'users_joined'
}
},
{ $unwind: { path: '$users_joined', preserveNullAndEmptyArrays: true } }
])
```
### Window Functions (MongoDB 5.0+)
```sql theme={}
:ROW NUMBER OVER (PARTITION BY department ORDER BY salary) FROM Employee
```
```javascript theme={}
db.employees.aggregate([
{
$setWindowFields: {
partitionBy: '$department',
sortBy: { salary: 1 },
output: {
rowNumber: { $documentNumber: {} }
}
}
}
])
```
**Supported window functions:**
* ROW NUMBER → `$documentNumber`
* RANK → `$rank`
* DENSE RANK → `$denseRank`
* LAG/LEAD → `$shift`
### Set Operations (MongoDB 4.4+)
```sql theme={}
:UNION (GET User WHERE age > 50) (GET User WHERE role = "premium")
```
```javascript theme={}
db.users.aggregate([
{ $match: { age: { $gt: 50 } } },
{ $unionWith: { coll: 'users', pipeline: [{ $match: { role: 'premium' } }] } }
])
```
### CASE Expressions
```sql theme={}
:GET User WITH CASE WHEN age > 25 THEN "adult" ELSE "minor" END AS category
```
```javascript theme={}
db.users.aggregate([
{
$project: {
category: {
$switch: {
branches: [
{ case: { $gt: ['$age', 25] }, then: 'adult' }
],
default: 'minor'
}
}
}
}
])
```
### Transactions (Replica Set Required)
```sql theme={}
:BEGIN
:UPDATE Account SET balance = balance - 100 WHERE id = 1
:UPDATE Account SET balance = balance + 100 WHERE id = 2
:COMMIT
```
```javascript theme={}
session.startTransaction();
db.accounts.updateOne({ id: 1 }, { $inc: { balance: -100 } });
db.accounts.updateOne({ id: 2 }, { $inc: { balance: 100 } });
session.commitTransaction();
```
### Permissions
**CREATE USER:**
```sql theme={}
:CREATE USER analyst WITH PASSWORD secret123
```
```javascript theme={}
db.runCommand({ createUser: 'analyst', pwd: 'secret123', roles: [] })
```
**CREATE ROLE:**
```sql theme={}
:CREATE ROLE readonly
```
```javascript theme={}
db.runCommand({ createRole: 'readonly', privileges: [], roles: [] })
```
**GRANT ROLE:**
```sql theme={}
:ASSIGN ROLE readonly TO analyst
```
```javascript theme={}
db.runCommand({ grantRolesToUser: 'analyst', roles: ['readonly'] })
```
## Supported Operations
| Category | Operations | Support |
| ---------------- | --------------------------------------------------------- | ---------------- |
| CRUD | GET, CREATE, UPDATE, DELETE, UPSERT, BULK INSERT, REPLACE | Full |
| Filtering | All operators (=, !=, IN, BETWEEN, LIKE, IS NULL, etc.) | Full |
| Aggregation | COUNT, SUM, AVG, MIN, MAX | Full |
| Grouping | GROUP BY, HAVING | Full |
| Sorting | ORDER BY, LIMIT, OFFSET | Full |
| Joins | INNER, LEFT, RIGHT, FULL | Via \$lookup |
| Window Functions | ROW NUMBER, RANK, DENSE RANK, LAG, LEAD | MongoDB 5.0+ |
| Set Operations | UNION, UNION ALL | MongoDB 4.4+ |
| Expressions | Arithmetic (+, -, \*, /, %), CASE WHEN | Full |
| Functions | UPPER, LOWER, CONCAT, LENGTH, ABS, ROUND | Full |
| Transactions | BEGIN, COMMIT, ROLLBACK | Replica set only |
| DDL | CREATE/DROP COLLECTION, RENAME, CREATE VIEW | Full |
| DCL | CREATE/DROP USER, CREATE/DROP ROLE, GRANT, REVOKE | Full |
## Limitations
| Feature | Status | Notes |
| ------------------------ | ------------- | --------------------------------- |
| SAVEPOINT | Not supported | MongoDB has no savepoint concept |
| ROLLBACK TO | Not supported | No partial rollback |
| RELEASE SAVEPOINT | Not supported | No savepoints |
| INTERSECT | Not supported | Use aggregation workarounds |
| EXCEPT | Not supported | Use aggregation workarounds |
| CTEs | Not supported | Use aggregation pipelines instead |
| Single-node transactions | Not supported | Requires replica set |
## Version Requirements
| Feature | Minimum MongoDB Version |
| ----------------- | ----------------------- |
| Basic CRUD | 3.6+ |
| Transactions | 4.0+ (replica set) |
| \$unionWith | 4.4+ |
| \$setWindowFields | 5.0+ |
## Next Steps
Redis specifics
Type mappings
# Redis
Source: https://docs.omniql.com/databases/redis
Using OmniQL with Redis
Redis is an in-memory data store used for caching, sessions, and real-time applications.
## Quick Start
```go theme={}
import (
"github.com/redis/go-redis/v9"
"github.com/omniql-engine/omniql"
)
// Your Redis connection
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// Wrap with OmniQL
client := oql.WrapRedis(rdb, "tenant_1")
// Query with OmniQL syntax
user, _ := client.Query(":GET User WHERE id = 42")
```
## How Redis Works
Redis is **key-value**, not relational. It cannot filter data natively like SQL databases.
| Database | WHERE Filtering |
| ---------- | ------------------------- |
| PostgreSQL | Native (database does it) |
| MySQL | Native (database does it) |
| MongoDB | Native (database does it) |
| Redis | OmniQL handles it in Go |
OmniQL bridges this gap by providing filtering logic that works seamlessly with your queries.
## Data Model
OmniQL maps entities to Redis Hash structures:
| Concept | OmniQL | Redis |
| ------- | ------------------- | ----------------------- |
| Entity | `User` | Hash keys with prefix |
| Record | `User WHERE id = 1` | `HGETALL tenant:user:1` |
| Field | `name`, `email` | Hash fields |
### Key Pattern
```
{tenant}:{entity}:{id}
```
Examples:
```
tenant_1:user:1 → User with id 1
tenant_1:user:2 → User with id 2
tenant_1:order:1001 → Order with id 1001
```
## Query Types
### Direct Key Lookup (Fast)
When you query by `id`, OmniQL translates directly to Redis commands:
```go theme={}
// OmniQL
user, _ := client.Query(":GET User WHERE id = 42")
// Internally executes:
// HGETALL tenant_1:user:42
```
This is **instant** - same performance as native Redis.
### Filtered Query (Scan + Filter)
When you query by other fields, OmniQL scans keys and filters results:
```go theme={}
// OmniQL
users, _ := client.Query(":GET User WHERE age > 21 AND status = \"active\" LIMIT 10")
// Internally:
// 1. SCAN tenant_1:user:*
// 2. HGETALL each key
// 3. Filter using MatchesConditions()
// 4. Return matching results up to LIMIT
```
This works but scans data - use for smaller datasets or with LIMIT.
## CRUD Operations
### GET (HGETALL)
**By ID (direct lookup):**
```go theme={}
user, _ := client.Query(":GET User WHERE id = 1")
// → HGETALL tenant_1:user:1
```
**With filtering:**
```go theme={}
users, _ := client.Query(":GET User WHERE status = \"active\" LIMIT 10")
// → SCAN + HGETALL + filter
```
**All records:**
```go theme={}
users, _ := client.Query(":GET User")
// → SCAN tenant_1:user:* + HGETALL each
```
### CREATE (HMSET)
```go theme={}
result, _ := client.Query(`:CREATE User WITH name:"John", email:"john@example.com", age:30`)
// → HMSET tenant_1:user:{generated_id} name "John" email "john@example.com" age "30"
// result = []map[string]any{{"inserted_id": "uuid-here", "rows_affected": 1}}
```
### UPDATE (HSET)
```go theme={}
result, _ := client.Query(`:UPDATE User SET status:"active" WHERE id = 1`)
// → HSET tenant_1:user:1 status "active"
// result = []map[string]any{{"rows_affected": 1}}
```
### DELETE (DEL)
```go theme={}
result, _ := client.Query(`:DELETE User WHERE id = 1`)
// → DEL tenant_1:user:1
// result = []map[string]any{{"rows_affected": 1}}
```
### BULK INSERT
```go theme={}
result, _ := client.Query(`:BULK INSERT User WITH [name:"Alice", age:28], [name:"Bob", age:32]`)
// → HMSET tenant_1:user:1 name "Alice" age "28"
// → HMSET tenant_1:user:2 name "Bob" age "32"
```
### UPSERT
```go theme={}
result, _ := client.Query(`:UPSERT User WITH id:1, name:"John" ON id`)
// → HMSET tenant_1:user:1 name "John"
```
### DROP TABLE
```go theme={}
result, _ := client.Query(`:DROP TABLE User`)
// → Deletes all keys matching tenant_1:user:*
```
## Filtering Support
OmniQL supports all standard operators for Redis filtering:
| Operator | Example | Supported |
| ------------- | ------------------------------------- | --------- |
| `=` | `status = "active"` | ✅ |
| `!=` | `status != "banned"` | ✅ |
| `>` | `age > 21` | ✅ |
| `<` | `age < 65` | ✅ |
| `>=` | `score >= 100` | ✅ |
| `<=` | `price <= 50` | ✅ |
| `IN` | `status IN ("active", "pending")` | ✅ |
| `NOT IN` | `role NOT IN ("admin", "mod")` | ✅ |
| `BETWEEN` | `age BETWEEN 18 AND 65` | ✅ |
| `LIKE` | `name LIKE "John%"` | ✅ |
| `IS NULL` | `deleted_at IS NULL` | ✅ |
| `IS NOT NULL` | `email IS NOT NULL` | ✅ |
| `AND` | `age > 21 AND active = true` | ✅ |
| `OR` | `status = "active" OR role = "admin"` | ✅ |
### Example with Complex Filter
```go theme={}
users, _ := client.Query(`
:GET User
WHERE age > 21
AND status IN ("active", "pending")
AND email IS NOT NULL
ORDER BY name ASC
LIMIT 10
`)
```
## Aggregations
OmniQL provides aggregation operations:
### COUNT
```go theme={}
result, _ := client.Query(":COUNT User WHERE active = true")
// result = []map[string]any{{"count": 42}}
```
### SUM
```go theme={}
result, _ := client.Query(":SUM balance FROM Account")
// result = []map[string]any{{"sum": 15000.50}}
```
### AVG
```go theme={}
result, _ := client.Query(":AVG age FROM User")
// result = []map[string]any{{"avg": 28.5}}
```
### MIN / MAX
```go theme={}
result, _ := client.Query(":MIN price FROM Product")
// result = []map[string]any{{"min": 9.99}}
result, _ := client.Query(":MAX score FROM Player")
// result = []map[string]any{{"max": 99500}}
```
## Transactions
Redis supports transactions with MULTI/EXEC:
```go theme={}
client.Query(":BEGIN")
client.Query(`:UPDATE User SET login_count:5 WHERE id = 1`)
client.Query(`:UPDATE User SET last_login:"2025-01-15" WHERE id = 1`)
client.Query(":COMMIT")
```
Translates to:
```redis theme={}
MULTI
HSET tenant_1:user:1 login_count "5"
HSET tenant_1:user:1 last_login "2025-01-15"
EXEC
```
**Rollback:**
```go theme={}
client.Query(":BEGIN")
client.Query(`:UPDATE User SET status:"banned" WHERE id = 1`)
client.Query(":ROLLBACK") // Cancels - nothing executed
```
> **Note:** `DISCARD` cancels the transaction before execution. Once `EXEC` runs, changes cannot be rolled back.
## Permissions (ACL)
Redis uses ACL for user management:
### CREATE USER
```go theme={}
client.Query(`:CREATE USER analyst WITH PASSWORD "secret123"`)
// → ACL SETUSER analyst on >secret123
```
### GRANT
```go theme={}
client.Query(`:GRANT READ ON User TO analyst`)
// → ACL SETUSER analyst +hgetall +get
```
### REVOKE
```go theme={}
client.Query(`:REVOKE WRITE ON User FROM analyst`)
// → ACL SETUSER analyst -hset -hmset -del
```
### DROP USER
```go theme={}
client.Query(`:DROP USER analyst`)
// → ACL DELUSER analyst
```
> **Note:** Redis has users with permissions, not roles. `CREATE ROLE`, `DROP ROLE`, `ASSIGN ROLE` are not supported.
## Type Storage
All Redis values are stored as strings:
| OmniQL Type | Redis Storage |
| ----------- | ----------------------- |
| `STRING` | String |
| `INT` | String ("42") |
| `BOOLEAN` | String ("true"/"false") |
| `TIMESTAMP` | String (ISO format) |
| `JSON` | String (serialized) |
| `UUID` | String |
OmniQL automatically converts types when filtering.
## Supported Operations
| Operation | Supported | Notes |
| --------------------- | --------- | -------------------- |
| GET | ✅ | HGETALL |
| CREATE | ✅ | HMSET |
| UPDATE | ✅ | HSET |
| DELETE | ✅ | DEL |
| UPSERT | ✅ | HMSET |
| BULK INSERT | ✅ | Multiple HMSET |
| DROP TABLE | ✅ | DEL pattern |
| COUNT | ✅ | Via OmniQL |
| SUM / AVG / MIN / MAX | ✅ | Via OmniQL |
| WHERE (all operators) | ✅ | Via OmniQL filtering |
| ORDER BY | ✅ | Via OmniQL |
| LIMIT / OFFSET | ✅ | Via OmniQL |
| BEGIN / COMMIT | ✅ | MULTI / EXEC |
| ROLLBACK | ✅ | DISCARD |
| CREATE USER | ✅ | ACL SETUSER |
| GRANT / REVOKE | ✅ | ACL SETUSER |
## Not Supported
| Feature | Reason |
| ----------------- | -------------------------------- |
| JOIN | Key-value model has no relations |
| GROUP BY / HAVING | Use aggregations instead |
| Window functions | No SQL semantics |
| SAVEPOINT | Redis has no partial rollback |
| Roles | Redis has users only, not roles |
## Performance Considerations
| Query Type | Performance | When to Use |
| ---------------------------- | ---------------- | -------------------------- |
| `WHERE id = X` | ⚡ Instant | Always preferred |
| `WHERE field = X LIMIT N` | 🔄 Scan + filter | Small datasets, with LIMIT |
| `WHERE field = X` (no limit) | ⚠️ Full scan | Avoid on large datasets |
### Best Practices
1. **Use ID lookups when possible** - Direct key access is instant
2. **Always use LIMIT** - Prevents scanning entire keyspace
3. **Index hot queries in SQL** - For complex filtering, consider PostgreSQL
4. **Use Redis for what it's good at** - Sessions, caching, counters, real-time data
## When to Use Redis with OmniQL
**Good use cases:**
* Session storage
* User profiles / settings
* Caching layer
* Real-time counters
* Simple CRUD by ID
* Aggregations on bounded datasets
**Consider PostgreSQL instead:**
* Complex queries with multiple filters
* Relational data with joins
* Large datasets requiring full scans
* Data requiring GROUP BY
## Next Steps
Full-featured SQL database
All supported operators
# Go Package
Source: https://docs.omniql.com/integration/go-package
Using OmniQL in your Go application
## Installation
```bash theme={}
go get github.com/omniql-engine/omniql
```
## Quick Start
OmniQL wraps your existing database connection. You bring the driver, we handle the rest.
```go theme={}
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
"github.com/omniql-engine/omniql"
)
func main() {
// Your existing database connection
db, _ := sql.Open("postgres", "postgres://localhost/mydb")
// Wrap it with OmniQL
client := oql.WrapSQL(db, "PostgreSQL")
// Query with OmniQL syntax
users, _ := client.Query(":GET User WHERE age > 21")
for _, user := range users {
fmt.Println(user["name"], user["age"])
}
}
```
## Database Wrappers
### PostgreSQL
```go theme={}
import (
"database/sql"
_ "github.com/lib/pq"
"github.com/omniql-engine/omniql"
)
db, _ := sql.Open("postgres", "postgres://user:pass@localhost/mydb")
client := oql.WrapSQL(db, "PostgreSQL")
```
### MySQL
```go theme={}
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/omniql-engine/omniql"
)
db, _ := sql.Open("mysql", "user:pass@tcp(localhost:3306)/mydb")
client := oql.WrapSQL(db, "MySQL")
```
### MongoDB
```go theme={}
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/omniql-engine/omniql"
)
mongoClient, _ := mongo.Connect(context.Background(),
options.Client().ApplyURI("mongodb://localhost:27017"))
db := mongoClient.Database("mydb")
client := oql.WrapMongo(db)
```
### Redis
```go theme={}
import (
"github.com/redis/go-redis/v9"
"github.com/omniql-engine/omniql"
)
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
client := oql.WrapRedis(rdb, "") // Optional tenant prefix as second arg
```
## The Query Method
```go theme={}
func (c *Client) Query(input string) ([]map[string]any, error)
```
All queries return `[]map[string]any` regardless of database type.
### SELECT (GET)
```go theme={}
users, err := client.Query(":GET User WHERE age > 21 ORDER BY name ASC LIMIT 10")
// users = []map[string]any{
// {"id": 1, "name": "Alice", "age": 25},
// {"id": 2, "name": "Bob", "age": 30},
// }
for _, user := range users {
fmt.Printf("%s is %d years old\n", user["name"], user["age"])
}
```
### INSERT (CREATE)
```go theme={}
result, err := client.Query(`:CREATE User WITH name:"Alice", age:28, email:"alice@example.com"`)
// result = []map[string]any{
// {"inserted_id": 3, "rows_affected": 1},
// }
newID := result[0]["inserted_id"]
```
### UPDATE
```go theme={}
result, err := client.Query(`:UPDATE User SET verified:true WHERE id = 3`)
// result = []map[string]any{
// {"rows_affected": 1},
// }
```
### DELETE
```go theme={}
result, err := client.Query(`:DELETE User WHERE status = "inactive"`)
// result = []map[string]any{
// {"rows_affected": 5},
// }
```
### COUNT
```go theme={}
result, err := client.Query(`:COUNT User WHERE active = true`)
// result = []map[string]any{
// {"count": 42},
// }
count := result[0]["count"]
```
## Multi-Tenant Support
```go theme={}
// Set tenant context
client := oql.WrapSQL(db, "PostgreSQL")
client.SetTenant("acme_corp")
// Queries now include tenant context
users, _ := client.Query(":GET User WHERE age > 21")
// Internally adds: WHERE tenant_id = 'acme_corp' AND age > 21
```
## Polyglot Persistence
Use multiple databases with the same query syntax.
```go theme={}
// Setup
pgClient := oql.WrapSQL(pgDB, "PostgreSQL")
mongoClient := oql.WrapMongo(mongoDB)
redisClient := oql.WrapRedis(redisDB, "")
// Same query, any backend
query := ":GET User WHERE status = \"active\" LIMIT 10"
pgUsers, _ := pgClient.Query(query) // PostgreSQL
mongoUsers, _ := mongoClient.Query(query) // MongoDB
redisUsers, _ := redisClient.Query(query) // Redis
// All return []map[string]any
```
## Error Handling
```go theme={}
users, err := client.Query(":GET User WHERE age > 21")
if err != nil {
log.Printf("Query failed: %v", err)
return
}
fmt.Printf("Found %d users\n", len(users))
```
## Complete Example
```go theme={}
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
"github.com/omniql-engine/omniql"
)
func main() {
// Connect to database
db, err := sql.Open("postgres", "postgres://localhost/myapp?sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Wrap with OmniQL
client := oql.WrapSQL(db, "PostgreSQL")
// Create a user
result, err := client.Query(`:CREATE User WITH name:"John", age:25, email:"john@example.com"`)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created user with ID: %v\n", result[0]["inserted_id"])
// Query users
users, err := client.Query(":GET User WHERE age > 21 ORDER BY name ASC")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d users:\n", len(users))
for _, user := range users {
fmt.Printf(" - %s (%v years old)\n", user["name"], user["age"])
}
// Count users
countResult, _ := client.Query(":COUNT User WHERE active = true")
fmt.Printf("Active users: %v\n", countResult[0]["count"])
// Update user
client.Query(`:UPDATE User SET verified:true WHERE email = "john@example.com"`)
// Delete inactive users
deleteResult, _ := client.Query(`:DELETE User WHERE active = false`)
fmt.Printf("Deleted %v inactive users\n", deleteResult[0]["rows_affected"])
}
```
## Low-Level API
For advanced use cases, you can access the parse and translate steps directly.
### Parse Only
```go theme={}
import "github.com/omniql-engine/omniql"
query, isOQL, err := oql.Parse(":GET User WHERE age > 21")
// query = *models.Query (AST)
// isOQL = true
// err = nil
```
### Translate Only
```go theme={}
import "github.com/omniql-engine/omniql/engine/translator"
// Parse first
query, _, _ := oql.Parse(":GET User WHERE age > 21")
// Then translate to any database
result, _ := translator.Translate(query, "PostgreSQL", "tenant_1")
// Access native query
sql := result.GetRelational().Sql
// → SELECT * FROM "users" WHERE "age" > 21
```
### Database-Specific Accessors
| Database | Result Type | Accessor |
| ---------- | ----------------- | ------------------------------------ |
| PostgreSQL | `RelationalQuery` | `result.GetRelational().Sql` |
| MySQL | `RelationalQuery` | `result.GetRelational().Sql` |
| MongoDB | `DocumentQuery` | `result.GetDocument().Query` |
| Redis | `KeyValueQuery` | `result.GetKeyValue().CommandString` |
## Package Structure
```
github.com/omniql-engine/omniql/
├── oql.go # Parse(), WrapSQL(), WrapMongo(), WrapRedis()
├── client.go # Client struct and Query() method
├── engine/
│ ├── parser/ # Query parser
│ ├── lexer/ # Tokenizer
│ ├── models/ # Query AST
│ ├── translator/ # Database translators
│ ├── builders/ # Query builders (including Redis filters)
│ └── validator/ # Query validators
└── mapping/ # Type and operator mappings
```
## Next Steps
5-minute getting started
Learn GET operations
Redis-specific features
Insert and Update data
# Native Queries
Source: https://docs.omniql.com/integration/native-queries
Using native database drivers alongside OmniQL
OmniQL requires the `:` prefix for all queries. If you need to execute native database commands directly, use your driver alongside OmniQL.
## Pattern
OmniQL wraps your connection but doesn't replace it. Keep both references.
```go theme={}
// Keep both
db, _ := sql.Open("postgres", connString) // Native driver
client := oql.WrapSQL(db, "PostgreSQL") // OmniQL wrapper
// OmniQL
users, _ := client.Query(":GET User WHERE active = true")
// Native
rows, _ := db.Query("SELECT * FROM users WHERE active = true")
```
## PostgreSQL / MySQL
```go theme={}
db, _ := sql.Open("postgres", "postgres://localhost/mydb")
client := oql.WrapSQL(db, "PostgreSQL")
// OmniQL
client.Query(":GET User WHERE age > 21")
// Native
db.Query("SELECT * FROM users WHERE age > 21")
db.Exec("VACUUM ANALYZE users")
```
## MongoDB
```go theme={}
mongoClient, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
db := mongoClient.Database("mydb")
client := oql.WrapMongo(db)
// OmniQL
client.Query(":GET User WHERE age > 21")
// Native
db.Collection("users").Find(ctx, bson.M{"age": bson.M{"$gt": 21}})
db.RunCommand(ctx, bson.D{{"ping", 1}})
```
## Redis
```go theme={}
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
client := oql.WrapRedis(rdb, "")
// OmniQL
client.Query(":GET User WHERE id = 42")
// Native
rdb.HGetAll(ctx, "user:42")
rdb.Ping(ctx)
```
## Next Steps
Full OmniQL API reference
Learn OmniQL syntax
# Introduction
Source: https://docs.omniql.com/introduction
The universal query compiler for Go
# What is OmniQL?
OmniQL is an open-source **Go library** that compiles a single query language into native PostgreSQL, MySQL, MongoDB, and Redis syntax.
It wraps your existing database connection, allowing you to decouple your business logic from your database implementation.
```go theme={}
// Your database connection
db, _ := sql.Open("postgres", "postgres://localhost/mydb")
// Wrap it with OmniQL
client := oql.WrapSQL(db, "PostgreSQL")
// Query with universal syntax
users, _ := client.Query(":GET User WHERE age > 21")
```
The same query works on any supported database:
| Database | Native Output |
| ---------- | ------------------------------------------------ |
| PostgreSQL | `SELECT * FROM "users" WHERE "age" > 21` |
| MySQL | `SELECT * FROM \`users\` WHERE \`age\` > 21\` |
| MongoDB | `db.users.find({ "age": { "$gt": 21 } })` |
| Redis | `HGETALL` with conditions filtered via Go helper |
## Why OmniQL?
**Problem:** Switching databases (e.g., from Postgres to Mongo) usually requires rewriting your entire data access layer. Supporting multiple databases in one product requires maintaining multiple codebases.
**Solution:** OmniQL acts as a compiler. You write your queries once in OmniQL syntax, and the engine instantly translates them into native commands for your target database.
## Key Features
* **Universal Syntax:** Learn one syntax (`:GET`, `:CREATE`, `:UPDATE`, `:DELETE`) instead of memorizing SQL dialects, BSON maps, and Redis commands.
* **Plug and Play:** Wrap your existing database connection with `WrapSQL()`, `WrapMongo()`, or `WrapRedis()` and start querying immediately.
* **Zero Overhead:** OmniQL translates queries instantly. No ORM magic, no reflection—just native query strings executed by your standard drivers.
* **TrueAST Architecture:** Queries are parsed into a recursive Abstract Syntax Tree, enabling complex nested expressions and type safety.
* **Polyglot Persistence:** Use relational, document, and key-value stores with the same query syntax.
## Supported Databases
| Database | Wrapper | Status |
| ---------- | ------------- | ------------ |
| PostgreSQL | `WrapSQL()` | Full support |
| MySQL | `WrapSQL()` | Full support |
| MongoDB | `WrapMongo()` | Full support |
| Redis | `WrapRedis()` | Full support |
## How It Works
1. **Wrap:** Connect OmniQL to your existing database connection.
2. **Query:** Pass an OmniQL string (starting with `:`) to `client.Query()`.
3. **Compile:** The parser validates syntax and builds the AST.
4. **Translate:** The translator generates native commands for your database.
5. **Execute:** OmniQL runs the query and returns results as `[]map[string]any`.
## Quick Example
```go theme={}
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
"github.com/omniql-engine/omniql"
)
func main() {
// Your existing connection
db, _ := sql.Open("postgres", "postgres://localhost/mydb")
// Wrap with OmniQL
client := oql.WrapSQL(db, "PostgreSQL")
// CRUD operations
client.Query(`:CREATE User WITH name:"Alice", age:25`)
users, _ := client.Query(":GET User WHERE age > 21")
for _, user := range users {
fmt.Println(user["name"], user["age"])
}
client.Query(`:UPDATE User SET verified:true WHERE name = "Alice"`)
client.Query(`:DELETE User WHERE active = false`)
}
```
## Next Steps
Install the Go package and run your first query
Learn the : prefix and syntax rules
# Delete
Source: https://docs.omniql.com/mutations/delete
Remove records with DELETE
Remove records using the DELETE operation.
## Basic Syntax
```sql theme={}
:DELETE Entity WHERE condition
:DELETE FROM Entity WHERE condition
```
Both forms are valid. The `FROM` keyword is optional.
## Delete Single Record
```sql theme={}
:DELETE User WHERE id = 1
```
| Database | Output |
| ---------- | -------------------------------- |
| PostgreSQL | `DELETE FROM users WHERE id = 1` |
| MySQL | `DELETE FROM users WHERE id = 1` |
| MongoDB | `db.users.deleteOne({ _id: 1 })` |
| Redis | `DEL users:1` |
## Delete with Conditions
```sql theme={}
:DELETE User WHERE status = "inactive" AND last_login < "2023-01-01"
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------------- |
| PostgreSQL | `DELETE FROM users WHERE status = 'inactive' AND last_login < '2023-01-01'` |
| MongoDB | `db.users.deleteMany({ status: 'inactive', last_login: { $lt: '2023-01-01' } })` |
## Delete with IN
```sql theme={}
:DELETE User WHERE id IN (1, 2, 3, 4, 5)
```
| Database | Output |
| ---------- | -------------------------------------------------------- |
| PostgreSQL | `DELETE FROM users WHERE id IN (1, 2, 3, 4, 5)` |
| MongoDB | `db.users.deleteMany({ _id: { $in: [1, 2, 3, 4, 5] } })` |
## Delete with LIKE
```sql theme={}
:DELETE Log WHERE message LIKE "%debug%"
```
| Database | Output |
| ---------- | ------------------------------------------------------ |
| PostgreSQL | `DELETE FROM logs WHERE message LIKE '%debug%'` |
| MongoDB | `db.logs.deleteMany({ message: { $regex: 'debug' } })` |
## Delete with NULL Check
```sql theme={}
:DELETE User WHERE email IS NULL
```
| Database | Output |
| ---------- | --------------------------------------- |
| PostgreSQL | `DELETE FROM users WHERE email IS NULL` |
| MongoDB | `db.users.deleteMany({ email: null })` |
## Delete with BETWEEN
```sql theme={}
:DELETE Log WHERE created_at BETWEEN "2023-01-01" AND "2023-06-30"
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------- |
| PostgreSQL | `DELETE FROM logs WHERE created_at BETWEEN '2023-01-01' AND '2023-06-30'` |
## Complete Examples
### Remove Expired Sessions
```sql theme={}
:DELETE Session WHERE expires_at < "2024-01-15T00:00:00Z"
```
### Clean Up Old Logs
```sql theme={}
:DELETE Log WHERE created_at < "2023-01-01" AND level = "debug"
```
### Remove Unverified Users
```sql theme={}
:DELETE User WHERE verified = false AND created_at < "2023-12-01"
```
### Cancel Abandoned Orders
```sql theme={}
:DELETE Order WHERE status = "pending" AND created_at < "2024-01-01"
```
### Remove Test Data
```sql theme={}
:DELETE User WHERE email LIKE "%@test.com"
```
## Soft Delete Alternative
Instead of deleting, consider soft delete:
```sql theme={}
-- Soft delete (recommended)
:UPDATE User SET deleted_at:"2024-01-15T10:30:00Z", active:false WHERE id = 1
-- Hard delete (permanent)
:DELETE User WHERE id = 1
```
## Truncate
Remove all records from a table. Both syntaxes work:
```sql theme={}
:TRUNCATE Log
:TRUNCATE TABLE Log
```
| Database | Output |
| ---------- | ------------------------ |
| PostgreSQL | `TRUNCATE TABLE logs` |
| MySQL | `TRUNCATE TABLE logs` |
| MongoDB | `db.logs.deleteMany({})` |
**Warning:** TRUNCATE removes ALL records and cannot be rolled back in most databases.
## Warning
Always include a WHERE clause to avoid deleting all records accidentally.
```sql theme={}
-- DANGEROUS: Deletes ALL users
:DELETE User
-- SAFE: Deletes specific user
:DELETE User WHERE id = 1
```
## Next Steps
Define your schema
Group operations safely
# Insert
Source: https://docs.omniql.com/mutations/insert
Create new records with CREATE
Create new records using the CREATE operation.
## Basic Syntax
```sql theme={}
:CREATE Entity WITH field:value, field:value
```
## Create Single Record
```sql theme={}
:CREATE User WITH name:"John", email:"john@example.com", age:30
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------------ |
| PostgreSQL | `INSERT INTO users (name, email, age) VALUES ('John', 'john@example.com', 30)` |
| MySQL | `INSERT INTO users (name, email, age) VALUES ('John', 'john@example.com', 30)` |
| MongoDB | `db.users.insertOne({ name: 'John', email: 'john@example.com', age: 30 })` |
| Redis | `HMSET users:1 name "John" email "john@example.com" age 30` |
## Data Types
```sql theme={}
-- String
:CREATE User WITH name:"John"
-- Number
:CREATE User WITH age:30
-- Boolean
:CREATE User WITH active:true
-- Null
:CREATE User WITH deleted_at:null
```
## Multiple Fields
```sql theme={}
:CREATE Product WITH
name:"Laptop",
price:999.99,
category:"Electronics",
in_stock:true,
quantity:50
```
## With Timestamps
```sql theme={}
:CREATE User WITH
name:"John",
email:"john@example.com",
created_at:"2024-01-15T10:30:00Z"
```
## Bulk Insert
Insert multiple records at once. Each record is wrapped in square brackets with `=` assignments.
```sql theme={}
:BULK INSERT User WITH
[name = Alice, age = 25]
[name = Bob, age = 30]
[name = Charlie, age = 35]
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------------------------------- |
| PostgreSQL | `INSERT INTO users (name, age) VALUES ('Alice', 25), ('Bob', 30), ('Charlie', 35)` |
| MySQL | `INSERT INTO users (name, age) VALUES ('Alice', 25), ('Bob', 30), ('Charlie', 35)` |
| MongoDB | `db.users.insertMany([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Charlie', age: 35 }])` |
## Upsert
Insert or update if exists. Specify conflict field(s) with `ON`.
```sql theme={}
:UPSERT User WITH email:"john@example.com", name:"John Updated", login_count:1 ON email
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------------------------------- |
| PostgreSQL | `INSERT INTO users (...) ON CONFLICT (email) DO UPDATE SET name = 'John Updated', login_count = 1` |
| MySQL | `INSERT INTO users (...) ON DUPLICATE KEY UPDATE name = 'John Updated', login_count = 1` |
| MongoDB | `db.users.updateOne({ email: 'john@example.com' }, { $set: { ... } }, { upsert: true })` |
## Upsert with Composite Key
```sql theme={}
:UPSERT OrderItem WITH order_id:1, product_id:5, quantity:3 ON order_id, product_id
```
## Replace
Delete and insert (MySQL-specific behavior).
```sql theme={}
:REPLACE User WITH id:1, name:"John Replaced", email:"john.new@example.com"
```
| Database | Output |
| ---------- | --------------------------------------------------- |
| PostgreSQL | Uses UPSERT behavior |
| MySQL | `REPLACE INTO users (id, name, email) VALUES (...)` |
| MongoDB | `db.users.replaceOne({ _id: 1 }, { ... })` |
## Complete Examples
### User Registration
```sql theme={}
:CREATE User WITH
email:"jane@example.com",
password_hash:"$2b$10$...",
name:"Jane Doe",
role:"user",
active:true,
created_at:"2024-01-15T10:30:00Z"
```
### E-commerce Order
```sql theme={}
:CREATE Order WITH
user_id:42,
total:149.99,
status:"pending",
shipping_address:"123 Main St",
created_at:"2024-01-15T10:30:00Z"
```
### Bulk Product Import
```sql theme={}
:BULK INSERT Product WITH
[sku = ABC123, name = Widget, price = 9.99, quantity = 100]
[sku = DEF456, name = Gadget, price = 19.99, quantity = 50]
[sku = GHI789, name = Gizmo, price = 29.99, quantity = 25]
```
## Next Steps
Modify existing records
Remove records
# Update
Source: https://docs.omniql.com/mutations/update
Modify existing records with UPDATE
Modify existing records using the UPDATE operation.
## Basic Syntax
```sql theme={}
:UPDATE Entity SET field:value WHERE condition
```
## Update Single Field
```sql theme={}
:UPDATE User SET status:"active" WHERE id = 1
```
| Database | Output |
| ---------- | ---------------------------------------------------------------- |
| PostgreSQL | `UPDATE users SET status = 'active' WHERE id = 1` |
| MySQL | `UPDATE users SET status = 'active' WHERE id = 1` |
| MongoDB | `db.users.updateOne({ _id: 1 }, { $set: { status: 'active' } })` |
| Redis | `HSET users:1 status "active"` |
## Update Multiple Fields
```sql theme={}
:UPDATE User SET name:"John Updated", age:31, updated_at:"2024-01-15T10:30:00Z" WHERE id = 1
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------- |
| PostgreSQL | `UPDATE users SET name = 'John Updated', age = 31, updated_at = '...' WHERE id = 1` |
| MongoDB | `db.users.updateOne({ _id: 1 }, { $set: { name: 'John Updated', age: 31, ... } })` |
## Update with Conditions
```sql theme={}
:UPDATE User SET verified:true WHERE email_confirmed = true AND created_at < "2024-01-01"
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------------------------------- |
| PostgreSQL | `UPDATE users SET verified = true WHERE email_confirmed = true AND created_at < '2024-01-01'` |
| MongoDB | `db.users.updateMany({ email_confirmed: true, created_at: { $lt: '...' } }, { $set: { verified: true } })` |
## Bulk Update
Update multiple records matching condition.
```sql theme={}
:UPDATE Product SET on_sale:true WHERE category = "Electronics"
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------- |
| PostgreSQL | `UPDATE products SET on_sale = true WHERE category = 'Electronics'` |
| MongoDB | `db.products.updateMany({ category: 'Electronics' }, { $set: { on_sale: true } })` |
## Update with IN
```sql theme={}
:UPDATE User SET role:"premium" WHERE id IN (1, 2, 3, 4, 5)
```
| Database | Output |
| ---------- | --------------------------------------------------------------------------------------- |
| PostgreSQL | `UPDATE users SET role = 'premium' WHERE id IN (1, 2, 3, 4, 5)` |
| MongoDB | `db.users.updateMany({ _id: { $in: [1, 2, 3, 4, 5] } }, { $set: { role: 'premium' } })` |
## Set to NULL
```sql theme={}
:UPDATE User SET deleted_at:null WHERE id = 1
```
| Database | Output |
| ---------- | ---------------------------------------------------------------- |
| PostgreSQL | `UPDATE users SET deleted_at = NULL WHERE id = 1` |
| MongoDB | `db.users.updateOne({ _id: 1 }, { $set: { deleted_at: null } })` |
## Increment Values
```sql theme={}
:UPDATE Product SET quantity = quantity + 10 WHERE id = 1
:UPDATE User SET login_count = login_count + 1 WHERE id = 1
```
| Database | Output |
| ---------- | --------------------------------------------------------------- |
| PostgreSQL | `UPDATE products SET quantity = quantity + 10 WHERE id = 1` |
| MongoDB | `db.products.updateOne({ _id: 1 }, { $inc: { quantity: 10 } })` |
## Decrement Values
```sql theme={}
:UPDATE Product SET stock = stock - 1 WHERE id = 1
```
| Database | Output |
| ---------- | ------------------------------------------------------------ |
| PostgreSQL | `UPDATE products SET stock = stock - 1 WHERE id = 1` |
| MongoDB | `db.products.updateOne({ _id: 1 }, { $inc: { stock: -1 } })` |
## Using Functions
```sql theme={}
:UPDATE User SET name = UPPER(name) WHERE id = 1
```
| Database | Output |
| ---------- | -------------------------------------------------- |
| PostgreSQL | `UPDATE users SET name = UPPER(name) WHERE id = 1` |
## Complete Examples
### Soft Delete
```sql theme={}
:UPDATE User SET deleted_at:"2024-01-15T10:30:00Z", active:false WHERE id = 1
```
### Deactivate Inactive Users
```sql theme={}
:UPDATE User SET active:false WHERE last_login < "2023-01-01" AND active = true
```
### Update Order Status
```sql theme={}
:UPDATE Order SET
status:"shipped",
shipped_at:"2024-01-15T10:30:00Z",
tracking_number:"1Z999AA10123456784"
WHERE id = 1001
```
### Price Adjustment
```sql theme={}
:UPDATE Product SET price = price * 1.1 WHERE category = "Electronics"
```
Increases all electronics prices by 10%.
## Warning
Always include a WHERE clause to avoid updating all records accidentally.
```sql theme={}
-- DANGEROUS: Updates ALL users
:UPDATE User SET status:"inactive"
-- SAFE: Updates specific user
:UPDATE User SET status:"inactive" WHERE id = 1
```
## Next Steps
Remove records
Group operations
# Syntax Basics
Source: https://docs.omniql.com/prefix
Understanding the : prefix and syntax rules
# The : Prefix
All OmniQL queries start with a colon (`:`).
```sql theme={}
:GET User WHERE id = 1
```
## Why the Prefix?
OmniQL and native SQL share many keywords (`CREATE`, `UPDATE`, `SELECT`). The prefix acts as a switch, telling the system exactly how to handle the string.
| Query | Behavior |
| -------------- | ------------------------------------ |
| `:GET User...` | ✅ Parsed and translated to target DB |
| `SELECT * ...` | ❌ Error - missing `:` prefix |
All queries must use OmniQL syntax. For native database commands, use your database driver directly.
## Parser Behavior
The `oql.Parse()` function uses this prefix to determine if it should engage the compiler.
```go theme={}
import "github.com/omniql-engine/omniql"
// 1. OmniQL Query (Starts with :)
query, isOQL, err := oql.Parse(":GET User WHERE id = 1")
// isOQL = true
// query = *models.Query (AST)
// 2. Query without prefix = Error
query, isOQL, err := oql.Parse("SELECT * FROM users")
// isOQL = false
// client.Query() will return error: "OmniQL syntax required"
```
## Syntax Pattern
An OmniQL command follows this structure:
```
:OPERATION Entity [clauses]
```
| Part | Description | Example |
| ----------- | ----------------------- | ----------------------------------- |
| `:` | Required Prefix | `:` |
| `OPERATION` | Action to perform | `GET`, `CREATE`, `UPDATE`, `DELETE` |
| `Entity` | Target Table/Collection | `User`, `Product`, `Order` |
| `[clauses]` | Optional modifiers | `WHERE`, `ORDER BY`, `LIMIT` |
## Entity Naming
OmniQL uses PascalCase entity names. The translator automatically lowercases and pluralizes them:
| You Write | Output |
| ----------- | ------------ |
| `User` | `users` |
| `Product` | `products` |
| `OrderItem` | `orderitems` |
> **Note:** The engine lowercases then pluralizes. It does not add underscores (snake\_case).
## Examples
```sql theme={}
-- Queries
:GET User WHERE age > 21
:GET id, name, price FROM Product WHERE active = true
-- Mutations
:CREATE User WITH name:"John", age:30
:UPDATE User SET verified:true WHERE id = 1
:DELETE User WHERE status = "inactive"
-- Schema DDL
:CREATE TABLE User WITH id:AUTO, name:STRING, email:STRING
:CREATE INDEX idx_email ON User (email)
```
## Next Steps
Get running in 5 minutes
Learn WHERE clauses and operators
# Advanced Queries
Source: https://docs.omniql.com/queries/advanced
CTEs, subqueries, set operations, and CASE expressions
Advanced query features for complex data retrieval.
## Common Table Expressions (CTE)
CTEs create temporary named result sets for use in queries.
```sql theme={}
:CTE active_users AS (GET User WHERE active = true)
```
| Database | Output |
| ---------- | ---------------------------------------------------------------- |
| PostgreSQL | `WITH active_users AS (SELECT * FROM users WHERE active = true)` |
| MySQL | `WITH active_users AS (SELECT * FROM users WHERE active = true)` |
### Use Cases
CTEs are useful for:
* Breaking complex queries into readable parts
* Reusing the same subquery multiple times
* Recursive queries
## Subqueries
Filter using results from another query.
```sql theme={}
:SUBQUERY id IN (GET User WHERE active = true)
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM ... WHERE id IN (SELECT id FROM users WHERE active = true)` |
### Example: Users with Orders
```sql theme={}
:SUBQUERY user_id IN (GET Order WHERE total > 100)
```
## EXISTS
Check if a subquery returns any results.
```sql theme={}
:EXISTS (GET User WHERE email = "john@example.com")
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------- |
| PostgreSQL | `SELECT EXISTS(SELECT 1 FROM users WHERE email = 'john@example.com')` |
| MongoDB | `db.users.countDocuments({ email: 'john@example.com' }, { limit: 1 })` |
## Set Operations
Combine results from multiple queries.
### UNION
Combine results, removing duplicates.
```sql theme={}
:UNION (GET User WHERE age > 50) (GET User WHERE role = "premium")
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------------- |
| PostgreSQL | `(SELECT * FROM users WHERE age > 50) UNION (SELECT * FROM users WHERE role = 'premium')` |
### UNION ALL
Combine results, keeping duplicates.
```sql theme={}
:UNION ALL (GET User WHERE department = "sales") (GET User WHERE department = "marketing")
```
### INTERSECT
Return only rows that appear in both queries.
```sql theme={}
:INTERSECT (GET User WHERE age > 30) (GET User WHERE active = true)
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------------------------ |
| PostgreSQL | `(SELECT * FROM users WHERE age > 30) INTERSECT (SELECT * FROM users WHERE active = true)` |
### EXCEPT
Return rows from first query that don't appear in second.
```sql theme={}
:EXCEPT (GET User WHERE active = true) (GET User WHERE role = "banned")
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------------------- |
| PostgreSQL | `(SELECT * FROM users WHERE active = true) EXCEPT (SELECT * FROM users WHERE role = 'banned')` |
## CASE Expressions
Conditional logic within queries. CASE must be used within GET expressions.
```sql theme={}
:GET User WITH CASE WHEN age > 25 THEN "adult" ELSE "minor" END AS category
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT *, CASE WHEN age > 25 THEN 'adult' ELSE 'minor' END AS category FROM users` |
### Multiple Conditions
```sql theme={}
:GET User WITH CASE
WHEN age < 18 THEN "minor"
WHEN age < 65 THEN "adult"
ELSE "senior"
END AS age_group
```
### CASE in WHERE
```sql theme={}
:GET Order WHERE CASE WHEN total > 1000 THEN "large" ELSE "small" END = "large"
```
## Database Support
| Feature | PostgreSQL | MySQL | MongoDB |
| --------- | ---------- | ---------- | --------------- |
| CTE | Yes | Yes (8.0+) | No |
| SUBQUERY | Yes | Yes | Via aggregation |
| EXISTS | Yes | Yes | Via count |
| UNION | Yes | Yes | Via \$unionWith |
| UNION ALL | Yes | Yes | Via \$unionWith |
| INTERSECT | Yes | Yes (8.0+) | Via aggregation |
| EXCEPT | Yes | Yes (8.0+) | Via aggregation |
| CASE | Yes | Yes | Via \$cond |
## Complete Examples
### Active Premium Users with Order Stats
```sql theme={}
:CTE premium AS (GET User WHERE role = "premium" AND active = true)
```
### Users Without Recent Orders
```sql theme={}
:EXCEPT (GET User WHERE active = true) (SUBQUERY user_id IN (GET Order WHERE created_at > "2024-01-01"))
```
### Categorized Products
```sql theme={}
:GET Product WITH
name,
price,
CASE
WHEN price < 10 THEN "budget"
WHEN price < 100 THEN "standard"
ELSE "premium"
END AS tier
```
## Next Steps
ROW NUMBER, RANK, LAG, LEAD
Combine multiple tables
# Query Basics
Source: https://docs.omniql.com/queries/basics
Read data with GET operations
The `GET` operation retrieves data from a table.
## Basic Syntax
```sql theme={}
:GET Entity
:GET Entity WHERE conditions
:GET field1, field2 FROM Entity
:GET field1, field2 FROM Entity WHERE conditions
```
## Get All Records
```sql theme={}
:GET User
```
| Database | Output |
| ---------- | --------------------- |
| PostgreSQL | `SELECT * FROM users` |
| MySQL | `SELECT * FROM users` |
| MongoDB | `db.users.find({})` |
## Select Specific Columns
```sql theme={}
:GET id, name, email FROM User
```
| Database | Output |
| ---------- | ------------------------------------------------- |
| PostgreSQL | `SELECT id, name, email FROM users` |
| MySQL | `SELECT id, name, email FROM users` |
| MongoDB | `db.users.find({}, { id: 1, name: 1, email: 1 })` |
## Simple WHERE Clause
```sql theme={}
:GET User WHERE id = 1
```
| Database | Output |
| ---------- | ---------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE id = 1` |
| MySQL | `SELECT * FROM users WHERE id = 1` |
| MongoDB | `db.users.find({ id: 1 })` |
## Multiple Conditions
```sql theme={}
:GET User WHERE age > 21 AND status = "active"
```
| Database | Output |
| ---------- | ---------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE age > 21 AND status = 'active'` |
| MongoDB | `db.users.find({ age: { $gt: 21 }, status: 'active' })` |
## Combine Columns and Conditions
```sql theme={}
:GET id, name FROM User WHERE active = true
```
| Database | Output |
| ---------- | ----------------------------------------------------- |
| PostgreSQL | `SELECT id, name FROM users WHERE active = true` |
| MongoDB | `db.users.find({ active: true }, { id: 1, name: 1 })` |
## Limit Results
```sql theme={}
:GET User LIMIT 10
:GET User WHERE active = true LIMIT 5
```
| Database | Output |
| ---------- | ------------------------------ |
| PostgreSQL | `SELECT * FROM users LIMIT 10` |
| MySQL | `SELECT * FROM users LIMIT 10` |
| MongoDB | `db.users.find({}).limit(10)` |
## Skip Results (Pagination)
```sql theme={}
:GET User LIMIT 10 OFFSET 20
```
| Database | Output |
| ---------- | ---------------------------------------- |
| PostgreSQL | `SELECT * FROM users LIMIT 10 OFFSET 20` |
| MongoDB | `db.users.find({}).skip(20).limit(10)` |
## Standalone Aggregates
Aggregates can also be used as standalone operations:
```sql theme={}
:COUNT * FROM User
:COUNT * FROM User WHERE active = true
:SUM amount FROM Order WHERE status = "completed"
:AVG price FROM Product
:MIN created_at FROM User
:MAX total FROM Order
```
| Database | Output |
| ---------- | ----------------------------------------------------------- |
| PostgreSQL | `SELECT COUNT(*) FROM users` |
| PostgreSQL | `SELECT SUM(amount) FROM orders WHERE status = 'completed'` |
## DISTINCT
Get unique values:
```sql theme={}
:GET User DISTINCT
:GET role FROM User DISTINCT
```
## Next Steps
Advanced WHERE conditions
ORDER BY operations
# Filtering
Source: https://docs.omniql.com/queries/filtering
Advanced WHERE conditions and operators
Filter data using WHERE clauses with various operators.
## Comparison Operators
```sql theme={}
:GET User WHERE age = 25
:GET User WHERE age != 25
:GET User WHERE age > 21
:GET User WHERE age >= 21
:GET User WHERE age < 65
:GET User WHERE age <= 65
```
| Operator | Meaning |
| -------- | --------------------- |
| `=` | Equal |
| `!=` | Not equal |
| `>` | Greater than |
| `>=` | Greater than or equal |
| `<` | Less than |
| `<=` | Less than or equal |
## Logical Operators
### AND
```sql theme={}
:GET User WHERE age > 21 AND status = "active"
```
| Database | Output |
| ---------- | ---------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE age > 21 AND status = 'active'` |
| MongoDB | `db.users.find({ age: { $gt: 21 }, status: 'active' })` |
### OR
```sql theme={}
:GET User WHERE role = "admin" OR role = "moderator"
```
| Database | Output |
| ---------- | -------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE role = 'admin' OR role = 'moderator'` |
| MongoDB | `db.users.find({ $or: [{ role: 'admin' }, { role: 'moderator' }] })` |
### Combined with Parentheses
```sql theme={}
:GET User WHERE (age > 21 AND status = "active") OR role = "admin"
```
## IN Operator
Match against a list of values.
```sql theme={}
:GET User WHERE role IN ("admin", "moderator", "editor")
```
| Database | Output |
| ---------- | -------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE role IN ('admin', 'moderator', 'editor')` |
| MongoDB | `db.users.find({ role: { $in: ['admin', 'moderator', 'editor'] } })` |
### NOT IN
```sql theme={}
:GET User WHERE status NOT IN ("banned", "suspended")
```
## BETWEEN Operator
Match a range of values.
```sql theme={}
:GET User WHERE age BETWEEN 18 AND 65
```
| Database | Output |
| ---------- | ------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE age BETWEEN 18 AND 65` |
| MongoDB | `db.users.find({ age: { $gte: 18, $lte: 65 } })` |
### Date Range
```sql theme={}
:GET Order WHERE created_at BETWEEN "2024-01-01" AND "2024-12-31"
```
### NOT BETWEEN
```sql theme={}
:GET Product WHERE price NOT BETWEEN 10 AND 50
```
## LIKE Operator
Pattern matching for strings.
```sql theme={}
:GET User WHERE name LIKE "John%"
:GET User WHERE email LIKE "%@gmail.com"
:GET User WHERE name LIKE "%smith%"
```
| Pattern | Meaning |
| ------------- | ---------------------- |
| `John%` | Starts with "John" |
| `%@gmail.com` | Ends with "@gmail.com" |
| `%smith%` | Contains "smith" |
| Database | Output |
| ---------- | ---------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE name LIKE 'John%'` |
| MongoDB | `db.users.find({ name: { $regex: '^John' } })` |
### NOT LIKE
```sql theme={}
:GET User WHERE email NOT LIKE "%@test.com"
```
### ILIKE (Case Insensitive)
```sql theme={}
:GET User WHERE name ILIKE "john%"
```
| Database | Output |
| ---------- | ---------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE name ILIKE 'john%'` |
| MySQL | `SELECT * FROM users WHERE LOWER(name) LIKE 'john%'` |
## NULL Checks
```sql theme={}
:GET User WHERE deleted_at IS NULL
:GET User WHERE phone IS NOT NULL
```
| Database | Output |
| ---------- | ---------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE deleted_at IS NULL` |
| MongoDB | `db.users.find({ deleted_at: null })` |
## Complex Example
```sql theme={}
:GET id, name, email FROM User
WHERE status = "active"
AND age BETWEEN 21 AND 45
AND role IN ("user", "premium")
AND email LIKE "%@company.com"
AND deleted_at IS NULL
```
## Next Steps
ORDER BY operations
CTEs, subqueries, and set operations
# Grouping
Source: https://docs.omniql.com/queries/grouping
Aggregate data with GROUP BY
Aggregate data using GROUP BY with aggregate functions.
## Basic Syntax
```sql theme={}
:COUNT * FROM Entity GROUP BY field
:SUM field FROM Entity GROUP BY field
:AVG field FROM Entity GROUP BY field
```
## Aggregate Functions
| Function | Description |
| ------------- | ------------- |
| `COUNT(*)` | Count rows |
| `SUM(column)` | Sum values |
| `AVG(column)` | Average value |
| `MIN(column)` | Minimum value |
| `MAX(column)` | Maximum value |
## Count by Group
```sql theme={}
:COUNT * FROM User GROUP BY status
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------- |
| PostgreSQL | `SELECT status, COUNT(*) FROM users GROUP BY status` |
| MongoDB | `db.users.aggregate([{ $group: { _id: '$status', count: { $sum: 1 } } }])` |
## Sum by Group
```sql theme={}
:SUM amount FROM Order GROUP BY status
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT status, SUM(amount) FROM orders GROUP BY status` |
| MongoDB | `db.orders.aggregate([{ $group: { _id: '$status', total: { $sum: '$amount' } } }])` |
## Average by Group
```sql theme={}
:AVG price FROM Product GROUP BY category
```
| Database | Output |
| ---------- | ------------------------------------------------------------- |
| PostgreSQL | `SELECT category, AVG(price) FROM products GROUP BY category` |
## Min/Max by Group
```sql theme={}
:MIN price FROM Product GROUP BY category
:MAX price FROM Product GROUP BY category
```
## Multiple Columns with GET WITH
For multiple columns with aggregates (except COUNT):
```sql theme={}
:GET Order WITH status, SUM(amount) AS total GROUP BY status
```
| Database | Output |
| ---------- | ----------------------------------------------------------------- |
| PostgreSQL | `SELECT status, SUM(amount) AS total FROM orders GROUP BY status` |
## Group by Multiple Columns
```sql theme={}
:SUM amount FROM Order GROUP BY status, user_id
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------- |
| PostgreSQL | `SELECT status, user_id, SUM(amount) FROM orders GROUP BY status, user_id` |
## HAVING Clause
Filter groups after aggregation.
```sql theme={}
:COUNT * FROM User GROUP BY status HAVING COUNT(*) > 10
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------- |
| PostgreSQL | `SELECT status, COUNT(*) FROM users GROUP BY status HAVING COUNT(*) > 10` |
## WHERE vs HAVING
* `WHERE` filters rows before grouping
* `HAVING` filters groups after aggregation
```sql theme={}
:SUM amount FROM Order WHERE created_at > "2024-01-01" GROUP BY status HAVING SUM(amount) > 1000
```
## With ORDER BY
```sql theme={}
:COUNT * FROM Product GROUP BY category ORDER BY count DESC
```
## Complete Example
```sql theme={}
:SUM amount FROM Order
WHERE created_at BETWEEN "2024-01-01" AND "2024-12-31"
GROUP BY status
HAVING SUM(amount) >= 500
ORDER BY sum DESC
LIMIT 100
```
## Next Steps
Advanced analytics
Create new records
# Joins
Source: https://docs.omniql.com/queries/joins
Combine data from multiple tables
Combine data from multiple tables using JOIN operations.
## Basic Syntax
```sql theme={}
:INNER JOIN Entity1 Entity2 ON field1 = field2
:LEFT JOIN Entity1 Entity2 ON field1 = field2
:RIGHT JOIN Entity1 Entity2 ON field1 = field2
:FULL JOIN Entity1 Entity2 ON field1 = field2
:CROSS JOIN Entity1 Entity2
```
## Inner Join
Returns only matching rows from both tables.
```sql theme={}
:INNER JOIN Order User ON user_id = id
```
| Database | Output |
| ---------- | --------------------------------------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM orders INNER JOIN users ON user_id = id` |
| MySQL | `SELECT * FROM orders INNER JOIN users ON user_id = id` |
| MongoDB | `db.orders.aggregate([{ $lookup: { from: 'users', localField: 'user_id', foreignField: '_id', as: 'user' } }])` |
## Left Join
Returns all rows from the first table, matched rows from second.
```sql theme={}
:LEFT JOIN User Order ON id = user_id
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users LEFT JOIN orders ON id = user_id` |
| MongoDB | `db.users.aggregate([{ $lookup: { from: 'orders', localField: '_id', foreignField: 'user_id', as: 'orders' } }])` |
## Right Join
Returns all rows from the second table, matched rows from first.
```sql theme={}
:RIGHT JOIN Order User ON user_id = id
```
| Database | Output |
| ---------- | ------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM orders RIGHT JOIN users ON user_id = id` |
## Full Join
Returns all rows from both tables.
```sql theme={}
:FULL JOIN Order User ON user_id = id
```
| Database | Output |
| ---------- | ------------------------------------------------------ |
| PostgreSQL | `SELECT * FROM orders FULL JOIN users ON user_id = id` |
## Cross Join
Returns Cartesian product of both tables (no ON clause needed).
```sql theme={}
:CROSS JOIN Product Category
```
## MongoDB Note
MongoDB uses `$lookup` aggregation for joins. OmniQL automatically translates JOIN syntax to the appropriate aggregation pipeline.
## Limitations
Current JOIN implementation has these constraints:
* One JOIN per query (no chained joins)
* No table aliases
* No column selection within JOIN queries
* Field names only (no `Table.field` notation)
For complex multi-table queries, consider using multiple queries or your database driver directly.
## Next Steps
Aggregate with GROUP BY
Advanced analytics
# Sorting
Source: https://docs.omniql.com/queries/sorting
Order results with ORDER BY
Order results using the ORDER BY clause.
## Basic Syntax
```sql theme={}
:GET Entity ORDER BY field
:GET Entity ORDER BY field ASC
:GET Entity ORDER BY field DESC
```
## Ascending Order (Default)
```sql theme={}
:GET User ORDER BY name
:GET User ORDER BY name ASC
```
| Database | Output |
| ---------- | --------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY name ASC` |
| MySQL | `SELECT * FROM users ORDER BY name ASC` |
| MongoDB | `db.users.find({}).sort({ name: 1 })` |
## Descending Order
```sql theme={}
:GET User ORDER BY created_at DESC
```
| Database | Output |
| ---------- | ---------------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY created_at DESC` |
| MongoDB | `db.users.find({}).sort({ created_at: -1 })` |
## Multiple Columns
```sql theme={}
:GET User ORDER BY status ASC, created_at DESC
```
| Database | Output |
| ---------- | ---------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY status ASC, created_at DESC` |
| MongoDB | `db.users.find({}).sort({ status: 1, created_at: -1 })` |
## Expressions in ORDER BY
Sort by calculated values or function results.
```sql theme={}
:GET Product ORDER BY price * quantity DESC
:GET User ORDER BY UPPER(name) ASC
```
| Database | Output |
| ---------- | ------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM products ORDER BY price * quantity DESC` |
| MySQL | `SELECT * FROM products ORDER BY price * quantity DESC` |
### Function in ORDER BY
```sql theme={}
:GET User ORDER BY LENGTH(name) DESC
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY LENGTH(name) DESC` |
| MongoDB | `db.users.aggregate([{ $addFields: { nameLen: { $strLenCP: '$name' } } }, { $sort: { nameLen: -1 } }])` |
## With WHERE Clause
```sql theme={}
:GET User WHERE active = true ORDER BY name ASC
```
| Database | Output |
| ---------- | ----------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE active = true ORDER BY name ASC` |
| MongoDB | `db.users.find({ active: true }).sort({ name: 1 })` |
## With LIMIT
```sql theme={}
:GET User ORDER BY created_at DESC LIMIT 10
```
| Database | Output |
| ---------- | ------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY created_at DESC LIMIT 10` |
| MongoDB | `db.users.find({}).sort({ created_at: -1 }).limit(10)` |
## Pagination Pattern
```sql theme={}
:GET User ORDER BY id ASC LIMIT 20 OFFSET 40
```
| Database | Output |
| ---------- | -------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY id ASC LIMIT 20 OFFSET 40` |
| MongoDB | `db.users.find({}).sort({ id: 1 }).skip(40).limit(20)` |
## Complete Example
```sql theme={}
:GET id, name, email, created_at FROM User
WHERE status = "active"
AND role IN ("user", "premium")
ORDER BY created_at DESC, name ASC
LIMIT 25
OFFSET 50
```
## Next Steps
Combine multiple tables
Aggregate with GROUP BY
# Window Functions
Source: https://docs.omniql.com/queries/window-functions
Advanced analytics with window functions
Perform calculations across rows related to the current row.
## Supported Functions
| Function | Description |
| ------------ | ----------------------- |
| `ROW NUMBER` | Sequential row numbers |
| `RANK` | Rank with gaps for ties |
| `DENSE RANK` | Rank without gaps |
| `LAG` | Previous row value |
| `LEAD` | Next row value |
| `NTILE` | Divide into buckets |
## Basic Syntax
```sql theme={}
:GET Entity WITH *, ROW NUMBER OVER (ORDER BY field) AS row_num
:GET Entity WITH *, RANK OVER (PARTITION BY field ORDER BY field) AS rank
```
Use `*` to include all columns, or specify columns explicitly:
```sql theme={}
:GET Entity WITH id, name, ROW NUMBER OVER (ORDER BY field) AS row_num
```
## ROW NUMBER
Assign sequential numbers to rows.
```sql theme={}
:GET User WITH *, ROW NUMBER OVER (ORDER BY created_at) AS row_num
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------- |
| PostgreSQL | `SELECT *, ROW_NUMBER() OVER (ORDER BY created_at) AS row_num FROM users` |
### With Partition
```sql theme={}
:GET User WITH *, ROW NUMBER OVER (PARTITION BY department ORDER BY salary DESC) AS row_num
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT *, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num FROM users` |
## RANK
Assign rank with gaps for ties.
```sql theme={}
:GET Product WITH *, RANK OVER (PARTITION BY category ORDER BY price DESC) AS rank
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT *, RANK() OVER (PARTITION BY category ORDER BY price DESC) AS rank FROM products` |
## DENSE RANK
Assign rank without gaps for ties.
```sql theme={}
:GET Product WITH *, DENSE RANK OVER (ORDER BY price DESC) AS dense_rank
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------------- |
| PostgreSQL | `SELECT *, DENSE_RANK() OVER (ORDER BY price DESC) AS dense_rank FROM products` |
## Rank Comparison
| Score | ROW NUMBER | RANK | DENSE RANK |
| ----- | ---------- | ---- | ---------- |
| 100 | 1 | 1 | 1 |
| 100 | 2 | 1 | 1 |
| 90 | 3 | 3 | 2 |
| 80 | 4 | 4 | 3 |
## LAG
Access previous row value.
```sql theme={}
:GET Order WITH *, LAG amount OVER (ORDER BY created_at) AS prev_amount
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------- |
| PostgreSQL | `SELECT *, LAG(amount) OVER (ORDER BY created_at) AS prev_amount FROM orders` |
## LEAD
Access next row value.
```sql theme={}
:GET Order WITH *, LEAD amount OVER (ORDER BY created_at) AS next_amount
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------------ |
| PostgreSQL | `SELECT *, LEAD(amount) OVER (ORDER BY created_at) AS next_amount FROM orders` |
## NTILE
Divide rows into buckets.
```sql theme={}
:GET User WITH *, NTILE 4 OVER (ORDER BY salary) AS quartile
```
| Database | Output |
| ---------- | ------------------------------------------------------------------ |
| PostgreSQL | `SELECT *, NTILE(4) OVER (ORDER BY salary) AS quartile FROM users` |
Divides users into 4 salary quartiles.
## Complete Example
```sql theme={}
:GET Order WITH
id,
user_id,
amount,
ROW NUMBER OVER (PARTITION BY user_id ORDER BY created_at) AS order_num,
RANK OVER (ORDER BY amount DESC) AS amount_rank
WHERE created_at > "2024-01-01"
ORDER BY user_id, created_at
```
## MongoDB Note
MongoDB has limited window function support (5.0+). Complex window functions may require aggregation pipelines.
## Limitations
Current implementation supports:
* PARTITION BY
* ORDER BY within OVER clause
Not currently supported:
* Frame clauses (`ROWS BETWEEN`)
* `FIRST_VALUE`, `LAST_VALUE`
* Aggregate functions as window functions (`SUM() OVER`, `AVG() OVER`)
## Next Steps
Create new records
Modify existing records
# Quickstart
Source: https://docs.omniql.com/quickstart
Get running with OmniQL in 5 minutes
# Quickstart
Get OmniQL running in your Go project in 5 minutes.
## Installation
```bash theme={}
go get github.com/omniql-engine/omniql
```
## Basic Usage
OmniQL wraps your existing database connection.
```go theme={}
package main
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
"github.com/omniql-engine/omniql"
)
func main() {
// 1. Your existing database connection
db, _ := sql.Open("postgres", "postgres://localhost/mydb?sslmode=disable")
defer db.Close()
// 2. Wrap it with OmniQL
client := oql.WrapSQL(db, "PostgreSQL")
// 3. Query with OmniQL syntax
users, _ := client.Query(":GET User WHERE age > 21")
// 4. Results are []map[string]any
for _, user := range users {
fmt.Printf("%s is %v years old\n", user["name"], user["age"])
}
}
```
## CRUD Operations
All operations return `[]map[string]any`.
### Create
```go theme={}
result, _ := client.Query(`:CREATE User WITH name:"John", age:30, email:"john@example.com"`)
// result = []map[string]any{
// {"inserted_id": 1, "rows_affected": 1},
// }
fmt.Println("Created user:", result[0]["inserted_id"])
```
### Read
```go theme={}
users, _ := client.Query(":GET User WHERE active = true ORDER BY created_at DESC LIMIT 10")
// users = []map[string]any{
// {"id": 1, "name": "John", "age": 30, "active": true},
// {"id": 2, "name": "Jane", "age": 25, "active": true},
// }
for _, user := range users {
fmt.Println(user["name"])
}
```
### Update
```go theme={}
result, _ := client.Query(`:UPDATE User SET verified:true WHERE id = 1`)
// result = []map[string]any{
// {"rows_affected": 1},
// }
fmt.Println("Updated rows:", result[0]["rows_affected"])
```
### Delete
```go theme={}
result, _ := client.Query(`:DELETE User WHERE status = "inactive"`)
// result = []map[string]any{
// {"rows_affected": 5},
// }
fmt.Println("Deleted rows:", result[0]["rows_affected"])
```
### Count
```go theme={}
result, _ := client.Query(":COUNT User WHERE active = true")
// result = []map[string]any{
// {"count": 42},
// }
fmt.Println("Active users:", result[0]["count"])
```
## Other Databases
### MySQL
```go theme={}
import _ "github.com/go-sql-driver/mysql"
db, _ := sql.Open("mysql", "user:pass@tcp(localhost:3306)/mydb")
client := oql.WrapSQL(db, "MySQL")
users, _ := client.Query(":GET User WHERE age > 21")
```
### MongoDB
```go theme={}
import "go.mongodb.org/mongo-driver/mongo"
mongoClient, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
db := mongoClient.Database("mydb")
client := oql.WrapMongo(db)
users, _ := client.Query(":GET User WHERE age > 21")
```
### Redis
```go theme={}
import "github.com/redis/go-redis/v9"
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
client := oql.WrapRedis(rdb, "")
users, _ := client.Query(":GET User WHERE id = 42")
```
## Polyglot Persistence
Same query syntax, any database.
```go theme={}
// Setup multiple clients
pgClient := oql.WrapSQL(pgDB, "PostgreSQL")
mongoClient := oql.WrapMongo(mongoDB)
redisClient := oql.WrapRedis(redisDB, "")
// Same query works on all
query := ":GET User WHERE status = \"active\""
pgUsers, _ := pgClient.Query(query)
mongoUsers, _ := mongoClient.Query(query)
redisUsers, _ := redisClient.Query(query)
```
## Error Handling
```go theme={}
users, err := client.Query(":GET User WHERE age > 21")
if err != nil {
log.Printf("Query failed: %v", err)
return
}
fmt.Printf("Found %d users\n", len(users))
```
## Next Steps
Learn GET syntax and filtering
Define tables with DDL
Insert and Update data
Full API reference
# Clauses
Source: https://docs.omniql.com/reference/clauses
Complete clause reference
All clauses supported by OmniQL.
## WHERE
Filter records based on conditions.
```sql theme={}
:GET User WHERE condition
:UPDATE User SET field = value WHERE condition
:DELETE User WHERE condition
```
### Examples
```sql theme={}
:GET User WHERE id = 1
:GET User WHERE age > 21 AND status = "active"
:GET User WHERE role IN ("admin", "moderator")
```
## ORDER BY
Sort results.
```sql theme={}
:GET Entity ORDER BY column
:GET Entity ORDER BY column ASC
:GET Entity ORDER BY column DESC
:GET Entity ORDER BY column1 ASC, column2 DESC
```
### Examples
```sql theme={}
:GET User ORDER BY name
:GET User ORDER BY created_at DESC
:GET User ORDER BY status ASC, name ASC
```
| Database | Output |
| ---------- | ------------------------------------- |
| PostgreSQL | `SELECT * FROM users ORDER BY name` |
| MongoDB | `db.users.find({}).sort({ name: 1 })` |
## LIMIT
Restrict number of results.
```sql theme={}
:GET Entity LIMIT n
```
### Examples
```sql theme={}
:GET User LIMIT 10
:GET User WHERE active = true ORDER BY created_at DESC LIMIT 5
```
| Database | Output |
| ---------- | ------------------------------ |
| PostgreSQL | `SELECT * FROM users LIMIT 10` |
| MySQL | `SELECT * FROM users LIMIT 10` |
| MongoDB | `db.users.find({}).limit(10)` |
## OFFSET
Skip rows for pagination.
```sql theme={}
:GET Entity LIMIT n OFFSET m
```
### Examples
```sql theme={}
:GET User LIMIT 10 OFFSET 0
:GET User LIMIT 10 OFFSET 10
:GET User LIMIT 10 OFFSET 20
```
| Database | Output |
| ---------- | ---------------------------------------- |
| PostgreSQL | `SELECT * FROM users LIMIT 10 OFFSET 20` |
| MongoDB | `db.users.find({}).skip(20).limit(10)` |
### Pagination Pattern
```sql theme={}
-- Page 1
:GET User ORDER BY id LIMIT 20 OFFSET 0
-- Page 2
:GET User ORDER BY id LIMIT 20 OFFSET 20
-- Page 3
:GET User ORDER BY id LIMIT 20 OFFSET 40
```
## GROUP BY
Group rows for aggregation.
```sql theme={}
:COUNT * FROM Entity GROUP BY column
:SUM field FROM Entity GROUP BY column
:GET Entity WITH column, SUM(field) AS alias GROUP BY column
```
### Examples
```sql theme={}
:COUNT * FROM User GROUP BY status
:SUM total FROM Order GROUP BY user_id
:GET Product WITH category, AVG(price) AS avg_price GROUP BY category
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------- |
| PostgreSQL | `SELECT status, COUNT(*) FROM users GROUP BY status` |
| MongoDB | `db.users.aggregate([{ $group: { _id: '$status', count: { $sum: 1 } } }])` |
## HAVING
Filter groups after aggregation.
```sql theme={}
:COUNT * FROM Entity GROUP BY column HAVING COUNT(*) > n
:SUM field FROM Entity GROUP BY column HAVING SUM(field) > n
```
### Examples
```sql theme={}
:COUNT * FROM User GROUP BY status HAVING COUNT(*) > 10
:SUM total FROM Order GROUP BY user_id HAVING SUM(total) > 1000
```
| Database | Output |
| ---------- | ------------------------------------------------------------------------- |
| PostgreSQL | `SELECT status, COUNT(*) FROM users GROUP BY status HAVING COUNT(*) > 10` |
### WHERE vs HAVING
```sql theme={}
-- WHERE: filters rows BEFORE grouping
:COUNT * FROM Order WHERE created_at > "2024-01-01" GROUP BY status
-- HAVING: filters groups AFTER aggregation
:COUNT * FROM Order GROUP BY status HAVING COUNT(*) > 5
-- Combined
:COUNT * FROM Order
WHERE created_at > "2024-01-01"
GROUP BY status
HAVING COUNT(*) > 5
```
## DISTINCT
Return unique values. Add DISTINCT after the entity.
```sql theme={}
:GET Entity DISTINCT
:GET col1, col2 FROM Entity DISTINCT
```
### Examples
```sql theme={}
:GET User DISTINCT
:GET status FROM User DISTINCT
:GET user_id, status FROM Order DISTINCT
```
| Database | Output |
| ---------- | ------------------------------ |
| PostgreSQL | `SELECT DISTINCT * FROM users` |
| MongoDB | `db.users.distinct()` |
## AS (Alias)
Rename columns in output.
```sql theme={}
:GET Entity WITH column AS alias
:GET Entity WITH SUM(field) AS alias
```
### Examples
```sql theme={}
:GET User WITH id, name AS full_name
:GET Order WITH status, SUM(total) AS revenue GROUP BY status
```
| Database | Output |
| ---------- | ----------------------------------------- |
| PostgreSQL | `SELECT id, name AS full_name FROM users` |
## SET
Specify values for UPDATE.
```sql theme={}
:UPDATE Entity SET field = value WHERE condition
:UPDATE Entity SET field1 = value1, field2 = value2 WHERE condition
```
### Examples
```sql theme={}
:UPDATE User SET name = "John" WHERE id = 1
:UPDATE User SET status = "active", updated_at = CURRENT_TIMESTAMP WHERE id = 1
:UPDATE Product SET price = price * 1.1 WHERE category = "electronics"
```
## WITH
Specify columns for SELECT or values for CREATE.
### In SELECT (columns)
```sql theme={}
:GET Entity WITH column1, column2, column3
:GET Entity WITH column, SUM(field) AS alias GROUP BY column
```
### In CREATE (values)
```sql theme={}
:CREATE Entity WITH field = value, field = value
```
### Examples
```sql theme={}
:GET User WITH id, name, email
:GET Order WITH status, SUM(total) AS revenue GROUP BY status
:CREATE User WITH name = "John", email = "john@example.com", age = 30
:CREATE Product WITH name = "Widget", price = 9.99, quantity = 100
```
COUNT(\*) is not supported in WITH clause. Use standalone syntax: `:COUNT * FROM Entity`
## ON
Specify join conditions or conflict handling.
### In Joins
```sql theme={}
:INNER JOIN Entity1 Entity2 ON field1 = field2
```
### In Upsert
```sql theme={}
:UPSERT Entity WITH fields ON conflict_column
```
### Examples
```sql theme={}
:INNER JOIN Order User ON user_id = id
:UPSERT User WITH email = "john@example.com", name = "John" ON email
```
| Database | Output |
| ---------- | --------------------------------------------------------------- |
| PostgreSQL | `INSERT INTO users (...) ON CONFLICT (email) DO UPDATE SET ...` |
| MySQL | `INSERT INTO users (...) ON DUPLICATE KEY UPDATE ...` |
## OVER (Window Functions)
Define window for window functions.
```sql theme={}
:ROW NUMBER OVER (ORDER BY column) FROM Entity
:RANK OVER (PARTITION BY column ORDER BY column) FROM Entity
```
### Examples
```sql theme={}
:ROW NUMBER OVER (ORDER BY created_at) FROM User
:RANK OVER (PARTITION BY department ORDER BY salary DESC) FROM User
:DENSE RANK OVER (PARTITION BY category ORDER BY price) FROM Product
```
## PARTITION BY
Divide rows into groups for window functions. Used inside OVER.
```sql theme={}
:FUNCTION OVER (PARTITION BY column ORDER BY column) FROM Entity
```
### Examples
```sql theme={}
:ROW NUMBER OVER (PARTITION BY department ORDER BY name) FROM User
:LAG salary OVER (PARTITION BY department ORDER BY hire_date) FROM Employee
```
## TO / FROM
Used in DCL for permission targets.
```sql theme={}
:GRANT permission ON Entity TO user
:REVOKE permission ON Entity FROM user
:ASSIGN ROLE role TO user
:REVOKE ROLE role FROM user
```
### Examples
```sql theme={}
:GRANT READ ON User TO analyst
:REVOKE DELETE ON Order FROM intern
:ASSIGN ROLE admin TO john
```
## Clause Order
Clauses must appear in this order:
```sql theme={}
:GET Entity
WITH columns
WHERE conditions
GROUP BY column
HAVING condition
ORDER BY column
LIMIT n
OFFSET m
```
### Complete Example
```sql theme={}
:SUM total FROM Order
WHERE created_at > "2024-01-01"
GROUP BY user_id
HAVING SUM(total) >= 500
ORDER BY sum DESC
LIMIT 100
OFFSET 0
```
## Clause Summary
| Clause | Purpose | Used With |
| ------------ | ----------------------- | ------------------------------------ |
| WHERE | Filter rows | GET, UPDATE, DELETE, COUNT, SUM, AVG |
| ORDER BY | Sort results | GET |
| LIMIT | Restrict count | GET |
| OFFSET | Skip rows | GET |
| GROUP BY | Group for aggregation | COUNT, SUM, AVG, MIN, MAX |
| HAVING | Filter groups | Aggregates with GROUP BY |
| DISTINCT | Unique values | GET |
| WITH | Columns or values | GET, CREATE |
| SET | Update values | UPDATE |
| ON | Join/conflict condition | JOIN, UPSERT |
| AS | Column alias | GET |
| OVER | Window definition | Window functions |
| PARTITION BY | Window grouping | Window functions |
| TO | Permission target | GRANT, ASSIGN ROLE |
| FROM | Permission source | REVOKE, REVOKE ROLE, COUNT, SUM, AVG |
## Next Steps
Type reference
Operator reference
# Data Types
Source: https://docs.omniql.com/reference/data-types
Complete data type reference
All data types supported by OmniQL and their database mappings.
## Type Mappings
| OmniQL | PostgreSQL | MySQL | MongoDB |
| ----------- | ------------------ | ----------------------- | ------------ |
| `AUTO` | `SERIAL` | `INT AUTO_INCREMENT` | `ObjectId` |
| `BIGAUTO` | `BIGSERIAL` | `BIGINT AUTO_INCREMENT` | `ObjectId` |
| `INT` | `INTEGER` | `INT` | `Int32` |
| `BIGINT` | `BIGINT` | `BIGINT` | `Int64` |
| `SMALLINT` | `SMALLINT` | `SMALLINT` | `Int32` |
| `DECIMAL` | `DECIMAL` | `DECIMAL` | `Decimal128` |
| `NUMERIC` | `NUMERIC` | `DECIMAL` | `Decimal128` |
| `FLOAT` | `DOUBLE PRECISION` | `DOUBLE` | `Double` |
| `REAL` | `REAL` | `FLOAT` | `Double` |
| `STRING` | `VARCHAR` | `VARCHAR(255)` | `String` |
| `TEXT` | `TEXT` | `TEXT` | `String` |
| `CHAR` | `CHAR` | `CHAR` | `String` |
| `BOOLEAN` | `BOOLEAN` | `BOOLEAN` | `Boolean` |
| `BOOL` | `BOOLEAN` | `BOOLEAN` | `Boolean` |
| `TIMESTAMP` | `TIMESTAMP` | `TIMESTAMP` | `Date` |
| `DATETIME` | `TIMESTAMP` | `DATETIME` | `Date` |
| `DATE` | `DATE` | `DATE` | `Date` |
| `TIME` | `TIME` | `TIME` | `String` |
| `JSON` | `JSON` | `JSON` | `Object` |
| `JSONB` | `JSONB` | `JSON` | `Object` |
| `UUID` | `UUID` | `CHAR(36)` | `UUID` |
| `BINARY` | `BYTEA` | `BLOB` | `BinData` |
| `BLOB` | `BYTEA` | `BLOB` | `BinData` |
## Numeric Types
### AUTO
Auto-incrementing primary key.
```sql theme={}
:CREATE TABLE User WITH id:AUTO, name:STRING
```
| Database | Output |
| ---------- | --------------------------------------------------------------------------- |
| PostgreSQL | `CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR)` |
| MySQL | `CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255))` |
### BIGAUTO
Auto-incrementing for large tables.
```sql theme={}
:CREATE TABLE Event WITH id:BIGAUTO, name:STRING
```
| Database | Output |
| ---------- | -------------------------------------- |
| PostgreSQL | `id BIGSERIAL PRIMARY KEY` |
| MySQL | `id BIGINT AUTO_INCREMENT PRIMARY KEY` |
### INT
Standard integer (-2,147,483,648 to 2,147,483,647).
```sql theme={}
:CREATE TABLE Product WITH id:AUTO, quantity:INT
```
### BIGINT
Large integer for big numbers.
```sql theme={}
:CREATE TABLE Analytics WITH id:AUTO, views:BIGINT
```
### SMALLINT
Small integer (-32,768 to 32,767).
```sql theme={}
:CREATE TABLE Rating WITH id:AUTO, score:SMALLINT
```
### DECIMAL / NUMERIC
Exact numeric with precision.
```sql theme={}
:CREATE TABLE Product WITH id:AUTO, price:DECIMAL
:CREATE TABLE Product WITH id:AUTO, price:DECIMAL(10,2)
```
| Database | Output |
| ---------- | --------------------- |
| PostgreSQL | `price DECIMAL(10,2)` |
| MySQL | `price DECIMAL(10,2)` |
### FLOAT
Double-precision floating point.
```sql theme={}
:CREATE TABLE Sensor WITH id:AUTO, temperature:FLOAT
```
| Database | Output |
| ---------- | ------------------------------ |
| PostgreSQL | `temperature DOUBLE PRECISION` |
| MySQL | `temperature DOUBLE` |
### REAL
Single-precision floating point.
```sql theme={}
:CREATE TABLE Measurement WITH id:AUTO, value:REAL
```
| Database | Output |
| ---------- | ------------- |
| PostgreSQL | `value REAL` |
| MySQL | `value FLOAT` |
## String Types
### STRING
Variable-length string.
```sql theme={}
:CREATE TABLE User WITH id:AUTO, name:STRING
:CREATE TABLE User WITH id:AUTO, name:STRING(100)
```
| Database | Output |
| ---------- | ------------------------------------------ |
| PostgreSQL | `name VARCHAR` or `name VARCHAR(100)` |
| MySQL | `name VARCHAR(255)` or `name VARCHAR(100)` |
### TEXT
Unlimited length text.
```sql theme={}
:CREATE TABLE Post WITH id:AUTO, content:TEXT
```
### CHAR
Fixed-length string.
```sql theme={}
:CREATE TABLE Country WITH code:CHAR(2), name:STRING
```
## Boolean Types
### BOOLEAN / BOOL
True or false values.
```sql theme={}
:CREATE TABLE User WITH id:AUTO, active:BOOLEAN
:CREATE TABLE User WITH id:AUTO, verified:BOOL
```
| Database | Output |
| ---------- | ---------------- |
| PostgreSQL | `active BOOLEAN` |
| MySQL | `active BOOLEAN` |
| MongoDB | `Boolean` |
### Boolean in Queries
```sql theme={}
:GET User WHERE active = true
:GET User WHERE verified = false
:UPDATE User SET active = true WHERE id = 1
```
## Date and Time Types
### TIMESTAMP
Date and time.
```sql theme={}
:CREATE TABLE User WITH id:AUTO, created_at:TIMESTAMP
```
| Database | Output |
| ---------- | ---------------------- |
| PostgreSQL | `created_at TIMESTAMP` |
| MySQL | `created_at TIMESTAMP` |
| MongoDB | `Date` |
### DATETIME
Date and time (MySQL uses DATETIME, PostgreSQL uses TIMESTAMP).
```sql theme={}
:CREATE TABLE Event WITH id:AUTO, event_time:DATETIME
```
| Database | Output |
| ---------- | ---------------------- |
| PostgreSQL | `event_time TIMESTAMP` |
| MySQL | `event_time DATETIME` |
### DATE
Date only (no time).
```sql theme={}
:CREATE TABLE Event WITH id:AUTO, event_date:DATE
```
### TIME
Time only (no date).
```sql theme={}
:CREATE TABLE Schedule WITH id:AUTO, start_time:TIME
```
### Date Literals in Queries
```sql theme={}
:GET Order WHERE created_at > "2024-01-01"
:GET Order WHERE created_at BETWEEN "2024-01-01" AND "2024-12-31"
```
## JSON Types
### JSON
Standard JSON data.
```sql theme={}
:CREATE TABLE User WITH id:AUTO, metadata:JSON
```
| Database | Output |
| ---------- | ----------------- |
| PostgreSQL | `metadata JSON` |
| MySQL | `metadata JSON` |
| MongoDB | `Object` (native) |
### JSONB
PostgreSQL optimized binary JSON.
```sql theme={}
:CREATE TABLE User WITH id:AUTO, metadata:JSONB
```
| Database | Output |
| ---------- | ---------------- |
| PostgreSQL | `metadata JSONB` |
| MySQL | `metadata JSON` |
## UUID Type
Universally unique identifier.
```sql theme={}
:CREATE TABLE User WITH id:UUID, name:STRING
```
| Database | Output |
| ---------- | ------------- |
| PostgreSQL | `id UUID` |
| MySQL | `id CHAR(36)` |
| MongoDB | `UUID` |
### UUID in Queries
```sql theme={}
:GET User WHERE id = "550e8400-e29b-41d4-a716-446655440000"
```
## Binary Types
### BINARY / BLOB
Binary data (files, images).
```sql theme={}
:CREATE TABLE File WITH id:AUTO, content:BINARY
:CREATE TABLE Image WITH id:AUTO, data:BLOB
```
| Database | Output |
| ---------- | --------------- |
| PostgreSQL | `content BYTEA` |
| MySQL | `content BLOB` |
| MongoDB | `BinData` |
## Type with Constraints
Use colons to add constraints after the type:
```sql theme={}
:CREATE TABLE User WITH
id:AUTO,
email:STRING:NOTNULL:UNIQUE,
name:STRING:NOTNULL,
role:STRING
```
| Constraint | Syntax | Effect |
| ----------- | ------------- | --------------------- |
| Not Null | `:NOTNULL` | Column cannot be NULL |
| Unique | `:UNIQUE` | Values must be unique |
| Primary Key | `:PRIMARYKEY` | Column is primary key |
See [Advanced Schema](/schema/advanced) for more on constraints.
## Type with Size
Specify size in parentheses:
```sql theme={}
:CREATE TABLE User WITH
id:AUTO,
name:STRING(100),
code:CHAR(2),
price:DECIMAL(10,2)
```
## Limitations
Not currently supported:
* DEFAULT values
* CHECK constraints
* Array types
* Type casting
For these features, use native SQL.
## Next Steps
Create tables with types
Query operators
# Operators
Source: https://docs.omniql.com/reference/operators
Complete operator reference
All operators supported by OmniQL.
## Comparison Operators
| Operator | Description | Example |
| -------- | --------------------- | ---------------------------- |
| `=` | Equal | `WHERE age = 25` |
| `!=` | Not equal | `WHERE status != "inactive"` |
| `>` | Greater than | `WHERE age > 21` |
| `>=` | Greater than or equal | `WHERE age >= 18` |
| `<` | Less than | `WHERE price < 100` |
| `<=` | Less than or equal | `WHERE quantity <= 10` |
### Examples
```sql theme={}
:GET User WHERE age = 25
:GET User WHERE age != 25
:GET User WHERE age > 21
:GET User WHERE age >= 18
:GET User WHERE age < 65
:GET User WHERE age <= 30
```
## Logical Operators
| Operator | Description | Example |
| -------- | --------------------- | -------------------------------------- |
| `AND` | Both conditions true | `WHERE age > 21 AND active = true` |
| `OR` | Either condition true | `WHERE role = "admin" OR role = "mod"` |
| `NOT` | Negate condition | `WHERE NOT status = "banned"` |
### Examples
```sql theme={}
:GET User WHERE age > 21 AND status = "active"
:GET User WHERE role = "admin" OR role = "moderator"
:GET User WHERE NOT deleted = true
:GET User WHERE (age > 21 AND status = "active") OR role = "admin"
```
### Precedence
`NOT` > `AND` > `OR`
Use parentheses to control order:
```sql theme={}
-- Without parentheses: AND evaluated first
:GET User WHERE a = 1 OR b = 2 AND c = 3
-- Equivalent to: a = 1 OR (b = 2 AND c = 3)
-- With parentheses: OR evaluated first
:GET User WHERE (a = 1 OR b = 2) AND c = 3
```
## Arithmetic Operators
| Operator | Description | Example |
| -------- | -------------- | ------------------ |
| `+` | Addition | `price + tax` |
| `-` | Subtraction | `total - discount` |
| `*` | Multiplication | `quantity * price` |
| `/` | Division | `total / count` |
| `%` | Modulo | `id % 2` |
### Examples
```sql theme={}
:GET Product WITH name, price, price * 1.1 AS with_tax
:UPDATE Product SET price = price * 0.9 WHERE category = "sale"
:UPDATE Account SET balance = balance + 100 WHERE id = 1
:GET User WHERE id % 2 = 0
```
## Range Operators
### BETWEEN
```sql theme={}
:GET User WHERE age BETWEEN 18 AND 65
:GET Order WHERE created_at BETWEEN "2024-01-01" AND "2024-12-31"
```
| Database | Output |
| ---------- | ------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE age BETWEEN 18 AND 65` |
| MySQL | `SELECT * FROM users WHERE age BETWEEN 18 AND 65` |
| MongoDB | `db.users.find({ age: { $gte: 18, $lte: 65 } })` |
### NOT BETWEEN
```sql theme={}
:GET Product WHERE price NOT BETWEEN 10 AND 50
```
## Set Operators
### IN
```sql theme={}
:GET User WHERE role IN ("admin", "moderator", "editor")
:GET Order WHERE status IN ("pending", "processing")
```
| Database | Output |
| ---------- | -------------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE role IN ('admin', 'moderator', 'editor')` |
| MySQL | `SELECT * FROM users WHERE role IN ('admin', 'moderator', 'editor')` |
| MongoDB | `db.users.find({ role: { $in: ['admin', 'moderator', 'editor'] } })` |
### NOT IN
```sql theme={}
:GET User WHERE status NOT IN ("banned", "suspended")
```
| Database | Output |
| ---------- | ----------------------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE status NOT IN ('banned', 'suspended')` |
| MongoDB | `db.users.find({ status: { $nin: ['banned', 'suspended'] } })` |
## Pattern Operators
### LIKE
| Pattern | Meaning |
| ------- | -------------------------- |
| `%` | Any sequence of characters |
| `_` | Any single character |
```sql theme={}
:GET User WHERE name LIKE "John%"
:GET User WHERE email LIKE "%@gmail.com"
:GET User WHERE name LIKE "%smith%"
:GET User WHERE code LIKE "A_123"
```
| Database | Output |
| ---------- | ---------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE name LIKE 'John%'` |
| MySQL | `SELECT * FROM users WHERE name LIKE 'John%'` |
| MongoDB | `db.users.find({ name: { $regex: '^John' } })` |
### NOT LIKE
```sql theme={}
:GET User WHERE email NOT LIKE "%@test.com"
```
### ILIKE (Case Insensitive)
```sql theme={}
:GET User WHERE name ILIKE "john%"
```
| Database | Output |
| ---------- | ---------------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE name ILIKE 'john%'` |
| MySQL | `SELECT * FROM users WHERE LOWER(name) LIKE 'john%'` |
Note: ILIKE is PostgreSQL-native. MySQL translates to LIKE with LOWER().
## NULL Operators
### IS NULL
```sql theme={}
:GET User WHERE deleted_at IS NULL
```
| Database | Output |
| ---------- | ---------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE deleted_at IS NULL` |
| MySQL | `SELECT * FROM users WHERE deleted_at IS NULL` |
| MongoDB | `db.users.find({ deleted_at: null })` |
### IS NOT NULL
```sql theme={}
:GET User WHERE phone IS NOT NULL
```
| Database | Output |
| ---------- | --------------------------------------------- |
| PostgreSQL | `SELECT * FROM users WHERE phone IS NOT NULL` |
| MongoDB | `db.users.find({ phone: { $ne: null } })` |
## Operator Summary by Database
| Operator | PostgreSQL | MySQL | MongoDB |
| ------------- | ------------- | ------------- | -------------------- |
| `=` | `=` | `=` | `$eq` |
| `!=` | `!=` | `!=` | `$ne` |
| `>` | `>` | `>` | `$gt` |
| `>=` | `>=` | `>=` | `$gte` |
| `<` | `<` | `<` | `$lt` |
| `<=` | `<=` | `<=` | `$lte` |
| `IN` | `IN` | `IN` | `$in` |
| `NOT IN` | `NOT IN` | `NOT IN` | `$nin` |
| `BETWEEN` | `BETWEEN` | `BETWEEN` | `$gte/$lte` |
| `LIKE` | `LIKE` | `LIKE` | `$regex` |
| `ILIKE` | `ILIKE` | `LIKE` | `$regex` with i flag |
| `IS NULL` | `IS NULL` | `IS NULL` | `null` |
| `IS NOT NULL` | `IS NOT NULL` | `IS NOT NULL` | `$ne: null` |
| `AND` | `AND` | `AND` | implicit |
| `OR` | `OR` | `OR` | `$or` |
| `NOT` | `NOT` | `NOT` | `$not` |
## Limitations
Not currently supported in OmniQL (use native SQL):
* JSON operators (`->`, `->>`, `@>`, `?`)
* Array operators (`ANY`, `ALL`)
* String concatenation (`||`)
* EXISTS / NOT EXISTS subqueries
## Next Steps
All clause reference
Type reference
# Advanced Schema
Source: https://docs.omniql.com/schema/advanced
Column constraints
## Column Constraints
OmniQL supports three column constraints using colon syntax:
```sql theme={}
:CREATE TABLE User WITH
id:AUTO,
email:STRING:NOTNULL:UNIQUE,
name:STRING:NOTNULL
```
| Constraint | Syntax | Effect |
| ----------- | ------------- | --------------------- |
| Not Null | `:NOTNULL` | Column cannot be NULL |
| Unique | `:UNIQUE` | Values must be unique |
| Primary Key | `:PRIMARYKEY` | Column is primary key |
### Multiple Constraints
Chain multiple constraints with colons:
```sql theme={}
:CREATE TABLE Product WITH
id:AUTO,
sku:STRING:NOTNULL:UNIQUE,
name:STRING:NOTNULL,
price:DECIMAL
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| PostgreSQL | `CREATE TABLE products (id SERIAL PRIMARY KEY, sku VARCHAR NOT NULL UNIQUE, name VARCHAR NOT NULL, price DECIMAL)` |
| MySQL | `CREATE TABLE products (id INT AUTO_INCREMENT PRIMARY KEY, sku VARCHAR(255) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, price DECIMAL)` |
## Database-Specific Features
For advanced features like sequences, custom types, triggers, and stored procedures, see database-specific documentation:
* [PostgreSQL Features](/databases/postgresql) - Sequences, ENUM types, domains, triggers, policies
* [MySQL Features](/databases/mysql)
* [MongoDB Features](/databases/mongodb)
* [Redis Features](/databases/redis)
## Limitations
Not supported in OmniQL (use native SQL):
* CHECK constraints
* Foreign key references (REFERENCES)
* ON DELETE / ON UPDATE actions
* DEFAULT values
* Composite primary keys
## Next Steps
PostgreSQL-specific features
Group operations safely
# Indexes
Source: https://docs.omniql.com/schema/indexes
Optimize query performance with indexes
Indexes speed up data retrieval at the cost of slower writes.
## Basic Syntax
```sql theme={}
:CREATE INDEX Entity index_name:column
:CREATE INDEX Entity index_name:column UNIQUE
:DROP INDEX Entity index_name
```
## Create Index
```sql theme={}
:CREATE INDEX User idx_email:email
```
| Database | Output |
| ---------- | ----------------------------------------- |
| PostgreSQL | `CREATE INDEX idx_email ON users (email)` |
| MySQL | `CREATE INDEX idx_email ON users (email)` |
## Unique Index
```sql theme={}
:CREATE INDEX User idx_email:email UNIQUE
```
| Database | Output |
| ---------- | ------------------------------------------------ |
| PostgreSQL | `CREATE UNIQUE INDEX idx_email ON users (email)` |
| MySQL | `CREATE UNIQUE INDEX idx_email ON users (email)` |
## Drop Index
```sql theme={}
:DROP INDEX User idx_email
```
| Database | Output |
| ---------- | -------------------------------- |
| PostgreSQL | `DROP INDEX IF EXISTS idx_email` |
| MySQL | `DROP INDEX idx_email ON users` |
## When to Use Indexes
| Use Case | Recommendation |
| ---------------------- | -------------------------- |
| Frequent WHERE clauses | Index the filtered column |
| Unique constraints | Use UNIQUE index |
| Foreign keys | Index the reference column |
| Sorting | Index the ORDER BY column |
## Index Best Practices
**Do:**
* Index columns used in WHERE clauses
* Index columns used in JOIN conditions
* Index columns used in ORDER BY
* Use unique indexes for email, username, etc.
**Don't:**
* Over-index (slows down writes)
* Index rarely queried columns
* Index very small tables
## Complete Examples
### User Table
```sql theme={}
:CREATE INDEX User idx_email:email UNIQUE
:CREATE INDEX User idx_status:status
:CREATE INDEX User idx_created:created_at
```
### Order Table
```sql theme={}
:CREATE INDEX Order idx_user:user_id
:CREATE INDEX Order idx_status:status
:CREATE INDEX Order idx_created:created_at
```
## Database Support
| Feature | PostgreSQL | MySQL | MongoDB |
| ------------------- | ---------- | ----- | ---------- |
| Single column index | ✅ | ✅ | Via driver |
| UNIQUE modifier | ✅ | ✅ | Via driver |
| DROP INDEX | ✅ | ✅ | Via driver |
For MongoDB indexes, use native driver methods.
## Limitations
Current index implementation supports:
* Single column indexes
* UNIQUE constraint
For advanced indexes (composite, partial, full-text, GIN), use native SQL.
## Next Steps
Create virtual tables
Group operations safely
# Tables
Source: https://docs.omniql.com/schema/tables
Create and manage database tables
Create and manage tables using DDL operations.
## Create Table
```sql theme={}
:CREATE TABLE Entity WITH column:TYPE, column:TYPE:CONSTRAINT
```
## Basic Table
```sql theme={}
:CREATE TABLE User WITH id:AUTO, name:STRING, email:STRING
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------------- |
| PostgreSQL | `CREATE TABLE users (id SERIAL, name VARCHAR, email VARCHAR)` |
| MySQL | `CREATE TABLE users (id INT AUTO_INCREMENT, name VARCHAR(255), email VARCHAR(255))` |
| MongoDB | Creates collection on first insert |
## Data Types
| OmniQL Type | PostgreSQL | MySQL | MongoDB |
| ----------- | ------------------ | ----------------------- | ------------ |
| `AUTO` | `SERIAL` | `INT AUTO_INCREMENT` | `ObjectId` |
| `BIGAUTO` | `BIGSERIAL` | `BIGINT AUTO_INCREMENT` | `ObjectId` |
| `INT` | `INTEGER` | `INT` | `Int32` |
| `BIGINT` | `BIGINT` | `BIGINT` | `Int64` |
| `SMALLINT` | `SMALLINT` | `SMALLINT` | `Int32` |
| `STRING` | `VARCHAR` | `VARCHAR(255)` | `String` |
| `TEXT` | `TEXT` | `TEXT` | `String` |
| `CHAR` | `CHAR` | `CHAR` | `String` |
| `BOOLEAN` | `BOOLEAN` | `BOOLEAN` | `Boolean` |
| `BOOL` | `BOOLEAN` | `BOOLEAN` | `Boolean` |
| `TIMESTAMP` | `TIMESTAMP` | `TIMESTAMP` | `Date` |
| `DATETIME` | `TIMESTAMP` | `DATETIME` | `Date` |
| `DATE` | `DATE` | `DATE` | `Date` |
| `TIME` | `TIME` | `TIME` | `String` |
| `JSON` | `JSON` | `JSON` | `Object` |
| `JSONB` | `JSONB` | `JSON` | `Object` |
| `UUID` | `UUID` | `CHAR(36)` | `UUID` |
| `DECIMAL` | `DECIMAL` | `DECIMAL` | `Decimal128` |
| `NUMERIC` | `NUMERIC` | `DECIMAL` | `Decimal128` |
| `FLOAT` | `DOUBLE PRECISION` | `DOUBLE` | `Double` |
| `REAL` | `REAL` | `FLOAT` | `Double` |
| `BINARY` | `BYTEA` | `BLOB` | `BinData` |
| `BLOB` | `BYTEA` | `BLOB` | `BinData` |
## With Size
```sql theme={}
:CREATE TABLE User WITH id:AUTO, name:STRING(100), description:TEXT
```
## With Constraints
Constraints are added after the type using colons:
```sql theme={}
:CREATE TABLE User WITH id:AUTO, name:STRING:NOTNULL, email:STRING:UNIQUE
```
| Database | Output |
| ---------- | ----------------------------------------------------------------------------- |
| PostgreSQL | `CREATE TABLE users (id SERIAL, name VARCHAR NOT NULL, email VARCHAR UNIQUE)` |
### Multiple Constraints
```sql theme={}
:CREATE TABLE User WITH id:AUTO, email:STRING:NOTNULL:UNIQUE
```
## Complete Example
```sql theme={}
:CREATE TABLE User WITH
id:AUTO,
email:STRING:NOTNULL:UNIQUE,
name:STRING:NOTNULL,
active:BOOLEAN,
created_at:TIMESTAMP
```
## Drop Table
```sql theme={}
:DROP TABLE User
```
| Database | Output |
| ---------- | ------------------ |
| PostgreSQL | `DROP TABLE users` |
| MySQL | `DROP TABLE users` |
| MongoDB | `db.users.drop()` |
## Alter Table
### Add Column
```sql theme={}
:ALTER TABLE User ADD name:STRING
```
| Database | Output |
| ---------- | ------------------------------------------- |
| PostgreSQL | `ALTER TABLE users ADD COLUMN name VARCHAR` |
### Drop Column
```sql theme={}
:ALTER TABLE User DROP name
```
### Rename Column
```sql theme={}
:ALTER TABLE User RENAME name:full_name
```
### Modify Column Type
```sql theme={}
:ALTER TABLE User MODIFY name:TEXT
```
## Rename Table
```sql theme={}
:RENAME TABLE User TO Customer
```
| Database | Output |
| ---------- | --------------------------------------- |
| PostgreSQL | `ALTER TABLE users RENAME TO customers` |
| MySQL | `RENAME TABLE users TO customers` |
## MongoDB Collections
For MongoDB, use `COLLECTION` instead of `TABLE`:
```sql theme={}
:CREATE COLLECTION User
:DROP COLLECTION User
```
## Next Steps
Create virtual tables
Optimize query performance
# Views
Source: https://docs.omniql.com/schema/views
Create virtual tables with views
Views are virtual tables based on query results.
## Basic Syntax
```sql theme={}
:CREATE VIEW ViewName AS GET Entity WHERE condition
:ALTER VIEW ViewName AS GET Entity WHERE condition
:DROP VIEW ViewName
```
## Create View
```sql theme={}
:CREATE VIEW ActiveUser AS GET User WHERE active = true
```
| Database | Output |
| ---------- | ------------------------------------------------------------------- |
| PostgreSQL | `CREATE VIEW activeuser AS SELECT * FROM users WHERE active = true` |
| MySQL | `CREATE VIEW activeuser AS SELECT * FROM users WHERE active = true` |
## View with Columns
```sql theme={}
:CREATE VIEW UserSummary AS GET id, name, email FROM User WHERE active = true
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------- |
| PostgreSQL | `CREATE VIEW usersummary AS SELECT id, name, email FROM users WHERE active = true` |
## View with Aggregation
```sql theme={}
:CREATE VIEW OrderTotals AS GET Order WITH user_id, SUM(amount) AS total_spent GROUP BY user_id
```
| Database | Output |
| ---------- | ---------------------------------------------------------------------------------------------------- |
| PostgreSQL | `CREATE VIEW ordertotals AS SELECT user_id, SUM(amount) AS total_spent FROM orders GROUP BY user_id` |
Views with COUNT(\*) require native SQL. Use SUM, AVG, MIN, MAX with OmniQL views.
## Query a View
Views are queried like regular tables.
```sql theme={}
:GET ActiveUser WHERE role = "admin"
:GET OrderTotals WHERE total_spent > 1000
```
## Alter View
Update an existing view definition.
```sql theme={}
:ALTER VIEW ActiveUser AS GET User WHERE active = true AND verified = true
```
| Database | Output |
| ---------- | -------------------------------------------------------------------------------------------------- |
| PostgreSQL | `CREATE OR REPLACE VIEW activeuser AS SELECT * FROM users WHERE active = true AND verified = true` |
| MySQL | `CREATE OR REPLACE VIEW activeuser AS SELECT * FROM users WHERE active = true AND verified = true` |
## Drop View
```sql theme={}
:DROP VIEW ActiveUser
```
| Database | Output |
| ---------- | -------------------------------- |
| PostgreSQL | `DROP VIEW IF EXISTS activeuser` |
| MySQL | `DROP VIEW IF EXISTS activeuser` |
## Complete Examples
### Active Premium Users
```sql theme={}
:CREATE VIEW PremiumUser AS GET id, name, email, created_at FROM User WHERE role = "premium" AND active = true
```
### Low Stock Products
```sql theme={}
:CREATE VIEW LowStock AS GET id, name, sku, quantity FROM Product WHERE quantity < 10 AND active = true
```
### Revenue by Status
```sql theme={}
:CREATE VIEW RevenueByStatus AS GET Order WITH status, SUM(total) AS revenue GROUP BY status
```
## Limitations
Current view implementation supports:
* Simple SELECT queries with WHERE, ORDER BY, LIMIT
* Column selection
* Aggregations with SUM, AVG, MIN, MAX
Not currently supported in OmniQL views:
* COUNT(\*) aggregations (use native SQL)
* Views with JOINs (use native SQL)
* Materialized views (PostgreSQL-specific, use native SQL)
* CREATE OR REPLACE (use ALTER VIEW instead)
## MongoDB Note
MongoDB does not support traditional views. Use aggregation pipelines or read-only collections for similar functionality.
## Next Steps
Optimize query performance
Group operations safely