Module: Pgbus::QueueNameValidator

Defined in:
lib/pgbus/queue_name_validator.rb

Overview

Validates and sanitizes PGMQ queue names for safe use in SQL identifiers.

PGMQ queue names are interpolated into SQL as table/sequence names (e.g., pgmq.q_<name>, pgmq.a_<name>). This module enforces strict validation to prevent SQL injection via crafted queue names.

Constant Summary collapse

MAX_QUEUE_NAME_LENGTH =

PostgreSQL’s NAMEDATALEN caps identifiers at 63 bytes, but the effective limit is tighter: pgmq-ruby (our transport gem) rejects any queue name with ‘length >= 48` in `PGMQ::Client#validate_queue_name!`. That leaves an actual usable ceiling of 47 characters for the fully-prefixed name (`<queue_prefix>_<logical_name>`), which is what this constant expresses. pgmq-ruby picked 48 to leave headroom for PGMQ’s internal tables (‘pgmq.q_`, `pgmq.a_`, sequences, indexes) which all get suffixed beyond the base name.

Historically this was 61 (the raw PostgreSQL ceiling minus PGMQ’s ‘q_`/`a_` prefix). That was wrong in practice: names in the 48-61 range passed pgbus’s validator but blew up deep inside pgmq-ruby with an InvalidQueueNameError — exactly the kind of opaque failure the validator was meant to catch up front.

47
VALID_QUEUE_NAME_PATTERN =

Only alphanumeric characters and underscores are allowed.

/\A[a-zA-Z0-9_]+\z/

Class Method Summary collapse

Class Method Details

.normalize(name) ⇒ Object

Normalizes a queue name by replacing common separators (hyphens, dots, colons) with underscores, stripping remaining invalid characters, and collapsing consecutive underscores. Use this for names from external sources (e.g., Turbo stream names like “hotwire-livereload” or “gid://app/Foo/1”) where the intent is to derive a valid PGMQ queue name that preserves as much of the original identifier as possible.

Colons in particular are the turbo-rails stream-name separator (‘Pgbus.stream([user, :notifications])` → `“user_gid:notifications”`), so they must map to a safe character rather than be stripped —otherwise `“a:b”` and `“ab”` would collide on the same queue.



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/pgbus/queue_name_validator.rb', line 61

def normalize(name)
  name = name.to_s
  return validate!(name) if VALID_QUEUE_NAME_PATTERN.match?(name)

  normalized = name.gsub(/[-.:]/, "_")          # hyphens/dots/colons → underscores
                   .gsub(/[^a-zA-Z0-9_]/, "")   # strip remaining invalid chars
                   .gsub(/_+/, "_")              # collapse consecutive underscores
                   .gsub(/\A_|_\z/, "")          # strip leading/trailing underscores
  validate!(normalized)
  normalized
end

.sanitize!(name) ⇒ Object

Sanitizes a queue name by removing invalid characters, then validates. Use this for names from untrusted sources (e.g., URL params).



75
76
77
78
79
# File 'lib/pgbus/queue_name_validator.rb', line 75

def sanitize!(name)
  sanitized = name.to_s.gsub(/[^a-zA-Z0-9_]/, "")
  validate!(sanitized)
  sanitized
end

.validate!(name) ⇒ Object

Validates a queue name for safe SQL identifier use. Returns the name if valid, raises ArgumentError if not.

Raises:

  • (ArgumentError)


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/pgbus/queue_name_validator.rb', line 33

def validate!(name)
  name = name.to_s
  raise ArgumentError, "Queue name cannot be blank" if name.empty?
  if name.length > MAX_QUEUE_NAME_LENGTH
    raise ArgumentError,
          "Queue name too long (#{name.length} chars, max #{MAX_QUEUE_NAME_LENGTH}): #{name.inspect}"
  end

  unless VALID_QUEUE_NAME_PATTERN.match?(name)
    raise ArgumentError,
          "Invalid queue name: #{name.inspect}. Only alphanumeric characters and underscores are allowed."
  end

  name
end