Author: Eiko

Time: 2025-02-25 12:11:27 - 2025-02-25 12:11:27 (UTC)

We can PostgreSQL on Arch Linux for our Haskell projects with packages like persistent and persistent-postgresql setting up involves several steps: installing and setting up PostgreSQL, configuring your Haskell environment, and modifying your Haskell code to connect to PostgreSQL instead of SQLite.

Additionally look into the esqueleto package for advanced SQL-specific support.

1. Install PostgreSQL on Arch Linux

First, ensure that PostgreSQL is installed on your Arch Linux system.

sudo pacman -Syu postgresql

2. Initialize the Database Cluster

After installation, you need to initialize the PostgreSQL database cluster.

sudo -iu postgres initdb --locale $LANG -E UTF8 -D /var/lib/postgres/data

3. Start and Enable PostgreSQL Service

Start the PostgreSQL service and enable it to start automatically on boot.

sudo systemctl enable postgresql --now

4. Set Up PostgreSQL User and Database

Switch to the postgres user to create a new PostgreSQL role and database.

sudo -iu postgres

Create a new PostgreSQL user (replace yourusername and yourpassword with your desired credentials):

createuser yourusername --interactive

You’ll be prompted to answer some questions. For most Haskell applications, you might want to grant the user superuser privileges or specific privileges as needed.

Set a password for the new user:

psql
ALTER USER yourusername WITH PASSWORD 'yourpassword';
\q

Create a new database (typically the same name as your project or user):

createdb yourdatabase

Exit the postgres user session:

exit

5. Configure PostgreSQL to Allow Connections

By default, PostgreSQL may be configured to allow only local connections. You might need to modify the pg_hba.conf file to allow your application to connect.

Edit the pg_hba.conf file:

sudo nano /var/lib/postgres/data/pg_hba.conf

Ensure you have a line like the following to allow local connections using MD5 (password):

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     md5

Similarly, if you need to allow TCP/IP connections, ensure listen_addresses is set appropriately in postgresql.conf:

sudo nano /var/lib/postgres/data/postgresql.conf

Set listen_addresses to '*' to allow connections from any IP (use with caution):

listen_addresses = '*'

After making changes, restart PostgreSQL:

sudo systemctl restart postgresql

6. Install Haskell Libraries

Ensure that you have the necessary Haskell libraries installed for interacting with PostgreSQL via persistent.

Add the following dependencies to your .cabal file or package.yaml:

dependencies:
  - persistent
  - persistent-postgresql
  - postgresql-simple

Alternatively, if you’re using stack, add them to your package.yaml under dependencies.

Then, update your package list:

cabal update

or for stack:

stack update

7. Configure Your Haskell Application

Modify your Haskell code to use PostgreSQL instead of SQLite. Here’s a basic example of how to set up persistent with PostgreSQL.

a. Define Your Models

In your models file (commonly named models.hs or similar), ensure you have your entities defined. For example:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}

import Database.Persist.TH

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
    name String
    email String
    deriving Show
|]

b. Set Up the Connection

In your main application file, set up the PostgreSQL connection. Here’s a simple example using persistent:

{-# LANGUAGE OverloadedStrings #-}

import Control.Monad.IO.Class (liftIO)
import Database.Persist
import Database.Persist.Postgresql
import Database.Persist.TH
import Control.Monad.Logger (runStdoutLoggingT)
import qualified Data.Text as T

-- Import your models
import Models -- Replace with your actual models module

main :: IO ()
main = runStdoutLoggingT $ withPostgresqlPool connStr 10 $ \pool -> liftIO $ do
    flip runSqlPersistMPool pool $ do
        runMigration migrateAll

        -- Example: Insert a user
        userId <- insert $ User "John Doe" "john@example.com"

        -- Example: Retrieve users
        users <- selectList [] []
        liftIO $ print (users :: [Entity User])

  where
    connStr = "host=localhost dbname=yourdatabase user=yourusername password=yourpassword port=5432"

c. Environment Variables (Optional but Recommended)

For security reasons, it’s better to manage your database credentials using environment variables instead of hard-coding them.

Modify the connection string to read from environment variables:

import System.Environment (getEnv)

main :: IO ()
main = do
    host <- getEnv "DB_HOST"
    dbname <- getEnv "DB_NAME"
    user <- getEnv "DB_USER"
    password <- getEnv "DB_PASSWORD"
    let connStr = T.pack $ "host=" ++ host ++
                           " dbname=" ++ dbname ++
                           " user=" ++ user ++
                           " password=" ++ password ++
                           " port=5432"
    runStdoutLoggingT $ withPostgresqlPool connStr 10 $ \pool -> liftIO $ do
        flip runSqlPersistMPool pool $ do
            runMigration migrateAll
            -- Your database operations

Then, set the environment variables in your shell or using a .env file with a tool like direnv.

8. Test the Connection

Before integrating deeply into your application, it’s a good idea to test the connection separately to ensure everything is set up correctly.

You can use psql to test:

psql -h localhost -U yourusername -d yourdatabase

If you can connect and perform basic operations, your PostgreSQL setup is working correctly.

9. Migrate from SQLite to PostgreSQL (If Applicable)

If you’re transitioning an existing project from SQLite to PostgreSQL:

  1. Backup Your SQLite Data: Export your existing SQLite data, possibly to a format like CSV or SQL.

  2. Import into PostgreSQL: Use psql or tools like pgloader to import the data into your new PostgreSQL database.

    Example with pgloader:

    pgloader sqlite:///path/to/sqlite.db postgresql://yourusername:yourpassword@localhost/yourdatabase
  3. Update Your Haskell Code: Modify the connection settings as shown above to point to PostgreSQL instead of SQLite.

10. Additional Tips

  • Security: Always ensure that your database credentials are secured and not exposed in your codebase. Use environment variables or secure configuration management.

  • Performance Tuning: PostgreSQL offers many configuration options for performance tuning. Depending on your application’s needs, you might want to adjust settings in postgresql.conf.

  • Backup Strategy: Implement regular backups of your PostgreSQL database to prevent data loss.

  • Monitoring: Use tools like pgAdmin, PostgreSQL Exporter for Prometheus, or other monitoring tools to keep an eye on your database’s health and performance.

11. References