refactor: extract SQL performance tracking into shared logger

Create SQLPerformanceEntryLogger struct to eliminate code duplication
between MySQL and PostgreSQL query implementations. This shared logger
handles performance timing and reporting for SQL operations across
different database adapters.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2025-08-28 07:05:47 +00:00
parent 1b53aac5e4
commit e99d034e33
4 changed files with 104 additions and 118 deletions

View File

@@ -1002,6 +1002,7 @@ src/sql/shared/ObjectIterator.zig
src/sql/shared/QueryBindingIterator.zig
src/sql/shared/SQLDataCell.zig
src/sql/shared/SQLQueryResultMode.zig
src/sql/SQLPerformanceEntryLogger.zig
src/StandaloneModuleGraph.zig
src/StaticHashMap.zig
src/string.zig

View File

@@ -0,0 +1,91 @@
const std = @import("std");
const bun = @import("bun");
const jsc = bun.jsc;
extern "C" fn JSC__addSQLQueryPerformanceEntry(globalObject: *jsc.JSGlobalObject, name: [*:0]const u8, description: [*:0]const u8, startTime: f64, endTime: f64) void;
/// Shared SQL performance entry logger for tracking query performance across different SQL adapters
pub const SQLPerformanceEntryLogger = struct {
/// Start time for performance tracking (in nanoseconds)
start_time_ns: u64 = 0,
const Self = @This();
/// Extract the SQL command from the query string (e.g., "SELECT", "INSERT", etc.)
fn extractSQLCommand(query: []const u8) []const u8 {
if (query.len == 0) return "UNKNOWN";
var i: usize = 0;
// Skip leading whitespace
while (i < query.len and std.ascii.isWhitespace(query[i])) {
i += 1;
}
const start_pos = i;
// Find the end of the first word
while (i < query.len and !std.ascii.isWhitespace(query[i])) {
i += 1;
}
if (i <= start_pos) return "UNKNOWN";
const command = query[start_pos..i];
// Convert common commands to uppercase
if (std.ascii.eqlIgnoreCase(command, "select")) return "SELECT";
if (std.ascii.eqlIgnoreCase(command, "insert")) return "INSERT";
if (std.ascii.eqlIgnoreCase(command, "update")) return "UPDATE";
if (std.ascii.eqlIgnoreCase(command, "delete")) return "DELETE";
if (std.ascii.eqlIgnoreCase(command, "create")) return "CREATE";
if (std.ascii.eqlIgnoreCase(command, "drop")) return "DROP";
if (std.ascii.eqlIgnoreCase(command, "alter")) return "ALTER";
if (std.ascii.eqlIgnoreCase(command, "show")) return "SHOW";
if (std.ascii.eqlIgnoreCase(command, "describe") or std.ascii.eqlIgnoreCase(command, "desc")) return "DESCRIBE";
if (std.ascii.eqlIgnoreCase(command, "explain")) return "EXPLAIN";
if (std.ascii.eqlIgnoreCase(command, "truncate")) return "TRUNCATE";
if (std.ascii.eqlIgnoreCase(command, "grant")) return "GRANT";
if (std.ascii.eqlIgnoreCase(command, "revoke")) return "REVOKE";
return "UNKNOWN";
}
/// Start performance tracking for a query
pub fn start(self: *Self) void {
self.start_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
}
/// End performance tracking and report to the performance API
pub fn end(self: *Self, performance_entries_enabled: bool, query: bun.String, globalObject: *jsc.JSGlobalObject) void {
if (!performance_entries_enabled or self.start_time_ns == 0) return;
const end_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
const start_time_ms = @as(f64, @floatFromInt(self.start_time_ns)) / 1_000_000.0;
const end_time_ms = @as(f64, @floatFromInt(end_time_ns)) / 1_000_000.0;
// Get the SQL command and query string
var query_utf8 = query.toUTF8(bun.default_allocator);
defer query_utf8.deinit();
const command = extractSQLCommand(query_utf8.slice());
// Create null-terminated strings for the C function
const command_cstr = bun.default_allocator.dupeZ(u8, command) catch return;
defer bun.default_allocator.free(command_cstr);
const query_cstr = bun.default_allocator.dupeZ(u8, query_utf8.slice()) catch return;
defer bun.default_allocator.free(query_cstr);
// Call the C++ binding to add the performance entry
JSC__addSQLQueryPerformanceEntry(globalObject, command_cstr.ptr, query_cstr.ptr, start_time_ms, end_time_ms);
}
/// Initialize a new logger instance
pub fn init() Self {
return Self{};
}
/// Reset the logger (clear start time)
pub fn reset(self: *Self) void {
self.start_time_ns = 0;
}
};

View File

@@ -1,6 +1,9 @@
const MySQLQuery = @This();
const RefCount = bun.ptr.ThreadSafeRefCount(@This(), "ref_count", deinit, .{});
const std = @import("std");
const SQLPerformanceEntryLogger = @import("../SQLPerformanceEntryLogger.zig").SQLPerformanceEntryLogger;
extern "C" fn JSC__addSQLQueryPerformanceEntry(globalObject: *jsc.JSGlobalObject, name: [*:0]const u8, description: [*:0]const u8, startTime: f64, endTime: f64) void;
statement: ?*MySQLStatement = null,
@@ -12,8 +15,8 @@ status: Status = Status.pending,
ref_count: RefCount = RefCount.init(),
/// Start time for performance tracking (in nanoseconds)
start_time_ns: u64 = 0,
/// Performance tracking logger
performance_logger: SQLPerformanceEntryLogger = SQLPerformanceEntryLogger.init(),
flags: packed struct(u8) {
is_done: bool = false,
@@ -51,69 +54,14 @@ pub fn hasPendingActivity(this: *@This()) bool {
return this.ref_count.load(.monotonic) > 1;
}
/// Extract the SQL command from the query string (e.g., "SELECT", "INSERT", etc.)
fn extractSQLCommand(query: []const u8) []const u8 {
if (query.len == 0) return "UNKNOWN";
var i: usize = 0;
// Skip leading whitespace
while (i < query.len and std.ascii.isWhitespace(query[i])) {
i += 1;
}
const start = i;
// Find the end of the first word
while (i < query.len and !std.ascii.isWhitespace(query[i])) {
i += 1;
}
if (i <= start) return "UNKNOWN";
const command = query[start..i];
// Convert common commands to uppercase
if (std.ascii.eqlIgnoreCase(command, "select")) return "SELECT";
if (std.ascii.eqlIgnoreCase(command, "insert")) return "INSERT";
if (std.ascii.eqlIgnoreCase(command, "update")) return "UPDATE";
if (std.ascii.eqlIgnoreCase(command, "delete")) return "DELETE";
if (std.ascii.eqlIgnoreCase(command, "create")) return "CREATE";
if (std.ascii.eqlIgnoreCase(command, "drop")) return "DROP";
if (std.ascii.eqlIgnoreCase(command, "alter")) return "ALTER";
if (std.ascii.eqlIgnoreCase(command, "show")) return "SHOW";
if (std.ascii.eqlIgnoreCase(command, "describe") or std.ascii.eqlIgnoreCase(command, "desc")) return "DESCRIBE";
if (std.ascii.eqlIgnoreCase(command, "explain")) return "EXPLAIN";
return "UNKNOWN";
}
/// Start performance tracking for this query
pub fn startPerformanceTracking(this: *@This()) void {
this.start_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
this.performance_logger.start();
}
/// End performance tracking and report to the performance API
pub fn endPerformanceTracking(this: *@This(), connection: anytype, globalObject: *jsc.JSGlobalObject) void {
if (!connection.performance_entries_enabled or this.start_time_ns == 0) return;
const end_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
const start_time_ms = @as(f64, @floatFromInt(this.start_time_ns)) / 1_000_000.0;
const end_time_ms = @as(f64, @floatFromInt(end_time_ns)) / 1_000_000.0;
// Get the SQL command and query string
var query_utf8 = this.query.toUTF8(bun.default_allocator);
defer query_utf8.deinit();
const command = extractSQLCommand(query_utf8.slice());
// Create null-terminated strings for the C function
const command_cstr = bun.default_allocator.dupeZ(u8, command) catch return;
defer bun.default_allocator.free(command_cstr);
const query_cstr = bun.default_allocator.dupeZ(u8, query_utf8.slice()) catch return;
defer bun.default_allocator.free(query_cstr);
// Call the C++ binding to add the performance entry
JSC__addSQLQueryPerformanceEntry(globalObject, command_cstr.ptr, query_cstr.ptr, start_time_ms, end_time_ms);
this.performance_logger.end(connection.performance_entries_enabled, this.query, globalObject);
}
pub fn deinit(this: *@This()) void {

View File

@@ -1,6 +1,8 @@
const PostgresSQLQuery = @This();
const RefCount = bun.ptr.ThreadSafeRefCount(@This(), "ref_count", deinit, .{});
const std = @import("std");
const SQLPerformanceEntryLogger = @import("../SQLPerformanceEntryLogger.zig").SQLPerformanceEntryLogger;
extern "C" fn JSC__addSQLQueryPerformanceEntry(globalObject: *jsc.JSGlobalObject, name: [*:0]const u8, description: [*:0]const u8, startTime: f64, endTime: f64) void;
statement: ?*PostgresSQLStatement = null,
query: bun.String = bun.String.empty,
@@ -12,8 +14,7 @@ status: Status = Status.pending,
ref_count: RefCount = RefCount.init(),
/// Start time for performance tracking (in nanoseconds)
start_time_ns: u64 = 0,
performance_logger: SQLPerformanceEntryLogger = SQLPerformanceEntryLogger.init(),
flags: packed struct(u8) {
is_done: bool = false,
@@ -60,69 +61,14 @@ pub fn hasPendingActivity(this: *@This()) bool {
return this.ref_count.get() > 1;
}
/// Extract the SQL command from the query string (e.g., "SELECT", "INSERT", etc.)
fn extractSQLCommand(query: []const u8) []const u8 {
if (query.len == 0) return "UNKNOWN";
var i: usize = 0;
// Skip leading whitespace
while (i < query.len and std.ascii.isWhitespace(query[i])) {
i += 1;
}
const start = i;
// Find the end of the first word
while (i < query.len and !std.ascii.isWhitespace(query[i])) {
i += 1;
}
if (i <= start) return "UNKNOWN";
const command = query[start..i];
// Convert common commands to uppercase
if (std.ascii.eqlIgnoreCase(command, "select")) return "SELECT";
if (std.ascii.eqlIgnoreCase(command, "insert")) return "INSERT";
if (std.ascii.eqlIgnoreCase(command, "update")) return "UPDATE";
if (std.ascii.eqlIgnoreCase(command, "delete")) return "DELETE";
if (std.ascii.eqlIgnoreCase(command, "create")) return "CREATE";
if (std.ascii.eqlIgnoreCase(command, "drop")) return "DROP";
if (std.ascii.eqlIgnoreCase(command, "alter")) return "ALTER";
if (std.ascii.eqlIgnoreCase(command, "show")) return "SHOW";
if (std.ascii.eqlIgnoreCase(command, "describe") or std.ascii.eqlIgnoreCase(command, "desc")) return "DESCRIBE";
if (std.ascii.eqlIgnoreCase(command, "explain")) return "EXPLAIN";
return "UNKNOWN";
}
/// Start performance tracking for this query
pub fn startPerformanceTracking(this: *@This()) void {
this.start_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
this.performance_logger.start();
}
/// End performance tracking and report to the performance API
pub fn endPerformanceTracking(this: *@This(), connection: anytype, globalObject: *jsc.JSGlobalObject) void {
if (!connection.performance_entries_enabled or this.start_time_ns == 0) return;
const end_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
const start_time_ms = @as(f64, @floatFromInt(this.start_time_ns)) / 1_000_000.0;
const end_time_ms = @as(f64, @floatFromInt(end_time_ns)) / 1_000_000.0;
// Get the SQL command and query string
var query_utf8 = this.query.toUTF8(bun.default_allocator);
defer query_utf8.deinit();
const command = extractSQLCommand(query_utf8.slice());
// Create null-terminated strings for the C function
const command_cstr = bun.default_allocator.dupeZ(u8, command) catch return;
defer bun.default_allocator.free(command_cstr);
const query_cstr = bun.default_allocator.dupeZ(u8, query_utf8.slice()) catch return;
defer bun.default_allocator.free(query_cstr);
// Call the C++ binding to add the performance entry
JSC__addSQLQueryPerformanceEntry(globalObject, command_cstr.ptr, query_cstr.ptr, start_time_ms, end_time_ms);
this.performance_logger.end(connection.performance_entries_enabled, this.query, globalObject);
}
pub fn deinit(this: *@This()) void {