Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ go.work.sum

# env file
.env

./examples/logs/*.cpo
# Editor/IDE
# .idea/
# .vscode/
Expand Down
3 changes: 3 additions & 0 deletions config.cpo
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CDC_ADAPTER=cpo
CDC_FILE_PATH=examples/logs/sawit.cpo
LOG_PATH=examples/logs/sawit.log
Empty file added data.log
Empty file.
16 changes: 16 additions & 0 deletions docs/Bulog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
**Bulog** is module to create Log easier and pretty with JSON format. With this format Log can be analyzed more and easy to create aggregation log.

## Log Level
| Method | Level | Description|
|---------|------|-------------|
| Kabarin | Info | create log as info |
| Awas | Warning | create log as Warning |
| Kacau | Error | create log as Error |
| Dolog | Debug | create log as debug |

Output example:
```
{"level":"Info","message":"Give info to user","usage":80,"timestamp":"2026-01-09T11:37:23.392Z"}
{"level":"Warning","message":"CPU usage has been exceeded","usage":90,"timestamp":"2026-01-09T11:37:23.398Z"}
{"level":"Error","message":"No disk space available","usage":100,"timestamp":"2026-01-09T11:37:23.398Z"}
```
32 changes: 32 additions & 0 deletions docs/DB Event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

**DB Event** is a feature for `sawitDB` to provide Event Drivent capabilities especially regarding Change Data Capture. This feature is listening for event below:
| Event Name | Description|
|---------|-------------------|
| OnTableCreated | Triggered when new table is crated |
| OnTableDropped | Triggered when a table is dropped |
| OnTableSelected | Triggered when select record from a table |
| OnTableInserted | Triggered when insert record into a table |
| OnTableUpdated | Triggered when a table updataed rows |
| OnTableDeleted | Triggered when rows deleted from a table |

## Enabled CDC (Change Data Capture)
SawitDB is supported for CDC, to enabled CDC you need follow these step
1. Determined **CDC Adapter**, currently we still only support **CPO** adapter.
2. **CDC Adapter** captures change on SawitDB and write as AQL to file `.cpo`, all changed will be recorded on this file. Below how to config CDC and Adapter on `config.cpo` for server side

```
CDC_FILE_PATH=./examples/logs/sawit.cpo
CDC_ADAPTER=cpo
```
Next will be supported for Kafka
## Disabled CDC
If you don't want use CDC you just need let `CDC_ADAPTER` empty on `config.cpo`
## Custome Event Handler
By default EventHandler provide by `./internal/event/dbevent_handler.go` but you can use custom event handler as below:
```
const event = require('./dbeventHandlerExample')
const db = new SawitDB(dbPath,{dbevent:new event()});
```
Full example can view on `./examples/dbevent/main.go`.
To see output CDC on `./examples/logs/sawit.cpo`

11 changes: 11 additions & 0 deletions docs/config cpo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
**SawitDB** used file `.cpo` as config file, eg: `config.cpo` to use it similar to `.env` file, here you can store all configuration variable for entire database.

## Config
To load configuration it is provided in `internal/config/config.go` below is an example:
```
if err := config.LoadEnv("config.cpo"); err != nil {
log.Fatal(err)
}
```
## CPO vs ENV
you can use `.env` in service level such as to config the service like port, endpoint, IP etc. `.cpo` should be used for confi in database engine level like how to enabled/disabled feature, set path file storage, security, etc. `.cpo` similar to `.cfg` in Mysql
Binary file added example.sawit
Binary file not shown.
13 changes: 3 additions & 10 deletions examples/create_local/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,14 @@ import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"

"github.com/WowoEngine/SawitDB-Go/internal/app"
"github.com/WowoEngine/SawitDB-Go/internal/engine"
)

func main() {
dbPath := "example.sawit"
if _, err := os.Stat(dbPath); err == nil {
os.Remove(dbPath)
}

// Create absolute path to avoid directory confusion
absPath, _ := filepath.Abs(dbPath)

db, err := engine.NewSawitDB(absPath)
db, err := app.Bootstrap()
if err != nil {
log.Fatalf("Failed to create DB: %v", err)
}
Expand Down
31 changes: 31 additions & 0 deletions examples/dbevent/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"fmt"

"github.com/WowoEngine/SawitDB-Go/internal/app"
"github.com/WowoEngine/SawitDB-Go/internal/engine"
)

func main() {
db, _ := app.Bootstrap()

query(db, "LAHAN sawit")

query(db, "TANAM KE sawit (id, bibit, umur) BIBIT (101, 'Dura', 2)")
query(db, "TANAM KE sawit (id, bibit, umur) BIBIT (102, 'Tenera', 5)")
query(db, "TANAM KE sawit (id, bibit, umur) BIBIT (103, 'Pisifera', 1)")
query(db, "PANEN * DARI sawit DIMANA id='103'")
query(db, "PUPUK sawit DENGAN bibit='Dura' DIMANA id='103'")
query(db, "GUSUR DARI sawit DIMANA id='103'")
query(db, "BAKAR LAHAN sawit")
}

func query(db *engine.SawitDB, q string) {
res, err := db.Query(q, nil)
if err != nil {
fmt.Printf("Query error '%s': %v\n", q, err)
} else {
fmt.Printf("Query '%s': %v\n", q, res)
}
}
16 changes: 16 additions & 0 deletions examples/logs/sawit.cpo
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
LAHAN sawit
TANAM KE sawit (id, bibit, umur) BIBIT (101, 'Dura', 2)
TANAM KE sawit (id, bibit, umur) BIBIT (102, 'Tenera', 5)
TANAM KE sawit (id, bibit, umur) BIBIT (103, 'Pisifera', 1)
PANEN * DARI sawit DIMANA id='103'
PUPUK sawit DENGAN bibit='Dura' DIMANA id='103'
GUSUR DARI sawit DIMANA id='103'
BAKAR LAHAN sawit
LAHAN sawit
TANAM KE sawit (id, bibit, umur) BIBIT (101, 'Dura', 2)
TANAM KE sawit (id, bibit, umur) BIBIT (102, 'Tenera', 5)
TANAM KE sawit (id, bibit, umur) BIBIT (103, 'Pisifera', 1)
PANEN * DARI sawit DIMANA id='103'
PUPUK sawit DENGAN bibit='Dura' DIMANA id='103'
GUSUR DARI sawit DIMANA id='103'
BAKAR LAHAN sawit
Empty file added examples/logs/sawit.log
Empty file.
39 changes: 39 additions & 0 deletions internal/app/bootstrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package app

import (
"log"
"os"
"path/filepath"

"github.com/WowoEngine/SawitDB-Go/internal/config"
"github.com/WowoEngine/SawitDB-Go/internal/engine"
"github.com/WowoEngine/SawitDB-Go/internal/entity"
"github.com/WowoEngine/SawitDB-Go/internal/event"
"github.com/WowoEngine/SawitDB-Go/internal/utils"
)

func Bootstrap() (*engine.SawitDB, error) {
if err := config.LoadEnv("config.cpo"); err != nil {
log.Fatal(err)
}

cfg := config.NewConfig()

bulog, _ := utils.NewBulog(cfg.LogPath())
dbevent, _ := event.NewDBEventHandler(cfg.CdcFilePath(), bulog)
dbPath := "example.sawit"
if _, err := os.Stat(dbPath); err == nil {
os.Remove(dbPath)
}

absPath, _ := filepath.Abs(dbPath)

db, err := engine.NewSawitDB(absPath, dbevent, &entity.EngineOption{
IsEvent: cfg.CdcAdapter() != "",
})
if err != nil {
log.Fatalf("Failed to create DB: %v", err)
}

return db, nil
}
66 changes: 66 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package config

import (
"bufio"
"os"
"strings"

"github.com/WowoEngine/SawitDB-Go/internal/constant"
)

type Config struct {
cdcAdapter string
cdcFilePath string
logPath string
}

func NewConfig() *Config {
return &Config{
cdcAdapter: os.Getenv(constant.CDC_ADAPTER),
cdcFilePath: os.Getenv(constant.CDC_PATH),
logPath: os.Getenv(constant.LOG_PATH),
}
}

func (c *Config) CdcAdapter() string {
return c.cdcAdapter
}
func (c *Config) CdcFilePath() string {
return c.cdcFilePath
}
func (c *Config) LogPath() string {
return c.logPath
}

// LoadEnv membaca file .env dan memasukkan ke environment variable
func LoadEnv(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())

// skip comment & empty line
if line == "" || strings.HasPrefix(line, "#") {
continue
}

parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}

key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])

value = strings.Trim(value, `"'`)

os.Setenv(key, value)
}

return scanner.Err()
}
19 changes: 19 additions & 0 deletions internal/constant/constant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package constant

const (
CDC_ADAPTER string = "CDC_ADAPTER"
CDC_PATH string = "CDC_FILE_PATH"
)

const (
LOG_PATH string = "LOG_PATH"
)

type LogLevel string

const (
LOG_INFO LogLevel = "INFO"
LOG_WARN LogLevel = "WARN"
LOG_ERROR LogLevel = "ERROR"
LOG_DEBUG LogLevel = "DEBUG"
)
10 changes: 10 additions & 0 deletions internal/engine/dbevent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package engine

type DBEvent interface {
OnTableCreated(name string, index int, aql string)
OnTableDropped(name string, index int, aql string)
OnTableSelected(name string, data []map[string]interface{}, aql string)
OnTableInserted(name string, data map[string]interface{}, aql string)
OnTableDeleted(name string, data []map[string]interface{}, aql string)
OnTableUpdated(name string, data []map[string]interface{}, aql string)
}
Loading