Message-ID: From: "davecramer (@davecramer)" To: "pgjdbc/pgjdbc" Date: Tue, 12 Aug 2025 09:09:08 +0000 Subject: Re: [pgjdbc/pgjdbc] issue #3757: PreparedStatement.toString() fails for bytea parameters with at least 42.7.7 In-Reply-To: References: List-Id: X-GitHub-Author-Login: davecramer X-GitHub-Comment-Id: 3178437250 X-GitHub-Comment-Type: issue_comment X-GitHub-Issue: 3757 X-GitHub-Repo: pgjdbc/pgjdbc X-GitHub-Type: comment X-GitHub-Url: https://github.com/pgjdbc/pgjdbc/issues/3757#issuecomment-3178437250 Content-Type: text/plain; charset=utf-8 Amazon Q came up with the same code After analyzing the Netty PR #9896, we can apply similar optimizations using lookup tables and bitwise operations. Here's the reworked version: ```java if (value instanceof String) { final String str = (String) value; final int length = str.length(); // Fast fail if string is too short if (length < 2 || str.charAt(0) != '\\' || str.charAt(1) != 'x') { throw new IllegalArgumentException(GT.tr("bytea string parameters must be hex format")); } // Pre-calculate capacity: prefix('\x') + actual hex digits + suffix('::bytea') final StringBuilder sb = new StringBuilder(length + 7); // Conservative estimate sb.append("'\\x"); // Lookup table for hex validation - similar to Netty's approach // Using byte[] instead of int[] as we only need 1 bit per entry static final byte[] HEX_LOOKUP = new byte[256]; static { // Initialize lookup table for hex characters for (char c = '0'; c <= '9'; c++) HEX_LOOKUP[c] = 1; for (char c = 'a'; c <= 'f'; c++) HEX_LOOKUP[c] = 1; for (char c = 'A'; c <= 'F'; c++) HEX_LOOKUP[c] = 1; } int i = 2; while (i < length) { char c = str.charAt(i); // Skip whitespace using bitwise operation // Based on Netty's implementation if ((c <= ' ' && ((1L << c) & ((1L << ' ') | (1L << '\t') | (1L << '\r') | (1L << '\n'))) != 0)) { i++; continue; } // Check if we have enough characters left if (i + 2 > length) { throw new IllegalArgumentException(GT.tr("Truncated bytea hex format")); } // Get hex digits final char c1 = c; final char c2 = str.charAt(i + 1); // Validate hex digits using lookup table if (c1 >= HEX_LOOKUP.length || HEX_LOOKUP[c1] == 0) { throw new IllegalArgumentException(GT.tr("Invalid bytea hex format character {0}", c1)); } if (c2 >= HEX_LOOKUP.length || HEX_LOOKUP[c2] == 0) { throw new IllegalArgumentException(GT.tr("Invalid bytea hex format character {0}", c2)); } sb.append(c1); sb.append(c2); i += 2; } sb.append("'::bytea"); return sb.toString(); } ``` Key improvements based on Netty's PR: 1. More efficient prefix check - Direct char comparison instead of `startsWith()` - Removes string allocation from `startsWith()` 2. Optimized whitespace checking - Uses bitwise operations similar to Netty's implementation - Single operation to check for all whitespace characters - Avoids method call to `Character.isWhitespace()` 3. Better hex validation - Static lookup table using byte[] for minimal memory footprint - Single array bounds check and lookup instead of string search - Byte array uses 1 bit effectively vs boolean array's 8 bits - Table is shared across all instances 4. Removed separate counting pass - StringBuilder will grow if needed - Initial capacity is conservative estimate - Trade-off between extra counting pass vs potential resize 5. Simplified loop structure - Single while loop with continue for whitespace - Reduced number of method calls - More straightforward flow control 6. Memory efficiency - Static lookup table shared across all instances - No temporary object creation - Minimal memory footprint for validation table This implementation combines the best practices from both the original optimization and Netty's approaches: - Uses Netty's efficient whitespace checking - Employs lookup table for hex validation - Maintains good readability while being highly optimized - Reduces method calls and object creation - Uses minimal memory for validation structures The code should perform better than the original version while maintaining clear intent and good maintainability. As always, actual performance improvements should be verified through benchmarking in your specific use case.