Managing multiple PostgreSQL installs via Homebrew

Following on from this post, you probably have multiple versions of PostgreSQL installed on your Mac. In that post, I added an example function to help you manage all these concurrent installs. Today, I’m back with a full-fledged shell script to help manage all this. Without further ado, the script:

#!/bin/zsh

wanted_ver=$1
no_restart=

# is the version requested installed?
brew ls --version postgresql@${wanted_ver} &>/dev/null
if [[ $? -eq 0 ]] ; then
  # yes, carry on
  :
else
  # nope, so install it
  echo "Installing PostgreSQL ${wanted_ver}... "
  brew install petere/postgresql/postgresql@${wanted_ver}
fi

# is postgresql is running?
for i in /usr/local/var/postgres/*
do
  check_ver=$(basename ${i})
  is_running=$(ps -few|egrep -- "[p]ostgres.*-D.*${check_ver}")

  if [[ -z ${is_running} ]] ; then
    # nope, carry on
    :
  else
    # it is. is it the requested version?
    if [[ "${wanted_ver}" = "${check_ver}" ]] ; then
      # yup, carry on
      no_restart=t
    else
      # nope, so kill it
      echo -n "Stopping PostgreSQL ${check_ver}... "
      /usr/local/opt/postgresql@${check_ver}/bin/pg_ctl \
          -D /usr/local/var/postgres/${check_ver} \
          stop -w -mf | grep 'stopped'
    fi
  fi
done

# what version is active?
if [[ -e /usr/local/bin/psql ]] ; then
    active_ver=$(/usr/bin/stat -f %Y /usr/local/bin/psql | cut -d\/ -f3 | cut -d\- -f2 | cut -d\@ -f2)
else
    active_ver=0
fi

# is the active version the requested version?
if [[ "${active_ver}" = "${wanted_ver}" ]] ; then
  # yup, carry on
  :
else
  # nope, so deactivate it
  echo -n "Deactivating PostgreSQL ${active_ver}... "
  brew unlink --force --overwrite postgresql@${active_ver} | cut -d\  -f3-
  # and activate the correct version
  echo -n "Activating PostgreSQL ${wanted_ver}... "
  brew link --force --overwrite postgresql@${wanted_ver} | grep 'created' | cut -d\  -f3-
fi

# point to the correct data dir and port
PGDATA=/usr/local/var/postgres/${wanted_ver}
PGPORT="54$(echo ${wanted_ver} | tr -d .)"

# should we be starting a cluster?
if [[ "${no_restart}" = "t" ]] ; then
  # nope, carry on
  :
else
  # yup. has the cluster been initialized?
  if [[ ! -d ${PGDATA} ]] ; then
    # nope, so let's do that
    echo "Initializing PostgreSQL ${wanted_ver} cluster... "
    initdb -k ${PGDATA} || initdb ${PGDATA}
    echo "port = ${PGPORT}" >> ${PGDATA}/postgresql.conf
    echo "log_destination = 'stderr'" >> ${PGDATA}/postgresql.conf
    echo "logging_collector = on" >> ${PGDATA}/postgresql.conf
    echo "log_filename = 'postgresql-%a.log'" >> ${PGDATA}/postgresql.conf
    echo "log_truncate_on_rotation = on" >> ${PGDATA}/postgresql.conf
    echo "log_rotation_age = 1d" >> ${PGDATA}/postgresql.conf
    echo "log_rotation_size = 0" >> ${PGDATA}/postgresql.conf
    echo "log_timezone = 'US/Michigan'" >> ${PGDATA}/postgresql.conf
    echo "log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '" >> ${PGDATA}/postgresql.conf
  else
    # yup, carry on
    :
  fi
  # start the cluster
  echo -n "Starting PostgreSQL ${wanted_ver}... "
  pg_ctl -D ${PGDATA} start  > /tmp/pg.start 2>&1
  grep 'server start' /tmp/pg.start
  if [[ -x /usr/local/bin/pg_isready ]] ; then
      ret=1
      while [[ ${ret} -eq 1 ]]
      do
        # wait for the cluster to be available before exiting
        pg_isready -q
        ret=$?
      done
    fi
fi

echo "export PGDATA=${PGDATA}"
echo "export PGPORT=${PGPORT}"

But what does it do? It’s pretty simple actually. When you call this script, you tell it what version of PostgreSQL you want:

$ pg 9.6

and then the script does the following:

  • checks if the requested version is installed, and installs it if not
  • checks if another version of PostgreSQL is running, and stops it
  • checks if another version is linked as the active version, and unlinks it
  • links the requested version as the active version
  • sets PGDATA to point to the requested version’s data cluster
  • does an initdb for the requested version if needed
  • starts the requested version’s cluster

I’ll be the first to admit that the script could use additional work, but it’s functional enough to start using today. As I continue to improve the script, I’ll update the"post"with those changes, so check back every so often.

Enjoy.