Skip to content

Conversation

@pyramation
Copy link
Contributor

@pyramation pyramation commented Dec 18, 2025

refactor(core,pgsql-test): modular role management with SQL templates

Summary

This PR refactors the role creation code to use modular functions with optional advisory locks for concurrent CI/CD safety. The changes consolidate duplicated role creation logic between @pgpmjs/core and pgsql-test packages.

Key changes:

  • Created shared utility functions in role-utils.ts for role operations (ensureBaseRoles, ensureLoginRole, ensureRoleMembership, grantConnect, etc.)
  • Refactored PgpmInit class to use the new modular functions
  • Refactored DbAdmin class with new options: useLocks, grantAdmin, onMissingRole
  • Added escapeLiteral() helper for safe SQL string interpolation in DO blocks
  • Exported new types and functions from @pgpmjs/core

Advisory locks are optional (default: false) as requested.

Updates since last revision

Fixed CI failures caused by attempting to use parameterized queries ($1, $2, etc.) inside PostgreSQL DO blocks. DO blocks are anonymous PL/pgSQL code blocks that don't support query parameters - the parameters are parsed as PL/pgSQL positional parameters, not query placeholders.

The fix switches to safe string interpolation using escapeLiteral() which properly escapes single quotes. The SQL template files in sql/ directory remain for reference but the actual execution uses inline SQL in TypeScript with proper escaping.

Review & Testing Checklist for Human

  • Verify escapeLiteral() escaping is sufficient - The function doubles single quotes and wraps in single quotes. Verify this handles edge cases like backslashes, unicode, and null bytes correctly for your PostgreSQL version.
  • Test role creation with special characters - Try usernames/passwords containing single quotes, double quotes, and backslashes to verify SQL injection protection.
  • Test bootstrapRoles(), bootstrapTestRoles(), and bootstrapDbRoles() against a real PostgreSQL instance
  • Test createUserRole() in pgsql-test with both grantAdmin: true and grantAdmin: false
  • Verify advisory locks work correctly when useLocks: true is passed

Recommended test plan:

  1. Run the existing test suite for both packages
  2. Manually test role creation with lql admin-users bootstrap and lql admin-users add --test
  3. Test with a username containing a single quote: test'user
  4. Test concurrent role creation to verify advisory lock behavior when enabled

Notes

  • The SQL template files in packages/core/src/init/sql/ use $1, $2 syntax but are not currently loaded - the actual SQL is inline in role-utils.ts with escapeLiteral() escaping. Consider removing these files or documenting them as reference/future use.
  • Test credentials (app_password, admin_password) in createTestUsers are intentional fixtures matching existing bootstrap-test-roles.sql
  • The grantAdmin option defaults to true for backward compatibility in the test framework

Link to Devin run: https://app.devin.ai/sessions/3a1a859673d943189e0dafcfefc870ad
Requested by: Dan Lynch ([email protected]) / @pyramation

- Add new SQL template files for role operations:
  - ensure-base-roles.sql: Create anonymous/authenticated/administrator roles
  - ensure-login-role.sql: Create LOGIN role with optional advisory locks
  - ensure-membership.sql: Grant role membership with optional locks
  - grant-connect.sql: Grant CONNECT privilege on database

- Add TypeScript types for role operation options:
  - RoleNameMapping, EnsureLoginRoleOptions, EnsureRoleMembershipsOptions
  - CreateDbUserOptions, BootstrapTestUsersOptions
  - OnMissingRoleAction for configurable error handling

- Add shared role-utils.ts with modular functions:
  - ensureBaseRoles, ensureLoginRole, ensureRoleMembership
  - ensureRoleMemberships, grantConnect, createDbUser, createTestUsers

- Refactor PgpmInit to use new modular functions:
  - bootstrapRoles now uses ensureBaseRoles
  - bootstrapTestRoles now uses createTestUsers
  - bootstrapDbRoles now uses createDbUser with configurable options

- Refactor DbAdmin in pgsql-test:
  - grantRole now supports useLocks, lockNamespace, onMissingRole options
  - createUserRole now supports useLocks, grantAdmin options
  - Add streamSqlWithParams for parameterized queries

- Export new types and functions from @pgpmjs/core

This refactor consolidates duplicated role creation logic, adds optional
advisory locks for concurrent CI/CD safety, and makes admin role granting
explicit and configurable.

Note: Test credentials (app_user/app_password, app_admin/admin_password)
are intentional test fixtures matching existing bootstrap-test-roles.sql

Co-Authored-By: Dan Lynch <[email protected]>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration bot and others added 3 commits December 18, 2025 21:02
PostgreSQL's pg library doesn't support multiple statements in a
parameterized query. Consolidate the two DO blocks and BEGIN/COMMIT
into a single DO block to fix the 'cannot insert multiple commands
into a prepared statement' error.

Co-Authored-By: Dan Lynch <[email protected]>
DO blocks in PostgreSQL don't support parameterized queries (, , etc.)
because the block body is parsed as a string literal. This fix:

- Removes loadSqlTemplate and uses inline SQL with safe string interpolation
- Uses escapeLiteral() for SQL string values
- Uses format('%I', ...) inside PL/pgSQL for identifier escaping
- Applies fix to all role management functions

Co-Authored-By: Dan Lynch <[email protected]>
DO blocks in PostgreSQL don't support parameterized queries (, , etc.)
because the block body is parsed as a string literal. This fix:

- Uses escapeLiteral() for SQL string values in grantRole and createUserRole
- Uses conditional string interpolation for optional advisory locks
- Removes unused streamSqlWithParams method and Client import
- Applies same fix pattern as role-utils.ts

Co-Authored-By: Dan Lynch <[email protected]>
@pyramation pyramation marked this pull request as draft December 19, 2025 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants