##
## This Makefile is trying to mimic the targets and behaviour of the
## PGXS Makefile
##
## see https://github.com/postgres/postgres/blob/master/src/makefiles/pgxs.mk
##

PGRX?=cargo pgrx
PGVER?=$(shell grep 'default = \[".*\"]' Cargo.toml | sed -e 's/.*\["//' | sed -e 's/"].*//')
PG_MAJOR_VERSION=$(PGVER:pg%=%)
ANON_MINOR_VERSION?=$(shell grep '^version *= *' Cargo.toml | sed 's/^version *= *//' | tr -d '\"' | tr -d ' ' )

# use `TARGET=debug make run` for more detailed errors
TARGET?=release
TARGET_DIR?=target/$(TARGET)/anon-$(PGVER)/
PG_CONFIG?=`$(PGRX) info pg-config $(PGVER) 2> /dev/null || echo pg_config`
PG_SHAREDIR?=$(shell $(PG_CONFIG) --sharedir)
PG_LIBDIR?=$(shell $(PG_CONFIG) --libdir)
PG_PKGLIBDIR?=$(shell $(PG_CONFIG) --pkglibdir)
PG_BINDIR?=$(shell $(PG_CONFIG) --bindir)

ifeq ($(shell uname -s),Darwin)
    LIB_SUFFIX?=dylib
else
    LIB_SUFFIX?=so
endif
LIB=anon.$(LIB_SUFFIX)

# The instance
PGDATA_DIR=~/.pgrx/data-$(PG_MAJOR_VERSION)

# Be sure to use the PGRX version (PGVER) of the postgres binaries
# It's especially important for the pg_dump test in pg_regress
PATH:=$(PG_BINDIR):${PATH}

# This is where the package is placed
TARGET_SHAREDIR?=$(TARGET_DIR)$(PG_SHAREDIR)
TARGET_PKGLIBDIR?=$(TARGET_DIR)$(PG_PKGLIBDIR)

PG_REGRESS?=$(PG_PKGLIBDIR)/pgxs/src/test/regress/pg_regress
PG_SOCKET_DIR?=/var/lib/postgresql/.pgrx/
PGHOST?=localhost
PGPORT?=288$(subst pg,,$(PGVER))
PSQL_OPT?=--host $(PGHOST) --port $(PGPORT)
PGDATABASE?=contrib_regression

##
## PGXS tests
##

# DO NOT USE THIS !
# If you want to add new tests, push them directly into
# the `tests/pg_regress` folder and launch them with `make regress`

# /!\ The test files should not have the same name that source files located
# in the `src` folder

# !!! Migration in Progress !!!
#
# 1. mvtst() { git mv tests/sql/$1.sql tests/pg_regress/sql/; git mv tests/expected/$1.out tests/pg_regress/expected/; }
# 2. mvtst foo
#
REGRESS_TESTS = 000-setup

# Migration Failed
# The tests below need to be adapted to work with `make regress`
REGRESS_TESTS+= faking
REGRESS_TESTS+= foreign_keys
REGRESS_TESTS+= initialize
REGRESS_TESTS+= hashing
REGRESS_TESTS+= image_blur
REGRESS_TESTS+= permissions_masked_role
REGRESS_TESTS+= permissions_owner
REGRESS_TESTS+= pg_dump
REGRESS_TESTS+= privacy_by_default
REGRESS_TESTS+= pseudonymization
REGRESS_TESTS+= rename_objects
REGRESS_TESTS+= sampling
REGRESS_TESTS+= test_replica_masking
REGRESS_TESTS+= test_static_masking
REGRESS_TESTS+= ldp
REGRESS_TESTS+= random_functions
REGRESS_TESTS+= restricted
REGRESS_TESTS+= ternary
REGRESS_TESTS+= trusted_schemas

# We try our best to write tests that produce the same output on all the 5
# current Postgres major versions. But sometimes it's really hard to do and
# we generally prefer simplicity over complex output manipulation tricks.
#
# In these few special cases, we use conditional tests with the following
# naming rules:
# * the _PG15+ suffix means PostgreSQL 15 and all the major versions after
# * the _PG13- suffix means PostgreSQL 13 and all the major versions below

REGRESS_TESTS_PG14 = elevation_via_security_definer_function_PG14-

REGRESS_TESTS+=${REGRESS_TESTS_PG${PG_MAJOR_VERSION}}

# This can be overridden by an env variable
REGRESS?=$(REGRESS_TESTS)


EXTRA_CLEAN?=target


##
## BUILD
##

all: extension

extension:
	$(PGRX) package --pg-config $(PG_CONFIG)
	mkdir -p $(TARGET_SHAREDIR)/extension/anon/
	install data/*.csv $(TARGET_SHAREDIR)/extension/anon/
	install data/en_US/fake/*.csv $(TARGET_SHAREDIR)/extension/anon/


##
## INSTALL
##

install:
	cp -r $(TARGET_SHAREDIR)/extension/* $(PG_SHAREDIR)/extension/
	install $(TARGET_PKGLIBDIR)/$(LIB) $(PG_PKGLIBDIR)

##
## Regression tests
##
regress: extension install
	$(PGRX) regress $(PGVER) \
	   --profile=$(TARGET) \
	   --dbname=contrib_regression \
	   --postgresql-conf session_preload_libraries='anon' \
	   --postgresql-conf anon.masking_policies=devtests,analytics,impexp \
	   --postgresql-conf client_min_messages=error \
	   --psql-verbosity default \
	   --resetdb \
	   $(REGRESS_TEST)

regress_diff:
	diff tests/pg_regress/results tests/pg_regress/expected

##
## INSTALLCHECK
##
## These are the functional tests, the unit tests are run with Cargo
##

# With PGXS: the postgres instance is created on-the-fly to run the test.
# With PGRX: the postgres instance is created previously by `cargo run`. This
# means we have some extra tasks to prepare the instance

installcheck: stop start
	dropdb $(PSQL_OPT) --if-exists $(PGDATABASE)
	dropdb $(PSQL_OPT) --if-exists $(PGDATABASE)_source
	createdb $(PSQL_OPT) $(PGDATABASE)
	dropuser oscar_the_owner || echo 'ignored'
	createuser $(PSQL_OPT) postgres --superuser || echo 'ignored'
	psql $(PSQL_OPT) $(PGDATABASE) -c "ALTER DATABASE $(PGDATABASE) SET session_preload_libraries = 'anon';"
	psql $(PSQL_OPT) $(PGDATABASE) -c "ALTER DATABASE $(PGDATABASE) SET anon.masking_policies = 'devtests, analytics, impexp';"
	psql $(PSQL_OPT) $(PGDATABASE) -c "ALTER DATABASE $(PGDATABASE) SET client_min_messages = notice;"
	$(PG_REGRESS) \
		$(PSQL_OPT) \
		--use-existing \
		--inputdir=./tests/ \
		--dbname=$(PGDATABASE) \
		$(REGRESS_OPTS) \
		$(REGRESS)


##
## PGRX commands
##

ifeq ($(TARGET),release)
  RELEASE_OPT=--release
endif

test:
	$(PGRX) test $(PGVER) $(RELEASE_OPT) --verbose

start:
	sed --in-place 's/^#\?wal_level = .*/wal_level = logical/' $(PGDATA_DIR)/postgresql.conf
	$(PGRX) start $(PGVER)

stop:
	$(PGRX) stop $(PGVER)

run:
	# ensure that the wal_level is properly set
	sed --in-place 's/^#\?wal_level = .*/wal_level = logical/' $(PGDATA_DIR)/postgresql.conf
	$(PGRX) run $(PGVER) $(RELEASE_OPT)

psql:
	psql --host localhost --port 288$(PG_MAJOR_VERSION) anon

##
## Coverage
##

COVERAGE_DIR?=target/$(TARGET)/coverage

clean_profiles:
	rm -fr *.profraw

coverage: clean_profiles coverage_test covergage_report

coverage_test:
	export RUSTFLAGS=-Cinstrument-coverage \
	export LLVM_PROFILE_FILE=$(TARGET)-%p-%m.profraw \
	&& $(PGRX) test $(PGVER) $(RELEASE_OPT) --verbose

coverage_report:
	mkdir -p $(COVERAGE_DIR)
	export LLVM_PROFILE_FILE=$(TARGET)-%p-%m.profraw \
	&& grcov . \
	      --binary-path target/$(TARGET) \
	      --source-dir . \
	      --output-path $(COVERAGE_DIR)\
	      --keep-only 'src/*' \
	      --llvm \
	      --ignore-not-existing \
	      --output-types html,cobertura
	# Terse output
	grep '<p class="heading">Lines</p>' -A2 $(COVERAGE_DIR)/html/index.html \
	  | tail -n 1 \
	  | xargs \
	  | sed 's,%.*,,' \
	  | sed 's/.*>/Coverage: /'

##
## C L E A N
##

clean: clean_profiles
ifdef EXTRA_CLEAN
	rm -rf $(EXTRA_CLEAN)
endif


##
## All targets below are not part of the PGXS Makefile
##

##
## P A C K A G E S
##

# See https://nfpm.goreleaser.com/docs/arch-mapping/
ARCH?=amd64

# The packages are built from the $(TARGET_DIR) folder.
# So the $(PG_PKGLIBDIR) and $(PG_SHAREDIR) are relative to that folder
rpm deb: package
	export PG_PKGLIBDIR=".$(PG_PKGLIBDIR)" && \
	export PG_SHAREDIR=".$(PG_SHAREDIR)" && \
	export PG_MAJOR_VERSION="$(PG_MAJOR_VERSION)" && \
	export ANON_MINOR_VERSION="$(ANON_MINOR_VERSION)" && \
	envsubst < nfpm.template.yaml > $(TARGET_DIR)/nfpm.yaml
	cd $(TARGET_DIR) && nfpm package --packager $@

TARGET_CHECKSUMS_FILE := $(TARGET_DIR)/postgresql_anonymizer_$(ANON_MINOR_VERSION)_$(ARCH)_checksums.txt

checksums: $(TARGET_CHECKSUMS_FILE)

$(TARGET_CHECKSUMS_FILE):
	cd $(TARGET_DIR) && \
	sha256sum *.rpm *.deb > $(notdir $@)

# The `package` command needs pg_config from the target version
# https://github.com/pgcentralfoundation/pgrx/issues/288
package:
	$(PGRX) package --pg-config $(PG_CONFIG)


##
## R E L E A S E
##

release: release_checksums release_urls

RELEASE_CHECKSUMS_FILE := target/release/postgresql_anonymizer_$(ANON_MINOR_VERSION)_checksums.txt

# Aggregate all the checksum file ( 2 per PGVER )
release_checksums: $(RELEASE_CHECKSUMS_FILE)

# split in 2 steps to avoid recursive loops
$(RELEASE_CHECKSUMS_FILE):
	find target/release/ -type f -name '*_checksums.txt' -exec cat {} + > $@.tmp
	mv $@.tmp $@

RELEASE_BASE_URL := https://gitlab.com/dalibo/postgresql_anonymizer/-/releases/$(ANON_MINOR_VERSION)/downloads/

RELEASE_URLS_FILE := target/release/postgresql_anonymizer_$(ANON_MINOR_VERSION)_urls.txt

# Derivate the URLs of all the packages
release_urls: $(RELEASE_URLS_FILE)

$(RELEASE_URLS_FILE): $(RELEASE_CHECKSUMS_FILE)
	sed "s,.*  *,$(RELEASE_BASE_URL)," $^ > $@



##
## O C I
##

OCI_DISTRO?=bookworm
OCI_TAG?=$(PG_MAJOR_VERSION)-$(ANON_MINOR_VERSION)-$(OCI_DISTRO)
OCI_IMAGE?=registry.gitlab.com/dalibo/postgresql_anonymizer:$(OCI_TAG)
OCI_ARCH?=amd64

DOCKER_BUILD_ARG := \
  --build-arg PG_MAJOR_VERSION=$(PG_MAJOR_VERSION) \
  --build-arg OCI_DISTRO=$(OCI_DISTRO)

oci_image: docker/oci/Dockerfile #: build the OCI image
	docker build $(DOCKER_BUILD_ARG) --tag $(OCI_IMAGE) --file $^ .

oci_push: #: push the docker image to the registry
	docker push $(OCI_IMAGE)

##
## D O C K E R
##

DOCKER_TAG?=latest
DOCKER_IMAGE?=registry.gitlab.com/dalibo/postgresql_anonymizer:$(DOCKER_TAG)

DOCKER_BUILD_ARG := --build-arg ANON_MINOR_VERSION=${ANON_MINOR_VERSION}

ifneq ($(DOCKER_PG_MAJOR_VERSION),)
DOCKER_BUILD_ARG += --build-arg DOCKER_PG_MAJOR_VERSION=$(DOCKER_PG_MAJOR_VERSION)
endif

PGRX_VERSION?=0.17.0
PGRX_TAG?=pgrx-$(PGRX_VERSION)
PGRX_IMAGE?=registry.gitlab.com/dalibo/postgresql_anonymizer:$(PGRX_TAG)
PGRX_BUILD_ARGS:=--build-arg PGRX_VERSION=$(PGRX_VERSION)

docker_image: docker/Dockerfile #: build the docker image
	docker build --tag $(DOCKER_IMAGE) . --file $^  $(DOCKER_BUILD_ARG)

pgrx_image: docker/pgrx/Dockerfile
	docker build --tag $(PGRX_IMAGE) . --file $^ $(PGRX_BUILD_ARGS)

docker_push: #: push the docker image to the registry
	docker push $(DOCKER_IMAGE)

pgrx_push:
	docker push $(PGRX_IMAGE)

docker_bash: #: enter the docker image (useful for testing)
	docker exec -it docker-PostgreSQL-1 bash

pgrx_bash:
	docker run --rm --interactive --tty --volume  `pwd`:/pgrx $(PGRX_IMAGE)

COMPOSE=docker compose --file docker/docker-compose.yml

docker_init: #: start a docker container
	$(COMPOSE) down
	$(COMPOSE) up -d
	@echo "The Postgres server may take a few seconds to start. Please wait."

##
## L I N T
##


lint: lint-rs lint-md

lint-md:
	uvx rumdl check

lint-rs:
	cargo clippy --release -- -Dwarnings

##
## D O C S
##

MKDOCS?=uvx mkdocs

.PHONY: docs
docs:
	$(MKDOCS) build

##
## DALIBO-specific Makefile
##
-include dalibo/Makefile
