postgresql-connection-string-0.1.0.3: PostgreSQL connection string type, parser and builder
Safe HaskellNone
LanguageHaskell2010

PostgresqlConnectionString

Description

Structured model of PostgreSQL connection string, with a DSL for construction, access, parsing and rendering.

It supports both the URI format (postgresql:// and postgres://) and the keyword/value format as specified in the PostgreSQL documentation: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING

Usage

Parsing Connection Strings

Parse a connection string from Text, validate it and access its components:

>>> toDbname <$> parse "postgresql://user:password@localhost:5432/mydb"
Right (Just "mydb")

Or use its IsString instance for convenience (ignoring parse errors):

>>> toDbname "postgresql://user:password@localhost:5432/mydb"
Just "mydb"

Constructing Connection Strings

Build connection strings using the Semigroup instance and constructor functions:

>>> let connStr = mconcat [user "myuser", password "secret", hostAndPort "localhost" 5432, dbname "mydb"]
>>> toUrl connStr :: Text
"postgresql://myuser:secret@localhost:5432/mydb"

Converting Between Formats

Convert to URI format:

>>> toUrl "host=localhost port=5432 user=user password=password dbname=mydb"
"postgresql://user:password@localhost:5432/mydb"

Convert to keyword/value format (for use with libpq's PQconnectdb):

>>> toKeyValueString "postgresql://user:password@localhost:5432/mydb"
"host=localhost port=5432 user=user password=password dbname=mydb"

Note that these examples use the IsString instance for brevity.

Synopsis

Data Types

data ConnectionString Source #

A PostgreSQL connection string.

This type represents all the components of a PostgreSQL connection string as defined in: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS

ConnectionString has Semigroup and Monoid instances that allow combining connection strings. When combining, the right-hand side takes precedence for scalar values, while the list of hosts gets concatenated.

Instances

Instances details
Arbitrary ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

IsString ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString

Monoid ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

Semigroup ConnectionString Source #

Combine two connection strings.

When combining, the following rules apply:

  • For hosts: concatenated in order.
  • For scalar values and params: right-hand side takes precedence for duplicate keys, which gives you override behaviour.
Instance details

Defined in PostgresqlConnectionString.Types

Generic ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

Associated Types

type Rep ConnectionString 
Instance details

Defined in PostgresqlConnectionString.Types

Show ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString

Eq ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

Ord ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

Hashable ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

type Rep ConnectionString Source # 
Instance details

Defined in PostgresqlConnectionString.Types

Parsing

parse :: Text -> Either Text ConnectionString Source #

Parse a connection string from Text.

Supports both URI format and keyword/value format connection strings:

URI format examples:

>>> parse "postgresql://localhost"
Right ...
>>> parse "postgresql://user:password@localhost:5432/mydb"
Right ...
>>> parse "postgres://host1:5432,host2:5433/mydb?connect_timeout=10"
Right ...

Keyword/value format examples:

>>> parse "host=localhost port=5432 user=postgres"
Right ...
>>> parse "host=localhost dbname=mydb"
Right ...

Returns Left with an error message if parsing fails:

>>> parse "invalid://connection"
Left ...

The error message is quite detailed (it is produced by Megaparsec):

>>> parse "invalid://connection=" & either id (const "") & Data.Text.IO.putStrLn
1:8:
  |
1 | invalid://connection=
  |        ^
unexpected ':'
expecting '=' or Key

megaparsecOf :: Parsec Void Text ConnectionString Source #

Get the Megaparsec parser of connection strings.

This allows you to use the connection string parser as part of a larger Megaparsec parser combinator setup.

The parser accepts both URI format (postgresql:// or postgres://) and keyword/value format connection strings.

Rendering

toUrl :: ConnectionString -> Text Source #

Convert a connection string to the PostgreSQL URI format.

This produces a connection string in the form:

postgresql://[userspec@][hostspec][/dbname][?paramspec]

where:

  • userspec is user[:password]
  • hostspec is a comma-separated list of host[:port] specifications
  • dbname is the database name
  • paramspec is a query string of connection parameters

All components are percent-encoded as necessary.

Examples:

>>> toUrl (mconcat [user "myuser", hostAndPort "localhost" 5432, dbname "mydb"])
"postgresql://myuser@localhost:5432/mydb"
>>> toUrl (mconcat [user "user", password "secret", host "localhost"])
"postgresql://user:secret@localhost"
>>> toUrl (mconcat [hostAndPort "host1" 5432, hostAndPort "host2" 5433, dbname "mydb"])
"postgresql://host1:5432,host2:5433/mydb"

toKeyValueString :: ConnectionString -> Text Source #

Convert a connection string to the PostgreSQL keyword/value format.

The keyword/value format is a space-separated list of key=value pairs. Values containing spaces, quotes, backslashes, or equals signs are automatically quoted with single quotes, and backslashes and single quotes within values are escaped with backslashes.

Note: Only the first host from the hostspec is included, as the keyword/value format does not support multiple hosts in the same way as the URI format.

Examples:

>>> toKeyValueString (mconcat [hostAndPort "localhost" 5432, user "postgres"])
"host=localhost port=5432 user=postgres"
>>> toKeyValueString (password "secret pass")
"password='secret pass'"
>>> toKeyValueString (password "it's a secret")
"password='it\\'s a secret'"

Accessors

toHosts :: ConnectionString -> [(Text, Maybe Word16)] Source #

Extract the list of hosts and their optional ports from a connection string.

Each tuple contains a host (domain name or IP address) and an optional port number. If no port is specified, Nothing is returned for that host.

Examples:

>>> toHosts (hostAndPort "localhost" 5432)
[("localhost", Just 5432)]
>>> toHosts (mconcat [host "host1", hostAndPort "host2" 5433])
[("host1", Nothing), ("host2", Just 5433)]

toUser :: ConnectionString -> Maybe Text Source #

Extract the username from a connection string, if present.

Examples:

>>> toUser (user "myuser")
Just "myuser"
>>> toUser mempty
Nothing

toPassword :: ConnectionString -> Maybe Text Source #

Extract the password from a connection string, if present.

Examples:

>>> toPassword (password "secret")
Just "secret"
>>> toPassword mempty
Nothing

toDbname :: ConnectionString -> Maybe Text Source #

Extract the database name from a connection string, if present.

Examples:

>>> toDbname (dbname "mydb")
Just "mydb"
>>> toDbname mempty
Nothing

toParams :: ConnectionString -> Map Text Text Source #

Extract the connection parameters as a Map of key-value pairs.

These correspond to the query string parameters in the URI format, or additional connection parameters in the keyword/value format.

Examples:

>>> toParams (param "application_name" "myapp")
fromList [("application_name","myapp")]
>>> toParams (mconcat [param "connect_timeout" "10", param "application_name" "myapp"])
fromList [("application_name","myapp"),("connect_timeout","10")]

Transformations

interceptParam Source #

Arguments

:: Text

The key of the parameter to intercept.

-> ConnectionString 
-> Maybe (Text, ConnectionString) 

Extract a parameter by key and remove it from the connection string.

If the parameter is found, returns Just with a tuple of the parameter's value and the updated connection string (with the parameter removed). If the parameter is not found, returns Nothing.

This is useful for extracting connection parameters that need special handling before passing the connection string to PostgreSQL.

Examples:

>>> let connStr = mconcat [param "application_name" "myapp", param "connect_timeout" "10"]
>>> interceptParam "application_name" connStr
Just ("myapp", "postgresql://?connect_timeout=10")
>>> interceptParam "nonexistent" connStr
Nothing

Constructors

host :: Text -> ConnectionString Source #

Create a connection string with a single host and without specifying a port.

Multiple hosts can be specified by combining multiple host or hostAndPort values using the Semigroup instance.

When you need to specify a port, use hostAndPort instead.

Examples:

>>> host "localhost"
"postgresql://localhost"

hostAndPort :: Text -> Word16 -> ConnectionString Source #

Create a connection string with a single host and port.

Multiple hosts can be specified by combining multiple hostAndPort or host values using the Semigroup instance.

Examples:

>>> hostAndPort "localhost" 5432
"postgresql://localhost:5432"
>>> mconcat [hostAndPort "host1" 5432, hostAndPort "host2" 5433]
"postgresql://host1:5432,host2:5433"

user :: Text -> ConnectionString Source #

Create a connection string with a username.

Examples:

>>> user "myuser"
"postgresql://myuser@"
>>> mconcat [user "myuser", host "localhost"]
"postgresql://myuser@localhost"

password :: Text -> ConnectionString Source #

Create a connection string with a password.

Note: Passwords are typically used together with usernames.

Examples:

>>> mconcat [user "myuser", password "secret"]
"postgresql://myuser:secret@"
>>> mconcat [user "myuser", password "secret", host "localhost"]
"postgresql://myuser:secret@localhost"

dbname :: Text -> ConnectionString Source #

Create a connection string with a database name.

Examples:

>>> dbname "mydb"
"postgresql:///mydb"
>>> mconcat [host "localhost", dbname "mydb"]
"postgresql://localhost/mydb"

param :: Text -> Text -> ConnectionString Source #

Create a connection string with a single connection parameter.

Connection parameters are arbitrary key-value pairs that configure the PostgreSQL connection. Common parameters include:

  • application_name - Sets the application name
  • connect_timeout - Connection timeout in seconds
  • options - Command-line options for the server
  • sslmode - SSL mode (disable, require, verify-ca, verify-full)

See the PostgreSQL documentation for a complete list: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS

Examples:

>>> param "application_name" "myapp"
"postgresql://?application_name=myapp"
>>> mconcat [host "localhost", param "connect_timeout" "10"]
"postgresql://localhost?connect_timeout=10"
>>> mconcat [param "application_name" "myapp", param "connect_timeout" "10"]
"postgresql://?application_name=myapp&connect_timeout=10"

Orphan instances