From 18cbe54651c7a9533e2423a6cd11c28a77288797 Mon Sep 17 00:00:00 2001 From: Dan Gallagher Date: Thu, 8 Feb 2024 21:21:25 -0500 Subject: [PATCH] table renaming --- src/Table.zig | 44 ++++++++++++++++++++++++++++++++++++-------- src/shadow_table.zig | 11 +++++++++++ src/sqlite3/vtab.zig | 21 ++++++++++++++++++++- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/Table.zig b/src/Table.zig index a4f2fdb..6f98d60 100644 --- a/src/Table.zig +++ b/src/Table.zig @@ -46,6 +46,7 @@ table_static_arena: ArenaAllocator, ctx: VtabCtx, +schema_manager: SchemaManager, table_data: TableData, blob_manager: BlobManager, row_group_index: RowGroupIndex, @@ -99,14 +100,18 @@ pub fn create( const name = try self.table_static_arena.allocator().dupe(u8, args[2]); self.ctx.base = VtabCtxSchemaless.init(conn, name); - var schema_mgr = SchemaManager.init(&self.ctx.base); - defer schema_mgr.deinit(); - schema_mgr.table().create(cb_ctx.arena) catch |e| { + self.schema_manager = SchemaManager.init(&self.ctx.base); + errdefer self.schema_manager.deinit(); + self.schema_manager.table().create(cb_ctx.arena) catch |e| { cb_ctx.setErrorMessage("error creating columns table: {any}", .{e}); return e; }; - self.ctx.schema = schema_mgr.create(&self.table_static_arena, cb_ctx.arena, &def) catch |e| { + self.ctx.schema = self.schema_manager.create( + &self.table_static_arena, + cb_ctx.arena, + &def, + ) catch |e| { cb_ctx.setErrorMessage("error creating schema: {any}", .{e}); return e; }; @@ -216,14 +221,14 @@ pub fn connect( const name = try self.table_static_arena.allocator().dupe(u8, args[2]); self.ctx.base = VtabCtxSchemaless.init(conn, name); - var schema_mgr = SchemaManager.init(&self.ctx.base); - defer schema_mgr.deinit(); - schema_mgr.table().verifyExists(cb_ctx.arena) catch |e| { + self.schema_manager = SchemaManager.init(&self.ctx.base); + errdefer self.schema_manager.deinit(); + self.schema_manager.table().verifyExists(cb_ctx.arena) catch |e| { cb_ctx.setErrorMessage("columns shadow table does not exist: {any}", .{e}); return e; }; - self.ctx.schema = schema_mgr.load(&self.table_static_arena, cb_ctx.arena) catch |e| { + self.ctx.schema = self.schema_manager.load(&self.table_static_arena, cb_ctx.arena) catch |e| { cb_ctx.setErrorMessage("error loading schema: {any}", .{e}); return e; }; @@ -284,11 +289,13 @@ pub fn connect( } pub fn disconnect(self: *Self) void { + std.log.debug("disconnecting from table {s}", .{self.ctx.vtabName()}); self.row_group_creator.deinit(); self.pending_inserts.deinit(); self.row_group_index.deinit(); self.table_data.deinit(); self.blob_manager.deinit(); + self.schema_manager.deinit(); self.table_static_arena.deinit(); } @@ -318,6 +325,12 @@ pub fn destroy(self: *Self, cb_ctx: *vtab.CallbackContext) void { .{ self.ctx.vtabName(), e }, ); }; + self.schema_manager.table().drop(cb_ctx.arena) catch |e| { + std.log.err( + "failed to drop shadow table {s}_columns: {any}", + .{ self.ctx.vtabName(), e }, + ); + }; self.disconnect(); } @@ -330,6 +343,21 @@ pub fn ddl(self: *Self, allocator: Allocator) ![:0]const u8 { ); } +pub fn rename(self: *Self, cb_ctx: *vtab.CallbackContext, new_name: [:0]const u8) !void { + std.log.debug("renaming to {s}", .{new_name}); + // TODO savepoint + + try self.table_data.table().rename(cb_ctx.arena, new_name); + try self.pending_inserts.table().rename(cb_ctx.arena, new_name); + try self.row_group_index.table().rename(cb_ctx.arena, new_name); + try self.blob_manager.table().rename(cb_ctx.arena, new_name); + try self.schema_manager.table().rename(cb_ctx.arena, new_name); + + // After a rename succeeds, the virtual table is disconnected, which means that other places + // that the name is stored (like in `ctx` or `blob_manager`) do not need to be updated. + return; +} + pub fn update( self: *Self, cb_ctx: *vtab.CallbackContext, diff --git a/src/shadow_table.zig b/src/shadow_table.zig index 99dd08e..ba00210 100644 --- a/src/shadow_table.zig +++ b/src/shadow_table.zig @@ -46,6 +46,17 @@ pub fn ShadowTable(comptime VTabCtx: type, comptime ShadowTableDef: type) type { return true; } + /// `ctx` must have the existing name + pub fn rename(self: Self, tmp_arena: *ArenaAllocator, new_name: []const u8) !void { + const ddl = try fmt.allocPrintZ( + tmp_arena.allocator(), + \\ALTER TABLE "{s}_{s}" RENAME TO "{s}_{s}" + , + .{ self.ctx.vtabName(), ShadowTableDef.suffix, new_name, ShadowTableDef.suffix }, + ); + try self.ctx.conn().exec(ddl); + } + pub fn drop(self: Self, tmp_arena: *ArenaAllocator) !void { const ddl = try fmt.allocPrintZ( tmp_arena.allocator(), diff --git a/src/sqlite3/vtab.zig b/src/sqlite3/vtab.zig index d2bca46..864a93a 100644 --- a/src/sqlite3/vtab.zig +++ b/src/sqlite3/vtab.zig @@ -472,6 +472,25 @@ pub fn VirtualTable(comptime Table: type) type { } }; + const Renamable = struct { + fn xRename(vtab: [*c]c.sqlite3_vtab, new_name: [*c]const u8) callconv(.C) c_int { + const state = @fieldParentPtr(State, "vtab", vtab); + var cb_ctx = state.cbCtx() catch { + std.log.err("error allocating arena for callback context. out of memory", .{}); + return c.SQLITE_ERROR; + }; + defer state.reclaimCbCtx(&cb_ctx); + + const new_name_checked: [:0]const u8 = std.mem.span(new_name); + state.table.rename(&cb_ctx, new_name_checked) catch |e| { + std.log.err("error calling rename on table: {any}", .{e}); + return c.SQLITE_ERROR; + }; + + return c.SQLITE_OK; + } + }; + const HasShadowTables = struct { fn xShadowName(name: [*c]const u8) callconv(.C) c_int { const n: [:0]const u8 = std.mem.span(name); @@ -502,7 +521,7 @@ pub fn VirtualTable(comptime Table: type) type { .xCommit = if (tableHasDecl("commit")) Transactable.xCommit else null, .xRollback = if (tableHasDecl("rollback")) Transactable.xRollback else null, .xFindFunction = null, - .xRename = null, + .xRename = if (tableHasDecl("rename")) Renamable.xRename else null, .xSavepoint = if (tableHasDecl("savepoint")) Transactable.xSavepoint else null, .xRelease = if (tableHasDecl("release")) Transactable.xRelease else null, .xRollbackTo = if (tableHasDecl("rollbackTo")) Transactable.xRollbackTo else null,