Motty
原生桌面 SSH 工作台:终端、SFTP/SCP、端口转发、VNC 与 RDP 远程桌面、AI 助手都在一个窗口里。Go 编写,没有 Electron,空闲时保持安静。免费开源,支持 macOS、Windows 和 Linux。
翻译准备中
소규모 프로젝트를 위한 다목적 경량 관계형 DB (순수 Go, 의존성 0) — 임베디드·서버·웹 콘솔, 인메모리·시계열 테이블 / A multipurpose relational database for small projects in pure Go
翻译准备中
正在加载完整 README。
查看完整 README ↗Go로 작성된 경량 임베디드 관계형 데이터베이스입니다. 외부 의존성 0개, 단일 파일 저장, 프로젝트에 라이브러리로 바로 넣어 쓰는 것을 목표로 합니다 (SQLite와 같은 사용 방식).
go.mod에 require 없음)<파일>-wal) 기반 원자적·내구성 있는 커밋과 크래시 복구, 페이지 체크섬, 무결성 검사(IntegrityCheck):memory: 인메모리 모드CREATE/DROP TABLE, ALTER TABLE ADD/RENAME/DROP COLUMN, RENAME TO, CREATE [UNIQUE] INDEX(복합·부분 WHERE), CREATE TRIGGER, CREATE VIEW, INSERT/SELECT/UPDATE/DELETE, JOIN(INNER/LEFT/CROSS), GROUP BY/HAVING, ORDER BY, LIMIT/OFFSET, DISTINCT, CASE, IN, BETWEEN, LIKE, COLLATE, 집계/스칼라/날짜 함수, EXPLAIN, VACUUM
EXISTS·IN (SELECT)·FROM (SELECT), 상관 포함), UNION [ALL]/INTERSECT/EXCEPTON CONFLICT … DO NOTHING / DO UPDATE), INSERT OR IGNORE/REPLACE, INSERT … SELECT, RETURNINGPRIMARY KEY/UNIQUE, NOT NULL, DEFAULT, CHECK, FOREIGN KEY(CASCADE/SET NULL/RESTRICT)?, ?N, :name, @name, $nameOVER (PARTITION BY … ORDER BY … ROWS/RANGE …)), JSON 함수와 ->/->> 연산자CREATE TRIGGER … BEFORE|AFTER INSERT|UPDATE [OF cols]|DELETE ON t [WHEN cond] BEGIN … END, NEW/OLD, RAISE(ABORT, '메시지')·RAISE(IGNORE)ANALYZE 통계 기반 조인 순서internal/store, CREATE TABLE … USING <kind>) — 기본은 B+트리, USING memory 인메모리 테이블, USING timeseries 시계열 테이블BEGIN/COMMIT/ROLLBACK, 문장 단위 원자성, 스냅샷 읽기 — 읽기와 쓰기가 서로를 기다리지 않음context 기반 쿼리 취소·타임아웃, 그룹 커밋database/sql 드라이버(bangdb) 및 대화형 CLI 제공, 서버 모드(TCP/TLS, 인증, bangdb:// DSN)db.Stats(), 슬로우 쿼리 훅, EXPLAIN ANALYZE, SQL 덤프·SQLite 덤프 가져오기·CSV, 저장 데이터 암호화(AES-256-XTS), 반동기 복제·펜싱·특정 시점 복구(PITR)go get github.com/knoxxr/bangdb
안드로이드 앱에 내장: docs/ANDROID.md (.aar, Kotlin/Java API com.knoxxr.bangdb).
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/knoxxr/bangdb"
)
func main() {
db, err := bangdb.Open("app.db") // 인메모리: bangdb.Open(":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
age INT DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_users_age ON users (age);
`)
if err != nil {
log.Fatal(err)
}
res, _ := db.Exec(`INSERT INTO users (name, email, age) VALUES (?, ?, ?)`, "kim", "[email protected]", 31)
fmt.Println("new id:", res.LastInsertID)
// 트랜잭션: fn이 nil을 반환하면 커밋, 에러면 롤백
err = db.Update(func(tx *bangdb.Tx) error {
_, err := tx.Exec(`UPDATE users SET age = age + 1 WHERE id = ?`, res.LastInsertID)
return err
})
// 결과는 스트리밍됩니다. 끝까지 읽지 않을 때는 반드시 Close 하세요.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
rows, err := db.QueryContext(ctx, `SELECT id, name, age FROM users WHERE age > ? ORDER BY age DESC`, 20)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int64
var name string
var age int
rows.Scan(&id, &name, &age)
fmt.Println(id, name, age)
}
if err := rows.Err(); err != nil { // 예: 타임아웃
log.Fatal(err)
}
var count int
db.QueryRow(`SELECT COUNT(*) FROM users`).Scan(&count)
}
applied, err := db.Migrate(ctx, []bangdb.Migration{
{Version: 1, SQL: `CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)`},
{Version: 2, SQL: `ALTER TABLE users ADD COLUMN email TEXT`},
{Version: 3, Func: func(tx *bangdb.Tx) error { _, err := tx.Exec(`UPDATE users SET email = ''`); return err }},
})
PRAGMA user_version에 마지막으로 적용한 버전이 기록되고, 각 단계는 자기 트랜잭션에서 실행되어 실패하면 그 단계만 취소됩니다. 그 밖의 PRAGMA: table_info(t), index_list(t), index_info(i), table_list, integrity_check, foreign_keys, journal_mode, page_count, page_size.
get, _ := db.Prepare("SELECT name FROM users WHERE id = ?") // 파싱 1회, 플랜 보관 (동시 사용 가능)
var name string
get.QueryRow(42).Scan(&name)
import (
"database/sql"
_ "github.com/knoxxr/bangdb/driver"
)
db, err := sql.Open("bangdb", "app.db")
같은 *sql.DB의 모든 커넥션은 하나의 엔진 인스턴스를 공유합니다 (:memory:도 커넥션 간 공유됨).
db.Backup("backup.db") // 온라인 백업 (결과는 그대로 열 수 있는 DB 파일)
// primary
p := replication.NewPrimary(db, replication.PrimaryOptions{Token: "secret"})
go p.ListenAndServe(":7070")
// replica (다른 서버): 처음엔 스냅샷을 받고, 이후 커밋을 실시간 미러링
rdb, _ := bangdb.Open("replica.db")
r := replication.StartReplica(rdb, "primary:7070", replication.ReplicaOptions{Token: "secret"})
rdb.Query("SELECT ...") // 읽기 분산; 쓰기는 ErrReadOnly
r.Promote() // 장애 조치: 쓰기 가능한 primary로 승격
자세한 내용: docs/REPLICATION.md
// 서버: DB를 연 프로세스가 TCP/TLS로 제공
srv := server.New(db, server.Options{Password: "s3cret", MaxConns: 100, IdleTimeout: 10 * time.Minute})
go srv.ListenAndServe("127.0.0.1:7432") // 또는 ListenAndServeTLS(addr, tlsConfig)
// 클라이언트: 직접
c, _ := client.Dial(ctx, "db.internal:7432", &client.Options{User: "app", Password: "s3cret"})
rows, _ := c.Query(ctx, "SELECT id, name FROM users WHERE age > ?", 30)
// 클라이언트: database/sql (커넥션 풀)
sdb, _ := sql.Open("bangdb", "bangdb://app:[email protected]:7432?tls=1")
BEGIN/COMMIT이 그대로 동작하고, 연결이 끊기면 열린 트랜잭션은 롤백됩니다. 결과는 스트리밍되며 Rows.Close나 ctx 취소가 서버의 실행 중인 문장을 중단합니다. 오류 종류는 유지됩니다(errors.Is(err, bangdb.ErrConstraint) 등).Options.Authenticate(user, password) 콜백, 암호화는 TLS. 루프백 왕복 지연은 단건 조회 약 44 µs.bangdb -listen :7432 -password s3cret [-tls-cert c.pem -tls-key k.pem] app.db, 원격 셸 bangdb -connect bangdb://app:s3cret@host:7432.以上为 README 的开头部分。其余内容请阅读原文。 查看完整 README ↗