Got an error that I could find for a while. It was when parsing a lot of files. Turn out it was the Thread Pool or something because if I run on 1 core, it's ok. Different thing in this commit, I just want to freeze a state that benchmark work.
ZipponDB is a relational database written entirely in Zig from scratch with 0 dependencies.
ZipponDB's goal is to be ACID, light, simple, and high-performance. It aims at small to medium applications that don't need fancy features but a simple and reliable database.
Key Features:
- Small: Binary is small, around 2-3Mb.
- Fast: Parse millions of entities in milliseconds.
- Relationship: Build with focus on easy relationship.
- Query Language: Use it's own simple query language.
- No dependencies: Depend on nothing, every line of code running is in the codebase.
- Open-source: Open-source under MIT licence.
- Portable: Easily compiled and deployed across various platforms.
For more informations visit the docs: https://mrbounty.github.io/ZipponDB/
Note: ZipponDB is still in Alpha v0.2, see roadmap.
Declare a schema
In ZipponDB, you use structures, or structs for short, and not tables to organize how your data is stored and manipulated. A struct has a name like User
and members like name
and age
.
Create a file that contains a schema that describes all structs. Compared to SQL, you can see it as a file where you declare all table names, column names, data types, and relationships. All structs have an id of the type UUID by default.
Here an example of a file:
User (
name: str,
age: int,
email: str,
Parent: User,
childrens: []User,
orders: []Order,
)
Order (
at: datetime,
items: []Item,
)
Item (
name: str,
category: str,
)
Note that parent is a link to another User
and can be none
, []
mean an array. You can find more examples here.
ZipponQL
ZipponDB uses its own query language, ZipponQL or ZiQL for short. Here are the key points to remember:
- 4 actions available:
GRAB
,ADD
,UPDATE
,DELETE
- All queries start with an action followed by a struct name
{}
are filters[]
specify how much and what data()
contain new or updated data (not already in the file)
GRAB
The main action is GRAB
, it parse files and return data.
GRAB User {name = 'Bob' AND (age > 30 OR age < 10)}
Using []
before the filter tell what to return.
GRAB User [id, email] {name = 'Bob'}
Relationship use filter within filter.
GRAB User {best_friend IN {name = 'Bob'}}
GRAB queries return a list of JSON objects, e.g:
[{id:"1e170a80-84c9-429a-be25-ab4657894653", name: "Gwendolyn Ray", age: 70, email: "austin92@example.org", scores: [ 77 ], friends: [], }, ]
ADD
The ADD
action adds one entity to the database. The syntax is similar to GRAB
, but uses ()
. This signifies that the data is not yet in the database.
ADD User (name = 'Bob', age = 30)
DELETE
Similar to GRAB
but deletes all entities found using the filter and returns a list of deleted UUIDs.
DELETE User {name = 'Bob'}
UPDATE
A mix of GRAB
and ADD
. It takes a filter first, then the new data.
Here, we update the first 5 User
entities named 'bob' to capitalize the name and become 'Bob':
UPDATE User [5] {name='bob'} TO (name = 'Bob')
Link query - Not yet implemented
You can also link query. Each query returns a list of UUID of a specific struct. You can use it in the next query.
Here an example where I create a new Comment
that I then append to the list of comment of one specific User
.
ADD Comment (content='Hello world', at=NOW, like_by=[]) => added_comment => UPDATE User {id = '000'} TO (comments APPEND added_comment)
The name between =>
is the variable name of the list of UUID used for the next queries, you can have multiple one if the link has more than 2 queries.
You can also just use one =>
but the list of UUID is discarded in that case.
This can be use with GRAB too. So you can create variable before making the query. Here an example:
GRAB User {name = 'Bob'} => bobs =>
GRAB User {age > 18} => adults =>
GRAB User {IN adults AND !IN bobs}
Which is the same as:
GRAB User {name != 'Bob' AND age > 18}