feat: add scan and prefix to client
This commit is contained in:
1 parent
cc546fe216
commit
ecec822d5c
1 file changed
+164
+164
@@ -156,3 +156,167 @@ pub const Client = struct {
|
||||
if (status != .ok) return statusToError(status);
|
||||
}
|
||||
|
||||
pub fn scan(self: *Client, allocator: Allocator, args: protocol.ScanArgs) ![]protocol.OwnedEntry {
|
||||
const payload = try protocol.encodeScan(self.allocator, args);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.scan, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeEntries(allocator, frame.payload);
|
||||
}
|
||||
|
||||
pub fn prefix(self: *Client, allocator: Allocator, prefixBytes: []const u8, limit: u32) ![]protocol.OwnedEntry {
|
||||
const payload = try protocol.encodePrefix(self.allocator, prefixBytes, limit);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.prefix, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeEntries(allocator, frame.payload);
|
||||
}
|
||||
|
||||
pub fn stats(self: *Client) !dbmod.Stats {
|
||||
var frame = try self.roundTrip(.stats, "");
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeStats(frame.payload);
|
||||
}
|
||||
|
||||
/// Fetches the server's combined Prometheus text export (its own
|
||||
/// request/rate-limit/auth/session counters from `Server.registerMetrics`
|
||||
/// plus `Db.stats()`, in the one payload `Server.handleMetrics` builds) -
|
||||
/// the network counterpart of the CLI's local-only `stats --prom`.
|
||||
/// Returned text is independently owned; free with `allocator.free`.
|
||||
pub fn metricsText(self: *Client, allocator: Allocator) ![]u8 {
|
||||
var frame = try self.roundTrip(.metrics, "");
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return allocator.dupe(u8, frame.payload);
|
||||
}
|
||||
|
||||
pub fn flush(self: *Client) !void {
|
||||
var frame = try self.roundTrip(.flush, "");
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
}
|
||||
|
||||
pub fn compact(self: *Client) !void {
|
||||
var frame = try self.roundTrip(.compact, "");
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
}
|
||||
|
||||
pub fn batch(self: *Client, ops: []const protocol.BatchOp) !void {
|
||||
const payload = try protocol.encodeBatch(self.allocator, ops);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.batch, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
}
|
||||
|
||||
/// Remote `table explain`: the wire counterpart of the CLI's local
|
||||
/// `table explain`, reporting which strategy `query.Table.explain`
|
||||
/// says a matching `tableSelect` would take against the server's
|
||||
/// `*Db`, without running the query. `exprText` is the same `expr.zig`
|
||||
/// grammar as `tableSelect`'s (empty means no predicate, i.e. always a
|
||||
/// full scan). Returned `Plan` owns its `indexName` - free with
|
||||
/// `Plan.deinit`.
|
||||
pub fn tableExplain(self: *Client, allocator: Allocator, table: []const u8, exprText: []const u8) !querymod.Plan {
|
||||
const payload = try protocol.encodeTableExplain(self.allocator, table, exprText);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.table_explain, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodePlan(allocator, frame.payload);
|
||||
}
|
||||
|
||||
/// Remote `table update`: the wire counterpart of the CLI's local
|
||||
/// `table update`, applying `sets` to every row matching `exprText`
|
||||
/// (empty means every row, the same convention `tableSelect`/
|
||||
/// `tableExplain` use) against the server's `*Db` via
|
||||
/// `query.Table.updateWhere`. Returns the number of rows updated.
|
||||
pub fn tableUpdate(self: *Client, table: []const u8, sets: []const rowmod.Field, exprText: []const u8) !u64 {
|
||||
const payload = try protocol.encodeTableUpdate(self.allocator, table, sets, exprText);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.table_update, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeTtlSweepCount(frame.payload);
|
||||
}
|
||||
|
||||
/// Remote `table delete`: the wire counterpart of the CLI's local
|
||||
/// `table delete`, removing every row matching `exprText` (empty means
|
||||
/// every row, the same convention `tableUpdate`/`tableExplain` use)
|
||||
/// against the server's `*Db` via `query.Table.deleteWhere`. Returns the
|
||||
/// number of rows deleted.
|
||||
pub fn tableDelete(self: *Client, table: []const u8, exprText: []const u8) !u64 {
|
||||
const payload = try protocol.encodeTableDelete(self.allocator, table, exprText);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.table_delete, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeTtlSweepCount(frame.payload);
|
||||
}
|
||||
|
||||
/// Remote `table export`: the wire counterpart of the CLI's local
|
||||
/// `table export <table> csv|json <path>`, rendering the whole table on
|
||||
/// the server and returning both the row count and the rendered bytes
|
||||
/// in one round trip. `content` is a fresh allocation independent of
|
||||
/// the response frame - free it with `RemoteExport.deinit`.
|
||||
pub fn tableExport(self: *Client, allocator: Allocator, table: []const u8, format: protocol.DumpFormat) !RemoteExport {
|
||||
const payload = try protocol.encodeTableExport(self.allocator, table, format);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.table_export, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
const result = try protocol.decodeExportResult(frame.payload);
|
||||
const content = try allocator.dupe(u8, result.content);
|
||||
return .{ .rowCount = result.rowCount, .content = content };
|
||||
}
|
||||
|
||||
/// Remote `table import`: the wire counterpart of the CLI's local
|
||||
/// `table import <table> csv|json <path>`. `content` is data the
|
||||
/// caller already read into memory (e.g. from a local file) and sends
|
||||
/// to the server in one frame; the server runs it through `dump.zig`'s
|
||||
/// importer there. Returns the number of rows imported.
|
||||
pub fn tableImport(self: *Client, table: []const u8, format: protocol.DumpFormat, content: []const u8) !u64 {
|
||||
const payload = try protocol.encodeTableImport(self.allocator, table, format, content);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.table_import, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeTtlSweepCount(frame.payload);
|
||||
}
|
||||
|
||||
/// Remote `table select`: `exprText` is the same `expr.zig` predicate
|
||||
/// grammar the local CLI's `table select` accepts (empty selects every
|
||||
/// row). `offset`/`limit` page the result the same way
|
||||
/// `query.Table.selectPage` does locally (0 limit = unlimited), so a
|
||||
/// large remote result does not have to come back in a single frame.
|
||||
/// `orderBy`, when non-empty, sorts server-side via
|
||||
/// `Table.selectPageOrdered` before that page window applies (one or
|
||||
/// more columns applied left to right, ties on earlier columns broken
|
||||
/// by later ones) - the network counterpart of the local CLI's `table
|
||||
/// select --order-by`. Returned rows are independently owned - free
|
||||
/// with `protocol.freeRows`.
|
||||
pub fn tableSelect(self: *Client, allocator: Allocator, table: []const u8, exprText: []const u8, offset: u32, limit: u32, orderBy: []const querymod.OrderBy) ![]rowmod.Row {
|
||||
const payload = try protocol.encodeTableSelect(self.allocator, table, exprText, offset, limit, orderBy);
|
||||
defer self.allocator.free(payload);
|
||||
var frame = try self.roundTrip(.table_select, payload);
|
||||
defer frame.deinit();
|
||||
const status = try protocol.Status.fromByte(frame.tag);
|
||||
if (status != .ok) return statusToError(status);
|
||||
return protocol.decodeRows(allocator, frame.payload);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user