ZipponDB/README.md
MrBounty 7d2400b257 Update README and small stuff
Changed the zipponschema example   to have friends as list of string

Better README, created ZiQL.md for more details on query

Added fast build option
2024-10-13 23:40:30 +02:00

7.6 KiB

alt text

Introduction

ZipponDB is a relational database written entirely in Zig from scratch with 0 dependency.

ZipponDB goal is to be ACID, light, simple and high performance. It aim small to medium application that don't need fancy features but a simple and reliable database.

Why Zippon ?

  • Relational database (Soon)
  • Simple and minimal query language
  • Small, light, fast and implementable everywhere

Quickstart

  1. Get a binary: You can build the binary directly from the source code for any architecture (tuto is comming), or using the binary in the release (comming too).
  2. Create a database: You can then run the binary, this will start a Command Line Interface. The first thing to do is to create a new database. For that run the command db new path/to/directory, it will create a ZipponDB directory. Then database metrics to see if it worked.
  3. Select a database: You can select a database by using db use path/to/ZipponDB. You can also set the environment variable ZIPPONDB_PATH and it will and use this path, this need to be the path to a directory with proper DATA, BACKUP and LOG directory.
  4. Attach a schema: Once the database created, you need to attach a schema to it (see next section for how to define a schema). For that you can run schema init path/to/schema.txt. This will create new directories and empty files used to store data. You can test the current db schema by running schema describe.
  5. Use the database: You can now start using the database by sending query like that: run "ADD User (name = 'Bob')".

Note: For the moment ZipponDB use the current working directory as main directory so all path are a sub_path of it.

Declare a schema

In ZipponDB you use structures, or struct for short, and not tables to organize how your data is store and manipulate. A struct have a name like User and members like name and age.

Create a file with inside a schema that describe all structs. Compared to SQL, you can see it as a file where you declare all table name, columns name, data type and relationship. All struct have an id of the type UUID by default.

Here an example of a file:

User (
    name: str,
    email: str,
    best_friend: User,
)

Note that the best friend is a link to another User.

Here a more advance example with multiple struct:

User (
    name: str,
    email: str,
    friends: []User,
    posts: []Post,
    comments: []Comment,
)

Post (
    title: str,
    image: str,
    at: date,
    like_by: []User,
    comments: []Comment,
)

Comment (
    content: str,
    at: date,
    like_by: []User,
)

Note: [] before the type mean a list/array of this type.

Migration to a new schema - Not yet implemented

In the future, you will be able to update the schema like add a new member to a struct and update the database. For the moment, you can't change the schema once init.

ZipponQL

ZipponDB use it's own query language, ZipponQL or ZiQL for short. Here the keys point to remember:

  • 4 actions available: GRAB ADD UPDATE DELETE
  • All query start with an action then a struct name
  • {} For filters
  • [] For how much; what data
  • () For new or updated data (Not already in file)
  • || For additional options

Disclaimer: Lot of stuff are still missing and the language may change over time.

Quickstart

For more information see ZiQL Introduction

GRAB

The main action is GRAB, this will parse files and return data.

GRAB User {name = 'Bob' AND (age > 30 OR age < 10)}

GRAB query return a list of JSON with the data inside, e.g:

[{id:"1e170a80-84c9-429a-be25-ab4657894653", name: "Gwendolyn Ray", age: 70, email: "austin92@example.org", scores: [ 77 ], friends: [], }, ]

ADD

The ADD action will add one entity into the database.

ADD User (name = 'Bob', age = 30, email = 'bob@email.com', scores = [1 100 44 82])

DELETE

Similare to GRAB but delete all entity found using the filter.

DELETE User {name = 'Bob'}

UPDATE

A mix of GRAB and ADD. This take a filter first, then the new data.
Here we update the 5 first User named bob to add a capital and become Bob.

UPDATE User [5] {name='bob'} TO (name = 'Bob')

Not yet implemented

A lot of things are not yet implemented, you can find examples in the ZiQL Introduction.

This include:

  • Relationship
  • Ordering
  • Batch
  • Array manipulation
  • And more...

You can also link query. Each query return 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 have more than 2 queries. You can also just use one => but the list of UUID is discard in that case.

Data types

Their is 5 data type for the moment:

  • int: 64 bit integer
  • float: 64 bit float. Need to have a dot, 1. is a float 1 is an integer.
  • bool: Boolean, can be true or false
  • string: Character array between ''
  • UUID: Id in the UUID format, used for relationship, ect. All struct have an id member.

Comming soon:

  • date: A date in yyyy/mm/dd
  • datetime: A date time in yyyy/mm/dd/hh/mm/ss
  • time: A time in hh/mm/ss

All data type can be an array of those type using [] in front of it. So []int is an array of integer.

All data type can also be null. Expect array that can only be empty.

Why I created it ?

Well the first reason is to learn, both zig and databases.

The second is to use it in my apps. I like to deploy Golang + HTMX app on Fly.io but I often find myself struggelling to get a simple database. I can either host it myself but I need to link my app and the db securely. Or use a cloud db service but that mean my db is far from my app. All I want is to give to a Fly machine 10go of storage, do some backup on it and call it a day. But for that I need to include it to the Dockerfile of my app, what easier way than just a binary ?

So that my goal long term, to use it in my apps as a simple database that live WITH the app, sharing CPU and memory.

How does it work ?

TODO: Create a tech doc of what is happening inside.

Roadmap

Note: This will probably evolve over time.

v0.1 - Base

  • UUID
  • CLI
  • Tokenizers
  • ZiQL parser
  • Schema engine
  • File engine

v0.2 - Usable

  • B+Tree
  • Relationships
  • Date
  • Linked query
  • Docker

v0.3 - QoL

  • Schema migration
  • Dump/Bump data
  • Recovery
  • Better CLI
  • Logs

v0.4 - Usability

  • Server
  • Config file
  • Python interface
  • Go interface

v0.5 - In memory

  • In memory option
  • Cache

v0.6 - Performance

  • Transaction
  • Multi threading
  • Lock manager
  • Optimized data file

v0.7 - Safety

  • Auth
  • Metrics
  • Durability

v0.8 - Advanced

  • Query optimizer

v0.9 - Docs

  • ZiQL tuto
  • Deployment tuto
  • Code docs
  • CLI help

v1.0 - Web interface

  • Query builder
  • Tables
  • Schema visualization
  • Dashboard metrics

Let's see where it (or my brain) start explode ;)