Skip to content

RuanCampello/umbra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

1,161 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Umbra

(/หˆสŒm.brษ™/)

Logo
Self-contained, shadowy, and slightly suspicious database ๐Ÿฆ‡

Rust Linux MacOS


โœจ Not PostgreSQL. Not SQLite. Something... else

This project takes certain... liberties with database paradigms. Born from the existential void of a southern hemisphere summer (December 2024โ€“January 2025, when ennui struck like the heatwave), Umbra cosplays as PostgreSQL with its query planner (proper cost-based optimisation using expression trees, darlingโ€”weโ€™re civilised), while harbouring SQLiteโ€™s scandalous little secrets under the bonnet (ahem RowId, you disreputable little implementation detail).

The bat is non-negotiable.

Let's be clear: this isn't a replacement for anything, unless you're replacing sanity with chaos, which caseโ€”welcome. The focus here is the learning process, read as "the screams of debugging hand-rolled date parsing at 3AM."


Getting Started

cargo run -- file.db 8000

file.db: The path to your Umbra database file (will be created if it doesn't exist). 8000: The TCP port the server will listen on.

Use the usql CLI (like psql, but darker)

cargo run --package usql -- 8000

Connects to the Umbra server running on port 8000.

usql is your shadowy shell into the Umbra world. It understands SQL and the void.


Implementation Status

๐Ÿงพ Types

1๏ธโƒฃ Integer Types

The beating heart of databasesโ€”and poor life decisions.

Umbra supports both signed and unsigned integer types, as well as their attention-seeking cousins: the serial pseudo-types. These SERIAL types arenโ€™t realโ€”they're syntactic sugar that auto-magically generate sequences behind the scenes (just like PostgreSQL, but with fewer emotional boundaries).

Type Range Notes
SMALLINT ยฑ2ยนโต Petite regrets
INTEGER ยฑ2ยณยน Standard regret capacity
BIGINT ยฑ2โถยณ When regular regrets aren't enough
SMALLINT UNSIGNED 0 โ†’ 2ยนโถโˆ’1 For when you're cautiously hopeful
INTEGER UNSIGNED 0 โ†’ 2ยณยฒโˆ’1 Delusional optimism
BIGINT UNSIGNED 0 โ†’ 2โถโดโˆ’1 Sheer madness
SMALLSERIAL 1 โ†’ 2ยนโตโˆ’1 Small but permanent mistakes
SERIAL 1 โ†’ 2ยณยนโˆ’1 Commitment issues
BIGSERIAL 1 โ†’ 2โถยณโˆ’1 Lifelong consequences

Note

Unsigned Integers
PostgreSQL demands check constraints, SQLite shrugs, but Umbra embraces MySQL's blunt syntax for that.

Important

SERIAL Types are forgetful
Much like PostgreSQL, Umbra's serial values never look back. Once generatedโ€”even if your transaction failsโ€”theyโ€™re gone.

CREATE TABLE cursed_items (
    item_id SERIAL PRIMARY KEY,
    name VARCHAR(255),
    darkness_level SMALLINT UNSIGNED,
    soul_count BIGINT UNSIGNED
);

๐Ÿ”ข Floating-Point & Numeric Types

When integers just wonโ€™t cut it and you need approximate truths, floating-point types step inโ€”like unreliable narrators in a numerical novel. For precision, we offer NUMERIC.

Type Precision Notes
REAL ~7 decimal digits Single-precision daydreams. Fast, vague, and prone to making things up.
DOUBLE PRECISION ~15 decimal digits Double the bits, double the confidence. For those who still donโ€™t trust REAL.
NUMERIC(p, s) Arbitrary Base-1000 implementation. For when you need to count atoms or debt.

Warning

Floating-Point Lies
They're fast. They're fun. They're wrong. Read more about that here. If you're just plotting wiggly shadows over time, REAL is your friend.

CREATE TABLE entropy_watch (
    event_id SERIAL PRIMARY KEY,
    time TIMESTAMP,
    shadow_density REAL,
    void_pressure NUMERIC(10, 4) 
);

Tip

Casting Happens
Umbra will happily let you mix floats and integers. But you might want to think twice before comparing them for equality. That way lies madness. Also, insert will NOT coerce types, it'll strictly verify the types based on the defined schema.

SELECT time FROM entropy_watch WHERE shadow_density > 69; -- here we implicitly cast 69 to a float, so you can query without hustle

๐Ÿงฌ UUID Type

When numbers aren't unique enough and strings are too sincere, you reach for the UUID. Specifically: version 4, because determinism is for spreadsheets.

Type Description Notes
UUID Universally Unique Identifier (v4 only) Random, stateless, and perfect for plausible deniability.
CREATE TABLE shadow_agents (
    agent_id UUID PRIMARY KEY,
    codename VARCHAR(255),
    clearance_level SMALLINT
);

๐Ÿ“œ JSONB

Because shadows deserve structure, too. Umbra's JSONB implementation provides flexible syntax for JavaScript-like object literals, stores them in a compact binary format, and supports dot notation and path expression for nested access.

  • Keys can skip quotes: {name: 'Umbra', active: true}
  • Line comments // and block comments /* ... */ are ignored while parsing
  • Missing paths quietly become NULL instead of raising errors
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    metadata JSONB
);

INSERT INTO users (metadata) VALUES
({profile: {lang: 'en', hobbies: ['lurking', 'flying']}, active: true});

SELECT metadata.profile.lang FROM users;       -- dot access
SELECT metadata.hobbies[*] FROM users;         -- array expansion
SELECT metadata.hobbies[-1] FROM users;        -- last element via negative index
SELECT metadata.non_existent_field FROM users; -- NULL

Tip

You can filter on JSONB directly (WHERE metadata.active = true) or access nested profile data (like metadata.profile.lang) without extra casting.

  • VARCHAR
  • BOOLEAN
  • DECIMAL
  • TEXT (the loquacious one)
  • Temporal types (because time flies when... I ran out of jokes)
    • DATE
    • TIME
    • TIMESTAMP (precision: "ish")
    • INTERVAL

๐Ÿ”— Constraints

  • PRIMARY KEY
    • Auto-incrementing (via SERIAL, no manual gear-shifting required)
  • UNIQUE (because duplicates are tacky)
  • NULLABLE (explicit nullability, because I believe in clarity)
  • FOREIGN KEY (relationships are hard)
  • CHECK

Important

Nullability Philosophy
Unlike SQL standard where columns are nullable by default, requiring NOT NULL to prevent it. Umbra follows Rust philosophy: columns are not null by default, unless explicit marked as NULLABLE. This makes null handling intentional and not condescending.

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255),              -- non-null by default
    email VARCHAR(255) NULLABLE,    -- explicitly nullable
);

โš™๏ธ Operations

๐Ÿ’ผ Table Operations

  • CREATE TABLE (where hope begins)
  • DROP TABLE (where hope ends)
  • ALTER TABLE

๐Ÿ” SELECT Operations

  • WHERE (basic filtering, no existential crises)
  • ORDER BY (implemented after herding literal bats)
  • BETWEEN (for when life exists within bounds)
  • LIMIT/OFFSET (self-restraint is here)
  • Column aliases (SELECT tomb AS grave - identity is fluid)
  • Table aliases (FROM crypts AS c - naming things is hard)
  • JOIN (relationships require couples therapy)
  • GROUP BY (aggregation is a social construct)
  • HAVING

๐Ÿ’ธ Transactions

  • BEGIN
  • COMMIT
  • ROLLBACK

๐Ÿ“Š Indexes

  • Unique indexes (exclusivity is key)
  • Non-unique indexes (for the masses)
  • Partial indexes (discrimination coming soon)

๐Ÿ”ง Utilities

  • EXPLAIN (peeking behind the curtain)

๐Ÿงฎ Aggregate Functions

Now available: numerical seances. Umbra can perform ritualistic summoning of truths across rows using standard SQL aggregate functions. Useful for divining patterns, spotting anomalies, or just counting how many regrets youโ€™ve inserted into a table.

Function Description Example
COUNT(*) How many? (all of them) SELECT COUNT(*) FROM curses;
SUM(expr) Add them all up (tally your sins) SELECT SUM(darkness_level) FROM cursed_items;
AVG(expr) Arithmetic mean (because median is too mainstream) SELECT AVG(void_pressure) FROM entropy_watch;
MIN(expr) The lowest value (bottom of the abyss) SELECT MIN(shadow_density) FROM entropy_watch;
MAX(expr) The highest value (loftiest darkness) SELECT MAX(soul_count) FROM cursed_items;

โž— Mathematical Functions

For when you need to quantify the abyss. Basic arithmetic operations (+, -, *, /) work as expected, but these functions handle the unnatural calculations.

Function Description Example
ABS(x) Absolute value (distance from zero) SELECT ABS(-666) โ†’ 666
SQRT(x) Square root (measure of diagonal despair) SELECT SQRT(2) โ†’ 1.414...
POWER(x, y) Exponentiation (x raised to yth torment) SELECT POWER(2, 10) โ†’ 1024
TRUNC(x) Amputate decimals (integer-only suffering) SELECT TRUNC(3.14159) โ†’ 3
TRUNC(x, n) Precision mutilation (n decimal digits) SELECT TRUNC(3.14159, 2) โ†’ 3.14
SIGN(x) Returns -1, 0, or 1 (the algebra of alignment) SELECT SIGN(-13) โ†’ -1

Warning

Division by Zero
Attempting to divide by zero will summon an errorโ€”because some voids should remain unexplored.

๐Ÿ”ค String Functions

Words are powerful. Here theyโ€™re dangerous.

Function Description Example
LIKE Pattern matching with % and _ WHERE name LIKE 'Umb%'
CONCAT(a, b) Smashes values into one long string CONCAT('bat', 'man') โ†’ 'batman'
SUBSTRING(str FROM x FOR y) Extracts a slice, PostgreSQL-style SUBSTRING(name FROM 1 FOR 1) โ†’ first letter of name
POSITION(substr IN str) Finds the starting index of a substring POSITION('e' IN 'shadow') โ†’ 2
ASCII(char) Returns the ASCII code of a single character ASCII('A') โ†’ 65

Note

SUBSTRING Syntax
Umbra uses PostgreSQL-style SUBSTRING(string FROM start FOR length).

  • FROM and FOR are both optional, but at least one must be present.
  • Positions are 1-based (because zero-based indexing is for the living).
-- give me just the first letter of each name
SELECT name, SUBSTRING(name FROM 1 FOR 1) FROM customers;

-- or everything after the third character
SELECT SUBSTRING(name FROM 4) FROM customers;

-- or only the first 3 characters, starting from the beginning
SELECT SUBSTRING(name FOR 3) FROM customers;

๐Ÿ”ฎ System functions

Tools for introspection and survival.

Function Description
COALESCE(v1, v2, ...) Returns the first non-null argument
TYPEOF(value) Returns the data type of the value
EXTRACT(field FROM source) Retrieves sub-fields such as year or date from date/time

๐Ÿ“œ Licensing

Umbra is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).

This project includes portions of code derived from third-party projects licensed under the MIT License. Attributions and licence texts for those components are provided in the THIRD_PARTY_NOTICES file.


โœจ Just So You Know

  • Documentation: Here be dragons (and possibly bats)
  • Protocol: If you're implementing a client, or are just curious how the shadowy bits work under the hood, check the binary wire format here.
  • Philosophy: "Compiling is victory. Running is a miracle."

About

A zero-dependency Rust database built from scratch. Experimental and delightfully batty.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages