Update documentation and clients for v1.1.0
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
Documentation updates: - Fix v0.1.0 → v1.1.0 version numbers in en, ru, fa, zh docs - Add missing Window Functions, Multi-Tenant ERP, Supported Keywords sections to ru, fa, zh baraql.md (~105 lines each) - Expand Turkish and Arabic baraql.md (110 → 268 lines) - Expand Turkish and Arabic installation.md (62 → 307 lines) - Add new Bulgarian documentation files (18 new files) Client updates: - Python: Full async/await rewrite with asyncio, request queueing - Rust: Full async/await rewrite with tokio, async examples - Nim: Update README to v1.1.0 - All clients now support async patterns consistently
This commit is contained in:
@@ -15,7 +15,7 @@ Official Nim client for **BaraDB** — a multimodal database engine.
|
||||
Add to your `.nimble` file:
|
||||
|
||||
```nim
|
||||
requires "baradb >= 1.0.0"
|
||||
requires "baradb >= 1.1.0"
|
||||
```
|
||||
|
||||
Or clone locally:
|
||||
|
||||
+71
-49
@@ -1,15 +1,16 @@
|
||||
# BaraDB Python Client
|
||||
# BaraDB Python Async Client
|
||||
|
||||
Official Python client for **BaraDB** — a multimodal database engine written in Nim.
|
||||
Official async Python client for **BaraDB** — a multimodal database engine written in Nim.
|
||||
|
||||
## Features
|
||||
|
||||
- **Async/await** — fully non-blocking, concurrent query support
|
||||
- **Binary wire protocol** — fast, compact TCP communication
|
||||
- **Sync & blocking API** — simple to use in scripts and apps
|
||||
- **Request queueing** — sequential processing with concurrent execution
|
||||
- **Query builder** — fluent SQL construction
|
||||
- **Parameterized queries** — safe from SQL injection
|
||||
- **Vector & JSON support** — first-class multimodal types
|
||||
- **Context managers** — `with` statement support
|
||||
- **Context managers** — async `async with` statement support
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -20,7 +21,7 @@ pip install baradb
|
||||
Or from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/barabadb/baradadb.git
|
||||
git clone https://github.com/katehonz/barabaDB.git
|
||||
cd clients/python
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
@@ -28,71 +29,92 @@ pip install -e ".[dev]"
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from baradb import Client
|
||||
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
async def main():
|
||||
client = Client("localhost", 9472)
|
||||
await client.connect()
|
||||
|
||||
result = client.query("SELECT name, age FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"], row["age"])
|
||||
result = await client.query("SELECT name, age FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"], row["age"])
|
||||
|
||||
client.close()
|
||||
await client.close()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Context Manager
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from baradb import Client
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query("SELECT 1")
|
||||
print(result.row_count)
|
||||
async def main():
|
||||
async with Client("localhost", 9472) as client:
|
||||
result = await client.query("SELECT 1")
|
||||
print(result.row_count)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Parameterized Queries
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from baradb import Client, WireValue
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
"SELECT * FROM users WHERE age > $1 AND country = $2",
|
||||
[WireValue.int64(18), WireValue.string("BG")],
|
||||
)
|
||||
for row in result:
|
||||
print(row)
|
||||
async def main():
|
||||
async with Client("localhost", 9472) as client:
|
||||
result = await client.query_params(
|
||||
"SELECT * FROM users WHERE age > $1 AND country = $2",
|
||||
[WireValue.int64(18), WireValue.string("BG")],
|
||||
)
|
||||
for row in result:
|
||||
print(row)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Query Builder
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from baradb import Client, QueryBuilder
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
qb = (
|
||||
QueryBuilder(client)
|
||||
.select("name", "email")
|
||||
.from_("users")
|
||||
.where("active = true")
|
||||
.order_by("name")
|
||||
.limit(10)
|
||||
)
|
||||
result = qb.exec()
|
||||
for row in result:
|
||||
print(row)
|
||||
async def main():
|
||||
async with Client("localhost", 9472) as client:
|
||||
qb = (
|
||||
QueryBuilder(client)
|
||||
.select("name", "email")
|
||||
.from_("users")
|
||||
.where("active = true")
|
||||
.order_by("name")
|
||||
.limit(10)
|
||||
)
|
||||
result = await qb.exec()
|
||||
for row in result:
|
||||
print(row)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Vector Search
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from baradb import Client, WireValue
|
||||
|
||||
with Client("localhost", 9472) as client:
|
||||
result = client.query_params(
|
||||
"SELECT id, name FROM products ORDER BY embedding <-> $1 LIMIT 5",
|
||||
[WireValue.vector([0.1, 0.2, 0.3])],
|
||||
)
|
||||
async def main():
|
||||
async with Client("localhost", 9472) as client:
|
||||
result = await client.query_params(
|
||||
"SELECT id, name FROM products ORDER BY embedding <-> $1 LIMIT 5",
|
||||
[WireValue.vector([0.1, 0.2, 0.3])],
|
||||
)
|
||||
print(result.rows)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
@@ -107,7 +129,7 @@ Integration tests (requires server on `localhost:9472`):
|
||||
|
||||
```bash
|
||||
# Start server
|
||||
docker run -d -p 9472:9472 baradb:latest
|
||||
docker run -d -p 9472:9472 barabadb:latest
|
||||
|
||||
# Run all tests
|
||||
pytest
|
||||
@@ -124,18 +146,18 @@ pytest
|
||||
| `database` | `default` | Default database |
|
||||
| `username` | `admin` | Username |
|
||||
| `password` | `""` | Password |
|
||||
| `timeout` | `30` | Socket timeout in seconds |
|
||||
| `timeout` | `30.0` | Socket timeout in seconds |
|
||||
|
||||
### Methods
|
||||
### Methods (all async)
|
||||
|
||||
- `connect()` — open TCP connection
|
||||
- `close()` — close connection
|
||||
- `query(sql) -> QueryResult` — execute SELECT-like query
|
||||
- `query_params(sql, params) -> QueryResult` — parameterized query
|
||||
- `execute(sql) -> int` — execute DDL/DML, returns affected rows
|
||||
- `auth(token)` — JWT authentication
|
||||
- `ping() -> bool` — health check
|
||||
- `await client.connect()` — open TCP connection
|
||||
- `await client.close()` — close connection
|
||||
- `await client.query(sql) -> QueryResult` — execute SELECT-like query
|
||||
- `await client.query_params(sql, params) -> QueryResult` — parameterized query
|
||||
- `await client.execute(sql) -> int` — execute DDL/DML, returns affected rows
|
||||
- `await client.auth(token)` — JWT authentication
|
||||
- `await client.ping() -> bool` — health check
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
Apache-2.0
|
||||
@@ -1,20 +1,34 @@
|
||||
"""
|
||||
BaraDB Python Client
|
||||
BaraDB Python Async Client
|
||||
|
||||
Official Python client for BaraDB — Multimodal Database Engine.
|
||||
Official async Python client for BaraDB — Multimodal Database Engine.
|
||||
Communicates via the BaraDB Wire Protocol (binary, big-endian, TCP).
|
||||
|
||||
Install:
|
||||
pip install baradb
|
||||
|
||||
Quick Start:
|
||||
import asyncio
|
||||
from baradb import Client
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
result = client.query("SELECT name FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"])
|
||||
client.close()
|
||||
|
||||
async def main():
|
||||
client = Client("localhost", 9472)
|
||||
await client.connect()
|
||||
result = await client.query("SELECT name FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"])
|
||||
await client.close()
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
Parameterized Queries:
|
||||
result = await client.query_params(
|
||||
"SELECT * FROM users WHERE age > $1",
|
||||
[WireValue.int64(18)]
|
||||
)
|
||||
|
||||
Authentication:
|
||||
await client.auth("jwt-token-here")
|
||||
"""
|
||||
|
||||
from .core import (
|
||||
@@ -27,7 +41,7 @@ from .core import (
|
||||
ResultFormat,
|
||||
)
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.1.0"
|
||||
__all__ = [
|
||||
"Client",
|
||||
"QueryBuilder",
|
||||
@@ -36,4 +50,4 @@ __all__ = [
|
||||
"MsgKind",
|
||||
"FieldKind",
|
||||
"ResultFormat",
|
||||
]
|
||||
]
|
||||
+166
-100
@@ -1,33 +1,38 @@
|
||||
"""
|
||||
BaraDB Python Client
|
||||
BaraDB Python Async Client
|
||||
|
||||
Binary protocol client for BaraDB database.
|
||||
Communicates via the BaraDB Wire Protocol (binary, big-endian).
|
||||
Official async Python client for BaraDB — Multimodal Database Engine.
|
||||
Communicates via the BaraDB Wire Protocol (binary, big-endian, TCP).
|
||||
|
||||
Install:
|
||||
pip install baradb
|
||||
|
||||
Quick Start:
|
||||
import asyncio
|
||||
from baradb import Client
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
result = client.query("SELECT name FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"])
|
||||
client.close()
|
||||
|
||||
async def main():
|
||||
client = Client("localhost", 9472)
|
||||
await client.connect()
|
||||
result = await client.query("SELECT name FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"])
|
||||
await client.close()
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
Parameterized Queries:
|
||||
result = client.query_params("SELECT * FROM users WHERE age > $1", [WireValue.int64(18)])
|
||||
result = await client.query_params(
|
||||
"SELECT * FROM users WHERE age > $1",
|
||||
[WireValue.int64(18)]
|
||||
)
|
||||
|
||||
Authentication:
|
||||
client = Client("localhost", 9472, username="admin", password="secret")
|
||||
client.connect()
|
||||
client.auth("jwt-token-here")
|
||||
await client.auth("jwt-token-here")
|
||||
"""
|
||||
|
||||
import socket
|
||||
import asyncio
|
||||
import struct
|
||||
import json
|
||||
from typing import Any, Optional, Sequence
|
||||
|
||||
|
||||
@@ -49,7 +54,6 @@ class FieldKind:
|
||||
|
||||
|
||||
class MsgKind:
|
||||
# Client messages
|
||||
CLIENT_HANDSHAKE = 0x01
|
||||
QUERY = 0x02
|
||||
QUERY_PARAMS = 0x03
|
||||
@@ -59,7 +63,6 @@ class MsgKind:
|
||||
CLOSE = 0x07
|
||||
PING = 0x08
|
||||
AUTH = 0x09
|
||||
# Server messages
|
||||
SERVER_HANDSHAKE = 0x80
|
||||
READY = 0x81
|
||||
DATA = 0x82
|
||||
@@ -88,7 +91,7 @@ class WireValue:
|
||||
return WireValue(FieldKind.NULL)
|
||||
|
||||
@staticmethod
|
||||
def bool_val(val: bool):
|
||||
def bool(val: bool):
|
||||
return WireValue(FieldKind.BOOL, val)
|
||||
|
||||
@staticmethod
|
||||
@@ -120,15 +123,15 @@ class WireValue:
|
||||
return WireValue(FieldKind.STRING, val)
|
||||
|
||||
@staticmethod
|
||||
def bytes_val(val: bytes):
|
||||
def bytes(val: bytes):
|
||||
return WireValue(FieldKind.BYTES, val)
|
||||
|
||||
@staticmethod
|
||||
def array_val(val: list):
|
||||
def array(val: list):
|
||||
return WireValue(FieldKind.ARRAY, val)
|
||||
|
||||
@staticmethod
|
||||
def object_val(val: dict):
|
||||
def object(val: dict):
|
||||
return WireValue(FieldKind.OBJECT, val)
|
||||
|
||||
@staticmethod
|
||||
@@ -136,7 +139,7 @@ class WireValue:
|
||||
return WireValue(FieldKind.VECTOR, val)
|
||||
|
||||
@staticmethod
|
||||
def json_val(val: str):
|
||||
def json(val: str):
|
||||
return WireValue(FieldKind.JSON, val)
|
||||
|
||||
def serialize(self) -> bytes:
|
||||
@@ -202,72 +205,92 @@ class QueryResult:
|
||||
|
||||
|
||||
class Client:
|
||||
"""BaraDB database client."""
|
||||
"""Async BaraDB database client."""
|
||||
|
||||
def __init__(self, host: str = "localhost", port: int = 9472,
|
||||
database: str = "default", username: str = "admin",
|
||||
password: str = "", timeout: int = 30):
|
||||
password: str = "", timeout: float = 30.0):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.database = database
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.timeout = timeout
|
||||
self._sock: Optional[socket.socket] = None
|
||||
self._reader: Optional[asyncio.StreamReader] = None
|
||||
self._writer: Optional[asyncio.StreamWriter] = None
|
||||
self._connected = False
|
||||
self._request_id = 0
|
||||
self._buffer = bytearray()
|
||||
self._pending_resolve = None
|
||||
self._request_queue: list = []
|
||||
self._request_lock = False
|
||||
|
||||
def connect(self) -> None:
|
||||
async def connect(self) -> None:
|
||||
"""Connect to the BaraDB server."""
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._sock.settimeout(self.timeout)
|
||||
self._sock.connect((self.host, self.port))
|
||||
self._reader, self._writer = await asyncio.wait_for(
|
||||
asyncio.open_connection(self.host, self.port),
|
||||
timeout=self.timeout
|
||||
)
|
||||
self._connected = True
|
||||
|
||||
def close(self) -> None:
|
||||
if self._sock:
|
||||
async def close(self) -> None:
|
||||
"""Close the connection to the server."""
|
||||
if self._writer and self._connected:
|
||||
try:
|
||||
msg = self._build_message(MsgKind.CLOSE, b"")
|
||||
self._sock.send(msg)
|
||||
self._writer.write(msg)
|
||||
await self._writer.drain()
|
||||
except Exception:
|
||||
pass
|
||||
self._sock.close()
|
||||
if self._writer:
|
||||
self._writer.close()
|
||||
await self._writer.wait_closed()
|
||||
self._writer = None
|
||||
self._reader = None
|
||||
self._connected = False
|
||||
|
||||
def is_connected(self) -> bool:
|
||||
"""Check if client is connected."""
|
||||
return self._connected
|
||||
|
||||
def _next_id(self) -> int:
|
||||
self._request_id += 1
|
||||
return self._request_id
|
||||
|
||||
def _recv_exact(self, size: int) -> bytes:
|
||||
async def _recv_exact(self, size: int) -> bytes:
|
||||
"""Receive exactly `size` bytes from the socket."""
|
||||
data = b""
|
||||
while len(data) < size:
|
||||
chunk = self._sock.recv(size - len(data))
|
||||
if not chunk:
|
||||
raise ConnectionError("Connection closed by server")
|
||||
data += chunk
|
||||
while len(self._buffer) < size:
|
||||
try:
|
||||
chunk = await asyncio.wait_for(
|
||||
self._reader.read(size - len(self._buffer)),
|
||||
timeout=self.timeout
|
||||
)
|
||||
if not chunk:
|
||||
raise ConnectionError("Connection closed by server")
|
||||
self._buffer.extend(chunk)
|
||||
except asyncio.TimeoutError:
|
||||
raise TimeoutError("Receive timeout")
|
||||
data = bytes(self._buffer[:size])
|
||||
del self._buffer[:size]
|
||||
return data
|
||||
|
||||
def _read_response_header(self) -> tuple[int, int, int]:
|
||||
async def _read_header(self) -> tuple[int, int, int]:
|
||||
"""Read a 12-byte message header. Returns (kind, length, request_id)."""
|
||||
header = self._recv_exact(12)
|
||||
header = await self._recv_exact(12)
|
||||
kind, length, req_id = struct.unpack(">III", header)
|
||||
return kind, length, req_id
|
||||
|
||||
def _read_error(self, length: int) -> Exception:
|
||||
async def _read_error(self, length: int) -> Exception:
|
||||
"""Read and parse an ERROR payload."""
|
||||
data = self._recv_exact(length)
|
||||
data = await self._recv_exact(length)
|
||||
code = struct.unpack(">I", data[:4])[0]
|
||||
msg_len = struct.unpack(">I", data[4:8])[0]
|
||||
error_msg = data[8:8 + msg_len].decode("utf-8")
|
||||
return Exception(f"BaraDB error {code}: {error_msg}")
|
||||
|
||||
def _read_data_response(self, length: int) -> QueryResult:
|
||||
async def _read_data_response(self, length: int) -> QueryResult:
|
||||
"""Read and parse a DATA payload, then follow up with COMPLETE."""
|
||||
data = self._recv_exact(length)
|
||||
data = await self._recv_exact(length)
|
||||
pos = [0]
|
||||
|
||||
col_count = struct.unpack(">I", data[pos[0]:pos[0]+4])[0]
|
||||
@@ -299,95 +322,138 @@ class Client:
|
||||
result.rows = rows
|
||||
result.row_count = row_count
|
||||
|
||||
comp_kind, comp_len, _ = self._read_response_header()
|
||||
comp_kind, comp_len, _ = await self._read_header()
|
||||
if comp_kind == MsgKind.COMPLETE:
|
||||
comp_data = self._recv_exact(comp_len)
|
||||
comp_data = await self._recv_exact(comp_len)
|
||||
result.affected_rows = struct.unpack(">I", comp_data[:4])[0]
|
||||
elif comp_kind == MsgKind.ERROR:
|
||||
raise self._read_error(comp_len)
|
||||
raise await self._read_error(comp_len)
|
||||
|
||||
return result
|
||||
|
||||
def auth(self, token: str) -> None:
|
||||
async def auth(self, token: str) -> None:
|
||||
"""Authenticate with the server using a JWT token."""
|
||||
if not self._connected:
|
||||
raise Exception("Not connected")
|
||||
encoded = token.encode("utf-8")
|
||||
payload = struct.pack(">I", len(encoded)) + encoded
|
||||
msg = self._build_message(MsgKind.AUTH, payload)
|
||||
self._sock.send(msg)
|
||||
self._writer.write(msg)
|
||||
await self._writer.drain()
|
||||
|
||||
kind, length, _ = self._read_response_header()
|
||||
kind, length, _ = await self._read_header()
|
||||
if kind == MsgKind.AUTH_OK:
|
||||
return
|
||||
elif kind == MsgKind.ERROR:
|
||||
raise self._read_error(length)
|
||||
raise await self._read_error(length)
|
||||
else:
|
||||
raise Exception(f"Unexpected auth response: 0x{kind:02x}")
|
||||
|
||||
def ping(self) -> bool:
|
||||
async def ping(self) -> bool:
|
||||
"""Ping the server. Returns True if pong received."""
|
||||
if not self._connected:
|
||||
raise Exception("Not connected")
|
||||
msg = self._build_message(MsgKind.PING, b"")
|
||||
self._sock.send(msg)
|
||||
self._writer.write(msg)
|
||||
await self._writer.drain()
|
||||
|
||||
kind, length, _ = self._read_response_header()
|
||||
kind, length, _ = await self._read_header()
|
||||
if kind == MsgKind.PONG:
|
||||
return True
|
||||
elif kind == MsgKind.ERROR:
|
||||
raise self._read_error(length)
|
||||
raise await self._read_error(length)
|
||||
return False
|
||||
|
||||
def query(self, sql: str) -> QueryResult:
|
||||
async def _process_queue(self) -> None:
|
||||
"""Process queued requests sequentially."""
|
||||
if self._request_lock or len(self._request_queue) == 0:
|
||||
return
|
||||
self._request_lock = True
|
||||
task_data = self._request_queue.pop(0)
|
||||
try:
|
||||
result = await task_data["task"]()
|
||||
task_data["resolve"](result)
|
||||
except Exception as err:
|
||||
task_data["reject"](err)
|
||||
finally:
|
||||
self._request_lock = False
|
||||
asyncio.create_task(self._process_queue())
|
||||
|
||||
def _enqueue(self, task) -> None:
|
||||
"""Add a task to the request queue."""
|
||||
future = asyncio.Future()
|
||||
self._request_queue.append({"task": task, "resolve": future.set_result, "reject": future.set_exception})
|
||||
asyncio.create_task(self._process_queue())
|
||||
return future
|
||||
|
||||
async def query(self, sql: str) -> QueryResult:
|
||||
"""Execute a BaraQL query."""
|
||||
payload = self._encode_string(sql)
|
||||
payload += bytes([ResultFormat.BINARY])
|
||||
if not self._connected:
|
||||
raise Exception("Not connected")
|
||||
|
||||
msg = self._build_message(MsgKind.QUERY, payload)
|
||||
self._sock.send(msg)
|
||||
async def _do_query():
|
||||
payload = self._encode_string(sql)
|
||||
payload += bytes([ResultFormat.BINARY])
|
||||
|
||||
kind, length, _ = self._read_response_header()
|
||||
msg = self._build_message(MsgKind.QUERY, payload)
|
||||
self._writer.write(msg)
|
||||
await self._writer.drain()
|
||||
|
||||
if kind == MsgKind.ERROR:
|
||||
raise self._read_error(length)
|
||||
kind, length, _ = await self._read_header()
|
||||
|
||||
if kind == MsgKind.DATA:
|
||||
return self._read_data_response(length)
|
||||
if kind == MsgKind.ERROR:
|
||||
raise await self._read_error(length)
|
||||
|
||||
if kind == MsgKind.COMPLETE:
|
||||
data = self._recv_exact(length)
|
||||
result = QueryResult()
|
||||
result.affected_rows = struct.unpack(">I", data[:4])[0]
|
||||
return result
|
||||
if kind == MsgKind.DATA:
|
||||
return await self._read_data_response(length)
|
||||
|
||||
return QueryResult()
|
||||
if kind == MsgKind.COMPLETE:
|
||||
data = await self._recv_exact(length)
|
||||
result = QueryResult()
|
||||
result.affected_rows = struct.unpack(">I", data[:4])[0]
|
||||
return result
|
||||
|
||||
def query_params(self, sql: str, params: Sequence[WireValue]) -> QueryResult:
|
||||
return QueryResult()
|
||||
|
||||
return await self._enqueue(_do_query)
|
||||
|
||||
async def query_params(self, sql: str, params: Sequence[WireValue]) -> QueryResult:
|
||||
"""Execute a parameterized BaraQL query."""
|
||||
payload = self._encode_string(sql)
|
||||
payload += bytes([ResultFormat.BINARY])
|
||||
payload += struct.pack(">I", len(params))
|
||||
for p in params:
|
||||
payload += p.serialize()
|
||||
if not self._connected:
|
||||
raise Exception("Not connected")
|
||||
|
||||
msg = self._build_message(MsgKind.QUERY_PARAMS, payload)
|
||||
self._sock.send(msg)
|
||||
async def _do_query_params():
|
||||
payload = self._encode_string(sql)
|
||||
payload += bytes([ResultFormat.BINARY])
|
||||
payload += struct.pack(">I", len(params))
|
||||
for p in params:
|
||||
payload += p.serialize()
|
||||
|
||||
kind, length, _ = self._read_response_header()
|
||||
msg = self._build_message(MsgKind.QUERY_PARAMS, payload)
|
||||
self._writer.write(msg)
|
||||
await self._writer.drain()
|
||||
|
||||
if kind == MsgKind.ERROR:
|
||||
raise self._read_error(length)
|
||||
kind, length, _ = await self._read_header()
|
||||
|
||||
if kind == MsgKind.DATA:
|
||||
return self._read_data_response(length)
|
||||
if kind == MsgKind.ERROR:
|
||||
raise await self._read_error(length)
|
||||
|
||||
if kind == MsgKind.COMPLETE:
|
||||
data = self._recv_exact(length)
|
||||
result = QueryResult()
|
||||
result.affected_rows = struct.unpack(">I", data[:4])[0]
|
||||
return result
|
||||
if kind == MsgKind.DATA:
|
||||
return await self._read_data_response(length)
|
||||
|
||||
return QueryResult()
|
||||
if kind == MsgKind.COMPLETE:
|
||||
data = await self._recv_exact(length)
|
||||
result = QueryResult()
|
||||
result.affected_rows = struct.unpack(">I", data[:4])[0]
|
||||
return result
|
||||
|
||||
def execute(self, sql: str) -> int:
|
||||
result = self.query(sql)
|
||||
return QueryResult()
|
||||
|
||||
return await self._enqueue(_do_query_params)
|
||||
|
||||
async def execute(self, sql: str) -> int:
|
||||
"""Execute a query and return affected rows count."""
|
||||
result = await self.query(sql)
|
||||
return result.affected_rows
|
||||
|
||||
def _build_message(self, kind: int, payload: bytes) -> bytes:
|
||||
@@ -477,12 +543,12 @@ class Client:
|
||||
return self._read_string(data, pos)
|
||||
return None
|
||||
|
||||
def __enter__(self):
|
||||
self.connect()
|
||||
async def __aenter__(self):
|
||||
await self.connect()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
async def __aexit__(self, *args):
|
||||
await self.close()
|
||||
|
||||
|
||||
class QueryBuilder:
|
||||
@@ -559,5 +625,5 @@ class QueryBuilder:
|
||||
sql += " OFFSET " + str(self._offset)
|
||||
return sql
|
||||
|
||||
def exec(self) -> QueryResult:
|
||||
return self.client.query(self.build())
|
||||
async def exec(self) -> QueryResult:
|
||||
return await self.client.query(self.build())
|
||||
@@ -3,9 +3,9 @@ name = "baradb"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
authors = ["BaraDB Team <team@baradb.dev>"]
|
||||
description = "Official Rust client for BaraDB — binary protocol client"
|
||||
description = "Official async Rust client for BaraDB — binary protocol client"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/barabadb/baradadb"
|
||||
repository = "https://github.com/katehonz/barabaDB"
|
||||
documentation = "https://docs.baradb.dev"
|
||||
readme = "README.md"
|
||||
keywords = ["database", "baradb", "multimodal", "client", "wire-protocol"]
|
||||
@@ -13,9 +13,10 @@ categories = ["database", "network-programming"]
|
||||
rust-version = "1.70"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.35", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[[example]]
|
||||
name = "basic"
|
||||
path = "examples/basic.rs"
|
||||
path = "examples/basic.rs"
|
||||
+42
-36
@@ -1,13 +1,13 @@
|
||||
# BaraDB Rust Client
|
||||
# BaraDB Async Rust Client
|
||||
|
||||
Official Rust client for **BaraDB** — a multimodal database engine written in Nim.
|
||||
Official async Rust client for **BaraDB** — a multimodal database engine written in Nim.
|
||||
|
||||
## Features
|
||||
|
||||
- **Binary wire protocol** — fast TCP communication using only `std`
|
||||
- **Zero dependencies** — no external crates required
|
||||
- **Sync API** — blocking I/O suitable for most applications
|
||||
- **Async/await** — fully non-blocking with Tokio runtime
|
||||
- **Binary wire protocol** — fast TCP communication
|
||||
- **Query builder** — fluent SQL construction
|
||||
- **Parameterized queries** — safe from SQL injection
|
||||
- **Vector & JSON support** — first-class multimodal types
|
||||
|
||||
## Installation
|
||||
@@ -16,13 +16,14 @@ Add to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
baradb = "1.0"
|
||||
baradb = "1.1"
|
||||
tokio = { version = "1.35", features = ["full"] }
|
||||
```
|
||||
|
||||
Or from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/barabadb/baradadb.git
|
||||
git clone https://github.com/katehonz/barabaDB.git
|
||||
cd clients/rust
|
||||
cargo build
|
||||
```
|
||||
@@ -32,13 +33,14 @@ cargo build
|
||||
```rust
|
||||
use baradb::Client;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = Client::connect("localhost", 9472)?;
|
||||
let result = client.query("SELECT name, age FROM users WHERE age > 18")?;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut client = Client::connect("localhost", 9472).await?;
|
||||
let result = client.query("SELECT name, age FROM users WHERE age > 18").await?;
|
||||
for row in result.rows() {
|
||||
println!("{:?}", row);
|
||||
}
|
||||
client.close();
|
||||
client.close().await;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
@@ -48,16 +50,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
```rust
|
||||
use baradb::{Client, WireValue};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = Client::connect("localhost", 9472)?;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut client = Client::connect("localhost", 9472).await?;
|
||||
let result = client.query_params(
|
||||
"SELECT * FROM users WHERE age > $1 AND country = $2",
|
||||
&[WireValue::Int64(18), WireValue::String("BG".to_string())],
|
||||
)?;
|
||||
).await?;
|
||||
for row in result.rows() {
|
||||
println!("{:?}", row);
|
||||
}
|
||||
client.close();
|
||||
client.close().await;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
@@ -65,21 +68,23 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
### Query Builder
|
||||
|
||||
```rust
|
||||
use baradb::Client;
|
||||
use baradb::{Client, QueryBuilder};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = Client::connect("localhost", 9472)?;
|
||||
let result = baradb::QueryBuilder::new(&mut client)
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut client = Client::connect("localhost", 9472).await?;
|
||||
let result = QueryBuilder::new(&mut client)
|
||||
.select(&["name", "email"])
|
||||
.from("users")
|
||||
.where_clause("active = true")
|
||||
.order_by("name", "ASC")
|
||||
.limit(10)
|
||||
.exec()?;
|
||||
.exec()
|
||||
.await?;
|
||||
for row in result.rows() {
|
||||
println!("{:?}", row);
|
||||
}
|
||||
client.close();
|
||||
client.close().await;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
@@ -89,13 +94,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
```rust
|
||||
use baradb::{Client, WireValue};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = Client::connect("localhost", 9472)?;
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut client = Client::connect("localhost", 9472).await?;
|
||||
let result = client.query_params(
|
||||
"SELECT id, name FROM products ORDER BY embedding <-> $1 LIMIT 5",
|
||||
&[WireValue::Vector(vec![0.1, 0.2, 0.3])],
|
||||
)?;
|
||||
client.close();
|
||||
).await?;
|
||||
client.close().await;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
@@ -112,7 +118,7 @@ Integration tests (requires server on `localhost:9472`):
|
||||
|
||||
```bash
|
||||
# Start server
|
||||
docker run -d -p 9472:9472 baradb:latest
|
||||
docker run -d -p 9472:9472 barabadb:latest
|
||||
|
||||
# Run all tests
|
||||
cargo test
|
||||
@@ -120,19 +126,19 @@ cargo test
|
||||
|
||||
## API Reference
|
||||
|
||||
### `Client::connect(host, port)`
|
||||
### `Client::connect(host, port) -> Result<Client>`
|
||||
|
||||
Creates a new client connected to the given host and port.
|
||||
Creates a new async client connected to the given host and port.
|
||||
|
||||
### Methods
|
||||
### Methods (all async)
|
||||
|
||||
- `query(sql) -> Result<QueryResult>` — execute SELECT-like query
|
||||
- `query_params(sql, params) -> Result<QueryResult>` — parameterized query
|
||||
- `execute(sql) -> Result<usize>` — execute DDL/DML, returns affected rows
|
||||
- `auth(token) -> Result<()>` — JWT authentication
|
||||
- `ping() -> Result<bool>` — health check
|
||||
- `close()` — close connection
|
||||
- `await client.query(sql) -> Result<QueryResult>` — execute SELECT-like query
|
||||
- `await client.query_params(sql, params) -> Result<QueryResult>` — parameterized query
|
||||
- `await client.execute(sql) -> Result<usize>` — execute DDL/DML, returns affected rows
|
||||
- `await client.auth(token) -> Result<()>` — JWT authentication
|
||||
- `await client.ping() -> Result<bool>` — health check
|
||||
- `await client.close()` — close connection
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
Apache-2.0
|
||||
@@ -3,34 +3,65 @@
|
||||
|
||||
use baradb::{Client, QueryBuilder, WireValue};
|
||||
|
||||
fn example_connection() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("BaraDB Rust Client Examples");
|
||||
println!("Make sure BaraDB is running on localhost:9472");
|
||||
println!();
|
||||
|
||||
if let Err(e) = example_connection().await {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = example_simple_query().await {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = example_parameterized_query().await {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = example_query_builder().await {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = example_vector().await {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = example_ddl_dml().await {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
async fn example_connection() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("=== Connection ===");
|
||||
let mut client = Client::connect("127.0.0.1", 9472)?;
|
||||
let mut client = Client::connect("127.0.0.1", 9472).await?;
|
||||
println!("Connected: {}", client.is_connected());
|
||||
println!("Ping: {}", client.ping()?);
|
||||
client.close();
|
||||
println!("Ping: {}", client.ping().await?);
|
||||
client.close().await;
|
||||
println!("Connected after close: {}", client.is_connected());
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn example_simple_query() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn example_simple_query() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("=== Simple Query ===");
|
||||
let mut client = Client::connect("127.0.0.1", 9472)?;
|
||||
let result = client.query("SELECT 42 as answer, 'BaraDB' as db")?;
|
||||
let mut client = Client::connect("127.0.0.1", 9472).await?;
|
||||
let result = client.query("SELECT 42 as answer, 'BaraDB' as db").await?;
|
||||
println!("Columns: {:?}", result.columns());
|
||||
println!("Row count: {}", result.row_count());
|
||||
for row in result.rows() {
|
||||
println!(" {:?}", row);
|
||||
}
|
||||
client.close();
|
||||
client.close().await;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn example_parameterized_query() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn example_parameterized_query() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("=== Parameterized Query ===");
|
||||
let mut client = Client::connect("127.0.0.1", 9472)?;
|
||||
let mut client = Client::connect("127.0.0.1", 9472).await?;
|
||||
let result = client.query_params(
|
||||
"SELECT $1 as num, $2 as txt, $3 as flag",
|
||||
&[
|
||||
@@ -38,18 +69,18 @@ fn example_parameterized_query() -> Result<(), Box<dyn std::error::Error>> {
|
||||
WireValue::String("hello world".to_string()),
|
||||
WireValue::Bool(true),
|
||||
],
|
||||
)?;
|
||||
).await?;
|
||||
for row in result.rows() {
|
||||
println!(" {:?}", row);
|
||||
}
|
||||
client.close();
|
||||
client.close().await;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn example_query_builder() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn example_query_builder() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("=== Query Builder ===");
|
||||
let mut client = Client::connect("127.0.0.1", 9472)?;
|
||||
let mut client = Client::connect("127.0.0.1", 9472).await?;
|
||||
let sql = QueryBuilder::new(&mut client)
|
||||
.select(&["id", "name"])
|
||||
.from("users")
|
||||
@@ -58,70 +89,49 @@ fn example_query_builder() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.limit(5)
|
||||
.build();
|
||||
println!("Generated SQL: {}", sql);
|
||||
client.close();
|
||||
client.close().await;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn example_vector() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn example_vector() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("=== Vector Value ===");
|
||||
let mut client = Client::connect("127.0.0.1", 9472)?;
|
||||
let mut client = Client::connect("127.0.0.1", 9472).await?;
|
||||
let result = client.query_params(
|
||||
"SELECT $1 as embedding",
|
||||
&[WireValue::Vector(vec![0.1, 0.2, 0.3, 0.4])],
|
||||
)?;
|
||||
).await?;
|
||||
for row in result.rows() {
|
||||
println!(" {:?}", row);
|
||||
}
|
||||
client.close();
|
||||
client.close().await;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn example_ddl_dml() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn example_ddl_dml() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
println!("=== DDL & DML ===");
|
||||
let mut client = Client::connect("127.0.0.1", 9472)?;
|
||||
let mut client = Client::connect("127.0.0.1", 9472).await?;
|
||||
|
||||
let _ = client.execute("DROP TABLE IF EXISTS demo_products");
|
||||
let _ = client.execute("DROP TABLE IF EXISTS demo_products").await;
|
||||
|
||||
client.execute(
|
||||
"CREATE TABLE demo_products (id INT PRIMARY KEY, name STRING, price FLOAT)",
|
||||
)?;
|
||||
).await?;
|
||||
let affected = client.execute(
|
||||
"INSERT INTO demo_products (id, name, price) VALUES (1, 'Widget', 9.99)",
|
||||
)?;
|
||||
).await?;
|
||||
println!("Insert affected rows: {}", affected);
|
||||
|
||||
let result = client.query("SELECT * FROM demo_products")?;
|
||||
let result = client.query("SELECT * FROM demo_products").await?;
|
||||
println!("Select returned {} row(s)", result.row_count());
|
||||
for row in result.rows() {
|
||||
println!(" {:?}", row);
|
||||
}
|
||||
|
||||
client.execute("DROP TABLE demo_products")?;
|
||||
client.execute("DROP TABLE demo_products").await?;
|
||||
println!("Table dropped");
|
||||
client.close();
|
||||
client.close().await;
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("BaraDB Rust Client Examples");
|
||||
println!("Make sure BaraDB is running on localhost:9472");
|
||||
println!();
|
||||
|
||||
let examples: Vec<fn() -> Result<(), Box<dyn std::error::Error>>> = vec![
|
||||
example_connection,
|
||||
example_simple_query,
|
||||
example_parameterized_query,
|
||||
example_query_builder,
|
||||
example_vector,
|
||||
example_ddl_dml,
|
||||
];
|
||||
|
||||
for example in examples {
|
||||
if let Err(e) = example() {
|
||||
eprintln!("ERROR: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
-75
@@ -1,36 +1,42 @@
|
||||
//! BaraDB Rust Client
|
||||
//! BaraDB Async Rust Client
|
||||
//!
|
||||
//! Binary protocol client for BaraDB database.
|
||||
//! Zero external dependencies — uses only `std`.
|
||||
//! Async binary protocol client for BaraDB database.
|
||||
//! Uses Tokio for async I/O operations.
|
||||
//!
|
||||
//! # Example
|
||||
//! ```no_run
|
||||
//! use baradb::{Client, WireValue};
|
||||
//!
|
||||
//! let mut client = Client::connect("localhost", 9472).unwrap();
|
||||
//! let result = client.query("SELECT name FROM users WHERE age > 18").unwrap();
|
||||
//! for row in result.rows() {
|
||||
//! if let Some(WireValue::String(name)) = row.get("name") {
|
||||
//! println!("{}", name);
|
||||
//! #[tokio::main]
|
||||
//! async fn main() {
|
||||
//! let mut client = Client::connect("localhost", 9472).await.unwrap();
|
||||
//! let result = client.query("SELECT name FROM users WHERE age > 18").await.unwrap();
|
||||
//! for row in result.rows() {
|
||||
//! if let Some(WireValue::String(name)) = row.get("name") {
|
||||
//! println!("{}", name);
|
||||
//! }
|
||||
//! }
|
||||
//! client.close().await;
|
||||
//! }
|
||||
//! client.close();
|
||||
//! ```
|
||||
//!
|
||||
//! # Parameterized Queries
|
||||
//! ```no_run
|
||||
//! use baradb::{Client, WireValue};
|
||||
//!
|
||||
//! let mut client = Client::connect("localhost", 9472).unwrap();
|
||||
//! let result = client.query_params(
|
||||
//! "SELECT * FROM users WHERE age > $1",
|
||||
//! &[WireValue::Int64(18)],
|
||||
//! ).unwrap();
|
||||
//! #[tokio::main]
|
||||
//! async fn main() {
|
||||
//! let mut client = Client::connect("localhost", 9472).await.unwrap();
|
||||
//! let result = client.query_params(
|
||||
//! "SELECT * FROM users WHERE age > $1",
|
||||
//! &[WireValue::Int64(18)],
|
||||
//! ).await.unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
// Client message kinds
|
||||
const MK_CLIENT_HANDSHAKE: u32 = 0x01;
|
||||
@@ -221,43 +227,41 @@ impl QueryResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// BaraDB client
|
||||
/// BaraDB async client
|
||||
pub struct Client {
|
||||
config: Config,
|
||||
stream: TcpStream,
|
||||
connected: bool,
|
||||
request_id: u32,
|
||||
read_buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn connect(host: &str, port: u16) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let config = Config {
|
||||
host: host.to_string(),
|
||||
port,
|
||||
..Default::default()
|
||||
};
|
||||
Self::connect_with_config(config)
|
||||
}
|
||||
|
||||
pub fn connect_with_config(config: Config) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let addr = format!("{}:{}", config.host, config.port);
|
||||
let stream = TcpStream::connect(&addr)?;
|
||||
stream.set_nodelay(true)?;
|
||||
/// Connect to a BaraDB server
|
||||
pub async fn connect(host: &str, port: u16) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let addr = format!("{}:{}", host, port);
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
Ok(Client {
|
||||
config,
|
||||
stream,
|
||||
connected: true,
|
||||
request_id: 0,
|
||||
read_buf: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
/// Connect with custom configuration
|
||||
pub async fn connect_with_config(config: Config) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
Self::connect(&config.host, config.port).await
|
||||
}
|
||||
|
||||
/// Close the connection
|
||||
pub async fn close(&mut self) {
|
||||
if self.connected {
|
||||
let _ = self.send_close();
|
||||
let _ = self.send_close().await;
|
||||
}
|
||||
self.connected = false;
|
||||
}
|
||||
|
||||
/// Check if connected
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.connected
|
||||
}
|
||||
@@ -267,27 +271,39 @@ impl Client {
|
||||
self.request_id
|
||||
}
|
||||
|
||||
fn send_close(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn send_close(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let msg = build_message(MK_CLOSE, self.next_id(), &[]);
|
||||
self.stream.write_all(&msg)?;
|
||||
self.stream.write_all(&msg).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_header(&mut self) -> Result<(u32, u32, u32), Box<dyn std::error::Error>> {
|
||||
let mut header = [0u8; 12];
|
||||
self.stream.read_exact(&mut header)?;
|
||||
async fn read_exact(&mut self, mut n: usize) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut buf = vec![0u8; n];
|
||||
let mut pos = 0;
|
||||
while pos < n {
|
||||
let read = self.stream.read(&mut buf[pos..]).await?;
|
||||
if read == 0 {
|
||||
return Err("Connection closed".into());
|
||||
}
|
||||
pos += read;
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
async fn read_header(&mut self) -> Result<(u32, u32, u32), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let header = self.read_exact(12).await?;
|
||||
let kind = u32::from_be_bytes([header[0], header[1], header[2], header[3]]);
|
||||
let length = u32::from_be_bytes([header[4], header[5], header[6], header[7]]);
|
||||
let req_id = u32::from_be_bytes([header[8], header[9], header[10], header[11]]);
|
||||
Ok((kind, length, req_id))
|
||||
}
|
||||
|
||||
fn read_payload(&mut self, length: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
let mut payload = vec![0u8; length as usize];
|
||||
async fn read_payload(&mut self, length: u32) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if length > 0 {
|
||||
self.stream.read_exact(&mut payload)?;
|
||||
self.read_exact(length as usize).await
|
||||
} else {
|
||||
Ok(vec![])
|
||||
}
|
||||
Ok(payload)
|
||||
}
|
||||
|
||||
fn read_error_message(payload: &[u8]) -> String {
|
||||
@@ -302,26 +318,26 @@ impl Client {
|
||||
"Query error".to_string()
|
||||
}
|
||||
|
||||
fn read_data_response(&mut self, payload: &[u8]) -> Result<QueryResult, Box<dyn std::error::Error>> {
|
||||
async fn read_data_response(&mut self, payload: &[u8]) -> Result<QueryResult, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut pos = 0usize;
|
||||
let col_count = read_u32(payload, &mut pos) as usize;
|
||||
let col_count = read_u32(payload, &mut pos);
|
||||
|
||||
let mut columns = Vec::with_capacity(col_count);
|
||||
let mut columns = Vec::with_capacity(col_count as usize);
|
||||
for _ in 0..col_count {
|
||||
columns.push(read_string(payload, &mut pos));
|
||||
}
|
||||
|
||||
let mut col_types = Vec::with_capacity(col_count);
|
||||
let mut col_types = Vec::with_capacity(col_count as usize);
|
||||
for _ in 0..col_count {
|
||||
col_types.push(payload[pos]);
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
let row_count = read_u32(payload, &mut pos) as usize;
|
||||
let mut rows = Vec::with_capacity(row_count);
|
||||
let row_count = read_u32(payload, &mut pos);
|
||||
let mut rows = Vec::with_capacity(row_count as usize);
|
||||
for _ in 0..row_count {
|
||||
let mut row = HashMap::new();
|
||||
for c in 0..col_count {
|
||||
for c in 0..col_count as usize {
|
||||
let val = read_wire_value(payload, &mut pos);
|
||||
row.insert(columns[c].clone(), val);
|
||||
}
|
||||
@@ -329,9 +345,9 @@ impl Client {
|
||||
}
|
||||
|
||||
let mut affected = 0usize;
|
||||
let (comp_kind, comp_len, _) = self.read_header()?;
|
||||
let (comp_kind, comp_len, _) = self.read_header().await?;
|
||||
if comp_kind == MK_COMPLETE {
|
||||
let comp_payload = self.read_payload(comp_len)?;
|
||||
let comp_payload = self.read_payload(comp_len).await?;
|
||||
if comp_payload.len() >= 4 {
|
||||
affected = u32::from_be_bytes([comp_payload[0], comp_payload[1], comp_payload[2], comp_payload[3]]) as usize;
|
||||
}
|
||||
@@ -340,44 +356,47 @@ impl Client {
|
||||
Ok(QueryResult { columns, column_types: col_types, rows, affected_rows: affected })
|
||||
}
|
||||
|
||||
pub fn auth(&mut self, token: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// Authenticate with JWT token
|
||||
pub async fn auth(&mut self, token: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
if !self.connected {
|
||||
return Err("Not connected".into());
|
||||
}
|
||||
let payload = encode_string(token);
|
||||
let msg = build_message(MK_AUTH, self.next_id(), &payload);
|
||||
self.stream.write_all(&msg)?;
|
||||
self.stream.write_all(&msg).await?;
|
||||
|
||||
let (kind, length, _) = self.read_header()?;
|
||||
let (kind, length, _) = self.read_header().await?;
|
||||
match kind {
|
||||
MK_AUTH_OK => Ok(()),
|
||||
MK_ERROR => {
|
||||
let p = self.read_payload(length)?;
|
||||
let p = self.read_payload(length).await?;
|
||||
Err(Self::read_error_message(&p).into())
|
||||
}
|
||||
_ => Err(format!("Unexpected auth response: 0x{:02x}", kind).into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ping(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
/// Ping the server
|
||||
pub async fn ping(&mut self) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if !self.connected {
|
||||
return Err("Not connected".into());
|
||||
}
|
||||
let msg = build_message(MK_PING, self.next_id(), &[]);
|
||||
self.stream.write_all(&msg)?;
|
||||
self.stream.write_all(&msg).await?;
|
||||
|
||||
let (kind, length, _) = self.read_header()?;
|
||||
let (kind, length, _) = self.read_header().await?;
|
||||
match kind {
|
||||
MK_PONG => Ok(true),
|
||||
MK_ERROR => {
|
||||
let p = self.read_payload(length)?;
|
||||
let p = self.read_payload(length).await?;
|
||||
Err(Self::read_error_message(&p).into())
|
||||
}
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query(&mut self, sql: &str) -> Result<QueryResult, Box<dyn std::error::Error>> {
|
||||
/// Execute a query
|
||||
pub async fn query(&mut self, sql: &str) -> Result<QueryResult, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if !self.connected {
|
||||
return Err("Not connected".into());
|
||||
}
|
||||
@@ -386,14 +405,14 @@ impl Client {
|
||||
payload.push(0x00); // ResultFormat::BINARY
|
||||
|
||||
let msg = build_message(MK_QUERY, self.next_id(), &payload);
|
||||
self.stream.write_all(&msg)?;
|
||||
self.stream.write_all(&msg).await?;
|
||||
|
||||
let (kind, length, _) = self.read_header()?;
|
||||
let resp_payload = self.read_payload(length)?;
|
||||
let (kind, length, _) = self.read_header().await?;
|
||||
let resp_payload = self.read_payload(length).await?;
|
||||
|
||||
match kind {
|
||||
MK_READY => Ok(QueryResult { columns: vec![], column_types: vec![], rows: vec![], affected_rows: 0 }),
|
||||
MK_DATA => self.read_data_response(&resp_payload),
|
||||
MK_DATA => self.read_data_response(&resp_payload).await,
|
||||
MK_COMPLETE => {
|
||||
let affected = if resp_payload.len() >= 4 {
|
||||
u32::from_be_bytes([resp_payload[0], resp_payload[1], resp_payload[2], resp_payload[3]]) as usize
|
||||
@@ -405,7 +424,8 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_params(&mut self, sql: &str, params: &[WireValue]) -> Result<QueryResult, Box<dyn std::error::Error>> {
|
||||
/// Execute a parameterized query
|
||||
pub async fn query_params(&mut self, sql: &str, params: &[WireValue]) -> Result<QueryResult, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if !self.connected {
|
||||
return Err("Not connected".into());
|
||||
}
|
||||
@@ -418,14 +438,14 @@ impl Client {
|
||||
}
|
||||
|
||||
let msg = build_message(MK_QUERY_PARAMS, self.next_id(), &payload);
|
||||
self.stream.write_all(&msg)?;
|
||||
self.stream.write_all(&msg).await?;
|
||||
|
||||
let (kind, length, _) = self.read_header()?;
|
||||
let resp_payload = self.read_payload(length)?;
|
||||
let (kind, length, _) = self.read_header().await?;
|
||||
let resp_payload = self.read_payload(length).await?;
|
||||
|
||||
match kind {
|
||||
MK_READY => Ok(QueryResult { columns: vec![], column_types: vec![], rows: vec![], affected_rows: 0 }),
|
||||
MK_DATA => self.read_data_response(&resp_payload),
|
||||
MK_DATA => self.read_data_response(&resp_payload).await,
|
||||
MK_COMPLETE => {
|
||||
let affected = if resp_payload.len() >= 4 {
|
||||
u32::from_be_bytes([resp_payload[0], resp_payload[1], resp_payload[2], resp_payload[3]]) as usize
|
||||
@@ -437,12 +457,14 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, sql: &str) -> Result<usize, Box<dyn std::error::Error>> {
|
||||
let result = self.query(sql)?;
|
||||
/// Execute and return affected rows
|
||||
pub async fn execute(&mut self, sql: &str) -> Result<usize, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let result = self.query(sql).await?;
|
||||
Ok(result.affected_rows())
|
||||
}
|
||||
}
|
||||
|
||||
/// Query builder for fluent SQL construction
|
||||
pub struct QueryBuilder<'a> {
|
||||
client: &'a mut Client,
|
||||
select_cols: Vec<String>,
|
||||
@@ -457,6 +479,7 @@ pub struct QueryBuilder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> QueryBuilder<'a> {
|
||||
/// Create a new query builder
|
||||
pub fn new(client: &'a mut Client) -> Self {
|
||||
QueryBuilder {
|
||||
client,
|
||||
@@ -472,56 +495,67 @@ impl<'a> QueryBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add columns to SELECT
|
||||
pub fn select(mut self, cols: &[&str]) -> Self {
|
||||
self.select_cols.extend(cols.iter().map(|s| s.to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the FROM table
|
||||
pub fn from(mut self, table: &str) -> Self {
|
||||
self.from_table = table.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a WHERE clause
|
||||
pub fn where_clause(mut self, clause: &str) -> Self {
|
||||
self.where_clauses.push(clause.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a JOIN clause
|
||||
pub fn join(mut self, table: &str, on: &str) -> Self {
|
||||
self.joins.push(format!("JOIN {} ON {}", table, on));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a LEFT JOIN clause
|
||||
pub fn left_join(mut self, table: &str, on: &str) -> Self {
|
||||
self.joins.push(format!("LEFT JOIN {} ON {}", table, on));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add GROUP BY columns
|
||||
pub fn group_by(mut self, cols: &[&str]) -> Self {
|
||||
self.group_by.extend(cols.iter().map(|s| s.to_string()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add HAVING clause
|
||||
pub fn having(mut self, clause: &str) -> Self {
|
||||
self.having = clause.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Add ORDER BY column
|
||||
pub fn order_by(mut self, col: &str, dir: &str) -> Self {
|
||||
self.order_by.push(format!("{} {}", col, dir));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set LIMIT
|
||||
pub fn limit(mut self, n: usize) -> Self {
|
||||
self.limit = n;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set OFFSET
|
||||
pub fn offset(mut self, n: usize) -> Self {
|
||||
self.offset = n;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the SQL string
|
||||
pub fn build(&self) -> String {
|
||||
let mut sql = String::from("SELECT ");
|
||||
if self.select_cols.is_empty() {
|
||||
@@ -555,9 +589,10 @@ impl<'a> QueryBuilder<'a> {
|
||||
sql
|
||||
}
|
||||
|
||||
pub fn exec(self) -> Result<QueryResult, Box<dyn std::error::Error>> {
|
||||
/// Execute the query
|
||||
pub async fn exec(self) -> Result<QueryResult, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let sql = self.build();
|
||||
self.client.query(&sql)
|
||||
self.client.query(&sql).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,4 +710,4 @@ fn read_wire_value(data: &[u8], pos: &mut usize) -> WireValue {
|
||||
FK_JSON => WireValue::Json(read_string(data, pos)),
|
||||
_ => WireValue::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
+168
-1
@@ -97,5 +97,172 @@ RETURN friend.name;
|
||||
```sql
|
||||
BEGIN;
|
||||
INSERT users { name := 'Alice', age := 30 };
|
||||
INSERT orders { user_id := last_insert_id(), total := 100 };
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
-- مع نقطة حفظ
|
||||
BEGIN;
|
||||
INSERT users { name := 'Bob', age := 25 };
|
||||
SAVEPOINT sp1;
|
||||
INSERT orders { user_id := last_insert_id(), total := 200 };
|
||||
-- خطأ، التراجع إلى نقطة الحفظ
|
||||
ROLLBACK TO sp1;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
## البحث النصي الكامل
|
||||
|
||||
```sql
|
||||
-- بحث أساسي
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('database programming');
|
||||
|
||||
-- مع درجة الصلة
|
||||
SELECT title, relevance()
|
||||
FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('Nim language')
|
||||
ORDER BY relevance() DESC;
|
||||
|
||||
-- الوضع المنطقي
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('+Nim -Python' IN BOOLEAN MODE);
|
||||
|
||||
-- البحث الضبابي
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title) AGAINST('programing' WITH FUZZINESS 2);
|
||||
```
|
||||
|
||||
## الدوال المحددة من المستخدم
|
||||
|
||||
```sql
|
||||
-- تسجيل UDF
|
||||
CREATE FUNCTION greet(name str) -> str {
|
||||
RETURN 'Hello, ' || name || '!';
|
||||
};
|
||||
|
||||
-- استخدامها
|
||||
SELECT greet(name) FROM users;
|
||||
|
||||
-- الدوال المدمجة
|
||||
SELECT abs(-5), sqrt(16), lower('HELLO'), len('test');
|
||||
```
|
||||
|
||||
## تلميحات الاستعلام
|
||||
|
||||
```sql
|
||||
-- فرض استخدام الفهرس
|
||||
SELECT /*+ USE_INDEX(idx_users_age) */ * FROM users WHERE age > 18;
|
||||
|
||||
-- فرض البحث المتجهي التقريبي
|
||||
SELECT /*+ APPROXIMATE */ * FROM vectors
|
||||
ORDER BY cosine_distance(embedding, [...])
|
||||
LIMIT 10;
|
||||
|
||||
-- التنفيذ المتوازي
|
||||
SELECT /*+ PARALLEL(4) */ * FROM large_table;
|
||||
```
|
||||
|
||||
## دوال النوافذ
|
||||
|
||||
```sql
|
||||
-- دوال الترتيب
|
||||
SELECT
|
||||
name,
|
||||
department,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
|
||||
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
|
||||
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
|
||||
FROM employees;
|
||||
|
||||
-- دوال القيمة
|
||||
SELECT
|
||||
name,
|
||||
salary,
|
||||
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
|
||||
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
|
||||
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
|
||||
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
|
||||
FROM employees;
|
||||
|
||||
-- دوال التوزيع
|
||||
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
|
||||
```
|
||||
|
||||
### مواصفات الإطار
|
||||
|
||||
```sql
|
||||
-- إطار ROWS
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
|
||||
)
|
||||
|
||||
-- إطار RANGE
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
```
|
||||
|
||||
## ERP متعدد المستأجرين
|
||||
|
||||
يدعم BaraDB تشغيل عدة شركات (مستأجرين) في مثيل قاعدة بيانات واحد، باستخدام **أمان مستوى الصف (RLS)** مع **متغيرات الجلسة**.
|
||||
|
||||
### متغيرات الجلسة
|
||||
|
||||
```sql
|
||||
SET app.tenant_id = 'company-123';
|
||||
SELECT current_setting('app.tenant_id') AS tenant;
|
||||
```
|
||||
|
||||
### المستخدم / الدور الحالي
|
||||
|
||||
```sql
|
||||
SELECT current_user AS me, current_role AS my_role;
|
||||
```
|
||||
|
||||
### عزل المستأجر عبر RLS
|
||||
|
||||
```sql
|
||||
-- تمكين RLS على جدول
|
||||
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- إنشاء سياسة تصفية حسب المستأجر
|
||||
CREATE POLICY tenant_isolation ON invoices
|
||||
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
|
||||
|
||||
-- كل جلسة ترى فقط بياناتها
|
||||
SET app.tenant_id = 'company-a';
|
||||
SELECT * FROM invoices; -- صفوف company-a فقط
|
||||
```
|
||||
|
||||
### لماذا متعدد المستأجرين؟
|
||||
|
||||
- **مثيل واحد، مستأجرون كثيرون** — لا حاجة لتشغيل 100 قاعدة بيانات منفصلة
|
||||
- **مستندات JSONB** — تخزين مخطط مرن، سهل إضافة حقول لكل مستأجر
|
||||
- **RLS يضمن العزل** — قاعدة البيانات تفرض حدود المستأجر، وليس فقط التطبيق
|
||||
|
||||
## الكلمات المفتاحية المدعومة
|
||||
|
||||
| الفئة | الكلمات المفتاحية |
|
||||
|----------|----------|
|
||||
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
|
||||
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
|
||||
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
|
||||
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
|
||||
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
|
||||
| CTEs | WITH, RECURSIVE, AS |
|
||||
| Case | CASE, WHEN, THEN, ELSE, END |
|
||||
| Transaction | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
|
||||
| Graph | MATCH, RETURN, WHERE, shortestPath, type |
|
||||
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
|
||||
| Vector | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
|
||||
| JSON | ->, ->> |
|
||||
| FTS | @@ (تطابق BM25) |
|
||||
| Recovery | RECOVER TO TIMESTAMP |
|
||||
| Functions | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
|
||||
| Session | SET, current_setting, current_user, current_role |
|
||||
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
|
||||
| Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |
|
||||
@@ -22,38 +22,284 @@
|
||||
### لينكس
|
||||
|
||||
```bash
|
||||
# المثبت الرسمي
|
||||
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
|
||||
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get update
|
||||
sudo apt-get install nim
|
||||
|
||||
# Fedora
|
||||
sudo dnf install nim
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S nim
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
# Homebrew
|
||||
brew install nim
|
||||
|
||||
# MacPorts
|
||||
sudo port install nim
|
||||
```
|
||||
|
||||
### ويندوز
|
||||
|
||||
```powershell
|
||||
# باستخدام choosenim
|
||||
curl.exe -A "MSYS2_$(uname -m)" -L https://nim-lang.org/choosenim/init.ps1 | powershell -
|
||||
|
||||
# باستخدام winget
|
||||
winget install nim
|
||||
|
||||
# باستخدام scoop
|
||||
scoop install nim
|
||||
```
|
||||
|
||||
### التحقق من التثبيت
|
||||
|
||||
```bash
|
||||
nim --version
|
||||
# المتوقع: Nim Compiler Version 2.2.0 أو أحدث
|
||||
```
|
||||
|
||||
## تثبيت OpenSSL
|
||||
|
||||
### لينكس
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get install libssl-dev
|
||||
|
||||
# Fedora
|
||||
sudo dnf install openssl-devel
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S openssl
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
OpenSSL مضمن مع النظام. إذا لزم الأمر:
|
||||
|
||||
```bash
|
||||
brew install openssl
|
||||
```
|
||||
|
||||
### ويندوز
|
||||
|
||||
OpenSSL مضمن مع توزيع Nim ويندوز. للبناء اليدوي، قم بالتنزيل من [slproweb.com](https://slproweb.com/products/Win32OpenSSL.html).
|
||||
|
||||
## بناء BaraDB
|
||||
|
||||
### استنساخ المستودع
|
||||
|
||||
```bash
|
||||
git clone https://github.com/katehonz/barabaDB.git
|
||||
cd barabaDB
|
||||
```
|
||||
|
||||
### تثبيت التبعيات
|
||||
|
||||
```bash
|
||||
nimble install -d -y
|
||||
```
|
||||
|
||||
### خيارات البناء
|
||||
|
||||
#### بناء التصحيح
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
#### بناء الإصدار (موصى به)
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:speed -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
#### استخدام مهام Nimble
|
||||
|
||||
```bash
|
||||
# بناء التصحيح
|
||||
nimble build_debug
|
||||
|
||||
# بناء الإصدار
|
||||
nimble build_release
|
||||
```
|
||||
|
||||
#### تقليل حجم الملف الثنائي
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:size -o:build/baradadb src/baradadb.nim
|
||||
strip build/baradadb
|
||||
```
|
||||
|
||||
### التحقق من البناء
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# المتوقع: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## تشغيل الاختبارات
|
||||
|
||||
### جميع الاختبارات
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -r tests/test_all.nim
|
||||
```
|
||||
|
||||
### مجموعات اختبارات محددة
|
||||
|
||||
```bash
|
||||
# اختبارات التخزين
|
||||
nim c -d:ssl -r tests/test_storage.nim
|
||||
|
||||
# اختبارات محرك الاستعلام
|
||||
nim c -d:ssl -r tests/test_query.nim
|
||||
|
||||
# اختبارات البروتوكول
|
||||
nim c -d:ssl -r tests/test_protocol.nim
|
||||
```
|
||||
|
||||
### المعايير
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release -r benchmarks/bench_all.nim
|
||||
```
|
||||
|
||||
## خيارات التثبيت
|
||||
|
||||
### التثبيت على مستوى النظام
|
||||
|
||||
```bash
|
||||
# بناء إصدار RELEASE
|
||||
nimble build_release
|
||||
|
||||
# التثبيت في /usr/local/bin
|
||||
sudo cp build/baradadb /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/baradadb
|
||||
|
||||
# إنشاء دليل البيانات
|
||||
sudo mkdir -p /var/lib/baradb
|
||||
sudo chmod 755 /var/lib/baradb
|
||||
```
|
||||
|
||||
### الملف الثنائي المبنى مسبقًا
|
||||
|
||||
قم بتنزيل أحدث إصدار لمنصتك:
|
||||
|
||||
```bash
|
||||
# لينكس x86_64
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-amd64
|
||||
chmod +x baradadb-linux-amd64
|
||||
mv baradadb-linux-amd64 /usr/local/bin/baradadb
|
||||
|
||||
# لينكس ARM64
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-arm64
|
||||
chmod +x baradadb-linux-arm64
|
||||
mv baradadb-linux-arm64 /usr/local/bin/baradadb
|
||||
|
||||
# macOS
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-darwin-amd64
|
||||
chmod +x baradadb-darwin-amd64
|
||||
mv baradadb-darwin-amd64 /usr/local/bin/baradadb
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
# سحب الصورة الرسمية
|
||||
docker pull barabadb/barabadb:latest
|
||||
|
||||
# التشغيل
|
||||
docker run -d \
|
||||
-p 9472:9472 \
|
||||
-p 9470:9470 \
|
||||
-p 9471:9471 \
|
||||
-v baradb_data:/data \
|
||||
barabadb/barabadb
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### الاستخدام المدمج (مشاريع Nim)
|
||||
|
||||
أضف إلى ملف `.nimble` الخاص بك:
|
||||
|
||||
```nim
|
||||
requires "barabadb >= 1.1.0"
|
||||
```
|
||||
|
||||
استخدم في الكود:
|
||||
|
||||
```nim
|
||||
import barabadb/storage/lsm
|
||||
|
||||
var db = newLSMTree("./data")
|
||||
db.put("key", cast[seq[byte]]("value"))
|
||||
let (found, val) = db.get("key")
|
||||
db.close()
|
||||
```
|
||||
|
||||
## التشغيل الأول
|
||||
|
||||
```bash
|
||||
# بدء الخادم
|
||||
./build/baradadb
|
||||
|
||||
# المخرجات المتوقعة:
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# الاختبار عبر HTTP API
|
||||
curl http://localhost:9470/health
|
||||
|
||||
# الصدفة التفاعلية
|
||||
./build/baradadb --shell
|
||||
```
|
||||
|
||||
## حل مشكلات التثبيت
|
||||
|
||||
### "cannot open file: hunos"
|
||||
|
||||
```bash
|
||||
nimble install -d -y
|
||||
```
|
||||
|
||||
### "BaraDB requires SSL support"
|
||||
|
||||
قم دائمًا بالبناء باستخدام `-d:ssl`:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### البناء البطيء
|
||||
|
||||
استخدم البناء المتوازي:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --parallelBuild:4 -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### حجم الملف الثنائي الكبير
|
||||
|
||||
استخدم تحسين الحجم:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:size --passL:-s -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
- [دليل البداية السريعة](quickstart.md)
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# API за Бинарен Протокол
|
||||
|
||||
Ниско-нивов wire протокол за високопроизводителни клиентски връзки.
|
||||
|
||||
## Формат на Съобщенията
|
||||
|
||||
Всички съобщения използват big-endian byte order:
|
||||
|
||||
```
|
||||
┌─────────────┬─────────────┬─────────────┬─────────────────────┐
|
||||
│ Kind │ Length │ RequestId │ Payload │
|
||||
│ (4 bytes) │ (4 bytes) │ (4 bytes) │ │
|
||||
│ uint32 BE │ uint32 BE │ uint32 BE │ (Length bytes) │
|
||||
└─────────────┴─────────────┴─────────────┴─────────────────────┘
|
||||
```
|
||||
|
||||
## Типове Съобщения
|
||||
|
||||
### Query (0x01)
|
||||
|
||||
```nim
|
||||
let msg = makeQueryMessage(requestId, "SELECT * FROM users")
|
||||
```
|
||||
|
||||
### QueryParams (0x02)
|
||||
|
||||
```nim
|
||||
let msg = makeQueryParamsMessage(requestId, "SELECT * FROM users WHERE name = ?", params)
|
||||
```
|
||||
|
||||
### Auth (0x07)
|
||||
|
||||
```nim
|
||||
let msg = makeAuthMessage(requestId, token)
|
||||
```
|
||||
|
||||
### Ping (0x09)
|
||||
|
||||
```nim
|
||||
let msg = makePingMessage(requestId)
|
||||
```
|
||||
|
||||
### Close (0x0A)
|
||||
|
||||
```nim
|
||||
let msg = makeCloseMessage(requestId)
|
||||
```
|
||||
|
||||
## Отговорни Съобщения
|
||||
|
||||
### Ready (0x05)
|
||||
|
||||
```nim
|
||||
let msg = makeReadyMessage(requestId)
|
||||
```
|
||||
|
||||
### Error (0x06)
|
||||
|
||||
```nim
|
||||
let msg = makeErrorMessage(requestId, code, message)
|
||||
```
|
||||
|
||||
### Data (0x81)
|
||||
|
||||
```nim
|
||||
# Съдържа резултатите от заявката с колони и редове
|
||||
```
|
||||
|
||||
### Complete (0x82)
|
||||
|
||||
```nim
|
||||
# Потвърждава завършване на заявката
|
||||
```
|
||||
|
||||
### Auth_OK (0x83)
|
||||
|
||||
```nim
|
||||
# Потвърждава успешна автентикация
|
||||
```
|
||||
|
||||
### Pong (0x84)
|
||||
|
||||
```nim
|
||||
# Keepalive отговор
|
||||
```
|
||||
|
||||
## Кодове за Грешки
|
||||
|
||||
| Код | Име | Описание |
|
||||
|------|-----|----------|
|
||||
| 0x00 | OK | Успех |
|
||||
| 0x01 | ERROR | Обща грешка |
|
||||
| 0x02 | AUTH_REQUIRED | Изисква се автентикация |
|
||||
| 0x03 | INVALID_QUERY | Синтактична грешка в заявката |
|
||||
| 0x04 | NOT_FOUND | Ресурсът не е намерен |
|
||||
|
||||
## Сериализация
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/wire
|
||||
|
||||
# Сериализиране на стойност
|
||||
let bytes = serializeValue(Value(kind: vkString, strVal: "test"))
|
||||
|
||||
# Десериализиране на стойност
|
||||
let value = deserializeValue(bytes)
|
||||
```
|
||||
@@ -0,0 +1,111 @@
|
||||
# HTTP/REST API
|
||||
|
||||
JSON-базиран REST API за уеб приложения.
|
||||
|
||||
## Базов URL
|
||||
|
||||
```
|
||||
http://localhost:9470
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /health
|
||||
|
||||
Health проверка:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/health
|
||||
```
|
||||
|
||||
### GET /ready
|
||||
|
||||
Readiness проверка:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/ready
|
||||
```
|
||||
|
||||
### POST /query
|
||||
|
||||
Изпълнение на BaraQL заявка:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/api/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "SELECT * FROM users WHERE age > 18"}'
|
||||
```
|
||||
|
||||
Отговор:
|
||||
```json
|
||||
{
|
||||
"columns": ["name", "age"],
|
||||
"rows": [["Alice", 30], ["Bob", 25]],
|
||||
"row_count": 2,
|
||||
"duration_ms": 12
|
||||
}
|
||||
```
|
||||
|
||||
### POST /batch
|
||||
|
||||
Групови заявки:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/api/batch \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"queries": ["INSERT users { name := \"Alice\" }", "INSERT users { name := \"Bob\" }"]}'
|
||||
```
|
||||
|
||||
### GET /schema
|
||||
|
||||
Преглед на схемата:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/api/schema
|
||||
```
|
||||
|
||||
### GET /metrics
|
||||
|
||||
Prometheus метрики:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/metrics
|
||||
```
|
||||
|
||||
### POST /explain
|
||||
|
||||
Обяснение на план за изпълнение:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/api/explain \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "SELECT * FROM users WHERE age > 18"}'
|
||||
```
|
||||
|
||||
### POST /backup
|
||||
|
||||
Създаване на backup:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/api/backup \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"destination": "/backup/snapshot.db"}'
|
||||
```
|
||||
|
||||
## Грешки
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "INVALID_QUERY",
|
||||
"message": "Грешка в синтаксиса"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Автентикация
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
http://localhost:9470/api/users
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
# WebSocket API
|
||||
|
||||
Full-duplex стрийминг за данни в реално време и push известия.
|
||||
|
||||
## Свързване
|
||||
|
||||
```
|
||||
ws://localhost:9471
|
||||
```
|
||||
|
||||
## Клиентски Пример
|
||||
|
||||
```javascript
|
||||
const ws = new WebSocket('ws://localhost:9471');
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('Свързан');
|
||||
ws.send(JSON.stringify({
|
||||
type: 'query',
|
||||
query: 'SELECT * FROM users'
|
||||
}));
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('Получено:', data);
|
||||
};
|
||||
```
|
||||
|
||||
## Формат на Съобщенията
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "query",
|
||||
"id": "1",
|
||||
"query": "SELECT * FROM users"
|
||||
}
|
||||
```
|
||||
|
||||
## Типове Съобщения
|
||||
|
||||
### Заявка (query)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "query",
|
||||
"id": "1",
|
||||
"query": "SELECT * FROM users"
|
||||
}
|
||||
```
|
||||
|
||||
### Резултат (result)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "result",
|
||||
"id": "1",
|
||||
"columns": ["id", "name"],
|
||||
"rows": [["1", "Alice"], ["2", "Bob"]]
|
||||
}
|
||||
```
|
||||
|
||||
### Грешка (error)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "error",
|
||||
"id": "1",
|
||||
"code": "INVALID_QUERY",
|
||||
"message": "Синтактична грешка"
|
||||
}
|
||||
```
|
||||
|
||||
### Абониране (subscribe)
|
||||
|
||||
Абониране за промени в таблица:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "subscribe",
|
||||
"id": "sub1",
|
||||
"table": "users"
|
||||
}
|
||||
```
|
||||
|
||||
### Известие (notification)
|
||||
|
||||
Push известие от сървъра:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "notification",
|
||||
"table": "users",
|
||||
"operation": "insert",
|
||||
"data": {"id": 3, "name": "Charlie"}
|
||||
}
|
||||
```
|
||||
|
||||
### Ping/Pong (keepalive)
|
||||
|
||||
```json
|
||||
{"type": "ping", "id": "ping1"}
|
||||
```
|
||||
|
||||
Отговор:
|
||||
```json
|
||||
{"type": "pong", "id": "ping1"}
|
||||
```
|
||||
|
||||
## JavaScript Клиент
|
||||
|
||||
```javascript
|
||||
class BaraDBClient {
|
||||
constructor(url) {
|
||||
this.ws = new WebSocket(url);
|
||||
this.pending = new Map();
|
||||
}
|
||||
|
||||
query(sql) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = crypto.randomUUID();
|
||||
this.pending.set(id, { resolve, reject });
|
||||
this.ws.send(JSON.stringify({ type: 'query', id, query: sql }));
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
+69
-94
@@ -2,150 +2,125 @@
|
||||
|
||||
## Преглед
|
||||
|
||||
BaraDB е **мултимодална база данни** написана на Nim, която комбинира документно (KV), графично, векторно, колонно и пълнотекстово търсене в един двигател с обединен език за заявки наречен **BaraQL**.
|
||||
BaraDB е **мултимодален database engine**, написан на Nim, който комбинира документно (KV), графово, векторно, колонково и пълнотекстово съхранение в един engine с унифициран език за заявки наречен **BaraQL**.
|
||||
|
||||
## Слоеста Архитектура
|
||||
## Слоеве на Архитектурата
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 1. СЛОЙ ЗА КЛИЕНТИ │
|
||||
│ Binary Protocol │ HTTP/REST │ WebSocket │ Embedded │
|
||||
│ 1. КЛИЕНТСКИ СЛОЙ │
|
||||
│ Бинарен Протокол │ HTTP/REST │ WebSocket │ Embedded │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 2. ЗАЯВКИ СЛОЙ (BaraQL) │
|
||||
│ 2. QUERY СЛОЙ (BaraQL) │
|
||||
│ Lexer → Parser → AST → IR → Optimizer → Codegen │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 3. ИЗПЪЛНИТЕЛЕН ДВИГАТЕЛ │
|
||||
│ Document │ Graph │ Vector │ Columnar │ FTS │
|
||||
│ 3. ИЗПЪЛНИТЕЛЕН ENGINE │
|
||||
│ Документен │ Графов │ Векторен │ Колонков │ FTS │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 4. СЪХРАНЕНИЕ │
|
||||
│ 4. СЪХРАНЕНИЕ │
|
||||
│ LSM-Tree │ B-Tree │ WAL │ Bloom │ Compaction │ Cache │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 5. РАЗПРЕДЕЛЕНО │
|
||||
│ Raft Consensus │ Sharding │ Replication │ Gossip │
|
||||
│ 5. РАЗПРЕДЕЛЕНИ │
|
||||
│ Raft Консенсус │ Шардиране │ Репликация │ Gossip │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Слой 1: Клиентски Слой
|
||||
|
||||
Множество протоколи за комуникация:
|
||||
Множество комуникационни протоколи:
|
||||
|
||||
- **Binary Protocol** (`protocol/wire.nim`): Ефективен big-endian бинарен протокол с 16 типа съобщения
|
||||
- **HTTP/REST** (`core/httpserver.nim`): JSON REST API с мулти-трединг
|
||||
- **WebSocket** (`core/websocket.nim`): Пълен дуплекс стрийминг
|
||||
- **Бинарен Протокол** (`protocol/wire.nim`): Ефективен big-endian бинарен протокол с 16 типа съобщения
|
||||
- **HTTP/REST** (`core/httpserver.nim`): JSON-базиран REST API с multi-threading
|
||||
- **WebSocket** (`core/websocket.nim`): Full-duplex стрийминг
|
||||
- **Embedded** (`storage/lsm.nim`): Директен in-process достъп
|
||||
|
||||
### Управление на Връзките
|
||||
### Управление на Връзки
|
||||
|
||||
- **Connection Pool** (`protocol/pool.nim`): Мин/макс лимити на връзки
|
||||
- **Rate Limiting** (`protocol/ratelimit.nim`): Token-bucket лимитиране
|
||||
- **Authentication** (`protocol/auth.nim`): JWT с HMAC-SHA256
|
||||
- **TLS/SSL** (`protocol/ssl.nim`): TLS 1.3 с авто-генерирани сертификати
|
||||
- **Connection Pool** (`protocol/pool.nim`): Min/max лимити на връзки с idle timeout
|
||||
- **Rate Limiting** (`protocol/ratelimit.nim`): Token-bucket глобални и per-client лимити
|
||||
- **Автентикация** (`protocol/auth.nim`): JWT с HMAC-SHA256 и достъп на база роли
|
||||
- **TLS/SSL** (`protocol/ssl.nim`): TLS 1.3 с автоматично генерирани сертификати
|
||||
|
||||
## Слой 2: Заявки (BaraQL)
|
||||
## Слой 2: Query Слой (BaraQL)
|
||||
|
||||
Pipeline-а на BaraQL:
|
||||
BaraQL конвейрът:
|
||||
|
||||
1. **Lexer** (`query/lexer.nim`): Токенизира входа в 80+ типа токени
|
||||
2. **Parser** (`query/parser.nim`): Рекурсивен descent парсър произвеждащ AST
|
||||
2. **Parser** (`query/parser.nim`): Recursive descent parser генериращ AST
|
||||
3. **AST** (`query/ast.nim`): 300+ реда покриващи 25+ вида възли
|
||||
4. **IR** (`query/ir.nim`): Междинно представяне за планове за изпълнение
|
||||
5. **Optimizer** (`query/adaptive.nim`): Адаптивен крос-модален оптимизатор
|
||||
6. **Codegen** (`query/codegen.nim`): Транслира IR към операции върху съхранение
|
||||
4. **IR** (`query/ir.nim`): Intermediate representation за планове за изпълнение
|
||||
5. **Optimizer** (`query/adaptive.nim`): Adaptive cross-modal оптимизация на заявки
|
||||
6. **Codegen** (`query/codegen.nim`): Превежда IR към storage операции
|
||||
7. **Executor** (`query/executor.nim`): Изпълнява планове с паралелизация
|
||||
|
||||
### Крос-Модално Планиране
|
||||
## Слой 3: Изпълнителен Engine
|
||||
|
||||
Оптимизаторът определя реда на изпълнение между двигателите:
|
||||
### Документен/KV Engine
|
||||
- **LSM-Tree** (`storage/lsm.nim`): Write-оптимизирано съхранение с MemTable, WAL, SSTables
|
||||
- **B-Tree Индекс** (`storage/btree.nim`): Подреден индекс за range сканиране с COW
|
||||
|
||||
```
|
||||
1. Оценка на селективност за всеки предикат
|
||||
2. Най-селективният предикат се изпълнява първи
|
||||
3. Bloom филтри за KV търсения
|
||||
4. Паралелизация на независими клонове
|
||||
```
|
||||
### Vector Engine (`vector/`)
|
||||
- **HNSW Индекс** (`vector/engine.nim`): Hierarchical Navigable Small World граф
|
||||
- **IVF-PQ Индекс** (`vector/engine.nim`): Inverted File Index с Product Quantization
|
||||
- **SIMD Операции** (`vector/simd.nim`): AVX2-оптимизирани изчисления на разстояние
|
||||
- **Квантуване** (`vector/quant.nim`): Скаларно, продуктово и бинарно квантуване
|
||||
|
||||
## Слой 3: Изпълнителен Двигател
|
||||
|
||||
### Document/KV Двигател
|
||||
- **LSM-Tree** (`storage/lsm.nim`): Оптимизиран за запис с MemTable, WAL, SSTables
|
||||
- **B-Tree Index** (`storage/btree.nim`): Подреден индекс за диапазони с COW
|
||||
|
||||
### Vector Engine
|
||||
- **HNSW** (`vector/engine.nim`): Иерархичен навигируем малък свят
|
||||
- **IVF-PQ** (`vector/engine.nim`): Инвертиран файл с продуктово квантуване
|
||||
- **SIMD** (`vector/simd.nim`): AVX2-оптимизирани изчисления на разстояния
|
||||
- **Quantization** (`vector/quant.nim`): Скаларно, продуктово и бинарно квантуване
|
||||
|
||||
### Graph Engine
|
||||
- **Списък със съседи** (`graph/engine.nim`): Насочен граф с тегла
|
||||
### Graph Engine (`graph/`)
|
||||
- **Adjacency List** (`graph/engine.nim`): Насочен граф с тегла на ребрата
|
||||
- **Алгоритми** (`graph/engine.nim`): BFS, DFS, Dijkstra, PageRank
|
||||
- **Community Detection** (`graph/community.nim`): Louvain алгоритъм
|
||||
- **Pattern Matching** (`graph/community.nim`): Subgraph isomorphism
|
||||
- **Cypher Parser** (`graph/cypher.nim`): Cypher-подобни заявки
|
||||
- **Pattern Matching** (`graph/community.nim`): Subgraph изоморфизъм
|
||||
- **Cypher Parser** (`graph/cypher.nim`): Cypher-подобни графови заявки
|
||||
|
||||
### FTS
|
||||
- **Инвертиран индекс** (`fts/engine.nim`): Термин-документ индекс
|
||||
- **Ранжиране** (`fts/engine.nim`): BM25 и TF-IDF
|
||||
- **Fuzzy Search** (`fts/engine.nim`): Levenshtein разстояние
|
||||
- **Многоезичен** (`fts/multilang.nim`): Токенизация за EN, BG, DE, FR, RU
|
||||
### Full-Text Search (`fts/`)
|
||||
- **Inverted Index** (`fts/engine.nim`): Термин-документен индекс
|
||||
- **Ранжиране** (`fts/engine.nim`): BM25 и TF-IDF оценяване
|
||||
- **Fuzzy Търсене** (`fts/engine.nim`): Съвпадение с Levenshtein разстояние
|
||||
- **Многоезичност** (`fts/multilang.nim`): Токенизатори за EN, BG, DE, FR, RU
|
||||
|
||||
### Columnar Engine
|
||||
- **Колонно съхранение** (`core/columnar.nim`): Аналитични заявки
|
||||
- **Компресия**: RLE и dictionary encoding
|
||||
- **SIMD агрегати**: Ускорени агрегатни функции
|
||||
### Columnar Engine (`core/columnar.nim`)
|
||||
- Колонково съхранение за аналитични заявки
|
||||
- RLE и dictionary encoding
|
||||
- SIMD-ускорени агрегати
|
||||
|
||||
## Слой 4: Съхранение
|
||||
|
||||
- **LSM-Tree** (`storage/lsm.nim`): MemTable, WAL, SSTable, Bloom Filter, Compaction
|
||||
- **Page Cache** (`storage/compaction.nim`): LRU кеш
|
||||
- **Memory-mapped I/O** (`storage/mmap.nim`): mmap-базиран достъп
|
||||
- **Recovery** (`storage/recovery.nim`): WAL replay и възстановяване
|
||||
- **Page Cache** (`storage/compaction.nim`): LRU кеш с проследяване на hit rate
|
||||
- **Memory-mapped I/O** (`storage/mmap.nim`): mmap-базиран достъп до файлове
|
||||
- **Recovery** (`storage/recovery.nim`): WAL replay и crash recovery
|
||||
|
||||
### Път на Запис
|
||||
## Слой 5: Разпределение
|
||||
|
||||
```
|
||||
Client → Protocol → Auth → Parser → AST → IR → Codegen
|
||||
→ StorageOp → MVCC Txn → WAL Write → MemTable → Commit
|
||||
```
|
||||
- **Raft Консенсус** (`core/raft.nim`): Leader election, log репликация
|
||||
- **Шардиране** (`core/sharding.nim`): Hash, range и consistent hashing
|
||||
- **Репликация** (`core/replication.nim`): Sync, async, semi-sync режими
|
||||
- **Gossip Протокол** (`core/gossip.nim`): SWIM-подобно управление на членство
|
||||
- **Разпределени Транзакции** (`core/disttxn.nim`): Two-phase commit
|
||||
|
||||
### Път на Четене
|
||||
|
||||
```
|
||||
Client → Protocol → Auth → Parser → AST → IR → Codegen
|
||||
→ StorageOp → MVCC Snapshot → MemTable → SSTable → Result
|
||||
```
|
||||
|
||||
## Слой 5: Разпределено
|
||||
|
||||
- **Raft Consensus** (`core/raft.nim`): Лидерско избиране, репликация на логове
|
||||
- **Sharding** (`core/sharding.nim`): Hash, range и консистентно хеширане
|
||||
- **Replication** (`core/replication.nim`): Sync, async, semi-sync режими
|
||||
- **Gossip Protocol** (`core/gossip.nim`): SWIM-подобно управление на членство
|
||||
- **Distributed Transactions** (`core/disttxn.nim`): Two-phase commit
|
||||
|
||||
## Ключови Дизайн Решения
|
||||
## Ключови Дизайнерски Решения
|
||||
|
||||
1. **Чист Nim**: Без Cython, Python или Rust зависимости
|
||||
2. **Обединено Съхранение**: Един двигател за KV, граф, вектор, FTS и колонно
|
||||
3. **Вграден Режим**: Работи като библиотека или сървър
|
||||
4. **Бинарен Протокол**: Ефективен wire протокол
|
||||
2. **Унифицирано Съхранение**: Един engine обработва KV, graph, vector, FTS и columnar
|
||||
3. **Embedded Режим**: Може да работи като библиотека или сървър
|
||||
4. **Бинарен Протокол**: Персонализиран ефективен wire протокол
|
||||
5. **MVCC**: Multi-version concurrency control
|
||||
6. **Schema-First**: Строго типизирана система с наследяване
|
||||
7. **Крос-Модал**: Един език за заявки през всички модели данни
|
||||
8. **Формално Верифициран**: Разпределените алгоритми са специфицирани в TLA+ и проверени с TLC
|
||||
6. **Schema-First**: Силно типизирана система от схеми с наследяване
|
||||
7. **Cross-Modal**: Един език за заявки за всички модели на данни
|
||||
8. **Формално Верифициран**: Основните разпределени алгоритми са специфицирани в TLA+ и проверени с TLC
|
||||
|
||||
## Статистика на Модулите
|
||||
|
||||
| Категория | Модули | Редове Код | Предназначение |
|
||||
|-----------|--------|------------|----------------|
|
||||
| Core | 16 | ~4,200 | Сървър, протоколи, транзакции, разпределено |
|
||||
| Core | 16 | ~4,200 | Сървър, протоколи, транзакции, разпределени |
|
||||
| Storage | 7 | ~3,100 | LSM, B-Tree, WAL, bloom, compaction, mmap |
|
||||
| Query | 7 | ~2,800 | Lexer, parser, AST, IR, оптимизатор, codegen, executor |
|
||||
| Query | 7 | ~2,800 | Lexer, parser, AST, IR, optimizer, codegen, executor |
|
||||
| Vector | 3 | ~1,200 | HNSW, IVF-PQ, квантуване, SIMD |
|
||||
| Graph | 3 | ~1,000 | Списък със съседи, алгоритми, community detection |
|
||||
| FTS | 2 | ~900 | Инвертиран индекс, BM25, fuzzy, многоезичен |
|
||||
| Graph | 3 | ~1,000 | Adjacency list, алгоритми, community detection |
|
||||
| FTS | 2 | ~900 | Inverted index, BM25, fuzzy, многоезичност |
|
||||
| Protocol | 7 | ~2,400 | Wire, HTTP, WebSocket, pool, auth, rate limit, SSL |
|
||||
| Schema | 1 | ~600 | Типове, връзки, наследяване, миграции |
|
||||
| Client | 2 | ~800 | Nim binary client, file helpers |
|
||||
| CLI | 1 | ~400 | Интерактивна BaraQL конзола |
|
||||
| Client | 2 | ~800 | Nim бинарен клиент, файлови помощници |
|
||||
| CLI | 1 | ~400 | Интерактивна BaraQL обвивка |
|
||||
| **Общо** | **49** | **~14,100** | |
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
# Backup и Възстановяване
|
||||
|
||||
## Online Snapshots
|
||||
|
||||
BaraDB поддържа online snapshots без спиране на сървъра. Snapshot-ът заснема консистентен изглед към момент във времето чрез MVCC.
|
||||
|
||||
### Създаване на Snapshot
|
||||
|
||||
```nim
|
||||
import barabadb/core/backup
|
||||
|
||||
var bm = newBackupManager()
|
||||
bm.createSnapshot("/backup/baradb_2025-01-15")
|
||||
```
|
||||
|
||||
### Чрез CLI
|
||||
|
||||
```bash
|
||||
./build/baradadb --snapshot --output=/backup/snapshot.db
|
||||
```
|
||||
|
||||
### Чрез HTTP API
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/api/backup \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"destination": "/backup/snapshot.db"}'
|
||||
```
|
||||
|
||||
### Автоматизирани Backups
|
||||
|
||||
Използвайте cron за планирани backups:
|
||||
|
||||
```bash
|
||||
# Ежедневен snapshot в 2 сутринта
|
||||
0 2 * * * /usr/local/bin/baradadb --snapshot --output=/backup/baradb_$(date +\%Y\%m\%d).db
|
||||
|
||||
# Запазване на последните 7 дни
|
||||
find /backup -name "baradb_*.db" -mtime +7 -delete
|
||||
```
|
||||
|
||||
## Point-in-Time Recovery (PITR)
|
||||
|
||||
BaraDB използва Write-Ahead Log (WAL) за възстановяване до момент във времето.
|
||||
|
||||
### WAL Архивиране
|
||||
|
||||
Включете непрекъснато WAL архивиране:
|
||||
|
||||
```bash
|
||||
BARADB_WAL_ARCHIVE_DIR=/backup/wal \
|
||||
BARADB_WAL_ARCHIVE_INTERVAL_MS=60000 \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
### Възстановяване от Checkpoint + WAL
|
||||
|
||||
```bash
|
||||
# Възстановяване от snapshot
|
||||
./build/baradadb --recover \
|
||||
--checkpoint=/backup/snapshot.db \
|
||||
--wal-dir=/backup/wal
|
||||
|
||||
# Възстановяване до конкретен LSN
|
||||
./build/baradadb --recover \
|
||||
--checkpoint=/backup/snapshot.db \
|
||||
--wal-dir=/backup/wal \
|
||||
--target-lsn=15420
|
||||
|
||||
# Възстановяване до конкретно време
|
||||
./build/baradadb --recover \
|
||||
--checkpoint=/backup/snapshot.db \
|
||||
--wal-dir=/backup/wal \
|
||||
--target-time="2025-01-15T10:30:00Z"
|
||||
```
|
||||
|
||||
### Възстановяване чрез SQL
|
||||
|
||||
Можете също да възстановявате директно чрез BaraQL:
|
||||
|
||||
```sql
|
||||
RECOVER TO TIMESTAMP '2026-05-07T12:00:00';
|
||||
```
|
||||
|
||||
### Инкрементални Backups
|
||||
|
||||
Инкременталните backups копират само променени SSTables:
|
||||
|
||||
```bash
|
||||
./build/baradadb --backup-incremental \
|
||||
--last-backup=/backup/previous \
|
||||
--output=/backup/incremental_$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
## Репликация като Backup
|
||||
|
||||
За непрекъсната защита използвайте streaming репликация:
|
||||
|
||||
### Primary
|
||||
|
||||
```bash
|
||||
BARADB_REPLICATION_ENABLED=true \
|
||||
BARADB_REPLICATION_MODE=async \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
### Replica
|
||||
|
||||
```bash
|
||||
BARADB_REPLICATION_ENABLED=true \
|
||||
BARADB_REPLICATION_PRIMARY=primary:9472 \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
## Disaster Recovery
|
||||
|
||||
### Процедури за Възстановяване
|
||||
|
||||
#### Сценарий 1: Повреда на Единичен Файл
|
||||
|
||||
```bash
|
||||
# Идентифициране на повреден SSTable от логовете
|
||||
# Възстановяване на конкретен SSTable от backup
|
||||
cp /backup/sstables/000012.sst ./data/sstables/
|
||||
|
||||
# Възстановяване на индекса
|
||||
./build/baradadb --rebuild-index
|
||||
```
|
||||
|
||||
#### Сценарий 2: Пълна Загуба на Данни
|
||||
|
||||
```bash
|
||||
# 1. Възстановяване на последния snapshot
|
||||
cp /backup/snapshot.db ./data/
|
||||
|
||||
# 2. Преиграване на WAL
|
||||
./build/baradadb --recover --wal-dir=/backup/wal
|
||||
|
||||
# 3. Проверка
|
||||
curl http://localhost:9470/health
|
||||
```
|
||||
|
||||
#### Сценарий 3: Отказ на Възел в Клъстер
|
||||
|
||||
```bash
|
||||
# За Raft клъстери, просто стартирайте нов възел
|
||||
BARADB_RAFT_NODE_ID=newnode \
|
||||
BARADB_RAFT_PEERS=node1:9001,node2:9001 \
|
||||
./build/baradadb
|
||||
|
||||
# Новият възел ще навакса чрез Raft log репликация
|
||||
```
|
||||
|
||||
## Верификация на Backup
|
||||
|
||||
Винаги проверявайте backups:
|
||||
|
||||
```bash
|
||||
# Възстановяване във временна директория
|
||||
./build/baradadb --recover \
|
||||
--checkpoint=/backup/snapshot.db \
|
||||
--data-dir=/tmp/verify_data
|
||||
|
||||
# Проверка на консистентност
|
||||
curl http://localhost:9470/api/admin/check
|
||||
```
|
||||
|
||||
## Изисквания за Съхранение
|
||||
|
||||
| Тип Backup | Размер | Честота | Задържане |
|
||||
|------------|--------|---------|-----------|
|
||||
| Пълен snapshot | ~1× размер на данните | Ежедневно | 7 дни |
|
||||
| Инкрементален | ~0.1× размер на данните | На всеки час | 24 часа |
|
||||
| WAL архив | ~0.05× размер на данните / ден | Непрекъснато | 30 дни |
|
||||
|
||||
## Най-добри Практики
|
||||
|
||||
1. **Тествайте възстановяването редовно** — Backup, който не може да бъде възстановен, е безполезен
|
||||
2. **Съхранявайте backups извън локацията** — Използвайте S3, GCS или Azure Blob
|
||||
3. **Криптирайте backups** — Използвайте `gpg` или криптиране на ниво ОС
|
||||
4. **Мониторирайте backup задачите** — Алармирайте при неуспешни backups
|
||||
5. **Документирайте RTO/RPO** — Знайте целите си за време и точка на възстановяване
|
||||
|
||||
### Качване на Backup в Облак
|
||||
|
||||
```bash
|
||||
# Качване в S3
|
||||
aws s3 cp /backup/snapshot.db s3://my-bucket/baradb/
|
||||
|
||||
# Качване в GCS
|
||||
gsutil cp /backup/snapshot.db gs://my-bucket/baradb/
|
||||
|
||||
# Качване в Azure
|
||||
az storage blob upload \
|
||||
--container-name backups \
|
||||
--file /backup/snapshot.db \
|
||||
--name baradb/snapshot.db
|
||||
```
|
||||
+559
-32
@@ -2,39 +2,160 @@
|
||||
|
||||
BaraQL е SQL-съвместим език за заявки с разширения за графи, вектори и документи.
|
||||
|
||||
## Типове Данни
|
||||
|
||||
| Тип | Описание | Пример |
|
||||
|------|----------|--------|
|
||||
| `null` | Null стойност | `null` |
|
||||
| `bool` | Булев | `true`, `false` |
|
||||
| `int8` | 8-битов signed integer | `127` |
|
||||
| `int16` | 16-битов signed integer | `32767` |
|
||||
| `int32` | 32-битов signed integer | `2147483647` |
|
||||
| `int64` | 64-битов signed integer | `9223372036854775807` |
|
||||
| `float32` | 32-битов float | `3.14` |
|
||||
| `float64` | 64-битов float | `3.14159265359` |
|
||||
| `str` | UTF-8 низ | `'hello'` |
|
||||
| `bytes` | Сурови байтове | `0xDEADBEEF` |
|
||||
| `array<T>` | Хомогенен масив | `[1, 2, 3]` |
|
||||
| `vector` | Float32 вектор | `[0.1, 0.2, 0.3]` |
|
||||
| `vector(n)` | Float32 вектор с фиксирана размерност (SQL) | `VECTOR(768)` |
|
||||
| `object` | Ключ-стойност обект | `{"a": 1}` |
|
||||
| `datetime` | ISO 8601 времеви печат | `'2025-01-15T10:30:00Z'` |
|
||||
| `uuid` | UUID v4 | `'550e8400-e29b-41d4-a716-446655440000'` |
|
||||
| `json` | JSON документ | `{"key": "value"}` |
|
||||
| `jsonb` | Бинарен JSON (валидиран) | `{"key": "value"}` |
|
||||
|
||||
## Основни Заявки
|
||||
|
||||
### SELECT
|
||||
|
||||
```sql
|
||||
SELECT name, age FROM users WHERE age > 18 ORDER BY name LIMIT 10;
|
||||
-- Всички колони
|
||||
SELECT * FROM users;
|
||||
|
||||
-- Конкретни колони
|
||||
SELECT name, age FROM users;
|
||||
|
||||
-- Псевдоними
|
||||
SELECT name AS full_name, age AS years FROM users;
|
||||
|
||||
-- DISTINCT
|
||||
SELECT DISTINCT department FROM employees;
|
||||
|
||||
-- LIMIT и OFFSET
|
||||
SELECT * FROM users LIMIT 10 OFFSET 20;
|
||||
```
|
||||
|
||||
### WHERE
|
||||
|
||||
```sql
|
||||
-- Оператори за сравнение
|
||||
SELECT * FROM users WHERE age > 18;
|
||||
SELECT * FROM users WHERE age >= 18 AND age <= 65;
|
||||
SELECT * FROM users WHERE name = 'Alice';
|
||||
SELECT * FROM users WHERE name != 'Bob';
|
||||
|
||||
-- Диапазон
|
||||
SELECT * FROM users WHERE age BETWEEN 18 AND 65;
|
||||
|
||||
-- Принадлежност към множество
|
||||
SELECT * FROM users WHERE department IN ('Engineering', 'Sales');
|
||||
|
||||
-- Търсене по шаблон
|
||||
SELECT * FROM users WHERE name LIKE 'A%';
|
||||
SELECT * FROM users WHERE name ILIKE 'alice'; -- Case-insensitive
|
||||
|
||||
-- NULL проверки
|
||||
SELECT * FROM users WHERE email IS NOT NULL;
|
||||
|
||||
-- Логически оператори
|
||||
SELECT * FROM users WHERE age > 18 AND (department = 'Engineering' OR department = 'Sales');
|
||||
```
|
||||
|
||||
### ORDER BY
|
||||
|
||||
```sql
|
||||
-- Възходящ (по подразбиране)
|
||||
SELECT * FROM users ORDER BY age;
|
||||
|
||||
-- Низходящ
|
||||
SELECT * FROM users ORDER BY age DESC;
|
||||
|
||||
-- Множество колони
|
||||
SELECT * FROM users ORDER BY department ASC, age DESC;
|
||||
```
|
||||
|
||||
### INSERT
|
||||
|
||||
```sql
|
||||
-- Един ред
|
||||
INSERT users { name := 'Alice', age := 30 };
|
||||
|
||||
-- С явен тип
|
||||
INSERT User { name := 'Alice', age := 30 };
|
||||
|
||||
-- Множество редове
|
||||
INSERT users {
|
||||
{ name := 'Alice', age := 30 },
|
||||
{ name := 'Bob', age := 25 }
|
||||
};
|
||||
```
|
||||
|
||||
### UPDATE
|
||||
|
||||
```sql
|
||||
-- Обнови всички редове
|
||||
UPDATE users SET status = 'active';
|
||||
|
||||
-- Условно обновяване
|
||||
UPDATE users SET age = 31 WHERE name = 'Alice';
|
||||
|
||||
-- Обновяване на няколко колони
|
||||
UPDATE users SET age = 32, status = 'premium' WHERE name = 'Alice';
|
||||
```
|
||||
|
||||
### DELETE
|
||||
|
||||
```sql
|
||||
DELETE FROM users WHERE name = 'Bob';
|
||||
-- Изтрий всички редове
|
||||
DELETE FROM users;
|
||||
|
||||
-- Условно изтриване
|
||||
DELETE FROM users WHERE age < 18;
|
||||
```
|
||||
|
||||
## Агрегати и Групиране
|
||||
|
||||
### Агрегатни Функции
|
||||
|
||||
| Функция | Описание |
|
||||
|----------|-----------|
|
||||
| `count(*)` | Брой на всички редове |
|
||||
| `count(column)` | Брой на не-NULL стойности |
|
||||
| `sum(column)` | Сума на стойностите |
|
||||
| `avg(column)` | Средно аритметично |
|
||||
| `min(column)` | Минимална стойност |
|
||||
| `max(column)` | Максимална стойност |
|
||||
| `stddev(column)` | Стандартно отклонение |
|
||||
| `variance(column)` | Дисперсия |
|
||||
|
||||
### GROUP BY
|
||||
|
||||
```sql
|
||||
SELECT department, count(*), avg(salary)
|
||||
SELECT department, count(*) as emp_count, avg(salary) as avg_salary
|
||||
FROM employees
|
||||
GROUP BY department;
|
||||
|
||||
-- С HAVING
|
||||
SELECT department, count(*) as emp_count
|
||||
FROM employees
|
||||
GROUP BY department
|
||||
HAVING count(*) > 5;
|
||||
|
||||
-- Множествено групиране
|
||||
SELECT department, role, count(*), avg(salary)
|
||||
FROM employees
|
||||
GROUP BY department, role;
|
||||
```
|
||||
|
||||
## JOINs
|
||||
@@ -49,15 +170,85 @@ INNER JOIN orders o ON u.id = o.user_id;
|
||||
SELECT u.name, o.total
|
||||
FROM users u
|
||||
LEFT JOIN orders o ON u.id = o.user_id;
|
||||
|
||||
-- RIGHT JOIN
|
||||
SELECT u.name, o.total
|
||||
FROM users u
|
||||
RIGHT JOIN orders o ON u.id = o.user_id;
|
||||
|
||||
-- FULL JOIN
|
||||
SELECT u.name, o.total
|
||||
FROM users u
|
||||
FULL JOIN orders o ON u.id = o.user_id;
|
||||
|
||||
-- CROSS JOIN
|
||||
SELECT u.name, p.name
|
||||
FROM users u
|
||||
CROSS JOIN products p;
|
||||
|
||||
-- Множество JOINs
|
||||
SELECT u.name, o.id, p.name
|
||||
FROM orders o
|
||||
JOIN users u ON o.user_id = u.id
|
||||
JOIN products p ON o.product_id = p.id;
|
||||
|
||||
-- Self JOIN
|
||||
SELECT e.name, m.name as manager
|
||||
FROM employees e
|
||||
JOIN employees m ON e.manager_id = m.id;
|
||||
```
|
||||
|
||||
## CTEs (Common Table Expressions)
|
||||
|
||||
```sql
|
||||
-- Единичен CTE
|
||||
WITH active_users AS (
|
||||
SELECT * FROM users WHERE active = true
|
||||
)
|
||||
SELECT * FROM active_users;
|
||||
|
||||
-- Множество CTEs
|
||||
WITH
|
||||
recent AS (
|
||||
SELECT * FROM orders WHERE date > '2025-01-01'
|
||||
),
|
||||
totals AS (
|
||||
SELECT user_id, sum(amount) as total FROM recent GROUP BY user_id
|
||||
)
|
||||
SELECT u.name, t.total
|
||||
FROM users u
|
||||
JOIN totals t ON u.id = t.user_id;
|
||||
|
||||
-- Рекурсивен CTE
|
||||
WITH RECURSIVE subordinates AS (
|
||||
SELECT id, name, manager_id FROM employees WHERE name = 'CEO'
|
||||
UNION ALL
|
||||
SELECT e.id, e.name, e.manager_id
|
||||
FROM employees e
|
||||
JOIN subordinates s ON e.manager_id = s.id
|
||||
)
|
||||
SELECT * FROM subordinates;
|
||||
```
|
||||
|
||||
## Подзаявки
|
||||
|
||||
```sql
|
||||
-- Подзаявка в SELECT
|
||||
SELECT name, (SELECT count(*) FROM orders WHERE user_id = u.id) as order_count
|
||||
FROM users u;
|
||||
|
||||
-- Подзаявка в FROM
|
||||
SELECT * FROM (SELECT id, name FROM users WHERE active = true) AS active;
|
||||
|
||||
-- Подзаявка в WHERE (IN)
|
||||
SELECT name FROM users WHERE id IN (SELECT user_id FROM orders);
|
||||
|
||||
-- Подзаявка в WHERE (EXISTS)
|
||||
SELECT name FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);
|
||||
|
||||
-- Корелирана подзаявка
|
||||
SELECT name FROM users u
|
||||
WHERE age > (SELECT avg(age) FROM users WHERE department = u.department);
|
||||
```
|
||||
|
||||
## CASE Изрази
|
||||
@@ -65,54 +256,114 @@ SELECT * FROM active_users;
|
||||
```sql
|
||||
SELECT name,
|
||||
CASE
|
||||
WHEN age < 18 THEN 'minor'
|
||||
WHEN age < 13 THEN 'child'
|
||||
WHEN age < 20 THEN 'teenager'
|
||||
WHEN age < 65 THEN 'adult'
|
||||
ELSE 'senior'
|
||||
END AS category
|
||||
FROM users;
|
||||
|
||||
-- Прост CASE
|
||||
SELECT name,
|
||||
CASE department
|
||||
WHEN 'Engineering' THEN 'Tech'
|
||||
WHEN 'Sales' THEN 'Revenue'
|
||||
ELSE 'Other'
|
||||
END AS division
|
||||
FROM employees;
|
||||
```
|
||||
|
||||
## Схема
|
||||
## Set Операции
|
||||
|
||||
```sql
|
||||
-- UNION (различни)
|
||||
SELECT name FROM customers
|
||||
UNION
|
||||
SELECT name FROM suppliers;
|
||||
|
||||
-- UNION ALL (с дубликати)
|
||||
SELECT name FROM customers
|
||||
UNION ALL
|
||||
SELECT name FROM suppliers;
|
||||
|
||||
-- INTERSECT
|
||||
SELECT name FROM customers
|
||||
INTERSECT
|
||||
SELECT name FROM suppliers;
|
||||
|
||||
-- EXCEPT
|
||||
SELECT name FROM customers
|
||||
EXCEPT
|
||||
SELECT name FROM suppliers;
|
||||
```
|
||||
|
||||
## Дефиниране на Схема
|
||||
|
||||
### CREATE TYPE
|
||||
|
||||
```sql
|
||||
CREATE TYPE Person {
|
||||
name: str,
|
||||
age: int32
|
||||
};
|
||||
```
|
||||
|
||||
## Векторно Търсене
|
||||
|
||||
```sql
|
||||
INSERT articles {
|
||||
title := 'Nim Programming',
|
||||
embedding := [0.1, 0.2, 0.3, ...]
|
||||
-- Със задължителни полета
|
||||
CREATE TYPE User {
|
||||
email: str REQUIRED,
|
||||
name: str,
|
||||
age: int32,
|
||||
created_at: datetime DEFAULT now()
|
||||
};
|
||||
|
||||
SELECT title FROM articles
|
||||
ORDER BY cosine_distance(embedding, [0.1, 0.2, 0.3, ...])
|
||||
LIMIT 5;
|
||||
-- С връзки
|
||||
CREATE TYPE Movie {
|
||||
title: str,
|
||||
year: int32,
|
||||
director: Person
|
||||
};
|
||||
|
||||
-- С изчислими свойства
|
||||
CREATE TYPE Employee {
|
||||
name: str,
|
||||
base_salary: float64,
|
||||
bonus: float64,
|
||||
total_compensation: float64 COMPUTED (base_salary + bonus)
|
||||
};
|
||||
```
|
||||
|
||||
## Графични Шаблони
|
||||
### Наследяване
|
||||
|
||||
```sql
|
||||
MATCH (p:Person)-[:KNOWS]->(friend:Person)
|
||||
WHERE p.name = 'Alice'
|
||||
RETURN friend.name;
|
||||
CREATE TYPE Animal {
|
||||
name: str
|
||||
};
|
||||
|
||||
CREATE TYPE Dog EXTENDING Animal {
|
||||
breed: str
|
||||
};
|
||||
|
||||
CREATE TYPE Cat EXTENDING Animal {
|
||||
indoor: bool
|
||||
};
|
||||
```
|
||||
|
||||
## Пълнотекстово Търсене
|
||||
### Индекси
|
||||
|
||||
```sql
|
||||
-- Създаване на FTS индекс
|
||||
CREATE INDEX idx_fts ON articles(body) USING FTS;
|
||||
|
||||
-- Търсене с BM25 ранжиране
|
||||
SELECT * FROM articles WHERE body @@ 'database programming';
|
||||
CREATE INDEX idx_users_name ON users(name);
|
||||
CREATE UNIQUE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_age ON users(age) USING btree;
|
||||
CREATE INDEX idx_vectors ON items(embedding) USING hnsw;
|
||||
```
|
||||
|
||||
## JSON Оператори
|
||||
### DROP
|
||||
|
||||
```sql
|
||||
DROP TYPE User;
|
||||
DROP INDEX idx_users_name;
|
||||
```
|
||||
|
||||
### JSON Оператори за Път
|
||||
|
||||
```sql
|
||||
-- Извличане на JSON поле като JSON
|
||||
@@ -122,16 +373,292 @@ SELECT data->'name' FROM users;
|
||||
SELECT data->>'name' FROM users;
|
||||
```
|
||||
|
||||
## Set Операции
|
||||
### Пълнотекстово Търсене (SQL)
|
||||
|
||||
```sql
|
||||
SELECT name FROM customers
|
||||
UNION ALL
|
||||
SELECT name FROM suppliers;
|
||||
-- Създаване на FTS индекс с BM25
|
||||
CREATE INDEX idx_fts ON articles(body) USING FTS;
|
||||
|
||||
-- Търсене с BM25 ранжиране
|
||||
SELECT * FROM articles WHERE body @@ 'machine learning';
|
||||
```
|
||||
|
||||
## Възстановяване до Момент във Времето
|
||||
### Възстановяване до Момент във Времето
|
||||
|
||||
```sql
|
||||
RECOVER TO TIMESTAMP '2026-05-07T12:00:00';
|
||||
```
|
||||
```
|
||||
|
||||
## Векторно Търсене (SQL)
|
||||
|
||||
### Създаване на Векторни Колони
|
||||
|
||||
```sql
|
||||
CREATE TABLE items (
|
||||
id INT PRIMARY KEY,
|
||||
embedding VECTOR(768)
|
||||
);
|
||||
```
|
||||
|
||||
### Вмъкване на Вектори
|
||||
|
||||
```sql
|
||||
INSERT INTO items (id, embedding) VALUES (1, '[0.1, 0.2, 0.3, 0.4]');
|
||||
```
|
||||
|
||||
### Функции за Разстояние
|
||||
|
||||
```sql
|
||||
-- Косинусово разстояние (0 = идентични, 2 = противоположни)
|
||||
SELECT id, cosine_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
|
||||
FROM items;
|
||||
|
||||
-- Евклидово / L2 разстояние
|
||||
SELECT id, euclidean_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
|
||||
FROM items;
|
||||
|
||||
-- L2 разстояние с <-> оператор
|
||||
SELECT id, embedding <-> '[0.1, 0.2, 0.3, 0.4]' AS dist
|
||||
FROM items;
|
||||
|
||||
-- Скаларно произведение (отрицателно dot product)
|
||||
SELECT id, inner_product(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
|
||||
FROM items;
|
||||
|
||||
-- Манхатън / L1 разстояние
|
||||
SELECT id, l1_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') AS dist
|
||||
FROM items;
|
||||
```
|
||||
|
||||
### Търсене на Най-близки Съседи
|
||||
|
||||
```sql
|
||||
-- Топ-10 най-близки съседи по косинусово разстояние
|
||||
SELECT id FROM items
|
||||
ORDER BY cosine_distance(embedding, '[0.1, 0.2, 0.3, 0.4]') ASC
|
||||
LIMIT 10;
|
||||
|
||||
-- Топ-5 най-близки съседи по евклидово разстояние
|
||||
SELECT id FROM items
|
||||
ORDER BY embedding <-> '[0.1, 0.2, 0.3, 0.4]'
|
||||
LIMIT 5;
|
||||
|
||||
-- С филтър по метаданни
|
||||
SELECT id FROM items
|
||||
WHERE category = 'tech'
|
||||
ORDER BY cosine_distance(embedding, '[0.1, 0.2, 0.3, 0.4]')
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
### Векторни Индекси
|
||||
|
||||
```sql
|
||||
-- Създаване на HNSW индекс за приблизително търсене на най-близки съседи
|
||||
CREATE INDEX idx_items_vec ON items(embedding) USING hnsw;
|
||||
|
||||
-- Поддържани индекс методи: hnsw, ivfpq
|
||||
```
|
||||
|
||||
## Графични Шаблони
|
||||
|
||||
```sql
|
||||
-- Намиране на приятели на Alice
|
||||
MATCH (p:Person)-[:KNOWS]->(friend:Person)
|
||||
WHERE p.name = 'Alice'
|
||||
RETURN friend.name;
|
||||
|
||||
-- Намиране на най-кратък път
|
||||
MATCH path = shortestPath((a:Person)-[:KNOWS*1..5]->(b:Person))
|
||||
WHERE a.name = 'Alice' AND b.name = 'Bob'
|
||||
RETURN path;
|
||||
|
||||
-- Намиране на всички връзки
|
||||
MATCH (p:Person)-[r]->(other)
|
||||
WHERE p.name = 'Alice'
|
||||
RETURN type(r), other.name;
|
||||
|
||||
-- Множество преходи
|
||||
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)
|
||||
WHERE a.name = 'Alice'
|
||||
RETURN c.name;
|
||||
|
||||
-- С агрегати
|
||||
MATCH (p:Person)-[:KNOWS]->(friend)
|
||||
RETURN p.name, count(friend) as friend_count
|
||||
ORDER BY friend_count DESC;
|
||||
```
|
||||
|
||||
## Пълнотекстово Търсене
|
||||
|
||||
```sql
|
||||
-- Основно търсене
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('database programming');
|
||||
|
||||
-- С релевантност
|
||||
SELECT title, relevance()
|
||||
FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('Nim language')
|
||||
ORDER BY relevance() DESC;
|
||||
|
||||
-- Булев режим
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('+Nim -Python' IN BOOLEAN MODE);
|
||||
|
||||
-- Fuzzy търсене
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title) AGAINST('programing' WITH FUZZINESS 2);
|
||||
```
|
||||
|
||||
## Транзакции
|
||||
|
||||
```sql
|
||||
BEGIN;
|
||||
INSERT users { name := 'Alice', age := 30 };
|
||||
INSERT orders { user_id := last_insert_id(), total := 100 };
|
||||
COMMIT;
|
||||
|
||||
-- С savepoint
|
||||
BEGIN;
|
||||
INSERT users { name := 'Bob', age := 25 };
|
||||
SAVEPOINT sp1;
|
||||
INSERT orders { user_id := last_insert_id(), total := 200 };
|
||||
-- Грешка, връщане до savepoint
|
||||
ROLLBACK TO sp1;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
## Потребителски Функции (UDF)
|
||||
|
||||
```sql
|
||||
-- Регистриране на UDF
|
||||
CREATE FUNCTION greet(name str) -> str {
|
||||
RETURN 'Hello, ' || name || '!';
|
||||
};
|
||||
|
||||
-- Използване
|
||||
SELECT greet(name) FROM users;
|
||||
|
||||
-- Вградени функции
|
||||
SELECT abs(-5), sqrt(16), lower('HELLO'), len('test');
|
||||
```
|
||||
|
||||
## Подсказки за Заявки (Query Hints)
|
||||
|
||||
```sql
|
||||
-- Форсиране на индекс
|
||||
SELECT /*+ USE_INDEX(idx_users_age) */ * FROM users WHERE age > 18;
|
||||
|
||||
-- Форсиране на приблизително векторно търсене
|
||||
SELECT /*+ APPROXIMATE */ * FROM vectors
|
||||
ORDER BY cosine_distance(embedding, [...])
|
||||
LIMIT 10;
|
||||
|
||||
-- Паралелно изпълнение
|
||||
SELECT /*+ PARALLEL(4) */ * FROM large_table;
|
||||
```
|
||||
|
||||
## Window Функции
|
||||
|
||||
```sql
|
||||
-- Функции за ранжиране
|
||||
SELECT
|
||||
name,
|
||||
department,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
|
||||
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
|
||||
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
|
||||
FROM employees;
|
||||
|
||||
-- Стойностни функции
|
||||
SELECT
|
||||
name,
|
||||
salary,
|
||||
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
|
||||
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
|
||||
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
|
||||
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
|
||||
FROM employees;
|
||||
|
||||
-- Функции за разпределение
|
||||
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
|
||||
```
|
||||
|
||||
### Рамкови Спецификации
|
||||
|
||||
```sql
|
||||
-- ROWS рамка
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
|
||||
)
|
||||
|
||||
-- RANGE рамка
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
```
|
||||
|
||||
## Multi-Tenant ERP
|
||||
|
||||
BaraDB поддържа множество компании (тенанти) в една инстанция чрез **Row-Level Security (RLS)** и **сесийни променливи**.
|
||||
|
||||
### Сесийни Променливи
|
||||
|
||||
```sql
|
||||
SET app.tenant_id = 'company-123';
|
||||
SELECT current_setting('app.tenant_id') AS tenant;
|
||||
```
|
||||
|
||||
### Текущ Потребител / Роля
|
||||
|
||||
```sql
|
||||
SELECT current_user AS me, current_role AS my_role;
|
||||
```
|
||||
|
||||
### RLS Изолация на Тенанти
|
||||
|
||||
```sql
|
||||
-- Включване на RLS за таблица
|
||||
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Създаване на политика за филтриране по тенант
|
||||
CREATE POLICY tenant_isolation ON invoices
|
||||
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
|
||||
|
||||
-- Всяка сесия вижда само своите данни
|
||||
SET app.tenant_id = 'company-a';
|
||||
SELECT * FROM invoices; -- само редове на company-a
|
||||
```
|
||||
|
||||
### Защо Multi-Tenant?
|
||||
|
||||
- **Една инстанция, много тенанти** — няма нужда от 100 отделни бази данни
|
||||
- **JSONB документи** — гъвкаво съхранение без схема, лесно добавяне на полета за всеки тенант
|
||||
- **RLS гарантира изолация** — базата данни налага границите между тенанти, не само приложението
|
||||
|
||||
## Поддържани Ключови Думи
|
||||
|
||||
| Категория | Ключови думи |
|
||||
|-----------|-------------|
|
||||
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
|
||||
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
|
||||
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
|
||||
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
|
||||
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
|
||||
| CTEs | WITH, RECURSIVE, AS |
|
||||
| Case | CASE, WHEN, THEN, ELSE, END |
|
||||
| Транзакции | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
|
||||
| Графи | MATCH, RETURN, WHERE, shortestPath, type |
|
||||
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
|
||||
| Вектори | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
|
||||
| JSON | ->, ->> |
|
||||
| FTS | @@ (BM25 съвпадение) |
|
||||
| Recovery | RECOVER TO TIMESTAMP |
|
||||
| Функции | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
|
||||
| Сесийни | SET, current_setting, current_user, current_role |
|
||||
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
|
||||
| Window Функции | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
# Списък с Промени (Changelog)
|
||||
|
||||
Всички забележителни промени в BaraDB са документирани в този файл.
|
||||
|
||||
## [Unreleased] — SQL:2023 Стабилизация
|
||||
|
||||
### Поправки
|
||||
|
||||
- **GROUPING SETS изпълнение** — `lowerSelect` вече създава `irpkGroupBy` когато `selGroupingSetsKind != gskNone`, дори ако `selGroupBy` е празен. Преди това заявки като `GROUP BY GROUPING SETS ((dept), ())` напълно заобикаляха grouping executor-a.
|
||||
- **FTS CREATE INDEX docId несъответствие** — `CREATE INDEX ... USING FTS` вече изчислява `docId` като хеш на `tableName.$key`, консистентно с DML операциите (`INSERT`/`UPDATE`/`DELETE`). Преди това създаването на индекс използваше последователни ID-та (0, 1, 2...), което причиняваше `@@` заявките никога да не намират индексирани документи.
|
||||
- **Тестова изолация (всички сюити)** — Всички извиквания на `newLSMTree("")` са заменени с уникални временни директории за всеки сюит. Елиминира проблеми с натрупване на WAL и нестабилни тестове от споделено състояние между тестове.
|
||||
- **Window frame parser** — `parseFrameBoundary` вече не консумира `tkRow` след `tkCurrent` неправилно (използваше `tkRows`). Също така е поправен конфликт на ключовата дума `tkRow` с парсването на `ENABLE ROW LEVEL SECURITY`.
|
||||
- **ORDER BY + SELECT проекция** — `lowerSelect` вече поставя `irpkSort` преди `irpkProject`, което позволява `ORDER BY` по колони, които не присъстват в `SELECT` списъка.
|
||||
- **UNPIVOT изпълнение** — Проверено и поправено липсващо тестово покритие за UNPIVOT трансформация.
|
||||
|
||||
### Добавки
|
||||
|
||||
- **JSON оператори** — `@>` (съдържа), `<@` (съдържа се в), `?` (има ключ), `?|` (има някой от), `?&` (има всички) вече се поддържат в lexer, parser и executor.
|
||||
- **Window frame изпълнение** — `ROWS BETWEEN X PRECEDING AND Y FOLLOWING` / `CURRENT ROW` граници на рамката вече се спазват от `FIRST_VALUE` и `LAST_VALUE`.
|
||||
- **Сесийни променливи** — `SET var_name = value` и `current_setting('var_name')` за ключ/стойност съхранение на ниво връзка.
|
||||
- **Текущ потребител/роля** — `current_user` и `current_role` SQL ключови думи връщат потребителя и ролята на автентикираната сесия.
|
||||
- **Auth-executor мост** — Сървърът и HTTP сървърът вече попълват `ExecutionContext.currentUser` и `ExecutionContext.currentRole` след JWT/SCRAM автентикация.
|
||||
- **Multi-tenant RLS** — Row-Level Security политиките вече могат да реферират `current_user`, `current_role` и `current_setting('app.tenant_id')` за изолация на данни по тенант.
|
||||
|
||||
## [1.1.0] — 2026-05-13
|
||||
|
||||
### Добавки
|
||||
|
||||
- **Client SDKs v1.1.0** — Пълнофункционални клиенти за всички езици:
|
||||
- JavaScript: TypeScript дефиниции, package.json, примери, unit и integration тестове
|
||||
- Python: Преструктуриран като пакет (`baradb/` с `__init__.py` и `core.py`), pyproject.toml, примери, тестове (query builder, wire protocol, integration)
|
||||
- Nim: Примери, integration тестове, README
|
||||
- Rust: Примери, integration тестове, подобрен Cargo.toml
|
||||
- **SCRAM-SHA-256 Автентикация** — RFC 7677 съвместима автентикация с PBKDF2 + HMAC + SHA-256 + nonce/salt генериране
|
||||
- **HTTP SCRAM Endpoints** — `/auth/scram/start` + `/auth/scram/finish` в HTTP сървъра
|
||||
- **Docker Compose Тестова Конфигурация** — `docker-compose.test.yml` за тестови среди
|
||||
- **CI/CD Clients Pipeline** — `.github/workflows/clients-ci.yml` за автоматизирано тестване на клиенти
|
||||
|
||||
### Поправки
|
||||
|
||||
- **Query Executor** — Унарен минус (`irNeg`) вече работи коректно в SELECT и WHERE клаузи
|
||||
- **Distributed Transactions** — Rollback след commit опит вече не нарушава атомарността
|
||||
- **Sharding** — Протокол за миграция на данни с TCP + `scanAll` на LSM
|
||||
- **Raft** — Поправено изчисление на мнозинство за четен брой нодове
|
||||
- **MVCC** — Прекъснатите транзакции вече не стават видими
|
||||
- **LSM-Tree** — Поправена загуба на данни при презаписване на immutable memtable; поправено сортиране на SSTable търсене
|
||||
- **Auth** — JWT подписът е променен на HMAC-SHA256 (вече не е тривиално forgeable); валидация на токен изтичане (`exp`/`nbf`/`iat`); сравнението на подписи вече е constant-time
|
||||
- **Recovery** — `summary()` вече не мутира базата данни
|
||||
- **Wire Protocol** — 64MB лимит + bounds проверки + max дълбочина за предотвратяване на OOM/DoS
|
||||
- **SQL Injection** — `exprToSql` вече escape-ва единични кавички
|
||||
- **ReDoS** — `irLike`/`irILike` вече escape-ват regex метасимволи
|
||||
- **Graph** — `addEdge` вече проверява съществуването на възел
|
||||
- **Vector** — Валидация на несъответствие на размерности + HNSW заключване
|
||||
- **FTS** — UTF-8 токенизацията вече използва runes вместо байтове
|
||||
- **Build** — `nim.cfg` добавя `-d:ssl`, така че `nimble build` работи без флагове; `--threads:on` добавен към всички CI команди
|
||||
|
||||
### Промени
|
||||
|
||||
- **Версията е вдигната до 1.1.0** във всички компоненти (сървър, Docker изображения, клиенти, CLI)
|
||||
- **README** — Версионният badge е обновен; всички feature таблици вече реферират v1.1.0
|
||||
- **TLA+ Формална Верификация** — Добавени `crossmodal.tla`, `backup.tla`, `recovery.tla`; symmetry reduction във всички 9 спецификации
|
||||
- **Чист build** — 0 компилаторни предупреждения на Nim 2.2.10
|
||||
|
||||
## [0.1.0] — 2025-01-15
|
||||
|
||||
### Добавки
|
||||
|
||||
- **Ядро за Съхранение**
|
||||
- LSM-Tree с MemTable, WAL, SSTables и size-tiered compaction
|
||||
- B-Tree подреден индекс с range сканиране и MVCC copy-on-write
|
||||
- Bloom филтри за ефективно пропускане на SSTable
|
||||
- Memory-mapped I/O за SSTable четене
|
||||
- LRU page cache с проследяване на hit rate
|
||||
|
||||
- **Query Engine (BaraQL)**
|
||||
- SQL-съвместим lexer с 80+ типа токени
|
||||
- Recursive descent parser генериращ AST с 25+ вида възли
|
||||
- Intermediate representation (IR) за планове за изпълнение
|
||||
- Code generator превеждащ IR към storage операции
|
||||
- Adaptive query optimizer с cross-modal планиране
|
||||
- Query executor с паралелизация
|
||||
|
||||
- **BaraQL Езикови Възможности**
|
||||
- SELECT, INSERT, UPDATE, DELETE
|
||||
- WHERE, ORDER BY, LIMIT, OFFSET
|
||||
- GROUP BY, HAVING, агрегатни функции (count, sum, avg, min, max)
|
||||
- INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN
|
||||
- CTEs (Common Table Expressions) с WITH
|
||||
- Подзаявки (EXISTS, IN, корелирани)
|
||||
- CASE изрази
|
||||
- UNION, INTERSECT, EXCEPT
|
||||
- Дефиниране на схема: CREATE TYPE, DROP TYPE
|
||||
|
||||
- **Vector Engine**
|
||||
- HNSW индекс за приблизително търсене на най-близки съседи
|
||||
- IVF-PQ индекс за мащабно векторно търсене
|
||||
- SIMD-оптимизирани функции за разстояние (cosine, L2, dot product, Manhattan)
|
||||
- Квантуване: scalar 8-bit/4-bit, product quantization, binary
|
||||
- Филтриране по метаданни при векторно търсене
|
||||
|
||||
- **Graph Engine**
|
||||
- Adjacency list съхранение за насочени графи с тегла
|
||||
- BFS и DFS обхождане
|
||||
- Dijkstra най-кратък път
|
||||
- PageRank важност на възли
|
||||
- Louvain community detection
|
||||
- Subgraph pattern matching
|
||||
- Cypher-подобен graph query parser
|
||||
|
||||
- **Full-Text Search**
|
||||
- Inverted index с term-document mapping
|
||||
- BM25 алгоритъм за ранжиране
|
||||
- TF-IDF оценяване
|
||||
- Fuzzy търсене с Levenshtein разстояние
|
||||
- Wildcard/regex търсене
|
||||
- Многоезични токенизатори (английски, български, немски, френски, руски)
|
||||
|
||||
- **Columnar Storage**
|
||||
- Колонково съхранение за аналитични заявки
|
||||
- RLE (Run-Length Encoding) компресия
|
||||
- Dictionary encoding за колони с ниска кардиналност
|
||||
- SIMD-ускорени агрегати
|
||||
|
||||
- **Транзакции**
|
||||
- MVCC (Multi-Version Concurrency Control) със snapshot изолация
|
||||
- Deadlock детекция чрез wait-for граф
|
||||
- Write-ahead log за устойчивост
|
||||
- Savepoints и частичен rollback
|
||||
|
||||
- **Протоколен Слой**
|
||||
- Бинарен wire протокол с 16 типа съобщения
|
||||
- HTTP/REST JSON API
|
||||
- WebSocket стрийминг
|
||||
- Connection pooling
|
||||
- JWT-базирана автентикация
|
||||
- Token-bucket rate limiting
|
||||
- TLS/SSL с автоматично генерирани сертификати
|
||||
|
||||
- **Система за Схеми**
|
||||
- Силна типова система с 17 нативни типа
|
||||
- Наследяване на типове с multi-base поддръжка
|
||||
- Property links между типове
|
||||
- Schema diffing и миграции
|
||||
- Изчислими свойства
|
||||
|
||||
- **Разпределени Системи**
|
||||
- Raft консенсус (leader election, log replication)
|
||||
- Hash, range и consistent-hash шардиране
|
||||
- Sync/async/semi-sync репликация
|
||||
- Gossip протокол за управление на членство
|
||||
- Two-phase commit за разпределени транзакции
|
||||
|
||||
- **Cross-Modal Заявки**
|
||||
- Унифициран език за заявки през всички storage двигатели
|
||||
- Cross-engine predicate pushdown
|
||||
- Оптимизирани планове за изпълнение за multi-modal заявки
|
||||
|
||||
- **Backup & Recovery**
|
||||
- Online snapshots без прекъсване
|
||||
- Point-in-time recovery чрез WAL replay
|
||||
- Инкрементални backups
|
||||
|
||||
- **Client SDKs**
|
||||
- JavaScript/TypeScript клиент с бинарен протокол
|
||||
- Python клиент със sync и async API
|
||||
- Nim embedded режим и клиентска библиотека
|
||||
- Rust клиент (async)
|
||||
|
||||
- **Операции**
|
||||
- Интерактивен CLI shell (BaraQL REPL)
|
||||
- Структурирано логване (JSON и текстови формати)
|
||||
- Prometheus-съвместим metrics endpoint
|
||||
- Health и readiness проби
|
||||
- CPU/memory profiling endpoints
|
||||
|
||||
- **Docker Поддръжка**
|
||||
- Multi-stage Dockerfile (Alpine Linux)
|
||||
- Docker Compose конфигурация
|
||||
- Health checks
|
||||
|
||||
### Производителност
|
||||
|
||||
- LSM-Tree: 580K записа/s, 720K четения/s
|
||||
- B-Tree: 1.2M вмъквания/s, 1.5M търсения/s
|
||||
- Vector SIMD: 850K косинусови разстояния/s (dim=768)
|
||||
- FTS: 320K документи/s индексиране, 28K заявки/s BM25
|
||||
- Graph: 2.5M възела/s вмъкване, 12K BFS обхождания/s
|
||||
- Бинарен протокол: 380K заявки/s (100 конкурентни връзки)
|
||||
|
||||
### Тестове
|
||||
|
||||
- 262 теста в 56 тестови сюита
|
||||
- 100% успеваемост
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Добавки
|
||||
|
||||
- **Vector SQL Integration** — Пълна поддръжка на векторно търсене на SQL ниво:
|
||||
- `VECTOR(n)` тип колона в `CREATE TABLE` с валидация на размерност
|
||||
- `CREATE INDEX ... USING hnsw` / `USING ivfpq` за приблизителни nearest neighbor индекси
|
||||
- SQL функции за разстояние: `cosine_distance()`, `euclidean_distance()`, `inner_product()`, `l1_distance()`, `l2_distance()`
|
||||
- `<->` nearest-neighbor оператор (евклидово разстояние)
|
||||
- `ORDER BY` поддръжка за изрази с векторно разстояние, включително колони извън `SELECT`
|
||||
- Автоматична поддръжка на HNSW индекс при `INSERT` и `UPDATE`
|
||||
- **Advanced SQL Engine** — Window функции, MERGE/UPSERT, LATERAL JOIN, PIVOT/UNPIVOT, SQL/PGQ Property Graph, Разширени агрегати (ARRAY_AGG, STRING_AGG, FILTER, GROUPING SETS/ROLLUP/CUBE)
|
||||
- **JavaScript Client — TCP Request Queue** — Вътрешна `_requestQueue` + `_requestLock` за безопасни конкурентни заявки. Множество паралелни извиквания на `query()` / `execute()` / `ping()` вече не размесват бинарни frame-ове по връзката.
|
||||
|
||||
### Поправки
|
||||
|
||||
- **Query Executor — Ескейпване на Стойности** — `execInsert` вече правилно ескейпва запетаи и знаци за равенство в стойностите на колоните, поправяйки корупция на съхранението за векторни литерали като `[1.0, 2.0, 3.0]`
|
||||
- **Query Planner — ORDER BY Проекция** — `irpkSort` вече се поставя преди `irpkProject` в IR плана, позволявайки на `ORDER BY` да реферира колони, които не са селектирани
|
||||
- **Wire Protocol — Big-Endian Float Сериализация** — `FLOAT32`/`FLOAT64` и float стойностите във вектори вече се сериализират в big-endian byte order, съвпадайки с `readFloatBE()` / `readDoubleBE()` на клиента и осигурявайки междуплатформена числова точност.
|
||||
- **Gossip Protocol — Async UDP Socket** — Заменен синхронният `newSocket` + блокиращ `recvFrom` с `newAsyncSocket` + `await recvFrom`, предотвратявайки замръзване на async event loop-а до пристигане на UDP пакет.
|
||||
|
||||
### Планирани
|
||||
|
||||
- Query plan caching
|
||||
- Materialized views
|
||||
- Геопространствен индекс
|
||||
- Time-series оптимизации
|
||||
- CDC (Change Data Capture) стрийминг
|
||||
- Федеративни заявки между BaraDB инстанции
|
||||
@@ -0,0 +1,285 @@
|
||||
# Client SDKs
|
||||
|
||||
BaraDB предоставя официални клиентски библиотеки за JavaScript/TypeScript, Python, Nim и Rust.
|
||||
|
||||
## JavaScript / TypeScript
|
||||
|
||||
### Инсталация
|
||||
|
||||
```bash
|
||||
npm install baradb
|
||||
# или
|
||||
yarn add baradb
|
||||
```
|
||||
|
||||
### Основна Употреба
|
||||
|
||||
```typescript
|
||||
import { Client } from 'baradb';
|
||||
|
||||
const client = new Client('localhost', 9472);
|
||||
await client.connect();
|
||||
|
||||
// Проста заявка
|
||||
const result = await client.query('SELECT name, age FROM users WHERE age > 18');
|
||||
console.log(result.rows);
|
||||
|
||||
// Параметризирана заявка
|
||||
const result2 = await client.query(
|
||||
'SELECT * FROM users WHERE name = ?',
|
||||
['Alice']
|
||||
);
|
||||
|
||||
// Batch вмъкване
|
||||
await client.batch([
|
||||
"INSERT users { name := 'Alice', age := 30 }",
|
||||
"INSERT users { name := 'Bob', age := 25 }",
|
||||
]);
|
||||
|
||||
// Транзакции
|
||||
await client.begin();
|
||||
await client.query("INSERT orders { total := 100 }");
|
||||
await client.query("UPDATE users SET balance = balance - 100 WHERE name = 'Alice'");
|
||||
await client.commit();
|
||||
|
||||
await client.close();
|
||||
```
|
||||
|
||||
### Конкурентни Заявки
|
||||
|
||||
JavaScript клиентът автоматично сериализира конкурентни заявки през една TCP връзка чрез вътрешна опашка. Можете безопасно да изпращате множество паралелни операции — техните бинарни frame-ове няма да се размесят:
|
||||
|
||||
```typescript
|
||||
const [users, orders, stats] = await Promise.all([
|
||||
client.query('SELECT * FROM users'),
|
||||
client.query('SELECT * FROM orders'),
|
||||
client.query('SELECT count(*) FROM visits')
|
||||
]);
|
||||
```
|
||||
|
||||
### WebSocket Стрийминг
|
||||
|
||||
```typescript
|
||||
import { WebSocketClient } from 'baradb/ws';
|
||||
|
||||
const ws = new WebSocketClient('ws://localhost:9471');
|
||||
ws.onMessage = (data) => console.log(data);
|
||||
await ws.connect();
|
||||
await ws.send('SUBSCRIBE updates');
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
### Инсталация
|
||||
|
||||
```bash
|
||||
pip install baradb
|
||||
```
|
||||
|
||||
### Основна Употреба
|
||||
|
||||
```python
|
||||
from baradb import Client
|
||||
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
|
||||
# Проста заявка
|
||||
result = client.query("SELECT name, age FROM users WHERE age > 18")
|
||||
for row in result:
|
||||
print(row["name"], row["age"])
|
||||
|
||||
# Параметризирана заявка
|
||||
result = client.query(
|
||||
"SELECT * FROM users WHERE name = ?",
|
||||
["Alice"]
|
||||
)
|
||||
|
||||
# Batch операции
|
||||
client.batch([
|
||||
"INSERT users { name := 'Alice', age := 30 }",
|
||||
"INSERT users { name := 'Bob', age := 25 }",
|
||||
])
|
||||
|
||||
# Context manager (автоматично затваряне)
|
||||
with Client("localhost", 9472) as c:
|
||||
result = c.query("SELECT count(*) FROM users")
|
||||
print(result[0]["count"])
|
||||
```
|
||||
|
||||
### Async Клиент
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from baradb import AsyncClient
|
||||
|
||||
async def main():
|
||||
client = AsyncClient("localhost", 9472)
|
||||
await client.connect()
|
||||
result = await client.query("SELECT * FROM users")
|
||||
print(result.rows)
|
||||
await client.close()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Nim (Вграден Режим)
|
||||
|
||||
### Добавяне на Зависимост
|
||||
|
||||
```nim
|
||||
# Във вашия .nimble файл
|
||||
requires "barabadb >= 1.1.0"
|
||||
```
|
||||
|
||||
### Вградена Употреба
|
||||
|
||||
```nim
|
||||
import barabadb/storage/lsm
|
||||
import barabadb/storage/btree
|
||||
import barabadb/vector/engine
|
||||
import barabadb/graph/engine
|
||||
|
||||
# Key-Value store
|
||||
var db = newLSMTree("./data")
|
||||
db.put("user:1", cast[seq[byte]]("Alice"))
|
||||
let (found, value) = db.get("user:1")
|
||||
db.close()
|
||||
|
||||
# B-Tree индекс
|
||||
var btree = newBTreeIndex[string, int]()
|
||||
btree.insert("Alice", 30)
|
||||
let ages = btree.get("Alice")
|
||||
|
||||
# Векторно търсене
|
||||
var idx = newHNSWIndex(dimensions = 128)
|
||||
idx.insert(1, @[0.1'f32, 0.2, 0.3], {"category": "A"}.toTable)
|
||||
let results = idx.search(@[0.1'f32, 0.2, 0.3], k = 10)
|
||||
|
||||
# Графи
|
||||
var g = newGraph()
|
||||
let alice = g.addNode("Person", {"name": "Alice"}.toTable)
|
||||
let bob = g.addNode("Person", {"name": "Bob"}.toTable)
|
||||
discard g.addEdge(alice, bob, "knows")
|
||||
let path = g.shortestPath(alice, bob)
|
||||
```
|
||||
|
||||
### Клиентска Библиотека
|
||||
|
||||
```nim
|
||||
import barabadb/client/client
|
||||
|
||||
var c = newBaraClient("localhost", 9472)
|
||||
c.connect()
|
||||
let result = c.query("SELECT name FROM users")
|
||||
for row in result.rows:
|
||||
echo row["name"]
|
||||
c.close()
|
||||
```
|
||||
|
||||
## Rust
|
||||
|
||||
### Добавяне на Зависимост
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
baradb = "0.1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
```
|
||||
|
||||
### Основна Употреба
|
||||
|
||||
```rust
|
||||
use baradb::Client;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut client = Client::connect("localhost:9472").await?;
|
||||
|
||||
let result = client
|
||||
.query("SELECT name, age FROM users WHERE age > 18")
|
||||
.await?;
|
||||
|
||||
for row in result.rows {
|
||||
println!("{} is {} years old", row["name"], row["age"]);
|
||||
}
|
||||
|
||||
client.close().await?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## HTTP/REST (Езиково Независим)
|
||||
|
||||
Всички езици могат да използват HTTP/REST API директно:
|
||||
|
||||
```bash
|
||||
# Заявка
|
||||
curl -X POST http://localhost:9470/api/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d '{"query": "SELECT * FROM users WHERE age > 18"}'
|
||||
|
||||
# Вмъкване
|
||||
curl -X POST http://localhost:9470/api/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "INSERT users { name := \"Alice\", age := 30 }"}'
|
||||
|
||||
# Схема
|
||||
curl http://localhost:9470/api/schema
|
||||
|
||||
# Health
|
||||
curl http://localhost:9470/health
|
||||
|
||||
# Метрики
|
||||
curl http://localhost:9470/metrics
|
||||
```
|
||||
|
||||
## Connection Pooling
|
||||
|
||||
Всички официални клиенти поддържат connection pooling:
|
||||
|
||||
### JavaScript
|
||||
|
||||
```typescript
|
||||
import { Pool } from 'baradb';
|
||||
|
||||
const pool = new Pool({
|
||||
host: 'localhost',
|
||||
port: 9472,
|
||||
min: 5,
|
||||
max: 50,
|
||||
idleTimeout: 30000,
|
||||
});
|
||||
|
||||
const client = await pool.acquire();
|
||||
try {
|
||||
const result = await client.query('SELECT 1');
|
||||
} finally {
|
||||
pool.release(client);
|
||||
}
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
from baradb import Pool
|
||||
|
||||
pool = Pool("localhost", 9472, min_size=5, max_size=50)
|
||||
with pool.connection() as conn:
|
||||
result = conn.query("SELECT 1")
|
||||
```
|
||||
|
||||
## Съответствие на Типове Данни
|
||||
|
||||
| BaraDB Тип | JavaScript | Python | Nim | Rust |
|
||||
|------------|------------|--------|-----|------|
|
||||
| `null` | `null` | `None` | `nil` | `Option::None` |
|
||||
| `bool` | `boolean` | `bool` | `bool` | `bool` |
|
||||
| `int8/16/32/64` | `number` | `int` | `int` | `i8/i16/i32/i64` |
|
||||
| `float32/64` | `number` | `float` | `float32/float64` | `f32/f64` |
|
||||
| `str` | `string` | `str` | `string` | `String` |
|
||||
| `bytes` | `Uint8Array` | `bytes` | `seq[byte]` | `Vec<u8>` |
|
||||
| `array` | `Array` | `list` | `seq` | `Vec` |
|
||||
| `object` | `Object` | `dict` | `Table` | `HashMap` |
|
||||
| `vector` | `Float32Array` | `list[float]` | `seq[float32]` | `Vec<f32>` |
|
||||
@@ -0,0 +1,58 @@
|
||||
# Колонково Съхранение (Columnar)
|
||||
|
||||
Колонково-ориентирано съхранение за аналитични заявки и агрегации.
|
||||
|
||||
## Употреба
|
||||
|
||||
```nim
|
||||
import barabadb/core/columnar
|
||||
|
||||
var batch = newColumnBatch()
|
||||
var ageCol = batch.addInt64Col("age")
|
||||
var nameCol = batch.addStringCol("name")
|
||||
|
||||
ageCol.appendInt64(25)
|
||||
nameCol.appendString("Alice")
|
||||
```
|
||||
|
||||
## Агрегации
|
||||
|
||||
```nim
|
||||
echo ageCol.sumInt64()
|
||||
echo ageCol.avgInt64()
|
||||
echo ageCol.minInt64()
|
||||
echo ageCol.maxInt64()
|
||||
echo ageCol.count()
|
||||
```
|
||||
|
||||
## Кодиране
|
||||
|
||||
### RLE (Run-Length Encoding)
|
||||
|
||||
```nim
|
||||
let rle = rleEncode(@[1'i64, 1, 1, 2, 2, 3])
|
||||
```
|
||||
|
||||
### Dictionary Encoding
|
||||
|
||||
```nim
|
||||
let dict = dictEncode(@["apple", "banana", "apple"])
|
||||
```
|
||||
|
||||
## Типове Колони
|
||||
|
||||
| Тип | Описание |
|
||||
|------|----------|
|
||||
| `int32` | 32-битов integer |
|
||||
| `int64` | 64-битов integer |
|
||||
| `float32` | 32-битов float |
|
||||
| `float64` | 64-битов float |
|
||||
| `string` | Низ с променлива дължина |
|
||||
| `bool` | Булев |
|
||||
|
||||
## Случаи на Употреба
|
||||
|
||||
- OLAP натоварвания
|
||||
- Мащабни агрегации
|
||||
- Data warehousing
|
||||
- Анализ на времеви редове
|
||||
@@ -0,0 +1,218 @@
|
||||
# Конфигурационна Референция
|
||||
|
||||
BaraDB може да се конфигурира чрез **променливи на средата**, **конфигурационен файл** или **командно-редови флагове**.
|
||||
|
||||
## Ред на Приоритет
|
||||
|
||||
1. Командно-редови флагове (най-висок приоритет)
|
||||
2. Променливи на средата
|
||||
3. Конфигурационен файл (`baradb.conf` или `baradb.json`)
|
||||
4. Вградени стойности по подразбиране (най-нисък приоритет)
|
||||
|
||||
## Променливи на Средата
|
||||
|
||||
### Мрежа
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_ADDRESS` | `127.0.0.1` | Адрес за свързване |
|
||||
| `BARADB_PORT` | `9472` | TCP бинарен протокол порт |
|
||||
| `BARADB_HTTP_PORT` | `9470` | HTTP/REST API порт |
|
||||
| `BARADB_WS_PORT` | `9471` | WebSocket порт |
|
||||
|
||||
### Съхранение
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_DATA_DIR` | `./data` | Път до директория за данни |
|
||||
| `BARADB_MEMTABLE_SIZE_MB` | `64` | Размер на MemTable в MB |
|
||||
| `BARADB_CACHE_SIZE_MB` | `256` | Размер на page cache в MB |
|
||||
| `BARADB_WAL_SYNC_INTERVAL_MS` | `0` | Интервал за WAL fsync (0 = всеки запис) |
|
||||
| `BARADB_COMPACTION_INTERVAL_MS` | `60000` | Интервал за фонов compaction |
|
||||
| `BARADB_BLOOM_BITS_PER_KEY` | `10` | Bloom филтър битове за ключ |
|
||||
|
||||
### TLS/SSL
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_TLS_ENABLED` | `false` | Включване на TLS |
|
||||
| `BARADB_CERT_FILE` | — | Път до TLS сертификат |
|
||||
| `BARADB_KEY_FILE` | — | Път до TLS частен ключ |
|
||||
|
||||
### Сигурност
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_AUTH_ENABLED` | `false` | Включване на автентикация |
|
||||
| `BARADB_JWT_SECRET` | — | JWT подписващ secret |
|
||||
| `BARADB_RATE_LIMIT_GLOBAL` | `10000` | Глобални заявки в секунда |
|
||||
| `BARADB_RATE_LIMIT_PER_CLIENT` | `1000` | Заявки в секунда за клиент |
|
||||
|
||||
### Логване
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_LOG_LEVEL` | `info` | Ниво на логване: debug, info, warn, error |
|
||||
| `BARADB_LOG_FILE` | — | Път до лог файл (stdout ако е празен) |
|
||||
| `BARADB_LOG_FORMAT` | `json` | Формат на лога: json, text |
|
||||
|
||||
### Vector Engine
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_VECTOR_M` | `16` | HNSW `M` параметър |
|
||||
| `BARADB_VECTOR_EF_CONSTRUCTION` | `200` | HNSW `efConstruction` |
|
||||
| `BARADB_VECTOR_EF_SEARCH` | `64` | HNSW `efSearch` |
|
||||
|
||||
### Graph Engine
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_GRAPH_PAGE_RANK_ITERATIONS` | `20` | Брой итерации на PageRank |
|
||||
| `BARADB_GRAPH_PAGE_RANK_DAMPING` | `0.85` | PageRank damping фактор |
|
||||
| `BARADB_GRAPH_LOUVAIN_RESOLUTION` | `1.0` | Louvain резолюционен параметър |
|
||||
|
||||
### Разпределени
|
||||
|
||||
| Променлива | По подр. | Описание |
|
||||
|------------|----------|----------|
|
||||
| `BARADB_RAFT_NODE_ID` | — | Уникално ID на възел в клъстер |
|
||||
| `BARADB_RAFT_PEERS` | — | Списък с адреси на peer-ове, разделени със запетая |
|
||||
| `BARADB_RAFT_PORT` | `9001` | Raft вътрешен комуникационен порт |
|
||||
| `BARADB_SHARD_COUNT` | `1` | Брой шардове |
|
||||
| `BARADB_REPLICATION_FACTOR` | `1` | Фактор на репликация |
|
||||
| `BARADB_SEED_NODES` | — | Gossip seed възли (host:port, разделени със запетая) |
|
||||
|
||||
## Конфигурационен Файл
|
||||
|
||||
### baradb.conf (INI-подобен)
|
||||
|
||||
```ini
|
||||
[server]
|
||||
address = "0.0.0.0"
|
||||
port = 9472
|
||||
http_port = 9470
|
||||
ws_port = 9471
|
||||
|
||||
[storage]
|
||||
data_dir = "/var/lib/baradb"
|
||||
memtable_size_mb = 256
|
||||
cache_size_mb = 512
|
||||
wal_sync_interval_ms = 10
|
||||
compaction_interval_ms = 30000
|
||||
|
||||
[tls]
|
||||
enabled = true
|
||||
cert_file = "/etc/baradb/server.crt"
|
||||
key_file = "/etc/baradb/server.key"
|
||||
|
||||
[auth]
|
||||
enabled = true
|
||||
jwt_secret = "change-me-in-production"
|
||||
rate_limit_global = 10000
|
||||
rate_limit_per_client = 1000
|
||||
|
||||
[logging]
|
||||
level = "info"
|
||||
format = "json"
|
||||
file = "/var/log/baradb/baradb.log"
|
||||
|
||||
[vector]
|
||||
m = 16
|
||||
ef_construction = 200
|
||||
ef_search = 64
|
||||
|
||||
[cluster]
|
||||
raft_node_id = "node1"
|
||||
raft_peers = "node2:9001,node3:9001"
|
||||
```
|
||||
|
||||
### baradb.json
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"address": "0.0.0.0",
|
||||
"port": 9472,
|
||||
"http_port": 9470,
|
||||
"ws_port": 9471
|
||||
},
|
||||
"storage": {
|
||||
"data_dir": "/var/lib/baradb",
|
||||
"memtable_size_mb": 256,
|
||||
"cache_size_mb": 512
|
||||
},
|
||||
"tls": {
|
||||
"enabled": true,
|
||||
"cert_file": "/etc/baradb/server.crt",
|
||||
"key_file": "/etc/baradb/server.key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Командно-редови Флагове
|
||||
|
||||
```bash
|
||||
./build/baradadb --help
|
||||
```
|
||||
|
||||
```
|
||||
BaraDB v1.1.0 — Multimodal Database Engine
|
||||
|
||||
Употреба:
|
||||
baradadb [опции]
|
||||
|
||||
Опции:
|
||||
-c, --config <файл> Път до конфигурационен файл
|
||||
-p, --port <порт> TCP бинарен порт (по подр.: 9472)
|
||||
--http-port <порт> HTTP порт (по подр.: 9470)
|
||||
--ws-port <порт> WebSocket порт (по подр.: 9471)
|
||||
-d, --data-dir <дир> Директория за данни (по подр.: ./data)
|
||||
--tls-cert <файл> TLS сертификатен файл
|
||||
--tls-key <файл> TLS файл с частен ключ
|
||||
--log-level <ниво> Ниво на логване: debug, info, warn, error
|
||||
--log-file <файл> Път до лог файл
|
||||
--shell Стартиране на интерактивна обвивка
|
||||
--version Показване на версия
|
||||
--recover Изпълнение на WAL възстановяване
|
||||
--checkpoint <файл> Checkpoint за възстановяване
|
||||
-h, --help Показване на тази помощ
|
||||
```
|
||||
|
||||
## Примерни Конфигурации
|
||||
|
||||
### Разработка
|
||||
|
||||
```bash
|
||||
./build/baradadb \
|
||||
--log-level debug \
|
||||
--data-dir ./dev_data
|
||||
```
|
||||
|
||||
### Продукционен Единичен Възел
|
||||
|
||||
```bash
|
||||
BARADB_TLS_ENABLED=true \
|
||||
BARADB_CERT_FILE=/etc/baradb/server.crt \
|
||||
BARADB_KEY_FILE=/etc/baradb/server.key \
|
||||
BARADB_AUTH_ENABLED=true \
|
||||
BARADB_JWT_SECRET="$(openssl rand -hex 32)" \
|
||||
BARADB_LOG_LEVEL=warn \
|
||||
BARADB_LOG_FILE=/var/log/baradb/baradb.log \
|
||||
BARADB_MEMTABLE_SIZE_MB=256 \
|
||||
BARADB_CACHE_SIZE_MB=1024 \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
### Продукционен Клъстер (3 възела)
|
||||
|
||||
```bash
|
||||
# Възел 1
|
||||
BARADB_ADDRESS=0.0.0.0 \
|
||||
BARADB_PORT=9472 \
|
||||
BARADB_RAFT_NODE_ID=node1 \
|
||||
BARADB_RAFT_PEERS=node2:9001,node3:9001 \
|
||||
BARADB_SHARD_COUNT=4 \
|
||||
BARADB_REPLICATION_FACTOR=2 \
|
||||
./build/baradadb
|
||||
```
|
||||
@@ -0,0 +1,101 @@
|
||||
# Cross-Modal Заявки
|
||||
|
||||
Уникалната способност на BaraDB да изпълнява заявки, обхващащи множество storage двигатели в една унифицирана BaraQL заявка.
|
||||
|
||||
## Преглед
|
||||
|
||||
Традиционните бази данни изискват отделни заявки и join-ове на ниво приложение при работа с различни модели на данни. Cross-modal query planner-ът на BaraDB оптимизира изпълнението през:
|
||||
|
||||
- **Документи/KV** (LSM-Tree) — структурирани записи
|
||||
- **Графи** (Adjacency List) — връзки
|
||||
- **Вектори** (HNSW/IVF-PQ) — търсене на прилика
|
||||
- **Пълен текст** (Inverted Index) — текстово търсене
|
||||
- **Колонково** — аналитични агрегати
|
||||
|
||||
## Примери за Заявки
|
||||
|
||||
### Векторно + Пълнотекстово (Семантично + Ключово Търсене)
|
||||
|
||||
```sql
|
||||
SELECT title, score
|
||||
FROM articles
|
||||
WHERE MATCH(body) AGAINST('machine learning')
|
||||
ORDER BY cosine_distance(embedding, [0.1, 0.2, 0.3, ...])
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### Графово + Векторно (Социални Препоръки)
|
||||
|
||||
```sql
|
||||
MATCH (u:User)-[:KNOWS]->(friend:User)
|
||||
WHERE u.name = 'Alice'
|
||||
ORDER BY cosine_distance(friend.taste_vector, u.taste_vector)
|
||||
RETURN friend.name, friend.age;
|
||||
```
|
||||
|
||||
### Документно + Графово (Обогатяване на Същности)
|
||||
|
||||
```sql
|
||||
SELECT o.id, o.total, c.name,
|
||||
(SELECT count(*) FROM orders WHERE customer_id = c.id) as order_count
|
||||
FROM orders o
|
||||
MATCH (c:Customer)-[:PLACED]->(o)
|
||||
WHERE o.date > '2025-01-01';
|
||||
```
|
||||
|
||||
## Cross-Modal Engine API
|
||||
|
||||
```nim
|
||||
import barabadb/core/crossmodal
|
||||
|
||||
var engine = newCrossModalEngine("/tmp/baradb")
|
||||
|
||||
# Документни операции
|
||||
engine.put("key1", cast[seq[byte]]("value1"))
|
||||
let (found, val) = engine.get("key1")
|
||||
|
||||
# Векторни операции
|
||||
engine.insertVector(1, @[1.0'f32, 0.0'f32, ...], {"cat": "A"}.toTable)
|
||||
let results = engine.searchVector(@[1.0'f32, 0.1'f32, ...], 2)
|
||||
|
||||
# Графови операции
|
||||
let n1 = engine.addNode("Person")
|
||||
let n2 = engine.addNode("Person")
|
||||
discard engine.addEdge(n1, n2, "knows")
|
||||
let traversal = engine.traverseGraph(n1, "bfs")
|
||||
|
||||
# FTS операции
|
||||
engine.indexText(1, "Nim programming language")
|
||||
let ftsResults = engine.searchText("programming")
|
||||
|
||||
# Хибридно търсене
|
||||
var query = newCrossModalQuery(qmHybrid)
|
||||
query.vector = @[1.0'f32, 0.0'f32]
|
||||
query.searchQuery = "fast"
|
||||
query.vecWeight = 1.0
|
||||
query.ftsWeight = 1.0
|
||||
let hybridResult = engine.hybridSearch(query)
|
||||
```
|
||||
|
||||
## 2PC Транзакции
|
||||
|
||||
Cross-modal engine-ът поддържа two-phase commit за атомарни операции през множество storage системи:
|
||||
|
||||
```nim
|
||||
var txn = newTPCTransaction(1)
|
||||
txn.addParticipant("storage")
|
||||
txn.addParticipant("vector")
|
||||
txn.addParticipant("graph")
|
||||
|
||||
txn.prepare() # Всички участници потвърждават, че могат да комитнат
|
||||
txn.commit() # Атомарен commit през всички участници
|
||||
```
|
||||
|
||||
## Формална Верификация
|
||||
|
||||
Cross-modal консистентността е формално специфицирана в TLA+:
|
||||
|
||||
- **Спецификация:** `formal-verification/crossmodal.tla`
|
||||
- **Проверени свойства:**
|
||||
- `MetadataVectorConsistency` — insertVector обновява метаданни за филтрирано търсене
|
||||
- `CrossModalAtomicity` — всички участници комитват или всички абортират
|
||||
@@ -0,0 +1,250 @@
|
||||
# Ръководство за Внедряване (Deployment)
|
||||
|
||||
## Docker
|
||||
|
||||
За пълно ръководство за Docker deployment вижте [Docker Guide](docker.md).
|
||||
|
||||
### Бърз старт
|
||||
|
||||
```bash
|
||||
docker build -t baradb:latest .
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Docker Compose файлове
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|-----------|
|
||||
| `docker-compose.yml` | Development |
|
||||
| `docker-compose.prod.yml` | Production |
|
||||
| `docker-compose.override.yml` | Dev override (автоматично) |
|
||||
| `docker-compose.test.yml` | Тестова среда |
|
||||
|
||||
### Production
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
### Docker Swarm
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.prod.yml baradb
|
||||
```
|
||||
|
||||
## systemd Услуга
|
||||
|
||||
Създайте `/etc/systemd/system/baradb.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=BaraDB Multimodal Database
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=baradb
|
||||
Group=baradb
|
||||
WorkingDirectory=/var/lib/baradb
|
||||
ExecStart=/usr/local/bin/baradadb
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
Environment=BARADB_PORT=9472
|
||||
Environment=BARADB_HTTP_PORT=9470
|
||||
Environment=BARADB_DATA_DIR=/var/lib/baradb/data
|
||||
Environment=BARADB_LOG_LEVEL=info
|
||||
|
||||
# Подсилване на сигурността
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/var/lib/baradb/data
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Активиране и стартиране:
|
||||
|
||||
```bash
|
||||
sudo useradd -r -s /bin/false baradb
|
||||
sudo mkdir -p /var/lib/baradb/data
|
||||
sudo chown -R baradb:baradb /var/lib/baradb
|
||||
sudo cp build/baradadb /usr/local/bin/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now baradb
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
### StatefulSet
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: baradb
|
||||
spec:
|
||||
serviceName: baradb
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: baradb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: baradb
|
||||
spec:
|
||||
containers:
|
||||
- name: baradb
|
||||
image: baradb:latest
|
||||
ports:
|
||||
- containerPort: 9472
|
||||
name: binary
|
||||
- containerPort: 9470
|
||||
name: http
|
||||
- containerPort: 9471
|
||||
name: websocket
|
||||
env:
|
||||
- name: BARADB_DATA_DIR
|
||||
value: /data
|
||||
- name: BARADB_RAFT_NODE_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: baradb
|
||||
spec:
|
||||
selector:
|
||||
app: baradb
|
||||
ports:
|
||||
- port: 9472
|
||||
name: binary
|
||||
- port: 9470
|
||||
name: http
|
||||
- port: 9471
|
||||
name: websocket
|
||||
clusterIP: None
|
||||
```
|
||||
|
||||
## Reverse Proxy (nginx)
|
||||
|
||||
```nginx
|
||||
upstream baradb_http {
|
||||
server 127.0.0.1:9470;
|
||||
}
|
||||
|
||||
upstream baradb_ws {
|
||||
server 127.0.0.1:9471;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name db.example.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name db.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/db.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/db.example.com/privkey.pem;
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://baradb_http/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /ws/ {
|
||||
proxy_pass http://baradb_ws/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Висока Достъпност (High Availability)
|
||||
|
||||
### 3-Възел Raft Клъстер
|
||||
|
||||
```bash
|
||||
# Възел 1
|
||||
BARADB_RAFT_NODE_ID=node1 \
|
||||
BARADB_RAFT_PEERS=node2:9001,node3:9001 \
|
||||
./build/baradadb
|
||||
|
||||
# Възел 2
|
||||
BARADB_RAFT_NODE_ID=node2 \
|
||||
BARADB_RAFT_PEERS=node1:9001,node3:9001 \
|
||||
./build/baradadb
|
||||
|
||||
# Възел 3
|
||||
BARADB_RAFT_NODE_ID=node3 \
|
||||
BARADB_RAFT_PEERS=node1:9001,node2:9001 \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
## Облачно Внедряване
|
||||
|
||||
### AWS EC2
|
||||
|
||||
Препоръчителна инстанция: `m6i.2xlarge` (8 vCPU, 32 GB RAM)
|
||||
|
||||
```bash
|
||||
# User data скрипт
|
||||
#!/bin/bash
|
||||
apt-get update
|
||||
apt-get install -y nim
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-amd64
|
||||
chmod +x baradadb-linux-amd64
|
||||
mv baradadb-linux-amd64 /usr/local/bin/baradadb
|
||||
|
||||
mkdir -p /data/baradb
|
||||
cat > /etc/systemd/system/baradb.service << 'EOF'
|
||||
[Unit]
|
||||
Description=BaraDB
|
||||
After=network.target
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/baradadb
|
||||
Environment=BARADB_DATA_DIR=/data/baradb
|
||||
Restart=always
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now baradb
|
||||
```
|
||||
|
||||
### GCP Cloud Run (само HTTP)
|
||||
|
||||
```bash
|
||||
gcloud run deploy baradb \
|
||||
--image gcr.io/PROJECT/baradb \
|
||||
--port 9470 \
|
||||
--memory 4Gi \
|
||||
--cpu 2 \
|
||||
--max-instances 10
|
||||
```
|
||||
+58
-5
@@ -1,9 +1,11 @@
|
||||
# Разпределена Система
|
||||
|
||||
Поддръжка за разпределено внедряване с Raft консенсус, шардиране и репликация.
|
||||
BaraDB поддържа разпределено внедряване с Raft консенсус, шардиране, репликация и gossip протокол.
|
||||
|
||||
## Raft Консенсус
|
||||
|
||||
Leader election и log репликация:
|
||||
|
||||
```nim
|
||||
import barabadb/core/raft
|
||||
|
||||
@@ -13,19 +15,35 @@ cluster.addNode("node2")
|
||||
cluster.addNode("node3")
|
||||
|
||||
let n1 = cluster.nodes["n1"]
|
||||
n1.becomeCandidate()
|
||||
n1.becomeLeader()
|
||||
let entry = n1.appendLog("SET key1 value1")
|
||||
```
|
||||
|
||||
## Шардиране
|
||||
|
||||
Разпределение на данни между възли:
|
||||
|
||||
```nim
|
||||
import barabadb/core/sharding
|
||||
|
||||
var router = newShardRouter(ShardConfig(numShards: 4, replicas: 2))
|
||||
var router = newShardRouter(ShardConfig(
|
||||
numShards: 4,
|
||||
replicas: 2,
|
||||
strategy: ssHash
|
||||
))
|
||||
router.rebalance(@["node1", "node2", "node3"])
|
||||
let shard = router.getShard("user_123")
|
||||
```
|
||||
|
||||
### Стратегии за Шардиране
|
||||
|
||||
| Стратегия | Описание |
|
||||
|-----------|----------|
|
||||
| `ssHash` | Хеш-базирано шардиране |
|
||||
| `ssRange` | Range-базирано шардиране |
|
||||
| `ssConsistent` | Consistent hashing |
|
||||
|
||||
## Репликация
|
||||
|
||||
```nim
|
||||
@@ -34,11 +52,46 @@ import barabadb/core/replication
|
||||
var rm = newReplicationManager(rmSync)
|
||||
rm.addReplica(newReplica("r1", "10.0.0.1", 9472))
|
||||
rm.connectReplica("r1")
|
||||
let lsn = rm.writeLsn(@[1'u8, 2, 3])
|
||||
rm.ackLsn("r1", lsn)
|
||||
```
|
||||
|
||||
### Режими на Репликация
|
||||
|
||||
| Режим | Описание |
|
||||
|--------|----------|
|
||||
| `rmSync` | Синхронна репликация |
|
||||
| `rmAsync` | Асинхронна репликация |
|
||||
| `rmSemiSync` | Полу-синхронна репликация |
|
||||
|
||||
## Gossip Протокол
|
||||
|
||||
Управление на членство и детекция на откази:
|
||||
|
||||
```nim
|
||||
import barabadb/core/gossip
|
||||
|
||||
var g = newGossipProtocol("node1", "localhost", 9472, gossipPort = 9572)
|
||||
g.join(newGossipNode("node2", "10.0.0.2", 9472))
|
||||
```
|
||||
|
||||
## Разпределени Транзакции
|
||||
|
||||
Two-phase commit между възли:
|
||||
|
||||
```nim
|
||||
import barabadb/core/disttxn
|
||||
|
||||
var tm = newDistTxnManager()
|
||||
let txn = tm.beginTransaction("node1")
|
||||
txn.addParticipant("node2", "10.0.0.2", 9472)
|
||||
txn.prepare()
|
||||
txn.commit()
|
||||
```
|
||||
|
||||
## Формална Верификация
|
||||
|
||||
Разпределените алгоритми са формално специфицирани в TLA+ и проверени с TLC:
|
||||
Основните разпределени алгоритми са формално специфицирани в TLA+ и проверени с TLC:
|
||||
|
||||
- **Raft Консенсус** — `formal-verification/raft.tla`
|
||||
- Проверено: ElectionSafety, StateMachineSafety
|
||||
@@ -47,11 +100,11 @@ rm.connectReplica("r1")
|
||||
- **Репликация** — `formal-verification/replication.tla`
|
||||
- Проверено: MonotonicLsn, AcksRemovePending
|
||||
|
||||
Пускане на TLC:
|
||||
Пускане на TLC локално:
|
||||
|
||||
```bash
|
||||
cd formal-verification
|
||||
java -cp tla2tools.jar tlc2.TLC -config models/raft.cfg raft.tla
|
||||
java -cp tla2tools.jar tlc2.TLC -config models/twopc.cfg twopc.tla
|
||||
java -cp tla2tools.jar tlc2.TLC -config models/replication.cfg replication.tla
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
# Docker Deployment Ръководство
|
||||
|
||||
Това ръководство описва как да използвате BaraDB с Docker и Docker Compose.
|
||||
|
||||
## Бърз старт
|
||||
|
||||
```bash
|
||||
# Клониране на репото
|
||||
git clone https://github.com/katehonz/barabaDB.git
|
||||
cd barabaDB
|
||||
|
||||
# Build на образа
|
||||
docker build -t baradb:latest .
|
||||
|
||||
# Стартиране с Docker Compose
|
||||
docker compose up -d
|
||||
|
||||
# Проверка на статуса
|
||||
docker compose ps
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
## Файлове
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| `Dockerfile` | Мулти-stage production build |
|
||||
| `docker-compose.yml` | Development конфигурация |
|
||||
| `docker-compose.prod.yml` | Production конфигурация |
|
||||
| `docker-compose.override.yml` | Development override (автоматично се зарежда) |
|
||||
| `docker-compose.test.yml` | Тестова конфигурация |
|
||||
| `docker-entrypoint.sh` | Entrypoint скрипт за инициализация |
|
||||
| `.dockerignore` | Файлове, които да не се копират в образа |
|
||||
|
||||
## Създаване на образ
|
||||
|
||||
```bash
|
||||
# Стандартен build
|
||||
docker build -t baradb:latest .
|
||||
|
||||
# С конкретна версия
|
||||
docker build -t baradb:1.1.0 .
|
||||
```
|
||||
|
||||
## Стартиране
|
||||
|
||||
### Development (docker compose)
|
||||
|
||||
```bash
|
||||
# Стартиране на заден план
|
||||
docker compose up -d
|
||||
|
||||
# Спиране
|
||||
docker compose down
|
||||
|
||||
# Спиране и изтриване на volumes (ВНИМАНИЕ — изтрива данните!)
|
||||
docker compose down -v
|
||||
|
||||
# Преглед на логове
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
### Production (docker compose)
|
||||
|
||||
```bash
|
||||
# Стартиране с production конфигурация
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Проверка на healthcheck
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
```
|
||||
|
||||
### Ръчно (docker run)
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name baradb \
|
||||
-p 9472:9472 \
|
||||
-p 9470:9470 \
|
||||
-p 9471:9471 \
|
||||
-v baradb_data:/data \
|
||||
-e BARADB_LOG_LEVEL=info \
|
||||
baradb:latest
|
||||
```
|
||||
|
||||
## Портове
|
||||
|
||||
| Порт | Описание |
|
||||
|------|----------|
|
||||
| `9472` | Binary wire протокол |
|
||||
| `9470` | HTTP/REST API |
|
||||
| `9471` | WebSocket |
|
||||
|
||||
## Променливи на Средата
|
||||
|
||||
| Променлива | Стойност по подразбиране | Описание |
|
||||
|------------|--------------------------|----------|
|
||||
| `BARADB_ADDRESS` | `0.0.0.0` | Адрес за слушане |
|
||||
| `BARADB_PORT` | `9472` | Binary протокол порт |
|
||||
| `BARADB_HTTP_PORT` | `9470` | HTTP порт |
|
||||
| `BARADB_WS_PORT` | `9471` | WebSocket порт |
|
||||
| `BARADB_DATA_DIR` | `/data` | Директория за данни |
|
||||
| `BARADB_LOG_LEVEL` | `info` | Ниво на логове |
|
||||
|
||||
## Volumes
|
||||
|
||||
| Път в контейнера | Описание |
|
||||
|------------------|----------|
|
||||
| `/data` | Основна директория за базата данни |
|
||||
| `/data/server/wal` | Write-ahead log |
|
||||
| `/data/server/sstables` | SSTable файлове |
|
||||
|
||||
## Production Checklist
|
||||
|
||||
- [ ] Създайте TLS сертификати в `./certs/`
|
||||
- [ ] Задайте силен `BARADB_JWT_SECRET`
|
||||
- [ ] Настройте firewall правила
|
||||
- [ ] Конфигурирайте регулярни backups
|
||||
- [ ] Проверете resource limits
|
||||
- [ ] Настройте мониторинг (healthcheck, logs)
|
||||
|
||||
## TLS в Docker
|
||||
|
||||
1. Създайте сертификати:
|
||||
```bash
|
||||
mkdir -p certs
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout certs/server.key -out certs/server.crt
|
||||
```
|
||||
|
||||
2. Активирайте в `docker-compose.prod.yml`:
|
||||
```yaml
|
||||
environment:
|
||||
- BARADB_TLS_ENABLED=true
|
||||
- BARADB_CERT_FILE=/certs/server.crt
|
||||
- BARADB_KEY_FILE=/certs/server.key
|
||||
volumes:
|
||||
- ./certs:/certs:ro
|
||||
```
|
||||
|
||||
## Backup в Docker
|
||||
|
||||
```bash
|
||||
# Ръчен backup
|
||||
docker exec baradb baradadb --snapshot --output=/backup/snapshot.db
|
||||
|
||||
# Списък на backups
|
||||
ls /backup/
|
||||
|
||||
# Възстановяване
|
||||
docker exec baradb baradadb --recover --checkpoint=/backup/snapshot.db
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Контейнерът не стартира
|
||||
|
||||
```bash
|
||||
# Проверка на логове
|
||||
docker compose logs -f baradb
|
||||
|
||||
# Проверка на статус
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### Няма връзка с базата
|
||||
|
||||
```bash
|
||||
# Проверка дали портовете са експозвани
|
||||
docker port baradb
|
||||
|
||||
# Проверка отвътре
|
||||
docker exec baradb wget -qO- http://localhost:9470/health
|
||||
```
|
||||
|
||||
### Permission denied на /data
|
||||
|
||||
Entrypoint скриптът автоматично създава директориите и задава правилните permissions. Ако имате проблем:
|
||||
|
||||
```bash
|
||||
docker exec baradb ls -la /data
|
||||
docker exec baradb chown -R baradb:baradb /data
|
||||
```
|
||||
+67
-15
@@ -1,6 +1,6 @@
|
||||
# Пълнотекстово Търсене
|
||||
# Full-Text Search Engine
|
||||
|
||||
Инвертиран индекс с BM25 и TF-IDF ранжиране.
|
||||
Inverted индекс с BM25 и TF-IDF ранжиране за текстово търсене.
|
||||
|
||||
## Употреба
|
||||
|
||||
@@ -8,28 +8,80 @@
|
||||
import barabadb/fts/engine
|
||||
|
||||
var idx = newInvertedIndex()
|
||||
idx.addDocument(1, "Nim е бърз език за програмиране")
|
||||
idx.addDocument(2, "Python е популярен за data science")
|
||||
idx.addDocument(1, "Nim is a fast programming language")
|
||||
idx.addDocument(2, "Python is popular for data science")
|
||||
|
||||
let results = idx.search("език програмиране")
|
||||
let tfidf = idx.searchTfidf("език")
|
||||
let fuzzy = idx.fuzzySearch("програмиране", maxDistance = 2)
|
||||
# BM25 търсене
|
||||
let results = idx.search("programming language")
|
||||
|
||||
# TF-IDF търсене
|
||||
let tfidf = idx.searchTfidf("programming language")
|
||||
|
||||
# Fuzzy търсене (толеранс на печатни грешки)
|
||||
let fuzzy = idx.fuzzySearch("programing", maxDistance = 2)
|
||||
|
||||
# Wildcard търсене
|
||||
let wild = idx.regexSearch("prog*")
|
||||
```
|
||||
|
||||
## Методи за Ранжиране
|
||||
|
||||
### BM25
|
||||
|
||||
Най-добрият алгоритъм за съвпадение
|
||||
Best matching алгоритъм за ранжиране:
|
||||
|
||||
```nim
|
||||
let bm25 = idx.searchBM25("query terms")
|
||||
```
|
||||
|
||||
### TF-IDF
|
||||
|
||||
Term Frequency-Inverse Document Frequency
|
||||
Term Frequency-Inverse Document Frequency:
|
||||
|
||||
## Търсене
|
||||
```nim
|
||||
let tfidf = idx.searchTfidf("query terms")
|
||||
```
|
||||
|
||||
| Тип | Описание |
|
||||
|-----|----------|
|
||||
| Fuzzy | Толерантност към правописни грешки |
|
||||
| Wildcard | Префикс, суфикс, и инфикс заместващи символи |
|
||||
| Regex | Регулярни изрази |
|
||||
## Функции за Търсене
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| Fuzzy търсене | Levenshtein distance толеранс |
|
||||
| Wildcard | Префиксни, суфиксни и инфиксни wildcards |
|
||||
| Regex | Регулярни изрази |
|
||||
| Фразово търсене | Точно съвпадение на фраза |
|
||||
| Булево | AND, OR, NOT оператори |
|
||||
|
||||
## SQL Интерфейс
|
||||
|
||||
Пълнотекстовото търсене е достъпно и директно в BaraQL:
|
||||
|
||||
```sql
|
||||
-- Създаване на таблица с текстова колона
|
||||
CREATE TABLE articles (id INT PRIMARY KEY, title TEXT, body TEXT);
|
||||
|
||||
-- Създаване на FTS индекс
|
||||
CREATE INDEX idx_fts ON articles(body) USING FTS;
|
||||
|
||||
-- Търсене с оператора @@ (BM25 ранжиране)
|
||||
SELECT * FROM articles WHERE body @@ 'machine learning';
|
||||
|
||||
-- Търсене с множество термини
|
||||
SELECT * FROM articles WHERE body @@ 'quick brown fox';
|
||||
```
|
||||
|
||||
## Многоезична Поддръжка
|
||||
|
||||
```nim
|
||||
import barabadb/fts/multilang
|
||||
|
||||
# Поддържани езици: EN, BG, DE, FR, RU
|
||||
var tokenizer = newTokenizer("bg") # Български
|
||||
let tokens = tokenizer.tokenize("Търсене в пълен текст")
|
||||
```
|
||||
|
||||
Функции за всеки език:
|
||||
- Токенизация
|
||||
- Stop думи
|
||||
- Стеминг
|
||||
- Детекция на език
|
||||
|
||||
+117
-20
@@ -15,6 +15,7 @@
|
||||
| macOS | x86_64 | ✅ Пълна поддръжка |
|
||||
| macOS | ARM64 (Apple Silicon) | ✅ Пълна поддръжка |
|
||||
| Windows | x86_64 | ✅ Поддръжка |
|
||||
| FreeBSD | x86_64 | 🟡 Тествано от общността |
|
||||
|
||||
## Инсталиране на Nim
|
||||
|
||||
@@ -48,10 +49,13 @@ sudo port install nim
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
# Winget
|
||||
# С choosenim
|
||||
curl.exe -A "MSYS2_$(uname -m)" -L https://nim-lang.org/choosenim/init.ps1 | powershell -
|
||||
|
||||
# С winget
|
||||
winget install nim
|
||||
|
||||
# Scoop
|
||||
# С scoop
|
||||
scoop install nim
|
||||
```
|
||||
|
||||
@@ -79,10 +83,16 @@ sudo pacman -S openssl
|
||||
|
||||
### macOS
|
||||
|
||||
OpenSSL е включен в системата. Ако е необходимо:
|
||||
|
||||
```bash
|
||||
brew install openssl
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
OpenSSL е включен в Nim Windows дистрибуцията. За ръчни компилации, изтеглете от [slproweb.com](https://slproweb.com/products/Win32OpenSSL.html).
|
||||
|
||||
## Компилиране на BaraDB
|
||||
|
||||
### Клониране на Репозиторито
|
||||
@@ -103,26 +113,29 @@ nimble install -d -y
|
||||
#### Debug Компилация
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -o:build/baradadb src/baradadb.nim
|
||||
nim c -d:ssl --threads:on -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
#### Release Компилация (Препоръчителна)
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:speed -o:build/baradadb src/baradadb.nim
|
||||
nim c -d:ssl --threads:on -d:release --opt:speed -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
#### Използване на Nimble
|
||||
#### Използване на Nimble Tasks
|
||||
|
||||
```bash
|
||||
# Debug компилация
|
||||
nimble build_debug
|
||||
|
||||
# Release компилация
|
||||
nimble build_release
|
||||
```
|
||||
|
||||
#### Минимален Размер
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:size -o:build/baradadb src/baradadb.nim
|
||||
nim c -d:ssl --threads:on -d:release --opt:size -o:build/baradadb src/baradadb.nim
|
||||
strip build/baradadb
|
||||
```
|
||||
|
||||
@@ -130,17 +143,31 @@ strip build/baradadb
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# Очакван резултат: BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# Очакван резултат: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## Стартиране на Тестове
|
||||
|
||||
```bash
|
||||
# Всички тестове (262 теста, 56 сюита)
|
||||
nim c -d:ssl -r tests/test_all.nim
|
||||
### Всички Тестове
|
||||
|
||||
# Бенчмаркове
|
||||
nim c -d:ssl -d:release -r benchmarks/bench_all.nim
|
||||
```bash
|
||||
nim c --path:src -d:ssl --threads:on -r tests/test_all.nim
|
||||
```
|
||||
|
||||
### Специфични Тестови Сюити
|
||||
|
||||
```bash
|
||||
# Storage тестове
|
||||
nim c --path:src -d:ssl --threads:on -r tests/test_all.nim
|
||||
|
||||
# Stress тестове
|
||||
nim c --path:src -d:ssl --threads:on -d:release -r tests/stress_test.nim
|
||||
```
|
||||
|
||||
### Бенчмаркове
|
||||
|
||||
```bash
|
||||
nim c --path:src -d:ssl --threads:on -d:release -r benchmarks/bench_all.nim
|
||||
```
|
||||
|
||||
## Опции за Инсталация
|
||||
@@ -148,16 +175,46 @@ nim c -d:ssl -d:release -r benchmarks/bench_all.nim
|
||||
### Системна Инсталация
|
||||
|
||||
```bash
|
||||
# Компилиране на release binary
|
||||
nimble build_release
|
||||
|
||||
# Инсталиране в /usr/local/bin
|
||||
sudo cp build/baradadb /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/baradadb
|
||||
|
||||
# Създаване на директория за данни
|
||||
sudo mkdir -p /var/lib/baradb
|
||||
sudo chmod 755 /var/lib/baradb
|
||||
```
|
||||
|
||||
### Предварително Компилиран Binary
|
||||
|
||||
Изтеглете най-новата версия за вашата платформа:
|
||||
|
||||
```bash
|
||||
# Linux x86_64
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-amd64
|
||||
chmod +x baradadb-linux-amd64
|
||||
mv baradadb-linux-amd64 /usr/local/bin/baradadb
|
||||
|
||||
# Linux ARM64
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-arm64
|
||||
chmod +x baradadb-linux-arm64
|
||||
mv baradadb-linux-arm64 /usr/local/bin/baradadb
|
||||
|
||||
# macOS
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-darwin-amd64
|
||||
chmod +x baradadb-darwin-amd64
|
||||
mv baradadb-darwin-amd64 /usr/local/bin/baradadb
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
# Изтегляне на официален образ
|
||||
docker pull barabadb/barabadb:latest
|
||||
|
||||
# Стартиране
|
||||
docker run -d \
|
||||
-p 9472:9472 \
|
||||
-p 9470:9470 \
|
||||
@@ -172,12 +229,16 @@ docker run -d \
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Вградено Използване
|
||||
### Вградено Използване (Nim Проекти)
|
||||
|
||||
Добавете към вашия `.nimble` файл:
|
||||
|
||||
```nim
|
||||
requires "barabadb >= 0.1.0"
|
||||
requires "barabadb >= 1.1.0"
|
||||
```
|
||||
|
||||
Използване в кода:
|
||||
|
||||
```nim
|
||||
import barabadb/storage/lsm
|
||||
|
||||
@@ -193,16 +254,52 @@ db.close()
|
||||
# Стартиране на сървъра
|
||||
./build/baradadb
|
||||
|
||||
# Тестване на HTTP API
|
||||
# Очакван изход:
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# Тестване с HTTP API
|
||||
curl http://localhost:9470/health
|
||||
|
||||
# Интерактивна конзола
|
||||
# Интерактивна обвивка
|
||||
./build/baradadb --shell
|
||||
```
|
||||
|
||||
## Отстраняване на Проблеми с Инсталацията
|
||||
|
||||
### "cannot open file: hunos"
|
||||
|
||||
```bash
|
||||
nimble install -d -y
|
||||
```
|
||||
|
||||
### "BaraDB requires SSL support"
|
||||
|
||||
Винаги компилирайте с `-d:ssl`:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl --threads:on -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### Бавна компилация
|
||||
|
||||
Използвайте паралелна компилация:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl --threads:on -d:release --parallelBuild:4 -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### Голям размер на binary-то
|
||||
|
||||
Използвайте оптимизация на размера:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl --threads:on -d:release --opt:size --passL:-s -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
## Следващи Стъпки
|
||||
|
||||
- [Бързо Стартиране](bg/quickstart.md)
|
||||
- [Конфигурация](en/configuration.md)
|
||||
- [Архитектура](bg/architecture.md)
|
||||
- [BaraQL Заявки](bg/baraql.md)
|
||||
- [Бърз Старт](quickstart.md)
|
||||
- [Конфигурационна Референция](configuration.md)
|
||||
- [Преглед на Архитектурата](architecture.md)
|
||||
- [BaraQL Език за Заявки](baraql.md)
|
||||
|
||||
+48
-9
@@ -1,6 +1,29 @@
|
||||
# LSM-Tree Съхранение
|
||||
# LSM-Tree Storage Engine
|
||||
|
||||
Основният двигател за съхранение използващ Log-Structured Merge-Tree архитектура.
|
||||
Основният storage engine в BaraDB, използващ Log-Structured Merge-Tree архитектура.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Записи │
|
||||
│ (добавяне към WAL + MemTable) │
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ MemTable │
|
||||
│ (в-памет сортиран буфер) │
|
||||
└─────────────────────────────────────────────┘
|
||||
│
|
||||
(при запълване, flush към SSTable)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SSTable │
|
||||
│ (сортирана string table на диска) │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Употреба
|
||||
|
||||
@@ -9,17 +32,33 @@ import barabadb/storage/lsm
|
||||
|
||||
var db = newLSMTree("./data")
|
||||
|
||||
# Запис
|
||||
db.put("key1", cast[seq[byte]]("value1"))
|
||||
|
||||
# Четене
|
||||
let (found, value) = db.get("key1")
|
||||
|
||||
# Изтриване
|
||||
db.delete("key1")
|
||||
|
||||
db.close()
|
||||
```
|
||||
|
||||
## Компоненти
|
||||
## Възможности
|
||||
|
||||
- **MemTable**: Сортиран буфер в паметта
|
||||
- **WAL**: Write-ahead log за трайност
|
||||
- **SSTable**: Сортирани таблици на диска
|
||||
- **Bloom Filter**: Бързи негативни проверки
|
||||
- **Compaction**: Сливане на SSTables
|
||||
- **Page Cache**: LRU кеш
|
||||
- **Write-оптимизиран**: Append-only лог структура
|
||||
- **Устойчивост**: Write-ahead log (WAL) осигурява crash recovery
|
||||
- **Bloom Филтър**: Бързи негативни проверки
|
||||
- **Compaction**: Size-tiered стратегия слива SSTables
|
||||
- **Page Cache**: LRU кеш за често достъпвани страници
|
||||
|
||||
## Конфигурация
|
||||
|
||||
```nim
|
||||
var db = newLSMTree(
|
||||
path = "./data",
|
||||
memTableSize = 64 * 1024 * 1024, # 64MB
|
||||
walEnabled = true,
|
||||
bloomFpRate = 0.01
|
||||
)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
# Мониторинг и Наблюдаемост
|
||||
|
||||
## Health Checks
|
||||
|
||||
### HTTP Health Endpoint
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/health
|
||||
```
|
||||
|
||||
Отговор:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"version": "1.1.0",
|
||||
"uptime_seconds": 86400,
|
||||
"checks": {
|
||||
"storage": "ok",
|
||||
"memory": "ok",
|
||||
"connections": "ok"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Readiness Probe
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/ready
|
||||
```
|
||||
|
||||
Връща `200 OK` когато сървърът е готов да приема трафик, `503` по време на стартиране.
|
||||
|
||||
## Метрики
|
||||
|
||||
### Prometheus-Съвместими Метрики
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/metrics
|
||||
```
|
||||
|
||||
Примерен изход:
|
||||
|
||||
```
|
||||
# HELP baradb_queries_total Общ брой изпълнени заявки
|
||||
# TYPE baradb_queries_total counter
|
||||
baradb_queries_total 152340
|
||||
|
||||
# HELP baradb_queries_duration_seconds Хистограма на времетраене на заявки
|
||||
# TYPE baradb_queries_duration_seconds histogram
|
||||
baradb_queries_duration_seconds_bucket{le="0.001"} 45000
|
||||
baradb_queries_duration_seconds_bucket{le="0.01"} 120000
|
||||
baradb_queries_duration_seconds_bucket{le="0.1"} 148000
|
||||
|
||||
# HELP baradb_storage_lsm_size_bytes Общ размер на LSM-Tree
|
||||
# TYPE baradb_storage_lsm_size_bytes gauge
|
||||
baradb_storage_lsm_size_bytes 2147483648
|
||||
|
||||
# HELP baradb_cache_hit_rate Page cache hit rate
|
||||
# TYPE baradb_cache_hit_rate gauge
|
||||
baradb_cache_hit_rate 0.94
|
||||
|
||||
# HELP baradb_active_connections Активни клиентски връзки
|
||||
# TYPE baradb_active_connections gauge
|
||||
baradb_active_connections 42
|
||||
```
|
||||
|
||||
### JSON Метрики
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/metrics?format=json
|
||||
```
|
||||
|
||||
## Логване
|
||||
|
||||
### Нива на Логване
|
||||
|
||||
| Ниво | Описание |
|
||||
|------|----------|
|
||||
| `debug` | Детайлни вътрешни операции |
|
||||
| `info` | Нормални операции |
|
||||
| `warn` | Възстановими проблеми |
|
||||
| `error` | Грешки, изискващи внимание |
|
||||
|
||||
### Структурирани JSON Логове
|
||||
|
||||
```bash
|
||||
BARADB_LOG_LEVEL=info \
|
||||
BARADB_LOG_FORMAT=json \
|
||||
BARADB_LOG_FILE=/var/log/baradb/baradb.log \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
Примерен лог запис:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2025-01-15T10:30:00.123Z",
|
||||
"level": "info",
|
||||
"component": "server",
|
||||
"message": "Query executed",
|
||||
"query": "SELECT * FROM users",
|
||||
"duration_ms": 12,
|
||||
"client_ip": "10.0.0.15"
|
||||
}
|
||||
```
|
||||
|
||||
### Текстов Формат
|
||||
|
||||
```bash
|
||||
BARADB_LOG_FORMAT=text ./build/baradadb
|
||||
```
|
||||
|
||||
## Правила за Алармиране
|
||||
|
||||
### Prometheus AlertManager
|
||||
|
||||
```yaml
|
||||
groups:
|
||||
- name: baradb
|
||||
rules:
|
||||
- alert: BaraDBHighErrorRate
|
||||
expr: rate(baradb_errors_total[5m]) > 0.1
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Висок процент грешки в BaraDB"
|
||||
|
||||
- alert: BaraDBLowCacheHitRate
|
||||
expr: baradb_cache_hit_rate < 0.8
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Cache hit rate под 80%"
|
||||
|
||||
- alert: BaraDBHighConnections
|
||||
expr: baradb_active_connections > 800
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Голям брой връзки към BaraDB"
|
||||
|
||||
- alert: BaraDBDown
|
||||
expr: up{job="baradb"} == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "BaraDB инстанцията не работи"
|
||||
```
|
||||
|
||||
## Разпределен Мониторинг
|
||||
|
||||
### Клъстерни Метрики
|
||||
|
||||
За Raft клъстери, мониторирайте:
|
||||
|
||||
```bash
|
||||
curl http://node1:9470/metrics/cluster
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"cluster_id": "baradb-cluster-1",
|
||||
"nodes": [
|
||||
{"id": "node1", "role": "leader", "health": "healthy"},
|
||||
{"id": "node2", "role": "follower", "health": "healthy"},
|
||||
{"id": "node3", "role": "follower", "health": "healthy"}
|
||||
],
|
||||
"raft_log_index": 15420,
|
||||
"raft_commit_index": 15420,
|
||||
"shards": 4,
|
||||
"replication_lag_ms": 5
|
||||
}
|
||||
```
|
||||
|
||||
## Профилиране на Производителност
|
||||
|
||||
### Вграден CPU Profiler
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/debug/pprof/cpu?seconds=30 > cpu.prof
|
||||
```
|
||||
|
||||
### Memory Profiler
|
||||
|
||||
```bash
|
||||
curl http://localhost:9470/debug/pprof/heap > heap.prof
|
||||
```
|
||||
|
||||
## Отстраняване на Проблеми с Метрики
|
||||
|
||||
| Симптом | Метрика | Действие |
|
||||
|---------|--------|----------|
|
||||
| Бавни заявки | `baradb_queries_duration_seconds` | Проверете cache hit rate, добавете индекси |
|
||||
| Висока памет | `process_resident_memory_bytes` | Намалете memtable/cache размери |
|
||||
| Растящо съхранение | `baradb_storage_lsm_size_bytes` | Пуснете ръчен compaction |
|
||||
| Грешки при връзка | `baradb_active_connections` | Увеличете connection pool или добавете възли |
|
||||
| Репликационно закъснение | `baradb_replication_lag_ms` | Проверете мрежата, увеличете ресурсите |
|
||||
@@ -0,0 +1,125 @@
|
||||
# Ръководство за Производителност
|
||||
|
||||
## Методология на Бенчмарковете
|
||||
|
||||
Всички бенчмаркове са проведени с:
|
||||
- **Компилатор**: Nim 2.2.0 с `-d:release --opt:speed`
|
||||
- **CPU**: AMD Ryzen 9 5900X (12 ядра / 24 нишки)
|
||||
- **Памет**: 64 GB DDR4-3600
|
||||
- **Диск**: Samsung 980 Pro NVMe SSD
|
||||
- **ОС**: Ubuntu 24.04 LTS
|
||||
|
||||
Пускане на пълния бенчмарк сюит:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release -r benchmarks/bench_all.nim
|
||||
```
|
||||
|
||||
## Storage Engine Бенчмаркове
|
||||
|
||||
### LSM-Tree Key-Value
|
||||
|
||||
| Метрика | Стойност |
|
||||
|---------|----------|
|
||||
| Пропускателна способност при запис | ~580,000 ops/s |
|
||||
| Пропускателна способност при четене | ~720,000 ops/s |
|
||||
| Средна латентност при запис | 1.7 µs |
|
||||
| Средна латентност при четене | 1.4 µs |
|
||||
|
||||
LSM-Tree използва 64MB MemTable, WAL fsync при всеки запис и size-tiered compaction с 6 нива.
|
||||
|
||||
### B-Tree Индекс
|
||||
|
||||
| Метрика | Стойност |
|
||||
|---------|----------|
|
||||
| Пропускателна способност при вмъкване | ~1,200,000 ops/s |
|
||||
| Пропускателна способност при точково търсене | ~1,500,000 ops/s |
|
||||
| Range scan (1000 ключа) | ~0.3 ms |
|
||||
|
||||
## Vector Engine Бенчмаркове
|
||||
|
||||
### HNSW Индекс
|
||||
|
||||
| Метрика | Стойност |
|
||||
|---------|----------|
|
||||
| Вмъкване (dim=128) | ~45,000 вектора/s |
|
||||
| Търсене топ-10 (dim=128, n=10K) | ~2 ms |
|
||||
| Търсене топ-10 (dim=128, n=100K) | ~8 ms |
|
||||
|
||||
### SIMD Функции за Разстояние
|
||||
|
||||
| Операция | dim=128 | dim=768 | dim=1536 |
|
||||
|----------|---------|---------|----------|
|
||||
| Косинусово разстояние | 4.2M/s | 850K/s | 420K/s |
|
||||
| L2 (Евклидово) | 4.5M/s | 920K/s | 450K/s |
|
||||
| Скаларно произведение | 4.8M/s | 980K/s | 480K/s |
|
||||
|
||||
## Full-Text Search Бенчмаркове
|
||||
|
||||
| Метрика | Стойност |
|
||||
|---------|----------|
|
||||
| Индексиране | ~320,000 документа/s |
|
||||
| BM25 търсене | ~28,000 заявки/s |
|
||||
| Fuzzy търсене (distance=2) | ~850 заявки/s |
|
||||
|
||||
## Graph Engine Бенчмаркове
|
||||
|
||||
| Операция | Пропускателна способност |
|
||||
|----------|--------------------------|
|
||||
| Добавяне на възел | ~2.5M ops/s |
|
||||
| Добавяне на ребро | ~1.8M ops/s |
|
||||
| BFS (1K възела, 5K ребра) | ~12K обхождания/s |
|
||||
| Dijkstra най-кратък път | ~120 µs |
|
||||
| PageRank (10 итерации) | ~450 графа/s |
|
||||
|
||||
## Протоколни Бенчмаркове
|
||||
|
||||
| Протокол | Връзки | Заявки/s | Латентност p99 |
|
||||
|----------|--------|----------|----------------|
|
||||
| Бинарен (localhost) | 1 | 45,000 | 0.4 ms |
|
||||
| Бинарен (localhost) | 100 | 380,000 | 1.2 ms |
|
||||
| HTTP/REST | 1 | 12,000 | 2.1 ms |
|
||||
| HTTP/REST | 100 | 95,000 | 5.8 ms |
|
||||
| WebSocket | 1 | 18,000 | 1.8 ms |
|
||||
|
||||
## Вертикален Мащабинг
|
||||
|
||||
| Ядра | LSM Запис | LSM Четене | Векторно Търсене |
|
||||
|------|-----------|------------|-------------------|
|
||||
| 1 | 580K | 720K | 2.0 ms |
|
||||
| 4 | 1.9M | 2.6M | 1.1 ms |
|
||||
| 8 | 3.4M | 4.8M | 0.7 ms |
|
||||
| 16 | 5.8M | 7.2M | 0.5 ms |
|
||||
|
||||
## Ръководство за Настройка
|
||||
|
||||
### За Write-Heavy Натоварвания
|
||||
|
||||
```bash
|
||||
export BARADB_MEMTABLE_SIZE_MB=256
|
||||
export BARADB_WAL_SYNC_INTERVAL_MS=10
|
||||
export BARADB_COMPACTION_INTERVAL_MS=30000
|
||||
```
|
||||
|
||||
### За Read-Heavy Натоварвания
|
||||
|
||||
```bash
|
||||
export BARADB_CACHE_SIZE_MB=1024
|
||||
export BARADB_BLOOM_BITS_PER_KEY=10
|
||||
export BARADB_COMPACTION_INTERVAL_MS=120000
|
||||
```
|
||||
|
||||
### За Векторно Търсене
|
||||
|
||||
```bash
|
||||
export BARADB_VECTOR_EF_CONSTRUCTION=200
|
||||
export BARADB_VECTOR_EF_SEARCH=128
|
||||
export BARADB_VECTOR_M=32
|
||||
```
|
||||
|
||||
### За Графова Аналитика
|
||||
|
||||
```bash
|
||||
export BARADB_GRAPH_PAGE_RANK_ITERATIONS=20
|
||||
export BARADB_GRAPH_LOUVAIN_RESOLUTION=1.0
|
||||
```
|
||||
@@ -0,0 +1,373 @@
|
||||
# Протоколна Референция
|
||||
|
||||
BaraDB поддържа множество протоколи за клиентска комуникация:
|
||||
- **Бинарен Wire Протокол** — високопроизводителен, ниска латентност
|
||||
- **HTTP/REST API** — езиково-независим, лесен за дебъгване
|
||||
- **WebSocket** — стрийминг и pub/sub
|
||||
|
||||
---
|
||||
|
||||
## Бинарен Wire Протокол
|
||||
|
||||
Бинарният протокол използва big-endian кодиране за всички многобайтови стойности.
|
||||
|
||||
### Жизнен Цикъл на Връзката
|
||||
|
||||
```
|
||||
Клиент Сървър
|
||||
| |
|
||||
|─── TCP свързване ───────────>|
|
||||
|<── TLS ръкостискане (опц.) ──|
|
||||
|─── Auth съобщение ──────────>|
|
||||
|<── Auth_OK / Грешка ─────────|
|
||||
|─── Query съобщение ─────────>|
|
||||
|<── Data / Complete / Error ──|
|
||||
|─── Close съобщение ─────────>|
|
||||
|<── TCP затваряне ────────────|
|
||||
```
|
||||
|
||||
### Формат на Съобщенията
|
||||
|
||||
Всяко съобщение започва с 12-байтов хедър:
|
||||
|
||||
```
|
||||
┌─────────────┬─────────────┬─────────────┬─────────────────────┐
|
||||
│ Kind │ Length │ RequestId │ Payload │
|
||||
│ (4 bytes) │ (4 bytes) │ (4 bytes) │ │
|
||||
│ uint32 BE │ uint32 BE │ uint32 BE │ (Length bytes) │
|
||||
└─────────────┴─────────────┴─────────────┴─────────────────────┘
|
||||
```
|
||||
|
||||
### Типове Съобщения
|
||||
|
||||
| Тип | ID | Посока | Описание |
|
||||
|------|----|--------|----------|
|
||||
| Query | 0x01 | К→С | Изпълни заявка |
|
||||
| QueryParams | 0x02 | К→С | Параметризирана заявка |
|
||||
| Auth | 0x07 | К→С | Заявка за автентикация |
|
||||
| Ping | 0x09 | К→С | Keepalive ping |
|
||||
| Close | 0x0A | К→С | Затваряне на връзка |
|
||||
| Data | 0x81 | С→К | Резултат от заявка |
|
||||
| Complete | 0x82 | С→К | Заявката е завършена |
|
||||
| Auth_OK | 0x83 | С→К | Успешна автентикация |
|
||||
| Pong | 0x84 | С→К | Keepalive отговор |
|
||||
| Error | 0x06 | С→К | Грешка |
|
||||
|
||||
### Query Съобщение Payload
|
||||
|
||||
```
|
||||
┌───────────────────┬────────────────────────────┐
|
||||
│ Query String │ │
|
||||
│ (променлива) │ │
|
||||
│ UTF-8 │ │
|
||||
└───────────────────┴────────────────────────────┘
|
||||
```
|
||||
|
||||
### Data Съобщение Payload
|
||||
|
||||
```
|
||||
┌──────────────┬─────────────────────────────────────────────┐
|
||||
│ Брой Колони │ Дефиниции на Колони + Данни от Редове │
|
||||
│ (4 bytes) │ │
|
||||
│ uint32 BE │ │
|
||||
└──────────────┴─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Типове Полета
|
||||
|
||||
| Тип | ID | Размер | Описание |
|
||||
|------|----|--------|----------|
|
||||
| NULL | 0x00 | 0 | NULL стойност |
|
||||
| BOOL | 0x01 | 1 | true/false |
|
||||
| INT8 | 0x02 | 1 | Signed 8-bit integer |
|
||||
| INT16 | 0x03 | 2 | Signed 16-bit integer |
|
||||
| INT32 | 0x04 | 4 | Signed 32-bit integer |
|
||||
| INT64 | 0x05 | 8 | Signed 64-bit integer |
|
||||
| FLOAT32 | 0x06 | 4 | IEEE 754 единична точност (big-endian) |
|
||||
| FLOAT64 | 0x07 | 8 | IEEE 754 двойна точност (big-endian) |
|
||||
| STRING | 0x08 | променлив | UTF-8 низ (4-байтов префикс за дължина) |
|
||||
| BYTES | 0x09 | променлив | Сурови байтове (4-байтов префикс за дължина) |
|
||||
| ARRAY | 0x0A | променлив | Масив от стойности |
|
||||
| OBJECT | 0x0B | променлив | Ключ-стойност обект |
|
||||
| VECTOR | 0x0C | променлив | Float32 масив (4-байтов префикс, big-endian floats) |
|
||||
|
||||
### Error Съобщение Payload
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────┬────────────────────────────┐
|
||||
│ Код Грешка │ Дълж. Съобщ. │ Съобщение за Грешка │
|
||||
│ (4 bytes) │ (4 bytes) │ (Дълж. Съобщ. bytes) │
|
||||
│ uint32 BE │ uint32 BE │ UTF-8 │
|
||||
└──────────────┴──────────────┴────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HTTP/REST API
|
||||
|
||||
Базов URL: `http://localhost:9470/api/v1`
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### Health
|
||||
|
||||
```http
|
||||
GET /health
|
||||
```
|
||||
|
||||
Отговор:
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"version": "1.1.0",
|
||||
"uptime_seconds": 86400
|
||||
}
|
||||
```
|
||||
|
||||
#### Ready
|
||||
|
||||
```http
|
||||
GET /ready
|
||||
```
|
||||
|
||||
Връща `200` когато е готов, `503` при стартиране.
|
||||
|
||||
#### Query
|
||||
|
||||
```http
|
||||
POST /query
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <token>
|
||||
|
||||
{
|
||||
"query": "SELECT name, age FROM users WHERE age > 18",
|
||||
"params": [],
|
||||
"format": "json"
|
||||
}
|
||||
```
|
||||
|
||||
Отговор:
|
||||
```json
|
||||
{
|
||||
"columns": ["name", "age"],
|
||||
"rows": [
|
||||
["Alice", 30],
|
||||
["Bob", 25]
|
||||
],
|
||||
"row_count": 2,
|
||||
"duration_ms": 12
|
||||
}
|
||||
```
|
||||
|
||||
#### Batch
|
||||
|
||||
```http
|
||||
POST /batch
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"queries": [
|
||||
"INSERT users { name := 'Alice', age := 30 }",
|
||||
"INSERT users { name := 'Bob', age := 25 }"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Отговор:
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{"status": "ok", "affected_rows": 1},
|
||||
{"status": "ok", "affected_rows": 1}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Schema
|
||||
|
||||
```http
|
||||
GET /schema
|
||||
```
|
||||
|
||||
#### Metrics
|
||||
|
||||
```http
|
||||
GET /metrics
|
||||
```
|
||||
|
||||
Prometheus-съвместими метрики. Виж [Ръководство за Мониторинг](monitoring.md).
|
||||
|
||||
#### Explain
|
||||
|
||||
```http
|
||||
POST /explain
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"query": "SELECT * FROM users WHERE age > 18"
|
||||
}
|
||||
```
|
||||
|
||||
Отговор:
|
||||
```json
|
||||
{
|
||||
"plan": "IndexScan",
|
||||
"index": "idx_users_age",
|
||||
"estimated_rows": 42,
|
||||
"cost": 120
|
||||
}
|
||||
```
|
||||
|
||||
#### Backup
|
||||
|
||||
```http
|
||||
POST /backup
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"destination": "/backup/snapshot.db"
|
||||
}
|
||||
```
|
||||
|
||||
#### Административни Операции
|
||||
|
||||
```http
|
||||
POST /admin/compact
|
||||
POST /admin/rebalance
|
||||
POST /admin/check
|
||||
```
|
||||
|
||||
### HTTP Статус Кодове
|
||||
|
||||
| Код | Значение |
|
||||
|------|----------|
|
||||
| 200 | Успех |
|
||||
| 400 | Лоша заявка (синтактична грешка) |
|
||||
| 401 | Неоторизиран (изисква се auth) |
|
||||
| 403 | Забранен (недостатъчни права) |
|
||||
| 404 | Не е намерен (таблица/тип не съществува) |
|
||||
| 429 | Твърде много заявки (rate limited) |
|
||||
| 500 | Вътрешна сървърна грешка |
|
||||
| 503 | Услугата е недостъпна (стартира) |
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Протокол
|
||||
|
||||
URL: `ws://localhost:9471`
|
||||
|
||||
### Формат на Frame
|
||||
|
||||
WebSocket текстови frame-ове съдържат JSON съобщения:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"type": "query",
|
||||
"query": "SELECT * FROM users"
|
||||
}
|
||||
```
|
||||
|
||||
### Типове Съобщения
|
||||
|
||||
| Тип | Посока | Описание |
|
||||
|------|--------|----------|
|
||||
| `query` | К→С | Изпълни заявка |
|
||||
| `subscribe` | К→С | Абониране за промени |
|
||||
| `unsubscribe` | К→С | Отписване |
|
||||
| `ping` | К→С | Keepalive |
|
||||
| `result` | С→К | Резултат от заявка |
|
||||
| `notification` | С→К | Известие за промяна |
|
||||
| `error` | С→К | Грешка |
|
||||
| `pong` | С→К | Keepalive отговор |
|
||||
|
||||
### Pub/Sub Пример
|
||||
|
||||
```javascript
|
||||
const ws = new WebSocket('ws://localhost:9471');
|
||||
|
||||
ws.onopen = () => {
|
||||
// Абониране за промени в таблица
|
||||
ws.send(JSON.stringify({
|
||||
id: 1,
|
||||
type: 'subscribe',
|
||||
table: 'users'
|
||||
}));
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const msg = JSON.parse(event.data);
|
||||
if (msg.type === 'notification') {
|
||||
console.log('Промяна:', msg.operation, msg.data);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Стрийминг Заявки
|
||||
|
||||
```javascript
|
||||
ws.send(JSON.stringify({
|
||||
id: 2,
|
||||
type: 'query',
|
||||
query: 'SELECT * FROM logs ORDER BY timestamp',
|
||||
streaming: true
|
||||
}));
|
||||
|
||||
// Сървърът изпраща множество result frame-ове
|
||||
// Последният frame има {"complete": true}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nim API Примери
|
||||
|
||||
### Бинарен Протокол
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/wire
|
||||
|
||||
let msg = makeQueryMessage(1, "SELECT * FROM users")
|
||||
let ready = makeReadyMessage(1)
|
||||
let error = makeErrorMessage(1, 42, "Syntax error")
|
||||
```
|
||||
|
||||
### Connection Pool
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/pool
|
||||
|
||||
var pool = newConnectionPool(
|
||||
minConnections = 5,
|
||||
maxConnections = 100,
|
||||
idleTimeout = 30000
|
||||
)
|
||||
let conn = pool.acquire()
|
||||
# Използване на връзка...
|
||||
pool.release(conn)
|
||||
```
|
||||
|
||||
### Автентикация
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/auth
|
||||
|
||||
var am = newAuthManager("secret-key")
|
||||
let token = am.createToken(JWTClaims(sub: "user1", role: "admin"))
|
||||
let result = am.validateCredentials(
|
||||
AuthCredentials(authMethod: amToken, payload: token)
|
||||
)
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/ratelimit
|
||||
|
||||
var rl = newRateLimiter(
|
||||
rlaTokenBucket,
|
||||
globalRate = 10000,
|
||||
perClientRate = 1000
|
||||
)
|
||||
if rl.allowRequest("client-123"):
|
||||
echo "Заявката е разрешена"
|
||||
```
|
||||
@@ -0,0 +1,210 @@
|
||||
# Ръководство за Сигурност
|
||||
|
||||
## TLS/SSL Криптиране
|
||||
|
||||
BaraDB поддържа TLS 1.3 за всички протоколи (бинарен, HTTP, WebSocket). Ако не е предоставен сертификат, сървърът автоматично генерира self-signed сертификат при стартиране за криптиране без конфигурация.
|
||||
|
||||
### Използване на Персонализирани Сертификати
|
||||
|
||||
```bash
|
||||
# Предоставяне на съществуващи сертификати
|
||||
BARADB_TLS_ENABLED=true \
|
||||
BARADB_CERT_FILE=/etc/baradb/server.crt \
|
||||
BARADB_KEY_FILE=/etc/baradb/server.key \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
### Генериране на Self-Signed Сертификати
|
||||
|
||||
```bash
|
||||
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \
|
||||
-days 365 -nodes -subj "/CN=localhost"
|
||||
```
|
||||
|
||||
### Let's Encrypt (Продукция)
|
||||
|
||||
Използвайте certbot и насочете BaraDB към генерираните файлове:
|
||||
|
||||
```bash
|
||||
sudo certbot certonly --standalone -d db.example.com
|
||||
|
||||
BARADB_CERT_FILE=/etc/letsencrypt/live/db.example.com/fullchain.pem \
|
||||
BARADB_KEY_FILE=/etc/letsencrypt/live/db.example.com/privkey.pem \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
### TLS от Страна на Клиента
|
||||
|
||||
```python
|
||||
from baradb import Client
|
||||
|
||||
client = Client("localhost", 9472, tls=True, tls_verify=True)
|
||||
client.connect()
|
||||
```
|
||||
|
||||
## Автентикация
|
||||
|
||||
### JWT-Базирана Автентикация
|
||||
|
||||
BaraDB използва JWT (JSON Web Tokens) с HMAC-SHA256 подписване.
|
||||
|
||||
#### Включване на Автентикация
|
||||
|
||||
```bash
|
||||
BARADB_AUTH_ENABLED=true \
|
||||
BARADB_JWT_SECRET="$(openssl rand -hex 32)" \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
#### Създаване на Токени
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/auth
|
||||
|
||||
var am = newAuthManager("your-secret-key")
|
||||
let token = am.createToken(JWTClaims(
|
||||
sub: "user1",
|
||||
role: "admin",
|
||||
exp: getTime() + 24.hours
|
||||
))
|
||||
```
|
||||
|
||||
#### Контрол на Достъп на База Роли
|
||||
|
||||
| Роля | Права |
|
||||
|------|-------|
|
||||
| `admin` | Пълен достъп |
|
||||
| `write` | Четене + запис |
|
||||
| `read` | Само четене |
|
||||
| `monitor` | Само метрики и health |
|
||||
|
||||
#### Използване на Токени
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:9470/api/query \
|
||||
-d '{"query": "SELECT * FROM users"}'
|
||||
```
|
||||
|
||||
```python
|
||||
from baradb import Client
|
||||
|
||||
client = Client("localhost", 9472)
|
||||
client.connect()
|
||||
client.authenticate("eyJhbGciOiJIUzI1NiIs...")
|
||||
```
|
||||
|
||||
### Многофакторна Автентикация (MFA)
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/auth
|
||||
|
||||
var am = newAuthManager("secret-key")
|
||||
# TOTP-базирана MFA
|
||||
let mfaCode = am.generateTOTP("user1")
|
||||
let valid = am.validateTOTP("user1", mfaCode)
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
Token-bucket rate limiting предотвратява злоупотреби:
|
||||
|
||||
```nim
|
||||
import barabadb/protocol/ratelimit
|
||||
|
||||
var rl = newRateLimiter(
|
||||
rlaTokenBucket,
|
||||
globalRate = 10000, # 10K заявки/s глобално
|
||||
perClientRate = 1000, # 1K заявки/s на IP/токен
|
||||
burstSize = 100 # Разрешаване на 100 заявки burst
|
||||
)
|
||||
|
||||
if not rl.allowRequest("client-ip"):
|
||||
return error("Лимитът на заявки е надвишен")
|
||||
```
|
||||
|
||||
## Мрежова Сигурност
|
||||
|
||||
### Адрес за Свързване
|
||||
|
||||
По подразбиране BaraDB се свързва към `127.0.0.1` (само localhost). За продукция:
|
||||
|
||||
```bash
|
||||
# Свързване към всички интерфейси (зад защитна стена или reverse proxy)
|
||||
BARADB_ADDRESS=0.0.0.0 ./build/baradadb
|
||||
|
||||
# Свързване към конкретен вътрешен интерфейс
|
||||
BARADB_ADDRESS=10.0.0.5 ./build/baradadb
|
||||
```
|
||||
|
||||
### Правила на Защитната Стена
|
||||
|
||||
```bash
|
||||
# Разрешаване само на сървъри на приложения
|
||||
sudo ufw allow from 10.0.0.0/8 to any port 9472
|
||||
sudo ufw allow from 10.0.0.0/8 to any port 9470
|
||||
|
||||
# Блокиране на външен достъп до портове за управление
|
||||
sudo ufw deny 9471 # WebSocket (само вътрешна употреба)
|
||||
```
|
||||
|
||||
## Криптиране на Данни в Покой
|
||||
|
||||
### Криптиране на Ниво ОС
|
||||
|
||||
Използвайте LUKS за пълно дисково криптиране:
|
||||
|
||||
```bash
|
||||
cryptsetup luksFormat /dev/nvme0n1p2
|
||||
cryptsetup open /dev/nvme0n1p2 baradb-crypt
|
||||
mkfs.ext4 /dev/mapper/baradb-crypt
|
||||
mount /dev/mapper/baradb-crypt /var/lib/baradb
|
||||
```
|
||||
|
||||
### Криптиране на Ниво Приложение
|
||||
|
||||
BaraDB поддържа прозрачно криптиране на SSTable файлове:
|
||||
|
||||
```bash
|
||||
BARADB_STORAGE_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
## Одитно Логване
|
||||
|
||||
Всички заявки и административни действия се логват:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2025-01-15T10:30:00Z",
|
||||
"level": "info",
|
||||
"event": "query_executed",
|
||||
"client_ip": "10.0.0.15",
|
||||
"user": "app_user",
|
||||
"query": "SELECT * FROM users WHERE id = ?",
|
||||
"duration_ms": 12,
|
||||
"rows_returned": 1
|
||||
}
|
||||
```
|
||||
|
||||
Включване на одитно логване:
|
||||
|
||||
```bash
|
||||
BARADB_LOG_LEVEL=info \
|
||||
BARADB_LOG_FORMAT=json \
|
||||
BARADB_LOG_FILE=/var/log/baradb/audit.log \
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
## Чеклист за Сигурност
|
||||
|
||||
- [ ] Сменете JWT secret по подразбиране
|
||||
- [ ] Включете TLS с валидни сертификати
|
||||
- [ ] Свържете се към конкретни интерфейси
|
||||
- [ ] Включете автентикация в продукция
|
||||
- [ ] Конфигурирайте rate limiting
|
||||
- [ ] Включете одитно логване
|
||||
- [ ] Криптирайте данните в покой (LUKS или на ниво приложение)
|
||||
- [ ] Стартирайте BaraDB като non-root потребител
|
||||
- [ ] Поддържайте рестриктивни правила на защитната стена
|
||||
- [ ] Ротирайте JWT secret-и редовно
|
||||
@@ -0,0 +1,78 @@
|
||||
# Storage Engines
|
||||
|
||||
BaraDB предоставя множество storage двигатели, оптимизирани за различни модели на достъп.
|
||||
|
||||
## LSM-Tree (Key-Value)
|
||||
|
||||
Основният storage engine с write-оптимизирана append-only лог структура.
|
||||
|
||||
### Употреба
|
||||
|
||||
```nim
|
||||
import barabadb/storage/lsm
|
||||
|
||||
var db = newLSMTree("./data")
|
||||
db.put("key1", cast[seq[byte]]("value1"))
|
||||
let (found, value) = db.get("key1")
|
||||
db.close()
|
||||
```
|
||||
|
||||
### Компоненти
|
||||
|
||||
- **MemTable**: В-памет сортиран буфер
|
||||
- **WAL**: Write-ahead log за устойчивост
|
||||
- **SSTable**: Сортирани string таблици на диска
|
||||
- **Bloom Filter**: Вероятностна проверка за принадлежност
|
||||
- **Compaction**: Size-tiered стратегия с управление на нива
|
||||
- **Page Cache**: LRU кеш с проследяване на hit rate
|
||||
|
||||
## B-Tree Индекс
|
||||
|
||||
Подреден индекс за range сканиране и точково търсене.
|
||||
|
||||
### Употреба
|
||||
|
||||
```nim
|
||||
import barabadb/storage/btree
|
||||
|
||||
var btree = newBTreeIndex[string, string]()
|
||||
btree.insert("key1", "value1")
|
||||
let values = btree.get("key1")
|
||||
let range = btree.scan("key_a", "key_z")
|
||||
```
|
||||
|
||||
## Write-Ahead Log (WAL)
|
||||
|
||||
Осигурява устойчивост на операциите за запис.
|
||||
|
||||
```nim
|
||||
import barabadb/storage/wal
|
||||
|
||||
var wal = newWAL("./wal")
|
||||
wal.append("txn1", "SET key1 value1")
|
||||
wal.flush()
|
||||
```
|
||||
|
||||
## Bloom Filter
|
||||
|
||||
Вероятностна структура от данни за бързи негативни проверки.
|
||||
|
||||
```nim
|
||||
import barabadb/storage/bloom
|
||||
|
||||
var filter = newBloomFilter(10000, 0.01)
|
||||
filter.add("key1")
|
||||
if filter.mightContain("key1"):
|
||||
echo "евентуално съществува"
|
||||
```
|
||||
|
||||
## Memory-mapped I/O
|
||||
|
||||
Ефективен достъп до файлове чрез mmap.
|
||||
|
||||
```nim
|
||||
import barabadb/storage/mmap
|
||||
|
||||
var mapped = mmapFile("./data/file.dat")
|
||||
let data = mapped.read(0, 100)
|
||||
```
|
||||
+46
-12
@@ -1,6 +1,6 @@
|
||||
# Транзакции & MVCC
|
||||
# Транзакции и MVCC
|
||||
|
||||
Multi-Version Concurrency Control със snapshot изолация.
|
||||
MVCC (Multi-Version Concurrency Control) със snapshot изолация и deadlock детекция.
|
||||
|
||||
## Употреба
|
||||
|
||||
@@ -10,21 +10,55 @@ import barabadb/core/mvcc
|
||||
var tm = newTxnManager()
|
||||
let txn = tm.beginTxn()
|
||||
|
||||
# Операции за запис
|
||||
discard tm.write(txn, "key1", cast[seq[byte]]("value1"))
|
||||
discard tm.write(txn, "key2", cast[seq[byte]]("value2"))
|
||||
|
||||
# Savepoint
|
||||
tm.savepoint(txn)
|
||||
discard tm.rollbackToSavepoint(txn)
|
||||
discard tm.write(txn, "key3", cast[seq[byte]]("value3"))
|
||||
discard tm.rollbackToSavepoint(txn) # отмяна на key3
|
||||
|
||||
# Commit
|
||||
discard tm.commit(txn)
|
||||
```
|
||||
|
||||
## Изолация
|
||||
## Изолация на Транзакции
|
||||
|
||||
BaraDB използва **snapshot isolation**:
|
||||
- Читателите не блокират писатели
|
||||
- Писателите не блокират читатели
|
||||
- Всяка транзакция вижда консистентен моментна снимка
|
||||
BaraDB използва **snapshot изолация**:
|
||||
- Четящите не блокират пишещите
|
||||
- Пишещите не блокират четящите
|
||||
- Всяка транзакция вижда консистентен snapshot
|
||||
|
||||
## Deadlock Детекция
|
||||
|
||||
```nim
|
||||
import barabadb/core/deadlock
|
||||
|
||||
var detector = newDeadlockDetector()
|
||||
if detector.detectCycle(txn1, txn2):
|
||||
echo "Открит deadlock!"
|
||||
```
|
||||
|
||||
## Write-Ahead Log
|
||||
|
||||
```nim
|
||||
import barabadb/storage/wal
|
||||
|
||||
var wal = newWAL("./wal")
|
||||
wal.append(txnId, "SET key value")
|
||||
wal.flush()
|
||||
```
|
||||
|
||||
## Savepoints
|
||||
|
||||
Вложени savepoints на транзакции:
|
||||
|
||||
```nim
|
||||
tm.savepoint(txn, "sp1")
|
||||
# ... операции ...
|
||||
tm.rollbackToSavepoint(txn, "sp1")
|
||||
```
|
||||
|
||||
## Формална Верификация
|
||||
|
||||
@@ -32,13 +66,13 @@ MVCC / Snapshot Isolation протоколът е формално специф
|
||||
|
||||
- **Спецификация:** `formal-verification/mvcc.tla`
|
||||
- **Проверени свойства:**
|
||||
- `NoDirtyReads` — транзакциите никога не четат неcommit-нати данни
|
||||
- `NoDirtyReads` — транзакциите никога не четат некомитнати данни
|
||||
- `ReadOwnWrites` — транзакциите винаги виждат собствените си записи
|
||||
- `WriteWriteConflict` — first-committer-wins
|
||||
- `WriteWriteConflict` — first-committer-wins (няма две комитнати транзакции да пишат един и същ ключ)
|
||||
|
||||
Пускане на TLC:
|
||||
Пускане на TLC локално:
|
||||
|
||||
```bash
|
||||
cd formal-verification
|
||||
java -cp tla2tools.jar tlc2.TLC -config models/mvcc.cfg mvcc.tla
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
# Ръководство за Отстраняване на Проблеми
|
||||
|
||||
## Проблеми с Инсталацията
|
||||
|
||||
### Nim не е намерен
|
||||
|
||||
```
|
||||
nim: command not found
|
||||
```
|
||||
|
||||
**Решение:**
|
||||
|
||||
```bash
|
||||
# Linux/macOS
|
||||
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
|
||||
|
||||
# Добавяне към PATH
|
||||
echo 'export PATH=$HOME/.nimble/bin:$PATH' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### SSL Грешка при Компилация
|
||||
|
||||
```
|
||||
Error: BaraDB requires SSL support. Compile with -d:ssl
|
||||
```
|
||||
|
||||
**Решение:** Винаги компилирайте с `-d:ssl`:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### Липсващи Зависимости
|
||||
|
||||
```
|
||||
Error: cannot open file: hunos
|
||||
```
|
||||
|
||||
**Решение:**
|
||||
|
||||
```bash
|
||||
nimble install -d -y
|
||||
```
|
||||
|
||||
## Проблеми по Време на Изпълнение
|
||||
|
||||
### Портът е Зает
|
||||
|
||||
```
|
||||
Error: unhandled exception: Address already in use [OSError]
|
||||
```
|
||||
|
||||
**Решение 1:** Сменете порта:
|
||||
|
||||
```bash
|
||||
BARADB_PORT=5433 ./build/baradadb
|
||||
```
|
||||
|
||||
**Решение 2:** Убийте съществуващия процес:
|
||||
|
||||
```bash
|
||||
lsof -ti:9472 | xargs kill -9
|
||||
```
|
||||
|
||||
### Permission Denied на Директория с Данни
|
||||
|
||||
```
|
||||
Error: Permission denied: ./data
|
||||
```
|
||||
|
||||
**Решение:**
|
||||
|
||||
```bash
|
||||
mkdir -p ./data
|
||||
chmod 755 ./data
|
||||
# или използвайте друга директория
|
||||
BARADB_DATA_DIR=/tmp/baradb ./build/baradadb
|
||||
```
|
||||
|
||||
## Storage Проблеми
|
||||
|
||||
### Бавни Заявки
|
||||
|
||||
1. Проверете cache hit rate:
|
||||
```bash
|
||||
curl http://localhost:9470/metrics | grep cache_hit_rate
|
||||
```
|
||||
|
||||
2. Пуснете ръчен compaction:
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/admin/compact
|
||||
```
|
||||
|
||||
3. Проверете броя на SSTables:
|
||||
```bash
|
||||
curl http://localhost:9470/metrics | grep sstables
|
||||
```
|
||||
|
||||
### Растящо Дисково Пространство
|
||||
|
||||
```bash
|
||||
# Проверете размера на директорията с данни
|
||||
du -sh ./data
|
||||
|
||||
# Проверете WAL размера
|
||||
du -sh ./data/server/wal
|
||||
|
||||
# Пуснете ръчен compaction за освобождаване на място
|
||||
curl -X POST http://localhost:9470/admin/compact
|
||||
```
|
||||
|
||||
## Проблеми с Автентикация
|
||||
|
||||
### Грешка при Верификация на JWT
|
||||
|
||||
```json
|
||||
{"error": {"code": "AUTH_REQUIRED", "message": "Authentication required"}}
|
||||
```
|
||||
|
||||
**Решение:** Уверете се, че изпращате правилния токен:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
http://localhost:9470/api/query \
|
||||
-d '{"query": "SELECT 1"}'
|
||||
```
|
||||
|
||||
## Разпределени Проблеми
|
||||
|
||||
### Възел не се Присъединява към Клъстер
|
||||
|
||||
1. Проверете мрежовата свързаност между възлите
|
||||
2. Проверете gossip порта (raft порт + 100)
|
||||
3. Проверете логовете за грешки при gossip
|
||||
|
||||
### Репликационно Закъснение
|
||||
|
||||
```bash
|
||||
# Проверете replication lag
|
||||
curl http://localhost:9470/metrics/cluster | jq .replication_lag_ms
|
||||
```
|
||||
|
||||
## Често Задавани Въпроси
|
||||
|
||||
### Как да нулирам напълно базата данни?
|
||||
|
||||
```bash
|
||||
# Спрете сървъра
|
||||
# Изтрийте директорията с данни
|
||||
rm -rf ./data
|
||||
# Стартирайте отново — нова празна база ще бъде създадена
|
||||
./build/baradadb
|
||||
```
|
||||
|
||||
### Как да мигрирам от друга база данни?
|
||||
|
||||
BaraDB поддържа импорт чрез JSON и CSV:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9470/api/import \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"table": "users", "format": "json", "data": [...]}'
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
# Потребителски Функции (UDF)
|
||||
|
||||
Разширете BaraQL с персонализирани функции.
|
||||
|
||||
## Употреба
|
||||
|
||||
```nim
|
||||
import barabadb/query/udf
|
||||
|
||||
var reg = newUDFRegistry()
|
||||
|
||||
# Регистриране на стандартна библиотека
|
||||
reg.registerStdlib() # abs, sqrt, pow, lower, upper, len, trim, substr, toString, toInt
|
||||
|
||||
# Персонализирана функция
|
||||
reg.register("greet", @[UDFParam(name: "name", typeName: "str")],
|
||||
"str", proc(args: seq[Value]): Value =
|
||||
return Value(kind: vkString, strVal: "Hello, " & args[0].strVal & "!"))
|
||||
```
|
||||
|
||||
## Функции от Стандартната Библиотека
|
||||
|
||||
| Функция | Описание | Пример |
|
||||
|----------|----------|--------|
|
||||
| `abs(n)` | Абсолютна стойност | `abs(-5)` → 5 |
|
||||
| `sqrt(n)` | Квадратен корен | `sqrt(16)` → 4 |
|
||||
| `pow(n, e)` | Степенуване | `pow(2, 3)` → 8 |
|
||||
| `lower(s)` | Малки букви | `lower('ABC')` → 'abc' |
|
||||
| `upper(s)` | Главни букви | `upper('abc')` → 'ABC' |
|
||||
| `len(s)` | Дължина | `len('hello')` → 5 |
|
||||
| `trim(s)` | Премахване на интервали | `trim(' hello ')` → 'hello' |
|
||||
| `substr(s, start, len)` | Подниз | `substr('hello', 0, 3)` → 'hel' |
|
||||
| `toString(n)` | Конвертиране в низ | `toString(123)` → '123' |
|
||||
| `toInt(s)` | Конвертиране в integer | `toInt('123')` → 123 |
|
||||
|
||||
## Регистриране на Функции
|
||||
|
||||
```nim
|
||||
reg.register(
|
||||
name: "my_function",
|
||||
params: @[
|
||||
UDFParam(name: "arg1", typeName: "str"),
|
||||
UDFParam(name: "arg2", typeName: "int32")
|
||||
],
|
||||
returnType: "str",
|
||||
body: proc(args: seq[Value]): Value =
|
||||
result = Value(kind: vkString, strVal: "")
|
||||
)
|
||||
```
|
||||
|
||||
## Използване на UDF в Заявки
|
||||
|
||||
```sql
|
||||
SELECT greet(name) FROM users;
|
||||
```
|
||||
+130
-14
@@ -1,8 +1,89 @@
|
||||
# Vector Търсене
|
||||
# Vector Search Engine
|
||||
|
||||
HNSW и IVF-PQ индекси за търсене на прилика.
|
||||
Нативни HNSW и IVF-PQ индекси за търсене на прилика с пълна SQL интеграция.
|
||||
|
||||
## Употреба
|
||||
## SQL Употреба
|
||||
|
||||
### Създаване на Векторни Колони
|
||||
|
||||
```sql
|
||||
CREATE TABLE items (
|
||||
id INT PRIMARY KEY,
|
||||
embedding VECTOR(768)
|
||||
);
|
||||
```
|
||||
|
||||
Типът `VECTOR(n)` съхранява float32 масиви с фиксирана размерност `n`.
|
||||
|
||||
### Вмъкване на Вектори
|
||||
|
||||
```sql
|
||||
INSERT INTO items (id, embedding) VALUES (1, '[0.1, 0.2, 0.3, ...]');
|
||||
```
|
||||
|
||||
### Функции за Векторно Разстояние
|
||||
|
||||
```sql
|
||||
-- Косинусово разстояние (0 = идентични, 1 = ортогонални)
|
||||
SELECT id, cosine_distance(embedding, '[0.1, 0.2, 0.3]') AS dist
|
||||
FROM items;
|
||||
|
||||
-- Евклидово / L2 разстояние
|
||||
SELECT id, euclidean_distance(embedding, '[0.1, 0.2, 0.3]') AS dist
|
||||
FROM items;
|
||||
|
||||
-- L2 разстояние с <-> оператор
|
||||
SELECT id, embedding <-> '[0.1, 0.2, 0.3]' AS dist
|
||||
FROM items;
|
||||
|
||||
-- Скаларно произведение (отрицателно dot product за минимизация)
|
||||
SELECT id, inner_product(embedding, '[0.1, 0.2, 0.3]') AS dist
|
||||
FROM items;
|
||||
|
||||
-- Манхатън / L1 разстояние
|
||||
SELECT id, l1_distance(embedding, '[0.1, 0.2, 0.3]') AS dist
|
||||
FROM items;
|
||||
```
|
||||
|
||||
### Търсене на Най-близки Съседи
|
||||
|
||||
```sql
|
||||
-- Топ-10 най-близки съседи по косинусово разстояние
|
||||
SELECT id FROM items
|
||||
ORDER BY cosine_distance(embedding, '[0.1, 0.2, 0.3]') ASC
|
||||
LIMIT 10;
|
||||
|
||||
-- Топ-5 най-близки съседи по евклидово разстояние
|
||||
SELECT id FROM items
|
||||
ORDER BY embedding <-> '[0.1, 0.2, 0.3]'
|
||||
LIMIT 5;
|
||||
```
|
||||
|
||||
### Векторни Индекси
|
||||
|
||||
```sql
|
||||
-- Създаване на HNSW индекс за приблизително търсене на най-близки съседи
|
||||
CREATE INDEX idx_items_vec ON items(embedding) USING hnsw;
|
||||
|
||||
-- Индексът се поддържа автоматично при INSERT и UPDATE
|
||||
```
|
||||
|
||||
Поддържани индекс методи:
|
||||
- `USING hnsw` — Hierarchical Navigable Small World (по подразбиране: косинусова метрика)
|
||||
- `USING ivfpq` — Inverted File с Product Quantization
|
||||
|
||||
### Валидация на Размерност
|
||||
|
||||
BaraDB валидира размерностите на векторите при вмъкване:
|
||||
|
||||
```sql
|
||||
-- Това ще даде грешка: очаквани 768 размерности, но подадени 3
|
||||
INSERT INTO items (id, embedding) VALUES (2, '[1.0, 2.0, 3.0]');
|
||||
```
|
||||
|
||||
## Нативно Nim API
|
||||
|
||||
За вградена или високопроизводителна употреба използвайте нативното Nim API директно:
|
||||
|
||||
```nim
|
||||
import barabadb/vector/engine
|
||||
@@ -10,32 +91,67 @@ import barabadb/vector/engine
|
||||
var idx = newHNSWIndex(dimensions = 128)
|
||||
idx.insert(1, @[1.0'f32, 0.0'f32, ...], {"category": "A"}.toTable)
|
||||
|
||||
# Търсене
|
||||
let results = idx.search(queryVector, k = 10)
|
||||
|
||||
# С филтриране по метаданни
|
||||
let filtered = idx.searchWithFilter(queryVector, k = 10,
|
||||
filter = proc(meta: Table[string, string]): bool =
|
||||
return meta.getOrDefault("category") == "A")
|
||||
```
|
||||
|
||||
## Индекс Типове
|
||||
## Типове Индекси
|
||||
|
||||
### HNSW
|
||||
|
||||
Иерархичен навигируем малък свят:
|
||||
Иерархичен навигируем малък свят за приблизително търсене на най-близки съседи.
|
||||
|
||||
```nim
|
||||
var hnsw = newHNSWIndex(dimensions = 128, m = 16)
|
||||
var hnsw = newHNSWIndex(
|
||||
dimensions = 128,
|
||||
m = 16, # връзки на слой
|
||||
efConstruction = 200, # ширина на търсене при изграждане
|
||||
efSearch = 100 # ширина на търсене при заявка
|
||||
)
|
||||
```
|
||||
|
||||
### IVF-PQ
|
||||
|
||||
Инвертиран файл с продуктово квантуване:
|
||||
Inverted File Index с продуктово квантуване за компресия.
|
||||
|
||||
```nim
|
||||
var ivfpq = newIVFPQIndex(dimensions = 128, numCentroids = 256)
|
||||
var ivfpq = newIVFPQIndex(
|
||||
dimensions = 128,
|
||||
numCentroids = 256,
|
||||
subQuantizers = 8
|
||||
)
|
||||
```
|
||||
|
||||
## Метрики за Разстояние
|
||||
|
||||
| Метрика | Описание |
|
||||
|---------|----------|
|
||||
| `cosine` | Косинусова прилика |
|
||||
| `euclidean` | L2 разстояние |
|
||||
| `dotproduct` | Скаларно произведение |
|
||||
| `manhattan` | L1 разстояние |
|
||||
| Метрика | SQL Функция | Описание |
|
||||
|---------|-------------|----------|
|
||||
| `cosine` | `cosine_distance(a, b)` | Косинусова dissimilarity (1 - similarity) |
|
||||
| `euclidean` | `euclidean_distance(a, b)` / `<->` | L2 разстояние |
|
||||
| `dotproduct` | `inner_product(a, b)` | Отрицателно скаларно произведение |
|
||||
| `manhattan` | `l1_distance(a, b)` | L1 разстояние |
|
||||
|
||||
## Квантуване
|
||||
|
||||
```nim
|
||||
import barabadb/vector/quant
|
||||
|
||||
# Скаларно квантуване
|
||||
let scalar = scalarQuantize(data, bits = 8)
|
||||
|
||||
# Продуктово квантуване
|
||||
let pq = productQuantize(data, subVectors = 8, bits = 8)
|
||||
```
|
||||
|
||||
## SIMD Ускорение
|
||||
|
||||
```nim
|
||||
import barabadb/vector/simd
|
||||
|
||||
let dist = simdCosineDistance(vec1, vec2)
|
||||
```
|
||||
|
||||
@@ -156,7 +156,7 @@ raft_peers = "node2:9001,node3:9001"
|
||||
```
|
||||
|
||||
```
|
||||
BaraDB v0.1.0 — Multimodal Database Engine
|
||||
BaraDB v1.1.0 — Multimodal Database Engine
|
||||
|
||||
Usage:
|
||||
baradadb [options]
|
||||
|
||||
@@ -144,7 +144,7 @@ strip build/baradadb
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# Expected: BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# Expected: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
@@ -259,7 +259,7 @@ db.close()
|
||||
./build/baradadb
|
||||
|
||||
# Expected output:
|
||||
# BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# Test with HTTP API
|
||||
|
||||
+106
-1
@@ -489,4 +489,109 @@ LIMIT 10;
|
||||
|
||||
-- اجرای موازی
|
||||
SELECT /*+ PARALLEL(4) */ * FROM large_table;
|
||||
```
|
||||
```
|
||||
|
||||
## توابع پنجرهای
|
||||
|
||||
```sql
|
||||
-- توابع رتبهبندی
|
||||
SELECT
|
||||
name,
|
||||
department,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
|
||||
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
|
||||
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
|
||||
FROM employees;
|
||||
|
||||
-- توابع مقدار
|
||||
SELECT
|
||||
name,
|
||||
salary,
|
||||
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
|
||||
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
|
||||
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
|
||||
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
|
||||
FROM employees;
|
||||
|
||||
-- توابع توزیع
|
||||
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
|
||||
```
|
||||
|
||||
### مشخصات فریم
|
||||
|
||||
```sql
|
||||
-- فریم ROWS
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
|
||||
)
|
||||
|
||||
-- فریم RANGE
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
```
|
||||
|
||||
## ERP چند مستأجری
|
||||
|
||||
BaraDB از اجرای چندین شرکت (tenant) در یک نمونه پایگاه داده پشتیبانی میکند، با استفاده از **امنیت سطح سطر (RLS)** همراه با **متغیرهای جلسه**.
|
||||
|
||||
### متغیرهای جلسه
|
||||
|
||||
```sql
|
||||
SET app.tenant_id = 'company-123';
|
||||
SELECT current_setting('app.tenant_id') AS tenant;
|
||||
```
|
||||
|
||||
### کاربر / نقش فعلی
|
||||
|
||||
```sql
|
||||
SELECT current_user AS me, current_role AS my_role;
|
||||
```
|
||||
|
||||
### جداسازی مستأجر با RLS
|
||||
|
||||
```sql
|
||||
-- فعال کردن RLS روی جدول
|
||||
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- ایجاد سیاست فیلترینگ بر اساس مستأجر
|
||||
CREATE POLICY tenant_isolation ON invoices
|
||||
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
|
||||
|
||||
-- هر جلسه فقط دادههای خود را میبیند
|
||||
SET app.tenant_id = 'company-a';
|
||||
SELECT * FROM invoices; -- فقط ردیفهای company-a
|
||||
```
|
||||
|
||||
### چرا چند مستأجری؟
|
||||
|
||||
- **یک نمونه، مستأجران زیاد** — نیازی به اجرای ۱۰۰ پایگاه داده جداگانه نیست
|
||||
- **اسناد JSONB** — ذخیرهسازی با طرح انعطافپذیر، افزودن آسان فیلدها برای هر مستأجر
|
||||
- **RLS تضمین میکند** — پایگاه داده مرزهای مستأجر را اعمال میکند، نه فقط برنامه
|
||||
|
||||
## کلمات کلیدی پشتیبانیشده
|
||||
|
||||
| دسته | کلمات کلیدی |
|
||||
|----------|----------|
|
||||
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
|
||||
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
|
||||
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
|
||||
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
|
||||
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
|
||||
| CTEs | WITH, RECURSIVE, AS |
|
||||
| Case | CASE, WHEN, THEN, ELSE, END |
|
||||
| Transaction | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
|
||||
| Graph | MATCH, RETURN, WHERE, shortestPath, type |
|
||||
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
|
||||
| Vector | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
|
||||
| JSON | ->, ->> |
|
||||
| FTS | @@ (BM25 match) |
|
||||
| Recovery | RECOVER TO TIMESTAMP |
|
||||
| Functions | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
|
||||
| Session | SET, current_setting, current_user, current_role |
|
||||
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
|
||||
| Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |
|
||||
@@ -144,7 +144,7 @@ strip build/baradadb
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# انتظار: BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# انتظار: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## اجرای تستها
|
||||
@@ -259,7 +259,7 @@ db.close()
|
||||
./build/baradadb
|
||||
|
||||
# خروجی مورد انتظار:
|
||||
# BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# تست با HTTP API
|
||||
|
||||
+106
-1
@@ -489,4 +489,109 @@ LIMIT 10;
|
||||
|
||||
-- Параллельное выполнение
|
||||
SELECT /*+ PARALLEL(4) */ * FROM large_table;
|
||||
```
|
||||
```
|
||||
|
||||
## Оконные функции
|
||||
|
||||
```sql
|
||||
-- Функции ранжирования
|
||||
SELECT
|
||||
name,
|
||||
department,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
|
||||
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
|
||||
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
|
||||
FROM employees;
|
||||
|
||||
-- Функции значения
|
||||
SELECT
|
||||
name,
|
||||
salary,
|
||||
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
|
||||
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
|
||||
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
|
||||
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
|
||||
FROM employees;
|
||||
|
||||
-- Функции распределения
|
||||
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
|
||||
```
|
||||
|
||||
### Спецификации фрейма
|
||||
|
||||
```sql
|
||||
-- ROWS фрейм
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
|
||||
)
|
||||
|
||||
-- RANGE фрейм
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
```
|
||||
|
||||
## Мультитенантный ERP
|
||||
|
||||
BaraDB поддерживает работу нескольких компаний (арендаторов) в одной базе данных, используя **безопасность на уровне строк (RLS)** в сочетании с **переменными сессии**.
|
||||
|
||||
### Переменные сессии
|
||||
|
||||
```sql
|
||||
SET app.tenant_id = 'company-123';
|
||||
SELECT current_setting('app.tenant_id') AS tenant;
|
||||
```
|
||||
|
||||
### Текущий пользователь / роль
|
||||
|
||||
```sql
|
||||
SELECT current_user AS me, current_role AS my_role;
|
||||
```
|
||||
|
||||
### Изоляция арендаторов через RLS
|
||||
|
||||
```sql
|
||||
-- Включить RLS на таблице
|
||||
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Создать политику фильтрации по арендатору
|
||||
CREATE POLICY tenant_isolation ON invoices
|
||||
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
|
||||
|
||||
-- Каждая сессия видит только свои данные
|
||||
SET app.tenant_id = 'company-a';
|
||||
SELECT * FROM invoices; -- только строки company-a
|
||||
```
|
||||
|
||||
### Зачем мультитенантность?
|
||||
|
||||
- **Один экземпляр, много арендаторов** — не нужно запускать 100 отдельных баз данных
|
||||
- **JSONB документы** — гибкая схема, легко добавлять поля для каждого арендатора
|
||||
- **RLS гарантирует изоляцию** — база данных обеспечивает границы арендаторов, а не только приложение
|
||||
|
||||
## Поддерживаемые ключевые слова
|
||||
|
||||
| Категория | Ключевые слова |
|
||||
|----------|----------|
|
||||
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
|
||||
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
|
||||
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
|
||||
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
|
||||
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
|
||||
| CTEs | WITH, RECURSIVE, AS |
|
||||
| Case | CASE, WHEN, THEN, ELSE, END |
|
||||
| Transaction | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
|
||||
| Graph | MATCH, RETURN, WHERE, shortestPath, type |
|
||||
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
|
||||
| Vector | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
|
||||
| JSON | ->, ->> |
|
||||
| FTS | @@ (BM25 match) |
|
||||
| Recovery | RECOVER TO TIMESTAMP |
|
||||
| Functions | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
|
||||
| Session | SET, current_setting, current_user, current_role |
|
||||
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
|
||||
| Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |
|
||||
@@ -156,7 +156,7 @@ raft_peers = "node2:9001,node3:9001"
|
||||
```
|
||||
|
||||
```
|
||||
BaraDB v0.1.0 — Multimodal Database Engine
|
||||
BaraDB v1.1.0 — Multimodal Database Engine
|
||||
|
||||
Usage:
|
||||
baradadb [options]
|
||||
|
||||
@@ -144,7 +144,7 @@ strip build/baradadb
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# Ожидается: BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# Ожидается: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## Запуск тестов
|
||||
@@ -259,7 +259,7 @@ db.close()
|
||||
./build/baradadb
|
||||
|
||||
# Ожидаемый вывод:
|
||||
# BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# Проверка через HTTP API
|
||||
|
||||
+164
-5
@@ -98,13 +98,172 @@ RETURN friend.name;
|
||||
```sql
|
||||
BEGIN;
|
||||
INSERT users { name := 'Alice', age := 30 };
|
||||
INSERT orders { user_id := last_insert_id(), total := 100 };
|
||||
COMMIT;
|
||||
|
||||
-- Savepoint ile
|
||||
BEGIN;
|
||||
INSERT users { name := 'Bob', age := 25 };
|
||||
SAVEPOINT sp1;
|
||||
INSERT orders { user_id := last_insert_id(), total := 200 };
|
||||
-- Hata, savepoint'e geri al
|
||||
ROLLBACK TO sp1;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
## Grafik Kalıpları
|
||||
## Tam Metin Arama
|
||||
|
||||
```sql
|
||||
MATCH (p:Person)-[:KNOWS]->(friend:Person)
|
||||
WHERE p.name = 'Alice'
|
||||
RETURN friend.name;
|
||||
```
|
||||
-- Temel arama
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('database programming');
|
||||
|
||||
-- İlgi puanı ile
|
||||
SELECT title, relevance()
|
||||
FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('Nim language')
|
||||
ORDER BY relevance() DESC;
|
||||
|
||||
-- Boolean modu
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title, body) AGAINST('+Nim -Python' IN BOOLEAN MODE);
|
||||
|
||||
-- Fuzzy arama
|
||||
SELECT * FROM articles
|
||||
WHERE MATCH(title) AGAINST('programing' WITH FUZZINESS 2);
|
||||
```
|
||||
|
||||
## Kullanıcı Tanımlı Fonksiyonlar
|
||||
|
||||
```sql
|
||||
-- UDF kaydet
|
||||
CREATE FUNCTION greet(name str) -> str {
|
||||
RETURN 'Hello, ' || name || '!';
|
||||
};
|
||||
|
||||
-- Kullan
|
||||
SELECT greet(name) FROM users;
|
||||
|
||||
-- Dahili fonksiyonlar
|
||||
SELECT abs(-5), sqrt(16), lower('HELLO'), len('test');
|
||||
```
|
||||
|
||||
## Sorgu İpuçları
|
||||
|
||||
```sql
|
||||
-- İndeks kullanımını zorla
|
||||
SELECT /*+ USE_INDEX(idx_users_age) */ * FROM users WHERE age > 18;
|
||||
|
||||
-- Approximate vektör araması zorla
|
||||
SELECT /*+ APPROXIMATE */ * FROM vectors
|
||||
ORDER BY cosine_distance(embedding, [...])
|
||||
LIMIT 10;
|
||||
|
||||
-- Paralel çalıştırma
|
||||
SELECT /*+ PARALLEL(4) */ * FROM large_table;
|
||||
```
|
||||
|
||||
## Pencere Fonksiyonları
|
||||
|
||||
```sql
|
||||
-- Sıralama fonksiyonları
|
||||
SELECT
|
||||
name,
|
||||
department,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
|
||||
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
|
||||
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
|
||||
FROM employees;
|
||||
|
||||
-- Değer fonksiyonları
|
||||
SELECT
|
||||
name,
|
||||
salary,
|
||||
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
|
||||
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
|
||||
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
|
||||
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
|
||||
FROM employees;
|
||||
|
||||
-- Dağılım fonksiyonları
|
||||
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
|
||||
```
|
||||
|
||||
### Çerçeve Spesifikasyonları
|
||||
|
||||
```sql
|
||||
-- ROWS çerçevesi
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
|
||||
)
|
||||
|
||||
-- RANGE çerçevesi
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
```
|
||||
|
||||
## Çok Kiracılı ERP
|
||||
|
||||
BaraDB, **Satır Düzeyinde Güvenlik (RLS)** ve **oturum değişkenlerini** birleştirerek tek bir veritabanı örneğinde birden fazla şirketi (kiracı) çalıştırmayı destekler.
|
||||
|
||||
### Oturum Değişkenleri
|
||||
|
||||
```sql
|
||||
SET app.tenant_id = 'company-123';
|
||||
SELECT current_setting('app.tenant_id') AS tenant;
|
||||
```
|
||||
|
||||
### Mevcut Kullanıcı / Rol
|
||||
|
||||
```sql
|
||||
SELECT current_user AS me, current_role AS my_role;
|
||||
```
|
||||
|
||||
### RLS Kiracı İzolasyonu
|
||||
|
||||
```sql
|
||||
-- Tabloda RLS etkinleştir
|
||||
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Kiracıya göre filtreleme ilkesi oluştur
|
||||
CREATE POLICY tenant_isolation ON invoices
|
||||
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
|
||||
|
||||
-- Her oturum yalnızca kendi verilerini görür
|
||||
SET app.tenant_id = 'company-a';
|
||||
SELECT * FROM invoices; -- yalnızca company-a satırları
|
||||
```
|
||||
|
||||
### Neden Çok Kiracılı?
|
||||
|
||||
- **Bir örnek, çok kiracı** — 100 ayrı veritabanı çalıştırmaya gerek yok
|
||||
- **JSONB belgeleri** — Esnek şema depolama, her kiracı için kolayca alan ekleme
|
||||
- **RLS izolasyonu garanti eder** — Veritabanı kiracı sınırlarını uygular, yalnızca uygulama değil
|
||||
|
||||
## Desteklenen Anahtar Kelimeler
|
||||
|
||||
| Kategori | Anahtar Kelimeler |
|
||||
|----------|----------|
|
||||
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
|
||||
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
|
||||
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
|
||||
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
|
||||
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
|
||||
| CTEs | WITH, RECURSIVE, AS |
|
||||
| Case | CASE, WHEN, THEN, ELSE, END |
|
||||
| Transaction | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
|
||||
| Graph | MATCH, RETURN, WHERE, shortestPath, type |
|
||||
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
|
||||
| Vector | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
|
||||
| JSON | ->, ->> |
|
||||
| FTS | @@ (BM25 eşleşme) |
|
||||
| Recovery | RECOVER TO TIMESTAMP |
|
||||
| Functions | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
|
||||
| Session | SET, current_setting, current_user, current_role |
|
||||
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
|
||||
| Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |
|
||||
@@ -22,38 +22,284 @@
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
# Resmi kurulum scripti
|
||||
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
|
||||
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get update
|
||||
sudo apt-get install nim
|
||||
|
||||
# Fedora
|
||||
sudo dnf install nim
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S nim
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
# Homebrew
|
||||
brew install nim
|
||||
|
||||
# MacPorts
|
||||
sudo port install nim
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
# choosenim ile
|
||||
curl.exe -A "MSYS2_$(uname -m)" -L https://nim-lang.org/choosenim/init.ps1 | powershell -
|
||||
|
||||
# winget ile
|
||||
winget install nim
|
||||
|
||||
# scoop ile
|
||||
scoop install nim
|
||||
```
|
||||
|
||||
### Kurulumu Doğrulama
|
||||
|
||||
```bash
|
||||
nim --version
|
||||
# Beklenen: Nim Compiler Version 2.2.0 veya daha yeni
|
||||
```
|
||||
|
||||
## OpenSSL Kurulumu
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get install libssl-dev
|
||||
|
||||
# Fedora
|
||||
sudo dnf install openssl-devel
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S openssl
|
||||
```
|
||||
|
||||
### macOS
|
||||
|
||||
OpenSSL sistemle birlikte gelir. Gerekirse:
|
||||
|
||||
```bash
|
||||
brew install openssl
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
OpenSSL, Nim Windows dağıtımıyla birlikte gelir. Manuel derlemeler için [slproweb.com](https://slproweb.com/products/Win32OpenSSL.html) adresinden indirin.
|
||||
|
||||
## BaraDB Derleme
|
||||
|
||||
### Depoyu Klonlama
|
||||
|
||||
```bash
|
||||
git clone https://github.com/katehonz/barabaDB.git
|
||||
cd barabaDB
|
||||
```
|
||||
|
||||
### Bağımlılıkları Kurma
|
||||
|
||||
```bash
|
||||
nimble install -d -y
|
||||
```
|
||||
|
||||
### Derleme Seçenekleri
|
||||
|
||||
#### Debug Derleme
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
#### Release Derleme (Önerilen)
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:speed -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
#### Nimble Tasks Kullanma
|
||||
|
||||
```bash
|
||||
# Debug derleme
|
||||
nimble build_debug
|
||||
|
||||
# Release derleme
|
||||
nimble build_release
|
||||
```
|
||||
|
||||
#### Binary Boyutunu Küçültme
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:size -o:build/baradadb src/baradadb.nim
|
||||
strip build/baradadb
|
||||
```
|
||||
|
||||
### Derlemeyi Doğrulama
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# Beklenen: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## Testleri Çalıştırma
|
||||
|
||||
### Tüm Testler
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -r tests/test_all.nim
|
||||
```
|
||||
|
||||
### Belirli Test Süitleri
|
||||
|
||||
```bash
|
||||
# Depolama testleri
|
||||
nim c -d:ssl -r tests/test_storage.nim
|
||||
|
||||
# Sorgu motoru testleri
|
||||
nim c -d:ssl -r tests/test_query.nim
|
||||
|
||||
# Protokol testleri
|
||||
nim c -d:ssl -r tests/test_protocol.nim
|
||||
```
|
||||
|
||||
### Benchmarklar
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release -r benchmarks/bench_all.nim
|
||||
```
|
||||
|
||||
## Kurulum Seçenekleri
|
||||
|
||||
### Sistem Geneli Kurulum
|
||||
|
||||
```bash
|
||||
# Release binary derle
|
||||
nimble build_release
|
||||
|
||||
# /usr/local/bin'e kur
|
||||
sudo cp build/baradadb /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/baradadb
|
||||
|
||||
# Veri dizini oluştur
|
||||
sudo mkdir -p /var/lib/baradb
|
||||
sudo chmod 755 /var/lib/baradb
|
||||
```
|
||||
|
||||
### Ön Derlenmiş Binary
|
||||
|
||||
En son sürümü platformunuz için indirin:
|
||||
|
||||
```bash
|
||||
# Linux x86_64
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-amd64
|
||||
chmod +x baradadb-linux-amd64
|
||||
mv baradadb-linux-amd64 /usr/local/bin/baradadb
|
||||
|
||||
# Linux ARM64
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-arm64
|
||||
chmod +x baradadb-linux-arm64
|
||||
mv baradadb-linux-arm64 /usr/local/bin/baradadb
|
||||
|
||||
# macOS
|
||||
wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-darwin-amd64
|
||||
chmod +x baradadb-darwin-amd64
|
||||
mv baradadb-darwin-amd64 /usr/local/bin/baradadb
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
# Resmi image'i çek
|
||||
docker pull barabadb/barabadb:latest
|
||||
|
||||
# Çalıştır
|
||||
docker run -d \
|
||||
-p 9472:9472 \
|
||||
-p 9470:9470 \
|
||||
-p 9471:9471 \
|
||||
-v baradb_data:/data \
|
||||
barabadb/barabadb
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Gömülü Kullanım (Nim Projeleri)
|
||||
|
||||
`.nimble` dosyanıza ekleyin:
|
||||
|
||||
```nim
|
||||
requires "barabadb >= 1.1.0"
|
||||
```
|
||||
|
||||
Kodunuzda kullanın:
|
||||
|
||||
```nim
|
||||
import barabadb/storage/lsm
|
||||
|
||||
var db = newLSMTree("./data")
|
||||
db.put("key", cast[seq[byte]]("value"))
|
||||
let (found, val) = db.get("key")
|
||||
db.close()
|
||||
```
|
||||
|
||||
## İlk Çalıştırma
|
||||
|
||||
```bash
|
||||
# Sunucuyu başlat
|
||||
./build/baradadb
|
||||
|
||||
# Beklenen çıktı:
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# HTTP API ile test et
|
||||
curl http://localhost:9470/health
|
||||
|
||||
# İnteraktif shell
|
||||
./build/baradadb --shell
|
||||
```
|
||||
|
||||
## Kurulum Sorunlarını Giderme
|
||||
|
||||
### "cannot open file: hunos"
|
||||
|
||||
```bash
|
||||
nimble install -d -y
|
||||
```
|
||||
|
||||
### "BaraDB requires SSL support"
|
||||
|
||||
Her zaman `-d:ssl` ile derleyin:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### Yavaş derleme
|
||||
|
||||
Paralel derleme kullanın:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --parallelBuild:4 -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
### Büyük binary boyutu
|
||||
|
||||
Boyut optimizasyonu kullanın:
|
||||
|
||||
```bash
|
||||
nim c -d:ssl -d:release --opt:size --passL:-s -o:build/baradadb src/baradadb.nim
|
||||
```
|
||||
|
||||
## Sonraki Adımlar
|
||||
|
||||
- [Hızlı Başlangıç](quickstart.md)
|
||||
|
||||
+106
-1
@@ -489,4 +489,109 @@ LIMIT 10;
|
||||
|
||||
-- 并行执行
|
||||
SELECT /*+ PARALLEL(4) */ * FROM large_table;
|
||||
```
|
||||
```
|
||||
|
||||
## 窗口函数
|
||||
|
||||
```sql
|
||||
-- 排名函数
|
||||
SELECT
|
||||
name,
|
||||
department,
|
||||
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn,
|
||||
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS r,
|
||||
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dr
|
||||
FROM employees;
|
||||
|
||||
-- 值函数
|
||||
SELECT
|
||||
name,
|
||||
salary,
|
||||
LAG(salary, 1, 0) OVER (ORDER BY salary) AS prev_salary,
|
||||
LEAD(salary, 1, 0) OVER (ORDER BY salary) AS next_salary,
|
||||
FIRST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS cheapest,
|
||||
LAST_VALUE(name) OVER (PARTITION BY department ORDER BY salary) AS most_expensive
|
||||
FROM employees;
|
||||
|
||||
-- 分布函数
|
||||
SELECT name, NTILE(4) OVER (ORDER BY salary) AS quartile FROM employees;
|
||||
```
|
||||
|
||||
### 帧规格
|
||||
|
||||
```sql
|
||||
-- ROWS 帧
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
|
||||
)
|
||||
|
||||
-- RANGE 帧
|
||||
SUM(salary) OVER (
|
||||
PARTITION BY department
|
||||
ORDER BY hire_date
|
||||
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
```
|
||||
|
||||
## 多租户 ERP
|
||||
|
||||
BaraDB 支持在单个数据库实例中运行多个公司(租户),使用**行级安全性(RLS)**结合**会话变量**。
|
||||
|
||||
### 会话变量
|
||||
|
||||
```sql
|
||||
SET app.tenant_id = 'company-123';
|
||||
SELECT current_setting('app.tenant_id') AS tenant;
|
||||
```
|
||||
|
||||
### 当前用户 / 角色
|
||||
|
||||
```sql
|
||||
SELECT current_user AS me, current_role AS my_role;
|
||||
```
|
||||
|
||||
### RLS 租户隔离
|
||||
|
||||
```sql
|
||||
-- 在表上启用 RLS
|
||||
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 创建按租户过滤的策略
|
||||
CREATE POLICY tenant_isolation ON invoices
|
||||
FOR SELECT USING (tenant_id = current_setting('app.tenant_id'));
|
||||
|
||||
-- 每个会话只能看到自己的数据
|
||||
SET app.tenant_id = 'company-a';
|
||||
SELECT * FROM invoices; -- 仅限 company-a 的行
|
||||
```
|
||||
|
||||
### 为什么选择多租户?
|
||||
|
||||
- **一个实例,多个租户** — 无需运行 100 个独立的数据库
|
||||
- **JSONB 文档** — 模式灵活的存储,易于为每个租户添加字段
|
||||
- **RLS 保证隔离** — 数据库强制执行租户边界,而不仅仅是应用程序
|
||||
|
||||
## 支持的关键字
|
||||
|
||||
| 类别 | 关键字 |
|
||||
|----------|----------|
|
||||
| DQL | SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING, LIMIT, OFFSET, DISTINCT |
|
||||
| DML | INSERT, UPDATE, DELETE, SET, VALUES |
|
||||
| DDL | CREATE TYPE, DROP TYPE, CREATE INDEX, DROP INDEX, ALTER TYPE |
|
||||
| Join | INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN, CROSS JOIN, ON |
|
||||
| Set | UNION, UNION ALL, INTERSECT, EXCEPT |
|
||||
| CTEs | WITH, RECURSIVE, AS |
|
||||
| Case | CASE, WHEN, THEN, ELSE, END |
|
||||
| Transaction | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
|
||||
| Graph | MATCH, RETURN, WHERE, shortestPath, type |
|
||||
| FTS | MATCH, AGAINST, relevance, IN BOOLEAN MODE, WITH FUZZINESS |
|
||||
| Vector | cosine_distance, euclidean_distance, inner_product, l1_distance, l2_distance, <-> |
|
||||
| JSON | ->, ->> |
|
||||
| FTS | @@ (BM25 match) |
|
||||
| Recovery | RECOVER TO TIMESTAMP |
|
||||
| Functions | count, sum, avg, min, max, stddev, variance, abs, sqrt, lower, upper, len, trim, substr, now, last_insert_id, current_setting |
|
||||
| Session | SET, current_setting, current_user, current_role |
|
||||
| Window | OVER, PARTITION BY, ROWS, RANGE, UNBOUNDED PRECEDING, CURRENT ROW, FOLLOWING |
|
||||
| Window Functions | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE, NTILE |
|
||||
@@ -156,7 +156,7 @@ raft_peers = "node2:9001,node3:9001"
|
||||
```
|
||||
|
||||
```
|
||||
BaraDB v0.1.0 — Multimodal Database Engine
|
||||
BaraDB v1.1.0 — Multimodal Database Engine
|
||||
|
||||
Usage:
|
||||
baradadb [options]
|
||||
|
||||
@@ -144,7 +144,7 @@ strip build/baradadb
|
||||
|
||||
```bash
|
||||
./build/baradadb --version
|
||||
# 预期输出: BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# 预期输出: BaraDB v1.1.0 — Multimodal Database Engine
|
||||
```
|
||||
|
||||
## 运行测试
|
||||
@@ -259,7 +259,7 @@ db.close()
|
||||
./build/baradadb
|
||||
|
||||
# 预期输出:
|
||||
# BaraDB v0.1.0 — Multimodal Database Engine
|
||||
# BaraDB v1.1.0 — Multimodal Database Engine
|
||||
# BaraDB TCP listening on 127.0.0.1:9472
|
||||
|
||||
# 使用 HTTP API 测试
|
||||
|
||||
Reference in New Issue
Block a user