introduce ddb.zig to serve as core DB operations/move core into ddb.zig
This commit is contained in:
parent
e94184419e
commit
796eed8de0
|
@ -14,4 +14,5 @@ pub fn handler(request: *AuthenticatedRequest, writer: anytype) ![]const u8 {
|
||||||
var parsed = try std.json.parseFromSlice(std.json.Value, allocator, request.event_data, .{});
|
var parsed = try std.json.parseFromSlice(std.json.Value, allocator, request.event_data, .{});
|
||||||
defer parsed.deinit();
|
defer parsed.deinit();
|
||||||
// const request_params = try parseRequest(request, parsed, writer);
|
// const request_params = try parseRequest(request, parsed, writer);
|
||||||
|
return "hi";
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,6 +505,7 @@ pub fn handler(request: *AuthenticatedRequest, writer: anytype) ![]const u8 {
|
||||||
// 3. Delete any existing records with that hash value (for delete requests, we're done here)
|
// 3. Delete any existing records with that hash value (for delete requests, we're done here)
|
||||||
// 4. If put request, put the new item in the table (with encrypted values, using table encryption)
|
// 4. If put request, put the new item in the table (with encrypted values, using table encryption)
|
||||||
// TODO: Capacity limiting and metrics
|
// TODO: Capacity limiting and metrics
|
||||||
|
return "hi";
|
||||||
}
|
}
|
||||||
|
|
||||||
test "basic request parsing failure" {
|
test "basic request parsing failure" {
|
||||||
|
|
|
@ -4,6 +4,7 @@ const AuthenticatedRequest = @import("AuthenticatedRequest.zig");
|
||||||
const Account = @import("Account.zig");
|
const Account = @import("Account.zig");
|
||||||
const encryption = @import("encryption.zig");
|
const encryption = @import("encryption.zig");
|
||||||
const returnException = @import("main.zig").returnException;
|
const returnException = @import("main.zig").returnException;
|
||||||
|
const ddb = @import("ddb.zig");
|
||||||
const ddb_types = @import("ddb_types.zig");
|
const ddb_types = @import("ddb_types.zig");
|
||||||
|
|
||||||
// These are in the original casing so as to make the error messages nice
|
// These are in the original casing so as to make the error messages nice
|
||||||
|
@ -15,17 +16,9 @@ const RequiredFields = enum(u3) {
|
||||||
// zig fmt: on
|
// zig fmt: on
|
||||||
};
|
};
|
||||||
|
|
||||||
const TableInfo = struct {
|
|
||||||
attribute_definitions: []*ddb_types.AttributeDefinition,
|
|
||||||
// gsi_list: []const u8, // Not sure how this is used
|
|
||||||
// gsi_description_list: []const u8, // Not sure how this is used
|
|
||||||
// sqlite_index: []const u8, // Not sure how this is used
|
|
||||||
table_key: [encryption.encoded_key_length]u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Params = struct {
|
const Params = struct {
|
||||||
table_name: []const u8,
|
table_name: []const u8,
|
||||||
table_info: TableInfo,
|
table_info: ddb_types.TableInfo,
|
||||||
read_capacity_units: ?i64 = null,
|
read_capacity_units: ?i64 = null,
|
||||||
write_capacity_units: ?i64 = null,
|
write_capacity_units: ?i64 = null,
|
||||||
billing_mode_pay_per_request: bool = false,
|
billing_mode_pay_per_request: bool = false,
|
||||||
|
@ -47,23 +40,18 @@ pub fn handler(request: *AuthenticatedRequest, writer: anytype) ![]const u8 {
|
||||||
var db = try Account.dbForAccount(allocator, account_id);
|
var db = try Account.dbForAccount(allocator, account_id);
|
||||||
const account = try Account.accountForId(allocator, account_id); // This will get us the encryption key needed
|
const account = try Account.accountForId(allocator, account_id); // This will get us the encryption key needed
|
||||||
defer account.deinit();
|
defer account.deinit();
|
||||||
// TODO: better to do all encryption when request params are parsed?
|
|
||||||
const table_name = try encryption.encryptAndEncode(allocator, account.root_account_key.*, request_params.table_name);
|
|
||||||
defer allocator.free(table_name);
|
|
||||||
// We'll json serialize our table_info structure, encrypt, encode, and plow in
|
|
||||||
const table_info_string = try std.json.stringifyAlloc(allocator, request_params.table_info, .{ .whitespace = .indent_2 });
|
|
||||||
defer allocator.free(table_info_string);
|
|
||||||
const table_info = try encryption.encryptAndEncode(allocator, account.root_account_key.*, table_info_string);
|
|
||||||
defer allocator.free(table_info);
|
|
||||||
|
|
||||||
try insertIntoDm(
|
try ddb.createDdbTable(
|
||||||
|
allocator,
|
||||||
&db,
|
&db,
|
||||||
table_name,
|
account,
|
||||||
table_info,
|
request_params.table_name,
|
||||||
|
request_params.table_info,
|
||||||
request_params.read_capacity_units orelse 0,
|
request_params.read_capacity_units orelse 0,
|
||||||
request_params.write_capacity_units orelse 0,
|
request_params.write_capacity_units orelse 0,
|
||||||
request_params.billing_mode_pay_per_request,
|
request_params.billing_mode_pay_per_request,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Server side Input validation error on live DDB results in this for a 2 char table name
|
// Server side Input validation error on live DDB results in this for a 2 char table name
|
||||||
// 400 - bad request
|
// 400 - bad request
|
||||||
// {"__type":"com.amazon.coral.validate#ValidationException","message":"TableName must be at least 3 characters long and at most 255 characters long"}
|
// {"__type":"com.amazon.coral.validate#ValidationException","message":"TableName must be at least 3 characters long and at most 255 characters long"}
|
||||||
|
@ -127,39 +115,6 @@ pub fn handler(request: *AuthenticatedRequest, writer: anytype) ![]const u8 {
|
||||||
// "UniqueGSIIndexes": []
|
// "UniqueGSIIndexes": []
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
var diags = sqlite.Diagnostics{};
|
|
||||||
|
|
||||||
// It doesn't seem that I can bind a variable here. But it actually doesn't matter as we're
|
|
||||||
// encoding the name...
|
|
||||||
// IF NOT EXISTS doesn't apply - we want this to bounce if the table exists
|
|
||||||
const create_stmt = try std.fmt.allocPrint(allocator,
|
|
||||||
\\CREATE TABLE '{s}' (
|
|
||||||
\\ hashKey TEXT DEFAULT NULL,
|
|
||||||
\\ rangeKey TEXT DEFAULT NULL,
|
|
||||||
\\ hashValue BLOB NOT NULL,
|
|
||||||
\\ rangeValue BLOB NOT NULL,
|
|
||||||
\\ itemSize INTEGER DEFAULT 0,
|
|
||||||
\\ ObjectJSON BLOB NOT NULL,
|
|
||||||
\\ PRIMARY KEY(hashKey, rangeKey)
|
|
||||||
\\)
|
|
||||||
, .{table_name});
|
|
||||||
defer allocator.free(create_stmt);
|
|
||||||
// db.exec requires a comptime statement. execDynamic does not
|
|
||||||
db.execDynamic(
|
|
||||||
create_stmt,
|
|
||||||
.{ .diags = &diags },
|
|
||||||
.{},
|
|
||||||
) catch |e| {
|
|
||||||
std.log.debug("SqlLite Diags: {}", .{diags});
|
|
||||||
return e;
|
|
||||||
};
|
|
||||||
const create_index_stmt = try std.fmt.allocPrint(
|
|
||||||
allocator,
|
|
||||||
"CREATE INDEX \"{s}*HVI\" ON \"{s}\" (hashValue)",
|
|
||||||
.{ table_name, table_name },
|
|
||||||
);
|
|
||||||
defer allocator.free(create_index_stmt);
|
|
||||||
try db.execDynamic(create_index_stmt, .{}, .{});
|
|
||||||
|
|
||||||
var al = std.ArrayList(u8).init(allocator);
|
var al = std.ArrayList(u8).init(allocator);
|
||||||
var response_writer = al.writer();
|
var response_writer = al.writer();
|
||||||
|
@ -167,54 +122,6 @@ pub fn handler(request: *AuthenticatedRequest, writer: anytype) ![]const u8 {
|
||||||
return al.toOwnedSlice();
|
return al.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insertIntoDm(
|
|
||||||
db: *sqlite.Db,
|
|
||||||
table_name: []const u8,
|
|
||||||
table_info: []const u8,
|
|
||||||
read_capacity_units: i64,
|
|
||||||
write_capacity_units: i64,
|
|
||||||
billing_mode_pay_per_request: bool,
|
|
||||||
) !void {
|
|
||||||
// const current_time = std.time.nanotimestamp();
|
|
||||||
const current_time = std.time.microTimestamp(); // SQLlite integers are only 64bit max
|
|
||||||
try db.exec(
|
|
||||||
\\INSERT INTO dm(
|
|
||||||
\\ TableName,
|
|
||||||
\\ CreationDateTime,
|
|
||||||
\\ LastDecreaseDate,
|
|
||||||
\\ LastIncreaseDate,
|
|
||||||
\\ NumberOfDecreasesToday,
|
|
||||||
\\ ReadCapacityUnits,
|
|
||||||
\\ WriteCapacityUnits,
|
|
||||||
\\ TableInfo,
|
|
||||||
\\ BillingMode,
|
|
||||||
\\ PayPerRequestDateTime
|
|
||||||
\\ ) VALUES (
|
|
||||||
\\ $tablename{[]const u8},
|
|
||||||
\\ $createdate{i64},
|
|
||||||
\\ $lastdecreasedate{usize},
|
|
||||||
\\ $lastincreasedate{usize},
|
|
||||||
\\ $numberofdecreasestoday{usize},
|
|
||||||
\\ $readcapacityunits{i64},
|
|
||||||
\\ $writecapacityunits{i64},
|
|
||||||
\\ $tableinfo{[]const u8},
|
|
||||||
\\ $billingmode{usize},
|
|
||||||
\\ $payperrequestdatetime{usize}
|
|
||||||
\\ )
|
|
||||||
, .{}, .{
|
|
||||||
table_name,
|
|
||||||
current_time,
|
|
||||||
@as(usize, 0),
|
|
||||||
@as(usize, 0),
|
|
||||||
@as(usize, 0),
|
|
||||||
read_capacity_units,
|
|
||||||
write_capacity_units,
|
|
||||||
table_info,
|
|
||||||
if (billing_mode_pay_per_request) @as(usize, 1) else @as(usize, 0),
|
|
||||||
@as(usize, 0),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parseRequest(
|
fn parseRequest(
|
||||||
request: *AuthenticatedRequest,
|
request: *AuthenticatedRequest,
|
||||||
parsed: std.json.Parsed(std.json.Value),
|
parsed: std.json.Parsed(std.json.Value),
|
||||||
|
@ -273,7 +180,7 @@ fn parseRequest(
|
||||||
errdefer {
|
errdefer {
|
||||||
if (attribute_definitions_assigned) {
|
if (attribute_definitions_assigned) {
|
||||||
for (request_params.table_info.attribute_definitions) |d| {
|
for (request_params.table_info.attribute_definitions) |d| {
|
||||||
request.allocator.free(d.*.name);
|
request.allocator.free(d.name);
|
||||||
request.allocator.destroy(d);
|
request.allocator.destroy(d);
|
||||||
}
|
}
|
||||||
request.allocator.free(request_params.table_info.attribute_definitions);
|
request.allocator.free(request_params.table_info.attribute_definitions);
|
||||||
|
|
273
src/ddb.zig
Normal file
273
src/ddb.zig
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const sqlite = @import("sqlite");
|
||||||
|
const AuthenticatedRequest = @import("AuthenticatedRequest.zig");
|
||||||
|
const Account = @import("Account.zig");
|
||||||
|
const ddb_types = @import("ddb_types.zig");
|
||||||
|
const encryption = @import("encryption.zig");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
pub const TableArray = struct {
|
||||||
|
items: []Table,
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator, length: usize) !TableArray {
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.items = try allocator.alloc(Table, length),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *TableArray) void {
|
||||||
|
for (self.items) |*item|
|
||||||
|
item.deinit();
|
||||||
|
self.allocator.free(self.items);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pub const Table = struct {
|
||||||
|
table_name: []const u8,
|
||||||
|
table_key: [encryption.key_length]u8,
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
pub fn deinit(self: *Table) void {
|
||||||
|
std.crypto.utils.secureZero(u8, &self.table_key);
|
||||||
|
self.allocator.free(self.table_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gets all table names/keys for the account. Caller owns returned array
|
||||||
|
pub fn tablesForAccount(allocator: std.mem.Allocator, account_id: []const u8) !TableArray {
|
||||||
|
var db = try Account.dbForAccount(allocator, account_id);
|
||||||
|
defer if (!builtin.is_test) db.deinit();
|
||||||
|
const account = try Account.accountForId(allocator, account_id); // This will get us the encryption key needed
|
||||||
|
defer account.deinit();
|
||||||
|
|
||||||
|
const query =
|
||||||
|
\\SELECT TableName as table_name, TableInfo as table_info FROM dm
|
||||||
|
;
|
||||||
|
|
||||||
|
var stmt = try db.prepare(query);
|
||||||
|
defer stmt.deinit();
|
||||||
|
|
||||||
|
const rows = try stmt.all(struct {
|
||||||
|
table_name: []const u8,
|
||||||
|
table_info: []const u8,
|
||||||
|
}, allocator, .{}, .{});
|
||||||
|
defer allocator.free(rows);
|
||||||
|
var rc = try TableArray.init(allocator, rows.len);
|
||||||
|
errdefer rc.deinit();
|
||||||
|
|
||||||
|
// std.debug.print(" \n===\nRow count: {d}\n===\n", .{rows.len});
|
||||||
|
for (rows, 0..) |row, inx| {
|
||||||
|
defer allocator.free(row.table_name);
|
||||||
|
defer allocator.free(row.table_info);
|
||||||
|
const table_name = try encryption.decodeAndDecrypt(
|
||||||
|
allocator,
|
||||||
|
account.root_account_key.*,
|
||||||
|
row.table_name,
|
||||||
|
);
|
||||||
|
errdefer allocator.free(table_name);
|
||||||
|
const table_info_str = try encryption.decodeAndDecrypt(
|
||||||
|
allocator,
|
||||||
|
account.root_account_key.*,
|
||||||
|
row.table_info,
|
||||||
|
);
|
||||||
|
defer allocator.free(table_info_str);
|
||||||
|
// std.debug.print(" \n===TableInfo: {s}\n===\n", .{table_info_str});
|
||||||
|
const table_info = try std.json.parseFromSlice(ddb_types.TableInfo, allocator, table_info_str, .{});
|
||||||
|
defer table_info.deinit();
|
||||||
|
// errdefer allocator.free(table_info.table_key);
|
||||||
|
// defer {
|
||||||
|
// // we don't even really need to defer this...
|
||||||
|
// for (table_info.value.attribute_definitions) |*def| {
|
||||||
|
// allocator.free(def.*.name);
|
||||||
|
// allocator.destroy(def);
|
||||||
|
// }
|
||||||
|
// allocator.free(table_info.table_key);
|
||||||
|
// }
|
||||||
|
|
||||||
|
rc.items[inx] = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.table_name = table_name,
|
||||||
|
.table_key = undefined,
|
||||||
|
};
|
||||||
|
try encryption.decodeKey(&rc.items[inx].table_key, table_info.value.table_key);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// creates a table in the underlying storage
|
||||||
|
pub fn createDdbTable(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
db: *sqlite.Db,
|
||||||
|
account: Account,
|
||||||
|
table_name: []const u8,
|
||||||
|
table_info: ddb_types.TableInfo,
|
||||||
|
read_capacity_units: i64,
|
||||||
|
write_capacity_units: i64,
|
||||||
|
billing_mode_pay_per_request: bool,
|
||||||
|
) !void {
|
||||||
|
const encrypted_table_name = try insertIntoDatabaseMetadata(
|
||||||
|
allocator,
|
||||||
|
db,
|
||||||
|
account,
|
||||||
|
table_name,
|
||||||
|
table_info,
|
||||||
|
read_capacity_units,
|
||||||
|
write_capacity_units,
|
||||||
|
billing_mode_pay_per_request,
|
||||||
|
);
|
||||||
|
defer allocator.free(encrypted_table_name);
|
||||||
|
|
||||||
|
var diags = sqlite.Diagnostics{};
|
||||||
|
|
||||||
|
// It doesn't seem that I can bind a variable here. But it actually doesn't matter as we're
|
||||||
|
// encoding the name...
|
||||||
|
// IF NOT EXISTS doesn't apply - we want this to bounce if the table exists
|
||||||
|
const create_stmt = try std.fmt.allocPrint(allocator,
|
||||||
|
\\CREATE TABLE '{s}' (
|
||||||
|
\\ hashKey TEXT DEFAULT NULL,
|
||||||
|
\\ rangeKey TEXT DEFAULT NULL,
|
||||||
|
\\ hashValue BLOB NOT NULL,
|
||||||
|
\\ rangeValue BLOB NOT NULL,
|
||||||
|
\\ itemSize INTEGER DEFAULT 0,
|
||||||
|
\\ ObjectJSON BLOB NOT NULL,
|
||||||
|
\\ PRIMARY KEY(hashKey, rangeKey)
|
||||||
|
\\)
|
||||||
|
, .{encrypted_table_name});
|
||||||
|
defer allocator.free(create_stmt);
|
||||||
|
// db.exec requires a comptime statement. execDynamic does not
|
||||||
|
db.execDynamic(
|
||||||
|
create_stmt,
|
||||||
|
.{ .diags = &diags },
|
||||||
|
.{},
|
||||||
|
) catch |e| {
|
||||||
|
std.log.debug("SqlLite Diags: {}", .{diags});
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
const create_index_stmt = try std.fmt.allocPrint(
|
||||||
|
allocator,
|
||||||
|
"CREATE INDEX \"{s}*HVI\" ON \"{s}\" (hashValue)",
|
||||||
|
.{ encrypted_table_name, encrypted_table_name },
|
||||||
|
);
|
||||||
|
defer allocator.free(create_index_stmt);
|
||||||
|
try db.execDynamic(create_index_stmt, .{}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new table into the database metadata (dm) table. Handles encryption
|
||||||
|
/// Returns encrypted table name
|
||||||
|
fn insertIntoDatabaseMetadata(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
db: *sqlite.Db,
|
||||||
|
account: Account,
|
||||||
|
table_name: []const u8,
|
||||||
|
table_info: ddb_types.TableInfo,
|
||||||
|
read_capacity_units: i64,
|
||||||
|
write_capacity_units: i64,
|
||||||
|
billing_mode_pay_per_request: bool,
|
||||||
|
) ![]const u8 {
|
||||||
|
// TODO: better to do all encryption when request params are parsed?
|
||||||
|
const encrypted_table_name = try encryption.encryptAndEncode(allocator, account.root_account_key.*, table_name);
|
||||||
|
errdefer allocator.free(encrypted_table_name);
|
||||||
|
// We'll json serialize our table_info structure, encrypt, encode, and plow in
|
||||||
|
const table_info_string = try std.json.stringifyAlloc(allocator, table_info, .{ .whitespace = .indent_2 });
|
||||||
|
defer allocator.free(table_info_string);
|
||||||
|
const encrypted_table_info = try encryption.encryptAndEncode(allocator, account.root_account_key.*, table_info_string);
|
||||||
|
defer allocator.free(encrypted_table_info);
|
||||||
|
try insertIntoDm(db, encrypted_table_name, encrypted_table_info, read_capacity_units, write_capacity_units, billing_mode_pay_per_request);
|
||||||
|
return encrypted_table_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insertIntoDm(
|
||||||
|
db: *sqlite.Db,
|
||||||
|
table_name: []const u8,
|
||||||
|
table_info: []const u8,
|
||||||
|
read_capacity_units: i64,
|
||||||
|
write_capacity_units: i64,
|
||||||
|
billing_mode_pay_per_request: bool,
|
||||||
|
) !void {
|
||||||
|
// const current_time = std.time.nanotimestamp();
|
||||||
|
const current_time = std.time.microTimestamp(); // SQLlite integers are only 64bit max
|
||||||
|
try db.exec(
|
||||||
|
\\INSERT INTO dm(
|
||||||
|
\\ TableName,
|
||||||
|
\\ CreationDateTime,
|
||||||
|
\\ LastDecreaseDate,
|
||||||
|
\\ LastIncreaseDate,
|
||||||
|
\\ NumberOfDecreasesToday,
|
||||||
|
\\ ReadCapacityUnits,
|
||||||
|
\\ WriteCapacityUnits,
|
||||||
|
\\ TableInfo,
|
||||||
|
\\ BillingMode,
|
||||||
|
\\ PayPerRequestDateTime
|
||||||
|
\\ ) VALUES (
|
||||||
|
\\ $tablename{[]const u8},
|
||||||
|
\\ $createdate{i64},
|
||||||
|
\\ $lastdecreasedate{usize},
|
||||||
|
\\ $lastincreasedate{usize},
|
||||||
|
\\ $numberofdecreasestoday{usize},
|
||||||
|
\\ $readcapacityunits{i64},
|
||||||
|
\\ $writecapacityunits{i64},
|
||||||
|
\\ $tableinfo{[]const u8},
|
||||||
|
\\ $billingmode{usize},
|
||||||
|
\\ $payperrequestdatetime{usize}
|
||||||
|
\\ )
|
||||||
|
, .{}, .{
|
||||||
|
table_name,
|
||||||
|
current_time,
|
||||||
|
@as(usize, 0),
|
||||||
|
@as(usize, 0),
|
||||||
|
@as(usize, 0),
|
||||||
|
read_capacity_units,
|
||||||
|
write_capacity_units,
|
||||||
|
table_info,
|
||||||
|
if (billing_mode_pay_per_request) @as(usize, 1) else @as(usize, 0),
|
||||||
|
@as(usize, 0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testCreateTable(allocator: std.mem.Allocator, account_id: []const u8) !sqlite.Db {
|
||||||
|
var db = try Account.dbForAccount(allocator, account_id);
|
||||||
|
const account = try Account.accountForId(allocator, account_id); // This will get us the encryption key needed
|
||||||
|
defer account.deinit();
|
||||||
|
var hash = ddb_types.AttributeDefinition{ .name = "Artist", .type = .S };
|
||||||
|
var range = ddb_types.AttributeDefinition{ .name = "SongTitle", .type = .S };
|
||||||
|
var definitions = @constCast(&[_]*ddb_types.AttributeDefinition{
|
||||||
|
&hash,
|
||||||
|
&range,
|
||||||
|
});
|
||||||
|
var table_info: ddb_types.TableInfo = .{
|
||||||
|
.table_key = undefined,
|
||||||
|
.attribute_definitions = definitions[0..],
|
||||||
|
};
|
||||||
|
encryption.randomEncodedKey(&table_info.table_key);
|
||||||
|
try createDdbTable(
|
||||||
|
allocator,
|
||||||
|
&db,
|
||||||
|
account,
|
||||||
|
"MusicCollection",
|
||||||
|
table_info,
|
||||||
|
5,
|
||||||
|
5,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
test "can create a table" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const account_id = "1234";
|
||||||
|
var db = try testCreateTable(allocator, account_id);
|
||||||
|
defer db.deinit();
|
||||||
|
}
|
||||||
|
test "can list tables in an account" {
|
||||||
|
Account.test_retain_db = true;
|
||||||
|
defer Account.test_retain_db = false;
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const account_id = "1234";
|
||||||
|
var db = try testCreateTable(allocator, account_id);
|
||||||
|
defer db.deinit();
|
||||||
|
var table_list = try tablesForAccount(allocator, account_id);
|
||||||
|
defer table_list.deinit();
|
||||||
|
try std.testing.expectEqual(@as(usize, 1), table_list.items.len);
|
||||||
|
try std.testing.expectEqualStrings("MusicCollection", table_list.items[0].table_name);
|
||||||
|
// std.debug.print(" \n===\nKey: {s}\n===\n", .{std.fmt.fmtSliceHexLower(&table_list.items[0].table_key)});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user