├── .dir-locals.el ├── .dockerignore ├── .editorconfig ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── LICENSE-3rdparty.csv ├── META.json.in ├── Makefile ├── Makefile.dist ├── README.md ├── common.mk ├── doc └── pg_tracing.md ├── expected ├── cleanup.out ├── cursor.out ├── full_buffer.out ├── guc.out ├── insert.out ├── json.out ├── nested.out ├── parallel.out ├── parameters.out ├── planstate.out ├── planstate_bitmap.out ├── planstate_hash.out ├── planstate_projectset.out ├── planstate_subplans.out ├── planstate_union.out ├── sample.out ├── select.out ├── select_multistatement.out ├── setup.out ├── subxact.out ├── transaction.out ├── trigger.out ├── utility.out └── wal.out ├── matcher.json ├── packages ├── Makefile └── deb.Dockerfile ├── pg_tracing--0.1.0.sql ├── pg_tracing.conf ├── pg_tracing.control ├── regress ├── 16 │ └── expected │ │ ├── cleanup.out │ │ ├── cursor.out │ │ ├── extended.out │ │ ├── full_buffer.out │ │ ├── guc.out │ │ ├── insert.out │ │ ├── json.out │ │ ├── nested.out │ │ ├── nested_17.out │ │ ├── parallel.out │ │ ├── parameters.out │ │ ├── parameters_extended.out │ │ ├── planstate.out │ │ ├── planstate_bitmap.out │ │ ├── planstate_hash.out │ │ ├── planstate_projectset.out │ │ ├── planstate_subplans.out │ │ ├── planstate_union.out │ │ ├── psql_extended_18.out │ │ ├── psql_extended_tx_18.out │ │ ├── sample.out │ │ ├── select.out │ │ ├── select_multistatement.out │ │ ├── setup.out │ │ ├── subxact.out │ │ ├── transaction.out │ │ ├── trigger.out │ │ ├── utility.out │ │ └── wal.out ├── 17 │ └── expected │ │ ├── cleanup.out │ │ ├── cursor.out │ │ ├── extended.out │ │ ├── full_buffer.out │ │ ├── guc.out │ │ ├── insert.out │ │ ├── json.out │ │ ├── nested.out │ │ ├── nested_17.out │ │ ├── parallel.out │ │ ├── parameters.out │ │ ├── parameters_extended.out │ │ ├── planstate.out │ │ ├── planstate_bitmap.out │ │ ├── planstate_hash.out │ │ ├── planstate_projectset.out │ │ ├── planstate_subplans.out │ │ ├── planstate_union.out │ │ ├── sample.out │ │ ├── select.out │ │ ├── select_multistatement.out │ │ ├── setup.out │ │ ├── subxact.out │ │ ├── transaction.out │ │ ├── trigger.out │ │ ├── utility.out │ │ └── wal.out └── 18 │ └── expected │ ├── cleanup.out │ ├── cursor.out │ ├── extended.out │ ├── fk_failure.out │ ├── full_buffer.out │ ├── guc.out │ ├── insert.out │ ├── json.out │ ├── nested.out │ ├── nested_17.out │ ├── parallel.out │ ├── parameters.out │ ├── parameters_extended.out │ ├── planstate.out │ ├── planstate_bitmap.out │ ├── planstate_cursor.out │ ├── planstate_hash.out │ ├── planstate_projectset.out │ ├── planstate_subplans.out │ ├── planstate_union.out │ ├── psql_extended.out │ ├── psql_extended_18.out │ ├── psql_extended_tx.out │ ├── psql_extended_tx_18.out │ ├── reset.out │ ├── sample.out │ ├── select.out │ ├── select_multistatement.out │ ├── setup.out │ ├── subxact.out │ ├── transaction.out │ ├── trigger.out │ ├── utility.out │ └── wal.out ├── sql ├── cleanup.sql ├── cursor.sql ├── extended.sql ├── full_buffer.sql ├── guc.sql ├── insert.sql ├── json.sql ├── nested.sql ├── nested_17.sql ├── parallel.sql ├── parameters.sql ├── parameters_extended.sql ├── planstate.sql ├── planstate_bitmap.sql ├── planstate_hash.sql ├── planstate_projectset.sql ├── planstate_subplans.sql ├── planstate_union.sql ├── psql_extended_18.sql ├── psql_extended_tx_18.sql ├── sample.sql ├── select.sql ├── select_multistatement.sql ├── setup.sql ├── subxact.sql ├── transaction.sql ├── trigger.sql ├── utility.sql └── wal.sql ├── src ├── pg_prng.c ├── pg_prng.h ├── pg_tracing.c ├── pg_tracing.h ├── pg_tracing_active_spans.c ├── pg_tracing_explain.c ├── pg_tracing_json.c ├── pg_tracing_operation_hash.c ├── pg_tracing_otel.c ├── pg_tracing_parallel.c ├── pg_tracing_planstate.c ├── pg_tracing_query_process.c ├── pg_tracing_rng.c ├── pg_tracing_span.c ├── pg_tracing_sql_functions.c ├── pg_tracing_strinfo.c ├── version_compat.c └── version_compat.h ├── t ├── 001_basic.pl ├── 002_connect_timeout.pl └── 003_change_endpoint.pl ├── tests └── postgresql.conf └── typedefs.list /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;; Copied from PostgreSQL sources, for same formatting. 2 | 3 | ;; see also src/tools/editors/emacs.samples for more complete settings 4 | 5 | ((c-mode . ((c-basic-offset . 4) 6 | (c-file-style . "bsd") 7 | (fill-column . 78) 8 | (indent-tabs-mode . t) 9 | (tab-width . 4))) 10 | (nxml-mode . ((fill-column . 78) 11 | (indent-tabs-mode . nil))) 12 | (perl-mode . ((perl-indent-level . 4) 13 | (perl-continued-statement-offset . 2) 14 | (perl-continued-brace-offset . -2) 15 | (perl-brace-offset . 0) 16 | (perl-brace-imaginary-offset . 0) 17 | (perl-label-offset . -2) 18 | (indent-tabs-mode . t) 19 | (tab-width . 4))) 20 | (sgml-mode . ((fill-column . 78) 21 | (indent-tabs-mode . nil)))) 22 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bc 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Copied from PostgreSQL sources, for same formatting. 2 | 3 | root = true 4 | 5 | [*.{c,h,l,y,pl,pm}] 6 | indent_style = tab 7 | indent_size = tab 8 | tab_width = 4 9 | 10 | [*.{sgml,xml}] 11 | indent_style = space 12 | indent_size = 1 13 | 14 | [*.xsl] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thanks for contributing! You can help pg_tracing in different ways: 4 | 5 | - Open an [issue](https://github.com/DataDog/pg_tracing/issues) with suggestions for improvements, potential bugs, etc... 6 | - Fork this repository and submit a pull request 7 | 8 | This document provides some basic guidelines for contributing to this repository. 9 | 10 | ## Pull Requests 11 | 12 | Have you fixed a bug or written a new check and want to share it? Many thanks! 13 | 14 | In order to ease/speed up our review, here are some items you can check/improve 15 | when submitting your PR: 16 | 17 | * Have a proper commit history (we advise you to rebase if needed). 18 | * Write tests for the code you wrote. 19 | * Make sure that all tests pass locally. 20 | 21 | Your pull request must pass all CI tests before we will merge it. 22 | 23 | ## Build With Debug Symbols 24 | 25 | To build and install pg_tracing with debug symbols, use `PG_CFLAGS` env to pass the debug flags: 26 | 27 | ```bash 28 | PG_CFLAGS="-g" make install 29 | ``` 30 | 31 | ## Indentation 32 | 33 | Code should be indented with pgindent. You need to install pg_bsd_indent first: 34 | 35 | ``` 36 | git clone https://git.postgresql.org/git/pg_bsd_indent.git 37 | cd pg_bsd_indent 38 | make install 39 | ``` 40 | 41 | With [pgindent](https://github.com/postgres/postgres/blob/master/src/tools/pgindent/pgindent) needs to be in your `$PATH`. Once this is done, you will be able to indent with: 42 | 43 | ``` 44 | make pgindent 45 | ``` 46 | 47 | ## Tests 48 | 49 | ### Run Tests Locally 50 | 51 | You can run checks on a local PostgreSQL version available in the `PATH` with: 52 | 53 | ```bash 54 | make regresscheck 55 | ``` 56 | 57 | This will create a temporary instance using `pg_regress`. On failure, the diffs will be available in the `regression.diffs`. 58 | 59 | ### Run Tests In Docker 60 | 61 | You can build a docker image with pg_tracing for a specific version. 62 | 63 | ```bash 64 | make PG_VERSION=16 build-test-image 65 | # Clean build 66 | make DOCKER_BUILD_OPTS=--no-cache PG_VERSION=17 build-test-image 67 | ``` 68 | 69 | Once the docker image is built, you can launch it with: 70 | 71 | ```bash 72 | docker run --rm --name pg_tracing_test -ti pg_tracing_test:pg16 bash 73 | 74 | # Within the docker image 75 | make regresscheck_noinstall 76 | ``` 77 | 78 | To launch test in the docker image: 79 | 80 | ```bash 81 | make PG_VERSION=16 run-test 82 | ``` 83 | 84 | ### Update Expected Outputs 85 | 86 | If you have added additional tests, the expected outputs needs to be updated. If the outputs match your expectations, you can simply copy them: 87 | 88 | ```bash 89 | # From local results 90 | cp results/*out expected/ 91 | 92 | # Run tests in a docker container and update expected output locally 93 | make PG_VERSION=15 update-regress-output 94 | ``` 95 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy AS base 2 | 3 | ARG PG_VERSION 4 | 5 | # Install packages 6 | RUN apt update 7 | RUN DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \ 8 | ca-certificates curl gnupg make sudo gcc \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | # Install PostgreSQL 12 | RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - 13 | 14 | RUN echo "deb http://apt.postgresql.org/pub/repos/apt jammy-pgdg main ${PG_VERSION}" > /etc/apt/sources.list.d/pgdg.list 15 | RUN if [ "${PG_VERSION}" = "18" ] ; then \ 16 | echo "deb http://apt.postgresql.org/pub/repos/apt jammy-pgdg-snapshot main ${PG_VERSION}" >> /etc/apt/sources.list.d/pgdg.list; \ 17 | echo "Package: *\nPin: release a=jammy-pgdg-snapshot\nPin-Priority: 900" > /etc/apt/preferences.d/99pgdg; \ 18 | fi 19 | 20 | # Install PostgreSQL 21 | RUN apt update \ 22 | && DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \ 23 | libcurl4-gnutls-dev libhttp-server-simple-perl libipc-run-perl \ 24 | postgresql-server-dev-${PG_VERSION} \ 25 | postgresql-${PG_VERSION} \ 26 | && rm -rf /var/lib/apt/lists/* 27 | 28 | RUN adduser postgres sudo 29 | RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers 30 | 31 | # 32 | # Build pg_tracing for a given --build-arg PG_VERSION target version of 33 | # PostgreSQL. 34 | # 35 | FROM base AS build 36 | 37 | ARG PG_VERSION 38 | USER postgres 39 | 40 | WORKDIR /usr/src/pg_tracing 41 | 42 | RUN mkdir src 43 | 44 | # Copy sources and compile 45 | COPY --chown=postgres common.mk ./ 46 | COPY --chown=postgres Makefile ./ 47 | COPY --chown=postgres Makefile.dist ./ 48 | COPY --chown=postgres META.json.in ./ 49 | COPY --chown=postgres typedefs.list ./ 50 | COPY --chown=postgres *.sql ./ 51 | COPY --chown=postgres pg_tracing.control ./ 52 | COPY --chown=postgres ./src/*.c ./src 53 | COPY --chown=postgres ./src/*.h ./src 54 | COPY --chown=postgres pg_tracing.conf ./pg_tracing.conf 55 | 56 | # Copy test files 57 | COPY --chown=postgres ./sql/ ./sql 58 | COPY --chown=postgres ./t/ ./t 59 | COPY --chown=postgres ./regress/ ./regress 60 | COPY --chown=postgres ./expected/ ./expected 61 | 62 | # Create empty results for mount bind 63 | # Used to copy test results and update expected output 64 | RUN mkdir results 65 | RUN chown postgres:postgres results 66 | 67 | # Compile 68 | RUN make -s 69 | # And install 70 | RUN sudo make -s install -j8 71 | 72 | # Create test cluster 73 | RUN /usr/lib/postgresql/${PG_VERSION}/bin/initdb -D /usr/src/pg_tracing/test_db/ -Atrust 74 | COPY --chown=postgres tests/postgresql.conf /usr/src/pg_tracing/test_db/ 75 | 76 | # 77 | # Given the build image above, we can now run our test suite targetting a 78 | # given version of Postgres. 79 | # 80 | FROM build AS test 81 | 82 | ARG PG_VERSION 83 | USER postgres 84 | ENV PATH=/usr/lib/postgresql/${PG_VERSION}/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024, Datadog, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-3rdparty.csv: -------------------------------------------------------------------------------- 1 | Component,Origin,License,Copyright 2 | pg_tracing,https://git.postgresql.org/git/postgresql.git,PostgreSQL,Copyright (c) 1996-2024 PostgreSQL Global Development Group 3 | -------------------------------------------------------------------------------- /META.json.in: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg_tracing", 3 | "abstract": "Distributed Tracing for PostgreSQL", 4 | "version": "@PG_TRACING_VERSION@", 5 | "maintainer": [ 6 | "Anthonin Bonnefoy " 7 | ], 8 | "tags": [ "monitoring", "tracing" ], 9 | "release_status": "unstable", 10 | "license": "mit", 11 | "prereqs": { 12 | "runtime": { 13 | "requires": { 14 | "PostgreSQL": "15.0.0" 15 | } 16 | } 17 | }, 18 | "provides": { 19 | "pg_tracing": { 20 | "file": "sql/pg_tracing--0.1.0.sql", 21 | "docfile": "doc/pg_tracing.md", 22 | "version": "@PG_TRACING_VERSION@", 23 | "abstract": "Distributed Tracing for PostgreSQL" 24 | } 25 | }, 26 | "resources": { 27 | "bugtracker": { 28 | "web": "https://github.com/DataDog/pg_tracing/issues" 29 | }, 30 | "repository": { 31 | "url": "git://github.com/DataDog/pg_tracing.git" , 32 | "web": "https://github.com/DataDog/pg_tracing", 33 | "type": "git" 34 | } 35 | }, 36 | "generated_by": "Anthonin Bonnefoy", 37 | "meta-spec": { 38 | "version": "1.0.0", 39 | "url": "http://pgxn.org/meta/spec.txt" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE_big = pg_tracing 2 | DATA = pg_tracing--0.1.0.sql 3 | PGFILEDESC = "pg_tracing - Distributed Tracing for PostgreSQL" 4 | SHLIB_LINK = -lcurl 5 | PG_CFLAGS = -Werror 6 | 7 | include common.mk 8 | 9 | OBJS = \ 10 | $(WIN32RES) \ 11 | src/pg_tracing.o \ 12 | src/pg_tracing_active_spans.o \ 13 | src/pg_tracing_explain.o \ 14 | src/pg_tracing_json.o \ 15 | src/pg_tracing_operation_hash.o \ 16 | src/pg_tracing_otel.o \ 17 | src/pg_tracing_parallel.o \ 18 | src/pg_tracing_planstate.o \ 19 | src/pg_tracing_query_process.o \ 20 | src/pg_tracing_rng.o \ 21 | src/pg_tracing_span.o \ 22 | src/pg_tracing_sql_functions.o \ 23 | src/pg_tracing_strinfo.o \ 24 | src/version_compat.o 25 | ifeq ($(shell test $(PG_VERSION) -le 14; echo $$?),0) 26 | # Include backport of pg_prng for PG 14 27 | OBJS += src/pg_prng.o 28 | endif 29 | 30 | ifdef PG_CONFIG_EXISTS 31 | PGXS := $(shell $(PG_CONFIG) --pgxs) 32 | include $(PGXS) 33 | endif 34 | 35 | REGRESSCHECKS = setup utility select parameters insert trigger cursor json transaction planstate_projectset 36 | 37 | REGRESSCHECKS_OPTS = --no-locale --encoding=UTF8 --temp-config pg_tracing.conf 38 | 39 | ifeq ($(shell test $(PG_VERSION) -ge 15; echo $$?),0) 40 | REGRESSCHECKS += select_multistatement planstate_subplans 41 | endif 42 | 43 | # \bind is only available starting PG 16 44 | ifeq ($(shell test $(PG_VERSION) -ge 16; echo $$?),0) 45 | REGRESSCHECKS += extended parameters_extended 46 | # expecteddir is not supported by PG15, expected at the project root will only be used for PG 15 expected outputs 47 | REGRESSCHECKS_OPTS += --expecteddir=regress/$(PG_VERSION) 48 | endif 49 | 50 | # infinity interval is only available starting PG 17 51 | ifeq ($(shell test $(PG_VERSION) -ge 17; echo $$?),0) 52 | REGRESSCHECKS += nested_17 53 | endif 54 | 55 | # PG 18 contains additional psql metacommand to test extended protocol 56 | ifeq ($(shell test $(PG_VERSION) -ge 18; echo $$?),0) 57 | REGRESSCHECKS += psql_extended_18 psql_extended_tx_18 58 | endif 59 | 60 | REGRESSCHECKS += sample planstate planstate_bitmap planstate_hash \ 61 | planstate_union parallel subxact full_buffer \ 62 | guc nested wal cleanup 63 | 64 | TAP_TESTS = 1 65 | 66 | regresscheck_noinstall: 67 | $(pg_regress_check) $(REGRESSCHECKS_OPTS) $(REGRESSCHECKS) || \ 68 | (cat regression.diffs && exit 1) 69 | 70 | regresscheck: install regresscheck_noinstall 71 | 72 | typedefs.list: 73 | wget -q -O typedefs.list https://buildfarm.postgresql.org/cgi-bin/typedefs.pl 74 | 75 | .PHONY: pgindent 76 | pgindent: typedefs.list 77 | pgindent --typedefs=typedefs.list src/*.c src/*.h 78 | 79 | # DOCKER BUILDS 80 | TEST_CONTAINER_NAME = pg_tracing_test 81 | BUILD_TEST_TARGETS = $(patsubst %,build-test-pg%,$(PG_VERSIONS)) 82 | DOCKER_RUN_OPTS = --rm --name $(TEST_CONTAINER_NAME) $(TEST_CONTAINER_NAME):pg$(PG_VERSION) 83 | 84 | .PHONY: build-test-image 85 | build-test-image: build-test-pg$(PG_VERSION) ; 86 | 87 | .PHONY: $(BUILD_TEST_TARGETS) 88 | $(BUILD_TEST_TARGETS): 89 | docker buildx build --load \ 90 | --rm --build-arg PG_VERSION=$(PG_VERSION) \ 91 | -t $(TEST_CONTAINER_NAME):$(subst build-test-,,$@) . 92 | 93 | .PHONY: run-test 94 | run-test: build-test-pg$(PG_VERSION) 95 | docker run $(DOCKER_RUN_OPTS) \ 96 | bash -c "make regresscheck_noinstall && make installcheck" 97 | 98 | .PHONY: run-pgindent-diff 99 | run-pgindent-diff: build-test-pg$(PG_VERSION) 100 | docker run $(DOCKER_RUN_OPTS) \ 101 | bash -c "pgindent --diff --check --typedefs=typedefs.list src/*.c src/*.h" 102 | 103 | .PHONY: update-regress-output 104 | update-regress-output: build-test-pg$(PG_VERSION) 105 | docker run $(DOCKER_RUN_OPTS) \ 106 | -v./results:/usr/src/pg_tracing/results \ 107 | bash -c "make regresscheck_noinstall || true" 108 | @if [ $(PG_VERSION) = "15" ]; then \ 109 | cp results/*.out expected/; \ 110 | else \ 111 | cp results/*.out regress/$(PG_VERSION)/expected; \ 112 | fi 113 | 114 | .PHONY: update-regress-output-local 115 | update-regress-output-local: regresscheck 116 | @if [ $(PG_VERSION) = "15" ]; then \ 117 | cp results/*.out expected/; \ 118 | else \ 119 | cp results/*.out regress/$(PG_VERSION)/expected; \ 120 | fi 121 | 122 | .PHONY: dist packages 123 | dist: 124 | @make -f Makefile.dist 125 | 126 | packages: 127 | @make -C packages 128 | -------------------------------------------------------------------------------- /Makefile.dist: -------------------------------------------------------------------------------- 1 | include common.mk 2 | 3 | default: dist 4 | 5 | # pgxn packaging 6 | $(TMP_DIR)/META.json: META.json.in 7 | @mkdir -p $(TMP_DIR) 8 | @sed "s/@PG_TRACING_VERSION@/$(EXT_VERSION)/g" $< > $@ 9 | 10 | $(TMP_DIR)/$(EXTENSION)-$(EXT_VERSION).zip: $(TMP_DIR)/META.json 11 | @git archive --format zip --prefix "$(EXTENSION)-$(EXT_VERSION)/" --add-file $< -o "$(TMP_DIR)/$(EXTENSION)-$(EXT_VERSION).zip" HEAD 12 | @echo "artifact=$(TMP_DIR)/$(EXTENSION)-$(EXT_VERSION).zip" 13 | 14 | dist: $(TMP_DIR)/$(EXTENSION)-$(EXT_VERSION).zip 15 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | # Supported PostgreSQL versions: 2 | PG_VERSIONS = 14 15 16 17 18 3 | 4 | EXTENSION = pg_tracing 5 | PG_CONFIG = pg_config 6 | 7 | # Get postgres version 8 | PG_CONFIG_EXISTS := $(shell command -v $(PG_CONFIG) 2> /dev/null) 9 | ifdef PG_CONFIG_EXISTS 10 | # Default to pg_config's advertised version 11 | PG_VERSION ?= $(shell $(PG_CONFIG) --version | cut -d' ' -f2 | cut -d'.' -f1 | tr -d 'devel') 12 | else 13 | # pg_config is not present, let's assume we are packaging and use the latest PG version 14 | PG_VERSION ?= $(lastword $(PG_VERSIONS)) 15 | endif 16 | 17 | ifeq ($(EXT_VERSION),) 18 | # Get extension version 19 | GIT_EXISTS := $(shell command -v git 2> /dev/null) 20 | ifdef GIT_EXISTS 21 | EXT_VERSION = $(shell git describe --tags | sed 's/^v//') 22 | else 23 | EXT_VERSION = "0.1.0" 24 | endif 25 | endif 26 | 27 | TMP_DIR=$(PWD)/tmp 28 | 29 | EXTRA_CLEAN = META.json $(TMP_DIR) 30 | -------------------------------------------------------------------------------- /expected/cleanup.out: -------------------------------------------------------------------------------- 1 | -- Drop test table and test functions 2 | DROP TABLE pg_tracing_test; 3 | DROP function test_function_project_set; 4 | DROP function test_function_result; 5 | DROP VIEW peek_ordered_spans; 6 | DROP VIEW peek_ordered_json_spans; 7 | DROP VIEW peek_ordered_spans_with_pos; 8 | DROP VIEW peek_json_spans_with_level; 9 | DROP VIEW peek_json_spans; 10 | DROP EXTENSION pg_tracing; 11 | -------------------------------------------------------------------------------- /expected/full_buffer.out: -------------------------------------------------------------------------------- 1 | SET pg_tracing.sample_rate = 0.0; 2 | -- A simple procedure creating nested calls 3 | CREATE OR REPLACE PROCEDURE loop_select(iterations int) AS 4 | $BODY$ 5 | BEGIN 6 | FOR i IN 1..iterations LOOP 7 | PERFORM 'SELECT 1;'; 8 | END LOOP; 9 | END; 10 | $BODY$ 11 | LANGUAGE plpgsql; 12 | -- Clear stats and spans 13 | CALL clean_spans(); 14 | select * from pg_tracing_reset(); 15 | pg_tracing_reset 16 | ------------------ 17 | 18 | (1 row) 19 | 20 | -- Check initial stats after reset 21 | select processed_traces, processed_spans, dropped_traces, dropped_spans from pg_tracing_info(); 22 | processed_traces | processed_spans | dropped_traces | dropped_spans 23 | ------------------+-----------------+----------------+--------------- 24 | 0 | 0 | 0 | 0 25 | (1 row) 26 | 27 | -- Saturate the span buffer. Each call should create at least 2 spans 28 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CALL loop_select(20); 29 | -- Check that we have dropped spans. The trace was still partially processed 30 | select processed_traces = 1, processed_spans = 50, dropped_traces = 0, dropped_spans > 0 from pg_tracing_info(); 31 | ?column? | ?column? | ?column? | ?column? 32 | ----------+----------+----------+---------- 33 | t | t | t | t 34 | (1 row) 35 | 36 | -- Try to create new traces while buffer is full 37 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1; 38 | ?column? 39 | ---------- 40 | 1 41 | (1 row) 42 | 43 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-00'*/ SELECT 1; 44 | ?column? 45 | ---------- 46 | 1 47 | (1 row) 48 | 49 | -- We should have only one additional dropped trace 50 | select processed_traces = 1, processed_spans = 50, dropped_traces = 1 from pg_tracing_info(); 51 | ?column? | ?column? | ?column? 52 | ----------+----------+---------- 53 | t | t | t 54 | (1 row) 55 | 56 | -- Clean current spans 57 | CALL clean_spans(); 58 | -------------------------------------------------------------------------------- /expected/insert.out: -------------------------------------------------------------------------------- 1 | -- Only trace queries with sample flag 2 | SET pg_tracing.sample_rate = 0.0; 3 | SET pg_tracing.caller_sample_rate = 1.0; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); 5 | SELECT span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 6 | span_type | span_operation | lvl 7 | -------------------+-----------------------------------------------------------------------------------------------------------------------------------+----- 8 | Utility query | CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); | 0 9 | ProcessUtility | ProcessUtility | 1 10 | Utility query | CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); | 2 11 | ProcessUtility | ProcessUtility | 3 12 | TransactionCommit | TransactionCommit | 0 13 | (5 rows) 14 | 15 | -- Simple insertion 16 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 17 | SELECT span_type, span_operation from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 18 | span_type | span_operation 19 | -------------------+------------------------------------------------------------------ 20 | Insert query | INSERT INTO pg_tracing_test_table_with_constraint VALUES($1, $2) 21 | Planner | Planner 22 | ExecutorRun | ExecutorRun 23 | Insert | Insert on pg_tracing_test_table_with_constraint 24 | Result | Result 25 | TransactionCommit | TransactionCommit 26 | (6 rows) 27 | 28 | -- Trigger constraint violation 29 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 30 | ERROR: duplicate key value violates unique constraint "pk_tracing_test" 31 | DETAIL: Key (a)=(1) already exists. 32 | SELECT span_type, span_operation, sql_error_code, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 33 | span_type | span_operation | sql_error_code | lvl 34 | --------------+------------------------------------------------------------------+----------------+----- 35 | Insert query | INSERT INTO pg_tracing_test_table_with_constraint VALUES($1, $2) | 23505 | 0 36 | Planner | Planner | 00000 | 1 37 | ExecutorRun | ExecutorRun | 23505 | 1 38 | Insert | Insert on pg_tracing_test_table_with_constraint | 23505 | 2 39 | Result | Result | 23505 | 3 40 | (5 rows) 41 | 42 | -- Trigger an error while calling pg_tracing_peek_spans which resets tracing, nothing should be generated 43 | CALL clean_spans(); 44 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(length((select sql_error_code from public.pg_tracing_peek_spans)), 'aaa'); 45 | ERROR: null value in column "a" of relation "pg_tracing_test_table_with_constraint" violates not-null constraint 46 | DETAIL: Failing row contains (null, aaa ). 47 | SELECT span_type, span_operation, sql_error_code from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 48 | span_type | span_operation | sql_error_code 49 | -----------+----------------+---------------- 50 | (0 rows) 51 | 52 | -- Cleanup 53 | CALL clean_spans(); 54 | -------------------------------------------------------------------------------- /expected/planstate_bitmap.out: -------------------------------------------------------------------------------- 1 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * from pg_tracing_test where a=1 OR b='2' OR c=3; 2 | a | b | c 3 | ---+----------------------+--- 4 | 1 | 1 | 1 5 | 2 | 2 | 2 6 | 3 | 3 | 3 7 | (3 rows) 8 | 9 | -- PG < 18 10 | -- +-----------------------------------------------------------------------------------------+ 11 | -- | A: BitmapOr | 12 | -- ++-----------------------------+-------------------------------+--------------------------+ 13 | -- |B: Bitmap Index Scan (a=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (c=3)| 14 | -- +-----------------------------+-------------------------------+--------------------------+ 15 | -- PG >= 18 16 | -- +-----------------------------------------------------------------------------------------+ 17 | -- | A: BitmapOr | 18 | -- ++-----------------------------+-------------------------------+--------------------------+ 19 | -- |B: Bitmap Index Scan (c=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (a=1)| 20 | -- +-----------------------------+-------------------------------+--------------------------+ 21 | SELECT span_operation, deparse_info, parameters, pos_start, pos_end, lvl from peek_ordered_spans_with_pos where trace_id='00000000000000000000000000000001'; 22 | span_operation | deparse_info | parameters | pos_start | pos_end | lvl 23 | ----------------------------------------------------------+---------------------------------------------------------+------------+-----------+---------+----- 24 | SELECT * from pg_tracing_test where a=$1 OR b=$2 OR c=$3 | | {1,'2',3} | 1 | 11 | 0 25 | Planner | | | 2 | 3 | 1 26 | ExecutorRun | | | 4 | 10 | 1 27 | BitmapHeapScan on pg_tracing_test | Recheck Cond: ((a = 1) OR (b = '2'::bpchar) OR (c = 3)) | | 5 | 9 | 2 28 | BitmapOr | | | 5 | 8 | 3 29 | BitmapIndexScan on pg_tracing_index_a | Index Cond: (a = 1) | | 5 | 6 | 4 30 | BitmapIndexScan on pg_tracing_index_b | Index Cond: (b = '2'::bpchar) | | 6 | 7 | 4 31 | BitmapIndexScan on pg_tracing_index_c | Index Cond: (c = 3) | | 7 | 8 | 4 32 | (8 rows) 33 | 34 | -- Clean created spans 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /expected/planstate_hash.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select count(*) from pg_tracing_test r join pg_tracing_test s using (a); 2 | count 3 | ------- 4 | 10000 5 | (1 row) 6 | 7 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 8 | span_operation | deparse_info | parameters | lvl 9 | --------------------------------------------------------------------------+--------------------+------------+----- 10 | select count(*) from pg_tracing_test r join pg_tracing_test s using (a); | | | 0 11 | Planner | | | 1 12 | ExecutorRun | | | 1 13 | Aggregate | | | 2 14 | Hash Join | Hash Cond: (a = a) | | 3 15 | SeqScan on pg_tracing_test r | | | 4 16 | Hash | | | 4 17 | SeqScan on pg_tracing_test s | | | 5 18 | (8 rows) 19 | 20 | -- +-----------------------------------------+ 21 | -- | A: HashJoin | 22 | -- ++-----------------+----------------------+ 23 | -- | B: SeqScan | 24 | -- +-----------------+ 25 | -- +--------------------+ 26 | -- | C: Hash | 27 | -- +--------------------+ 28 | -- | D: SeqScan | 29 | -- +--------------+ 30 | SELECT span_id AS span_a_id, 31 | get_epoch(span_start) as span_a_start, 32 | get_epoch(span_end) as span_a_end 33 | from pg_tracing_peek_spans 34 | where trace_id='00000000000000000000000000000001' AND span_operation='Hash Join' \gset 35 | SELECT span_id AS span_b_id, 36 | get_epoch(span_start) as span_b_start, 37 | get_epoch(span_end) as span_b_end 38 | from pg_tracing_peek_spans 39 | where parent_id =:'span_a_id' and span_operation='SeqScan on pg_tracing_test r' \gset 40 | SELECT span_id AS span_c_id, 41 | get_epoch(span_start) as span_c_start, 42 | get_epoch(span_end) as span_c_end 43 | from pg_tracing_peek_spans 44 | where parent_id =:'span_a_id' and span_operation='Hash' \gset 45 | SELECT span_id AS span_d_id, 46 | get_epoch(span_start) as span_d_start, 47 | get_epoch(span_end) as span_d_end 48 | from pg_tracing_peek_spans 49 | where parent_id =:'span_c_id' and span_operation='SeqScan on pg_tracing_test s' \gset 50 | SELECT :span_a_end >= :span_c_end as root_ends_last, 51 | :span_c_start >= :span_b_start as hash_start_after_seqscan, 52 | :span_c_start = :span_d_start as hash_start_same_as_child_seqscan, 53 | :span_d_end <= :span_c_end as nested_seq_scan_end_before_parent; 54 | root_ends_last | hash_start_after_seqscan | hash_start_same_as_child_seqscan | nested_seq_scan_end_before_parent 55 | ----------------+--------------------------+----------------------------------+----------------------------------- 56 | t | t | t | t 57 | (1 row) 58 | 59 | -- Clean created spans 60 | CALL clean_spans(); 61 | -------------------------------------------------------------------------------- /expected/planstate_projectset.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select information_schema._pg_expandarray('{0,1,2}'::int[]); 2 | _pg_expandarray 3 | ----------------- 4 | (0,1) 5 | (1,2) 6 | (2,3) 7 | (3 rows) 8 | 9 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | deparse_info | parameters | lvl 11 | ------------------------------------------------------------------------------------------+--------------+---------------+----- 12 | select information_schema._pg_expandarray($1::int[]) | | {"'{0,1,2}'"} | 0 13 | Planner | | | 1 14 | ExecutorRun | | | 1 15 | ProjectSet | | | 2 16 | Result | | | 3 17 | select $1[s], +| | {1,1,1,1,1} | 3 18 | s operator(pg_catalog.-) pg_catalog.array_lower($1,$2) operator(pg_catalog.+) $3+| | | 19 | from pg_catalog.generate_series(pg_catalog.array_lower($1,$4), +| | | 20 | pg_catalog.array_upper($1,$5), +| | | 21 | $6) as g(s) | | | 22 | Planner | | | 4 23 | (7 rows) 24 | 25 | -- +---------------------------------------------+ 26 | -- | A: ProjectSet | 27 | -- ++-----------------+--+----------------+------+ 28 | -- | B: Result | | C: TopSpan | 29 | -- +-----------------+ +-+------------+-+ 30 | -- | D: Planner | 31 | -- +------------+ 32 | SELECT span_id AS span_a_id, 33 | get_epoch(span_start) as span_a_start, 34 | get_epoch(span_end) as span_a_end 35 | from pg_tracing_peek_spans 36 | where trace_id='00000000000000000000000000000001' AND span_operation='ProjectSet' \gset 37 | SELECT span_id AS span_b_id, 38 | get_epoch(span_start) as span_b_start, 39 | get_epoch(span_end) as span_b_end 40 | from pg_tracing_peek_spans 41 | where parent_id =:'span_a_id' and span_operation='Result' \gset 42 | SELECT span_id AS span_c_id, 43 | get_epoch(span_start) as span_c_start, 44 | get_epoch(span_end) as span_c_end 45 | from pg_tracing_peek_spans 46 | where parent_id =:'span_a_id' and span_type='Select query' \gset 47 | SELECT span_id AS span_d_id, 48 | get_epoch(span_start) as span_d_start, 49 | get_epoch(span_end) as span_d_end 50 | from pg_tracing_peek_spans 51 | where parent_id =:'span_c_id' and span_type='Planner' \gset 52 | SELECT :span_a_end >= :span_c_end as project_set_ends_after_nested_top_span; 53 | project_set_ends_after_nested_top_span 54 | ---------------------------------------- 55 | t 56 | (1 row) 57 | 58 | -- Clean created spans 59 | CALL clean_spans(); 60 | -------------------------------------------------------------------------------- /expected/planstate_union.out: -------------------------------------------------------------------------------- 1 | -- Test simple append 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ 3 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10 4 | UNION ALL 5 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10000; 6 | count 7 | ------- 8 | 9 9 | 9999 10 | (2 rows) 11 | 12 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 13 | span_operation | deparse_info | parameters | lvl 14 | --------------------------------------------------------+----------------------------+----------------+----- 15 | SELECT count(*) FROM pg_tracing_test WHERE a + $1 < $2+| | {0,10,0,10000} | 0 16 | UNION ALL +| | | 17 | SELECT count(*) FROM pg_tracing_test WHERE a + $3 < $4 | | | 18 | Planner | | | 1 19 | ExecutorRun | | | 1 20 | Append | | | 2 21 | Aggregate | | | 3 22 | SeqScan on pg_tracing_test | Filter : ((a + 0) < 10) | | 4 23 | Aggregate | | | 3 24 | SeqScan on pg_tracing_test pg_tracing_test_1 | Filter : ((a + 0) < 10000) | | 4 25 | (8 rows) 26 | 27 | -- +------------------------------------------+ 28 | -- | A: Append | 29 | -- +-+-----------------+-+-----------------+--+ 30 | -- | B: Aggregate | | C: Aggregate | 31 | -- +-----------------+ +-----------------+ 32 | -- | D: IndexScan | | E: IndexScan | 33 | -- +-----------------+ +-----------------+ 34 | SELECT span_id AS span_a_id, 35 | get_epoch(span_start) as span_a_start, 36 | get_epoch(span_end) as span_a_end 37 | from pg_tracing_peek_spans 38 | where trace_id='00000000000000000000000000000001' AND span_operation='Append' \gset 39 | SELECT span_id AS span_b_id, 40 | get_epoch(span_start) as span_b_start, 41 | get_epoch(span_end) as span_b_end 42 | from pg_tracing_peek_spans 43 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 0 \gset 44 | SELECT span_id AS span_c_id, 45 | get_epoch(span_start) as span_c_start, 46 | get_epoch(span_end) as span_c_end 47 | from pg_tracing_peek_spans 48 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 1 \gset 49 | SELECT span_id AS span_d_id, 50 | get_epoch(span_start) as span_d_start, 51 | get_epoch(span_end) as span_d_end 52 | from pg_tracing_peek_spans 53 | where parent_id =:'span_b_id' \gset 54 | SELECT span_id AS span_e_id, 55 | get_epoch(span_start) as span_e_start, 56 | get_epoch(span_end) as span_e_end 57 | from pg_tracing_peek_spans 58 | where parent_id =:'span_c_id' \gset 59 | SELECT :span_a_end >= GREATEST(:span_c_end, :span_e_end) as root_ends_last, 60 | :span_c_start >= :span_b_start as second_index_start_after_first; 61 | root_ends_last | second_index_start_after_first 62 | ----------------+-------------------------------- 63 | t | t 64 | (1 row) 65 | 66 | -- Cleanup 67 | CALL clean_spans(); 68 | CALL reset_settings(); 69 | -------------------------------------------------------------------------------- /expected/select_multistatement.out: -------------------------------------------------------------------------------- 1 | -- Check multi statement query 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Test multiple statements in a single query 4 | /*dddbs='postgres.db',traceparent='00-0000000000000000000000000000000c-000000000000000c-01'*/ select 1; select 2; 5 | ?column? 6 | ---------- 7 | 1 8 | (1 row) 9 | 10 | ?column? 11 | ---------- 12 | 2 13 | (1 row) 14 | 15 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='0000000000000000000000000000000c'; 16 | span_operation | parameters | lvl 17 | ----------------+------------+----- 18 | select $1 | {1} | 0 19 | Planner | | 1 20 | ExecutorRun | | 1 21 | Result | | 2 22 | (4 rows) 23 | 24 | CALL clean_spans(); 25 | -- Force a multi-query statement with \; 26 | SELECT 1\; SELECT 1, 2; 27 | ?column? 28 | ---------- 29 | 1 30 | (1 row) 31 | 32 | ?column? | ?column? 33 | ----------+---------- 34 | 1 | 2 35 | (1 row) 36 | 37 | SELECT span_type, span_operation, parameters, lvl from peek_ordered_spans; 38 | span_type | span_operation | parameters | lvl 39 | --------------+----------------+------------+----- 40 | Select query | SELECT $1 | {1} | 0 41 | Planner | Planner | | 1 42 | ExecutorRun | ExecutorRun | | 1 43 | Result | Result | | 2 44 | Select query | SELECT $1, $2 | {1,2} | 0 45 | Planner | Planner | | 1 46 | ExecutorRun | ExecutorRun | | 1 47 | Result | Result | | 2 48 | (8 rows) 49 | 50 | CALL clean_spans(); 51 | -- Cleanup 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /expected/setup.out: -------------------------------------------------------------------------------- 1 | -- Some helper functions 2 | CREATE OR REPLACE FUNCTION get_epoch(ts timestamptz) RETURNS float AS 3 | $BODY$ 4 | SELECT extract(epoch from ts); 5 | $BODY$ 6 | LANGUAGE sql; 7 | CREATE OR REPLACE PROCEDURE clean_spans() AS $$ 8 | BEGIN 9 | PERFORM count(*) from pg_tracing_consume_spans; 10 | END; 11 | $$ LANGUAGE plpgsql; 12 | CREATE OR REPLACE PROCEDURE reset_settings() 13 | LANGUAGE SQL 14 | AS $$ 15 | SET pg_tracing.filter_query_ids TO DEFAULT; 16 | SET pg_tracing.sample_rate TO DEFAULT; 17 | SET pg_tracing.caller_sample_rate TO DEFAULT; 18 | SET pg_tracing.track_utility TO DEFAULT; 19 | SET pg_tracing.max_parameter_size TO DEFAULT; 20 | SET parallel_setup_cost TO DEFAULT; 21 | SET parallel_tuple_cost TO DEFAULT; 22 | SET min_parallel_table_scan_size TO DEFAULT; 23 | SET max_parallel_workers_per_gather TO DEFAULT; 24 | $$; 25 | CREATE OR REPLACE PROCEDURE reset_pg_tracing_test_table() AS $$ 26 | BEGIN 27 | DROP TABLE IF EXISTS pg_tracing_test; 28 | CREATE TABLE pg_tracing_test (a int, b char(20), c int); 29 | COMMIT; 30 | CREATE INDEX pg_tracing_index_a ON pg_tracing_test (a); 31 | CREATE INDEX pg_tracing_index_b ON pg_tracing_test (b); 32 | CREATE INDEX pg_tracing_index_c ON pg_tracing_test (c); 33 | INSERT INTO pg_tracing_test SELECT *, *, * FROM generate_series(1, 10000); 34 | ANALYZE pg_tracing_test; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -- Create test tables with data 38 | CALL reset_pg_tracing_test_table(); 39 | NOTICE: table "pg_tracing_test" does not exist, skipping 40 | -- Create test table to test modifications 41 | CREATE TABLE test_modifications (a int, b char(20)); 42 | CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; 43 | ALTER TABLE m ADD UNIQUE (k); 44 | -------------------------------------------------------------------------------- /expected/subxact.out: -------------------------------------------------------------------------------- 1 | -- Enable full sampling 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Start a transaction with subxaction 4 | BEGIN; 5 | SAVEPOINT s1; 6 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 7 | SAVEPOINT s2; 8 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 9 | SAVEPOINT s3; 10 | SELECT 1; 11 | ?column? 12 | ---------- 13 | 1 14 | (1 row) 15 | 16 | COMMIT; 17 | -- Check that subxact_count is correctly reported 18 | select span_operation, parameters, subxact_count, lvl FROM peek_ordered_spans WHERE span_operation NOT LIKE 'SAVEPOINT%'; 19 | span_operation | parameters | subxact_count | lvl 20 | -----------------------------------------------------------------+-------------+---------------+----- 21 | TransactionBlock | | 0 | 0 22 | BEGIN; | | 0 | 1 23 | ProcessUtility | | 0 | 2 24 | ProcessUtility | | 0 | 2 25 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 0 | 1 26 | Planner | | 0 | 2 27 | ExecutorRun | | 0 | 2 28 | Insert on pg_tracing_test | | 1 | 3 29 | ProjectSet | | 1 | 4 30 | Result | | 1 | 5 31 | ProcessUtility | | 1 | 2 32 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 1 | 1 33 | Planner | | 1 | 2 34 | ExecutorRun | | 1 | 2 35 | Insert on pg_tracing_test | | 2 | 3 36 | ProjectSet | | 2 | 4 37 | Result | | 2 | 5 38 | ProcessUtility | | 2 | 2 39 | SELECT $1 | {1} | 2 | 1 40 | Planner | | 2 | 2 41 | ExecutorRun | | 2 | 2 42 | Result | | 2 | 3 43 | COMMIT; | | 2 | 1 44 | ProcessUtility | | 2 | 2 45 | TransactionCommit | | 2 | 1 46 | (25 rows) 47 | 48 | -- Cleaning 49 | CALL clean_spans(); 50 | CALL reset_settings(); 51 | -------------------------------------------------------------------------------- /expected/wal.out: -------------------------------------------------------------------------------- 1 | -- Generate queries with wal write 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO pg_tracing_test VALUES(generate_series(1, 10), 'aaa'); 3 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ UPDATE pg_tracing_test SET b = 'bbb' WHERE a = 7; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ DELETE FROM pg_tracing_test WHERE a = 9; 5 | -- Check WAL is generated for the above statements 6 | SELECT trace_id, span_type, span_operation, 7 | wal_records > 0 as wal_records, 8 | wal_bytes > 0 as wal_bytes 9 | FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | wal_records | wal_bytes 11 | ----------------------------------+-------------------+-----------------------------------------------------------------+-------------+----------- 12 | 00000000000000000000000000000001 | Insert query | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | t | t 13 | 00000000000000000000000000000001 | Planner | Planner | f | f 14 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 15 | 00000000000000000000000000000001 | Insert | Insert on pg_tracing_test | t | t 16 | 00000000000000000000000000000001 | ProjectSet | ProjectSet | f | f 17 | 00000000000000000000000000000001 | Result | Result | f | f 18 | 00000000000000000000000000000001 | TransactionCommit | TransactionCommit | | 19 | 00000000000000000000000000000002 | Update query | UPDATE pg_tracing_test SET b = $1 WHERE a = $2 | t | t 20 | 00000000000000000000000000000002 | Planner | Planner | f | f 21 | 00000000000000000000000000000002 | ExecutorRun | ExecutorRun | | 22 | 00000000000000000000000000000002 | Update | Update on pg_tracing_test | t | t 23 | 00000000000000000000000000000002 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | f | f 24 | 00000000000000000000000000000002 | TransactionCommit | TransactionCommit | | 25 | 00000000000000000000000000000003 | Delete query | DELETE FROM pg_tracing_test WHERE a = $1 | t | t 26 | 00000000000000000000000000000003 | Planner | Planner | f | f 27 | 00000000000000000000000000000003 | ExecutorRun | ExecutorRun | | 28 | 00000000000000000000000000000003 | Delete | Delete on pg_tracing_test | t | t 29 | 00000000000000000000000000000003 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | t | t 30 | 00000000000000000000000000000003 | TransactionCommit | TransactionCommit | | 31 | (19 rows) 32 | 33 | CALL clean_spans(); 34 | -- Cleanup 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc-matcher", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/Makefile: -------------------------------------------------------------------------------- 1 | include ../common.mk 2 | 3 | BUILD_CONTAINER_NAME = pg_tracing_build 4 | DOCKER_BUILD_OPTS = --rm --build-arg PG_VERSION=$(PG_VERSION) --build-arg EXT_VERSION=$(EXT_VERSION) 5 | ARCH ?= $(shell uname -m) 6 | 7 | # Underscore are not allowed in deb package names 8 | DEB_PACKAGE=postgresql-$(PG_VERSION)-pg-tracing_$(EXT_VERSION)_$(ARCH).deb 9 | IMAGE_NAME = $(BUILD_CONTAINER_NAME):pg_$(PG_VERSION)_$(ARCH) 10 | 11 | .PHONY: all 12 | 13 | all: $(TMP_DIR)/$(DEB_PACKAGE) 14 | 15 | $(TMP_DIR)/$(DEB_PACKAGE): 16 | @mkdir -p $(TMP_DIR) 17 | docker build --rm --platform linux/$(ARCH) \ 18 | $(DOCKER_BUILD_OPTS) -f deb.Dockerfile -t $(IMAGE_NAME) .. 19 | docker create --name dummy $(IMAGE_NAME) 20 | docker cp dummy:/usr/src/pg_tracing/$(DEB_PACKAGE) ../tmp/$(DEB_PACKAGE) 21 | docker rm -f dummy 22 | -------------------------------------------------------------------------------- /packages/deb.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy AS base 2 | 3 | ARG PG_VERSION 4 | # Install packages 5 | RUN apt update 6 | RUN DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \ 7 | ca-certificates curl gnupg make gcc ruby libcurl4-gnutls-dev \ 8 | && rm -rf /var/lib/apt/lists/* 9 | 10 | # Install PostgreSQL 11 | RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - 12 | RUN echo "deb http://apt.postgresql.org/pub/repos/apt jammy-pgdg main ${PG_VERSION}" > /etc/apt/sources.list.d/pgdg.list 13 | RUN apt update \ 14 | && DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends \ 15 | postgresql-server-dev-${PG_VERSION} && rm -rf /var/lib/apt/lists/* 16 | 17 | # Install FPM 18 | RUN gem install fpm 19 | 20 | # Build pg_tracing for a given --build-arg PG_VERSION target version of 21 | # PostgreSQL. 22 | FROM base AS build 23 | 24 | ARG PG_VERSION 25 | ARG EXT_VERSION 26 | ENV LIB_DIR usr/lib/postgresql/${PG_VERSION}/lib/ 27 | ENV SHARE_DIR usr/share/postgresql/${PG_VERSION}/extension 28 | USER postgres 29 | 30 | WORKDIR /usr/src/pg_tracing 31 | 32 | RUN mkdir src 33 | 34 | # Copy sources and compile 35 | COPY --chown=postgres common.mk ./ 36 | COPY --chown=postgres Makefile ./ 37 | COPY --chown=postgres Makefile.dist ./ 38 | COPY --chown=postgres META.json.in ./ 39 | COPY --chown=postgres *.sql ./ 40 | COPY --chown=postgres pg_tracing.control ./ 41 | COPY --chown=postgres ./src/*.c ./src 42 | COPY --chown=postgres ./src/*.h ./src 43 | COPY --chown=postgres pg_tracing.conf ./pg_tracing.conf 44 | 45 | # Compile 46 | RUN make -s 47 | 48 | # Prepare package directories 49 | RUN mkdir -p ${LIB_DIR} 50 | RUN mkdir -p ${SHARE_DIR} 51 | 52 | # Copy package files 53 | RUN cp pg_tracing.so ${LIB_DIR} 54 | RUN cp *.sql ${SHARE_DIR} 55 | RUN cp pg_tracing.control ${SHARE_DIR} 56 | 57 | # Build package 58 | RUN fpm \ 59 | -n postgresql-${PG_VERSION}-pg-tracing -v ${EXT_VERSION} -t deb \ 60 | -m "" --url "https://www.datadoghq.com/" \ 61 | --description "Distributed Tracing for PostgreSQL" \ 62 | --vendor "datadoghq" --license="MIT" \ 63 | -s dir usr 64 | -------------------------------------------------------------------------------- /pg_tracing--0.1.0.sql: -------------------------------------------------------------------------------- 1 | /* contrib/pg_tracing/pg_tracing--0.1.0.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via ALTER EXTENSION 4 | \echo Use "CREATE EXTENSION pg_tracing" to load this file. \quit 5 | 6 | --- Define pg_tracing_info 7 | CREATE FUNCTION pg_tracing_info( 8 | OUT processed_traces bigint, 9 | OUT processed_spans bigint, 10 | OUT dropped_traces bigint, 11 | OUT dropped_spans bigint, 12 | OUT otel_sent_spans bigint, 13 | OUT otel_failures bigint, 14 | OUT last_consume timestamp with time zone, 15 | OUT stats_reset timestamp with time zone 16 | ) 17 | RETURNS record 18 | AS 'MODULE_PATHNAME' 19 | LANGUAGE C; 20 | 21 | CREATE FUNCTION pg_tracing_reset() 22 | RETURNS void 23 | AS 'MODULE_PATHNAME' 24 | LANGUAGE C; 25 | 26 | CREATE FUNCTION pg_tracing_json_spans() 27 | RETURNS text 28 | AS 'MODULE_PATHNAME' 29 | LANGUAGE C; 30 | 31 | CREATE FUNCTION pg_tracing_spans( 32 | IN consume bool, 33 | OUT trace_id char(32), 34 | OUT parent_id char(16), 35 | OUT span_id char(16), 36 | OUT query_id bigint, 37 | OUT span_type text, 38 | OUT span_operation text, 39 | OUT span_start timestamp with time zone, 40 | OUT span_end timestamp with time zone, 41 | OUT sql_error_code character(5), 42 | OUT pid int4, 43 | OUT userid oid, 44 | OUT dbid oid, 45 | OUT subxact_count smallint, 46 | 47 | -- Plan counters 48 | OUT plan_startup_cost float8, 49 | OUT plan_total_cost float8, 50 | OUT plan_rows float8, 51 | OUT plan_width int, 52 | 53 | -- Node Counters 54 | OUT rows int8, 55 | OUT nloops int8, 56 | 57 | OUT shared_blks_hit int8, 58 | OUT shared_blks_read int8, 59 | OUT shared_blks_dirtied int8, 60 | OUT shared_blks_written int8, 61 | 62 | OUT local_blks_hit int8, 63 | OUT local_blks_read int8, 64 | OUT local_blks_dirtied int8, 65 | OUT local_blks_written int8, 66 | 67 | OUT blk_read_time float8, 68 | OUT blk_write_time float8, 69 | 70 | OUT temp_blks_read int8, 71 | OUT temp_blks_written int8, 72 | OUT temp_blk_read_time float8, 73 | OUT temp_blk_write_time float8, 74 | 75 | OUT wal_records int8, 76 | OUT wal_fpi int8, 77 | OUT wal_bytes numeric, 78 | 79 | OUT jit_functions int8, 80 | OUT jit_generation_time float8, 81 | OUT jit_inlining_time float8, 82 | OUT jit_optimization_time float8, 83 | OUT jit_emission_time float8, 84 | 85 | -- Span node specific data 86 | OUT startup bigint, -- First tuple 87 | OUT parameters text[], 88 | OUT deparse_info text 89 | ) 90 | RETURNS SETOF record 91 | AS 'MODULE_PATHNAME' 92 | LANGUAGE C STRICT VOLATILE; 93 | 94 | CREATE VIEW pg_tracing_info AS 95 | SELECT * FROM pg_tracing_info(); 96 | 97 | CREATE VIEW pg_tracing_peek_spans AS 98 | SELECT * FROM pg_tracing_spans(false); 99 | 100 | -- Spans with their level 101 | CREATE VIEW pg_tracing_peek_spans_with_level AS 102 | WITH RECURSIVE list_trace_spans AS ( 103 | SELECT p.*, 0 as lvl 104 | FROM pg_tracing_peek_spans p where not parent_id=ANY(SELECT span_id from pg_tracing_peek_spans) 105 | UNION ALL 106 | SELECT s.*, lvl + 1 107 | FROM pg_tracing_peek_spans s, list_trace_spans st 108 | WHERE s.parent_id = st.span_id 109 | ) SELECT * FROM list_trace_spans; 110 | 111 | CREATE VIEW pg_tracing_peek_spans_tree AS 112 | SELECT trace_id, lpad(span_operation, length(span_operation) + lvl * 2, '_'), span_id, parent_id, span_start, span_end, lvl 113 | FROM pg_tracing_peek_spans_with_level s 114 | ORDER BY trace_id, span_start; 115 | 116 | CREATE VIEW pg_tracing_consume_spans AS 117 | SELECT * FROM pg_tracing_spans(true); 118 | 119 | GRANT SELECT ON pg_tracing_info TO PUBLIC; 120 | GRANT SELECT ON pg_tracing_peek_spans TO pg_read_all_stats; 121 | GRANT SELECT ON pg_tracing_consume_spans TO pg_read_all_stats; 122 | 123 | -- Don't want this to be available to non-superusers. 124 | REVOKE ALL ON FUNCTION pg_tracing_reset() FROM PUBLIC; 125 | REVOKE ALL ON FUNCTION pg_tracing_spans(boolean) FROM PUBLIC; 126 | -------------------------------------------------------------------------------- /pg_tracing.conf: -------------------------------------------------------------------------------- 1 | shared_preload_libraries = 'pg_tracing' 2 | pg_tracing.max_span = 50 3 | -------------------------------------------------------------------------------- /pg_tracing.control: -------------------------------------------------------------------------------- 1 | # pg_tracing extension 2 | comment = 'Distributed Tracing for PostgreSQL' 3 | default_version = '0.1.0' 4 | module_pathname = '$libdir/pg_tracing' 5 | relocatable = true 6 | -------------------------------------------------------------------------------- /regress/16/expected/cleanup.out: -------------------------------------------------------------------------------- 1 | -- Drop test table and test functions 2 | DROP TABLE pg_tracing_test; 3 | DROP function test_function_project_set; 4 | DROP function test_function_result; 5 | DROP VIEW peek_ordered_spans; 6 | DROP VIEW peek_ordered_json_spans; 7 | DROP VIEW peek_ordered_spans_with_pos; 8 | DROP VIEW peek_json_spans_with_level; 9 | DROP VIEW peek_json_spans; 10 | DROP EXTENSION pg_tracing; 11 | -------------------------------------------------------------------------------- /regress/16/expected/full_buffer.out: -------------------------------------------------------------------------------- 1 | SET pg_tracing.sample_rate = 0.0; 2 | -- A simple procedure creating nested calls 3 | CREATE OR REPLACE PROCEDURE loop_select(iterations int) AS 4 | $BODY$ 5 | BEGIN 6 | FOR i IN 1..iterations LOOP 7 | PERFORM 'SELECT 1;'; 8 | END LOOP; 9 | END; 10 | $BODY$ 11 | LANGUAGE plpgsql; 12 | -- Clear stats and spans 13 | CALL clean_spans(); 14 | select * from pg_tracing_reset(); 15 | pg_tracing_reset 16 | ------------------ 17 | 18 | (1 row) 19 | 20 | -- Check initial stats after reset 21 | select processed_traces, processed_spans, dropped_traces, dropped_spans from pg_tracing_info(); 22 | processed_traces | processed_spans | dropped_traces | dropped_spans 23 | ------------------+-----------------+----------------+--------------- 24 | 0 | 0 | 0 | 0 25 | (1 row) 26 | 27 | -- Saturate the span buffer. Each call should create at least 2 spans 28 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CALL loop_select(20); 29 | -- Check that we have dropped spans. The trace was still partially processed 30 | select processed_traces = 1, processed_spans = 50, dropped_traces = 0, dropped_spans > 0 from pg_tracing_info(); 31 | ?column? | ?column? | ?column? | ?column? 32 | ----------+----------+----------+---------- 33 | t | t | t | t 34 | (1 row) 35 | 36 | -- Try to create new traces while buffer is full 37 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1; 38 | ?column? 39 | ---------- 40 | 1 41 | (1 row) 42 | 43 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-00'*/ SELECT 1; 44 | ?column? 45 | ---------- 46 | 1 47 | (1 row) 48 | 49 | -- We should have only one additional dropped trace 50 | select processed_traces = 1, processed_spans = 50, dropped_traces = 1 from pg_tracing_info(); 51 | ?column? | ?column? | ?column? 52 | ----------+----------+---------- 53 | t | t | t 54 | (1 row) 55 | 56 | -- Clean current spans 57 | CALL clean_spans(); 58 | -------------------------------------------------------------------------------- /regress/16/expected/insert.out: -------------------------------------------------------------------------------- 1 | -- Only trace queries with sample flag 2 | SET pg_tracing.sample_rate = 0.0; 3 | SET pg_tracing.caller_sample_rate = 1.0; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); 5 | SELECT span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 6 | span_type | span_operation | lvl 7 | -------------------+-----------------------------------------------------------------------------------------------------------------------------------+----- 8 | Utility query | CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); | 0 9 | ProcessUtility | ProcessUtility | 1 10 | Utility query | CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); | 2 11 | ProcessUtility | ProcessUtility | 3 12 | TransactionCommit | TransactionCommit | 0 13 | (5 rows) 14 | 15 | -- Simple insertion 16 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 17 | SELECT span_type, span_operation from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 18 | span_type | span_operation 19 | -------------------+------------------------------------------------------------------ 20 | Insert query | INSERT INTO pg_tracing_test_table_with_constraint VALUES($1, $2) 21 | Planner | Planner 22 | ExecutorRun | ExecutorRun 23 | Insert | Insert on pg_tracing_test_table_with_constraint 24 | Result | Result 25 | TransactionCommit | TransactionCommit 26 | (6 rows) 27 | 28 | -- Trigger constraint violation 29 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 30 | ERROR: duplicate key value violates unique constraint "pk_tracing_test" 31 | DETAIL: Key (a)=(1) already exists. 32 | SELECT span_type, span_operation, sql_error_code, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 33 | span_type | span_operation | sql_error_code | lvl 34 | --------------+------------------------------------------------------------------+----------------+----- 35 | Insert query | INSERT INTO pg_tracing_test_table_with_constraint VALUES($1, $2) | 23505 | 0 36 | Planner | Planner | 00000 | 1 37 | ExecutorRun | ExecutorRun | 23505 | 1 38 | Insert | Insert on pg_tracing_test_table_with_constraint | 23505 | 2 39 | Result | Result | 23505 | 3 40 | (5 rows) 41 | 42 | -- Trigger an error while calling pg_tracing_peek_spans which resets tracing, nothing should be generated 43 | CALL clean_spans(); 44 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(length((select sql_error_code from public.pg_tracing_peek_spans)), 'aaa'); 45 | ERROR: null value in column "a" of relation "pg_tracing_test_table_with_constraint" violates not-null constraint 46 | DETAIL: Failing row contains (null, aaa ). 47 | SELECT span_type, span_operation, sql_error_code from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 48 | span_type | span_operation | sql_error_code 49 | -----------+----------------+---------------- 50 | (0 rows) 51 | 52 | -- Cleanup 53 | CALL clean_spans(); 54 | -------------------------------------------------------------------------------- /regress/16/expected/nested_17.out: -------------------------------------------------------------------------------- 1 | -- Test error thrown within a nested function 2 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 3 | eval_expr(format('date %L + interval %L', '-infinity', 'infinity')), 4 | eval_expr(format('date %L - interval %L', '-infinity', 'infinity')); 5 | eval_expr | eval_expr 6 | ------------------------+----------- 7 | timestamp out of range | -infinity 8 | (1 row) 9 | 10 | select span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001'; 11 | span_operation | lvl 12 | ------------------------------------+----- 13 | SELECT +| 0 14 | eval_expr(format($1, $2, $3)),+| 15 | eval_expr(format($4, $5, $6)) | 16 | Planner | 1 17 | ExecutorRun | 1 18 | Result | 2 19 | $2||expr | 3 20 | Planner | 4 21 | Planner | 4 22 | select date $1 - interval $2 | 3 23 | Planner | 4 24 | ExecutorRun | 4 25 | Result | 5 26 | (11 rows) 27 | 28 | -- Cleanup 29 | CALL clean_spans(); 30 | -------------------------------------------------------------------------------- /regress/16/expected/parameters_extended.out: -------------------------------------------------------------------------------- 1 | -- Saturate the parameter buffer with extended protocol 2 | SET pg_tracing.max_parameter_size=1; 3 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select $1, $2, $3 \bind 1 2 3 \g 4 | ?column? | ?column? | ?column? 5 | ----------+----------+---------- 6 | 1 | 2 | 3 7 | (1 row) 8 | 9 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | parameters | lvl 11 | -------------------+-------------+----- 12 | select $1, $2, $3 | {1,...,...} | 0 13 | Planner | | 1 14 | ExecutorRun | | 1 15 | Result | | 2 16 | (4 rows) 17 | 18 | SET pg_tracing.max_parameter_size=2; 19 | /*traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ select $1, $2, $3 \bind 1 2 3 \g 20 | ?column? | ?column? | ?column? 21 | ----------+----------+---------- 22 | 1 | 2 | 3 23 | (1 row) 24 | 25 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 26 | span_operation | parameters | lvl 27 | -------------------+-------------+----- 28 | select $1, $2, $3 | {1,...,...} | 0 29 | Planner | | 1 30 | ExecutorRun | | 1 31 | Result | | 2 32 | (4 rows) 33 | 34 | SET pg_tracing.max_parameter_size=3; 35 | /*traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ select $1, $2, $3 \bind 1 2 3 \g 36 | ?column? | ?column? | ?column? 37 | ----------+----------+---------- 38 | 1 | 2 | 3 39 | (1 row) 40 | 41 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 42 | span_operation | parameters | lvl 43 | -------------------+------------+----- 44 | select $1, $2, $3 | {1,2,...} | 0 45 | Planner | | 1 46 | ExecutorRun | | 1 47 | Result | | 2 48 | (4 rows) 49 | 50 | -- Cleanup 51 | CALL clean_spans(); 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /regress/16/expected/planstate_bitmap.out: -------------------------------------------------------------------------------- 1 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * from pg_tracing_test where a=1 OR b='2' OR c=3; 2 | a | b | c 3 | ---+----------------------+--- 4 | 1 | 1 | 1 5 | 2 | 2 | 2 6 | 3 | 3 | 3 7 | (3 rows) 8 | 9 | -- PG < 18 10 | -- +-----------------------------------------------------------------------------------------+ 11 | -- | A: BitmapOr | 12 | -- ++-----------------------------+-------------------------------+--------------------------+ 13 | -- |B: Bitmap Index Scan (a=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (c=3)| 14 | -- +-----------------------------+-------------------------------+--------------------------+ 15 | -- PG >= 18 16 | -- +-----------------------------------------------------------------------------------------+ 17 | -- | A: BitmapOr | 18 | -- ++-----------------------------+-------------------------------+--------------------------+ 19 | -- |B: Bitmap Index Scan (c=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (a=1)| 20 | -- +-----------------------------+-------------------------------+--------------------------+ 21 | SELECT span_operation, deparse_info, parameters, pos_start, pos_end, lvl from peek_ordered_spans_with_pos where trace_id='00000000000000000000000000000001'; 22 | span_operation | deparse_info | parameters | pos_start | pos_end | lvl 23 | ----------------------------------------------------------+---------------------------------------------------------+------------+-----------+---------+----- 24 | SELECT * from pg_tracing_test where a=$1 OR b=$2 OR c=$3 | | {1,'2',3} | 1 | 11 | 0 25 | Planner | | | 2 | 3 | 1 26 | ExecutorRun | | | 4 | 10 | 1 27 | BitmapHeapScan on pg_tracing_test | Recheck Cond: ((a = 1) OR (b = '2'::bpchar) OR (c = 3)) | | 5 | 9 | 2 28 | BitmapOr | | | 5 | 8 | 3 29 | BitmapIndexScan on pg_tracing_index_a | Index Cond: (a = 1) | | 5 | 6 | 4 30 | BitmapIndexScan on pg_tracing_index_b | Index Cond: (b = '2'::bpchar) | | 6 | 7 | 4 31 | BitmapIndexScan on pg_tracing_index_c | Index Cond: (c = 3) | | 7 | 8 | 4 32 | (8 rows) 33 | 34 | -- Clean created spans 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /regress/16/expected/planstate_hash.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select count(*) from pg_tracing_test r join pg_tracing_test s using (a); 2 | count 3 | ------- 4 | 10000 5 | (1 row) 6 | 7 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 8 | span_operation | deparse_info | parameters | lvl 9 | --------------------------------------------------------------------------+--------------------+------------+----- 10 | select count(*) from pg_tracing_test r join pg_tracing_test s using (a); | | | 0 11 | Planner | | | 1 12 | ExecutorRun | | | 1 13 | Aggregate | | | 2 14 | Hash Join | Hash Cond: (a = a) | | 3 15 | SeqScan on pg_tracing_test r | | | 4 16 | Hash | | | 4 17 | SeqScan on pg_tracing_test s | | | 5 18 | (8 rows) 19 | 20 | -- +-----------------------------------------+ 21 | -- | A: HashJoin | 22 | -- ++-----------------+----------------------+ 23 | -- | B: SeqScan | 24 | -- +-----------------+ 25 | -- +--------------------+ 26 | -- | C: Hash | 27 | -- +--------------------+ 28 | -- | D: SeqScan | 29 | -- +--------------+ 30 | SELECT span_id AS span_a_id, 31 | get_epoch(span_start) as span_a_start, 32 | get_epoch(span_end) as span_a_end 33 | from pg_tracing_peek_spans 34 | where trace_id='00000000000000000000000000000001' AND span_operation='Hash Join' \gset 35 | SELECT span_id AS span_b_id, 36 | get_epoch(span_start) as span_b_start, 37 | get_epoch(span_end) as span_b_end 38 | from pg_tracing_peek_spans 39 | where parent_id =:'span_a_id' and span_operation='SeqScan on pg_tracing_test r' \gset 40 | SELECT span_id AS span_c_id, 41 | get_epoch(span_start) as span_c_start, 42 | get_epoch(span_end) as span_c_end 43 | from pg_tracing_peek_spans 44 | where parent_id =:'span_a_id' and span_operation='Hash' \gset 45 | SELECT span_id AS span_d_id, 46 | get_epoch(span_start) as span_d_start, 47 | get_epoch(span_end) as span_d_end 48 | from pg_tracing_peek_spans 49 | where parent_id =:'span_c_id' and span_operation='SeqScan on pg_tracing_test s' \gset 50 | SELECT :span_a_end >= :span_c_end as root_ends_last, 51 | :span_c_start >= :span_b_start as hash_start_after_seqscan, 52 | :span_c_start = :span_d_start as hash_start_same_as_child_seqscan, 53 | :span_d_end <= :span_c_end as nested_seq_scan_end_before_parent; 54 | root_ends_last | hash_start_after_seqscan | hash_start_same_as_child_seqscan | nested_seq_scan_end_before_parent 55 | ----------------+--------------------------+----------------------------------+----------------------------------- 56 | t | t | t | t 57 | (1 row) 58 | 59 | -- Clean created spans 60 | CALL clean_spans(); 61 | -------------------------------------------------------------------------------- /regress/16/expected/planstate_projectset.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select information_schema._pg_expandarray('{0,1,2}'::int[]); 2 | _pg_expandarray 3 | ----------------- 4 | (0,1) 5 | (1,2) 6 | (2,3) 7 | (3 rows) 8 | 9 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | deparse_info | parameters | lvl 11 | ------------------------------------------------------------------------------------------+--------------+---------------+----- 12 | select information_schema._pg_expandarray($1::int[]) | | {"'{0,1,2}'"} | 0 13 | Planner | | | 1 14 | ExecutorRun | | | 1 15 | ProjectSet | | | 2 16 | Result | | | 3 17 | select $1[s], +| | {1,1,1,1,1} | 3 18 | s operator(pg_catalog.-) pg_catalog.array_lower($1,$2) operator(pg_catalog.+) $3+| | | 19 | from pg_catalog.generate_series(pg_catalog.array_lower($1,$4), +| | | 20 | pg_catalog.array_upper($1,$5), +| | | 21 | $6) as g(s) | | | 22 | Planner | | | 4 23 | (7 rows) 24 | 25 | -- +---------------------------------------------+ 26 | -- | A: ProjectSet | 27 | -- ++-----------------+--+----------------+------+ 28 | -- | B: Result | | C: TopSpan | 29 | -- +-----------------+ +-+------------+-+ 30 | -- | D: Planner | 31 | -- +------------+ 32 | SELECT span_id AS span_a_id, 33 | get_epoch(span_start) as span_a_start, 34 | get_epoch(span_end) as span_a_end 35 | from pg_tracing_peek_spans 36 | where trace_id='00000000000000000000000000000001' AND span_operation='ProjectSet' \gset 37 | SELECT span_id AS span_b_id, 38 | get_epoch(span_start) as span_b_start, 39 | get_epoch(span_end) as span_b_end 40 | from pg_tracing_peek_spans 41 | where parent_id =:'span_a_id' and span_operation='Result' \gset 42 | SELECT span_id AS span_c_id, 43 | get_epoch(span_start) as span_c_start, 44 | get_epoch(span_end) as span_c_end 45 | from pg_tracing_peek_spans 46 | where parent_id =:'span_a_id' and span_type='Select query' \gset 47 | SELECT span_id AS span_d_id, 48 | get_epoch(span_start) as span_d_start, 49 | get_epoch(span_end) as span_d_end 50 | from pg_tracing_peek_spans 51 | where parent_id =:'span_c_id' and span_type='Planner' \gset 52 | SELECT :span_a_end >= :span_c_end as project_set_ends_after_nested_top_span; 53 | project_set_ends_after_nested_top_span 54 | ---------------------------------------- 55 | t 56 | (1 row) 57 | 58 | -- Clean created spans 59 | CALL clean_spans(); 60 | -------------------------------------------------------------------------------- /regress/16/expected/planstate_union.out: -------------------------------------------------------------------------------- 1 | -- Test simple append 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ 3 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10 4 | UNION ALL 5 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10000; 6 | count 7 | ------- 8 | 9 9 | 9999 10 | (2 rows) 11 | 12 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 13 | span_operation | deparse_info | parameters | lvl 14 | --------------------------------------------------------+----------------------------+----------------+----- 15 | SELECT count(*) FROM pg_tracing_test WHERE a + $1 < $2+| | {0,10,0,10000} | 0 16 | UNION ALL +| | | 17 | SELECT count(*) FROM pg_tracing_test WHERE a + $3 < $4 | | | 18 | Planner | | | 1 19 | ExecutorRun | | | 1 20 | Append | | | 2 21 | Aggregate | | | 3 22 | SeqScan on pg_tracing_test | Filter : ((a + 0) < 10) | | 4 23 | Aggregate | | | 3 24 | SeqScan on pg_tracing_test pg_tracing_test_1 | Filter : ((a + 0) < 10000) | | 4 25 | (8 rows) 26 | 27 | -- +------------------------------------------+ 28 | -- | A: Append | 29 | -- +-+-----------------+-+-----------------+--+ 30 | -- | B: Aggregate | | C: Aggregate | 31 | -- +-----------------+ +-----------------+ 32 | -- | D: IndexScan | | E: IndexScan | 33 | -- +-----------------+ +-----------------+ 34 | SELECT span_id AS span_a_id, 35 | get_epoch(span_start) as span_a_start, 36 | get_epoch(span_end) as span_a_end 37 | from pg_tracing_peek_spans 38 | where trace_id='00000000000000000000000000000001' AND span_operation='Append' \gset 39 | SELECT span_id AS span_b_id, 40 | get_epoch(span_start) as span_b_start, 41 | get_epoch(span_end) as span_b_end 42 | from pg_tracing_peek_spans 43 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 0 \gset 44 | SELECT span_id AS span_c_id, 45 | get_epoch(span_start) as span_c_start, 46 | get_epoch(span_end) as span_c_end 47 | from pg_tracing_peek_spans 48 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 1 \gset 49 | SELECT span_id AS span_d_id, 50 | get_epoch(span_start) as span_d_start, 51 | get_epoch(span_end) as span_d_end 52 | from pg_tracing_peek_spans 53 | where parent_id =:'span_b_id' \gset 54 | SELECT span_id AS span_e_id, 55 | get_epoch(span_start) as span_e_start, 56 | get_epoch(span_end) as span_e_end 57 | from pg_tracing_peek_spans 58 | where parent_id =:'span_c_id' \gset 59 | SELECT :span_a_end >= GREATEST(:span_c_end, :span_e_end) as root_ends_last, 60 | :span_c_start >= :span_b_start as second_index_start_after_first; 61 | root_ends_last | second_index_start_after_first 62 | ----------------+-------------------------------- 63 | t | t 64 | (1 row) 65 | 66 | -- Cleanup 67 | CALL clean_spans(); 68 | CALL reset_settings(); 69 | -------------------------------------------------------------------------------- /regress/16/expected/psql_extended_18.out: -------------------------------------------------------------------------------- 1 | -- Select with extended protocol 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT $1, $2 \parse stmt1 3 | \bind_named stmt1 1 2 \g 4 | ?column? | ?column? 5 | ----------+---------- 6 | 1 | 2 7 | (1 row) 8 | 9 | SELECT trace_id, span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | parameters | lvl 11 | ----------------------------------+--------------+----------------+------------+----- 12 | 00000000000000000000000000000001 | Select query | SELECT $1, $2 | | 0 13 | 00000000000000000000000000000001 | Select query | SELECT $1, $2 | {1,2} | 0 14 | 00000000000000000000000000000001 | Planner | Planner | | 1 15 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 1 16 | 00000000000000000000000000000001 | Result | Result | | 2 17 | (5 rows) 18 | 19 | WITH max_end AS (select max(span_end) from pg_tracing_peek_spans) 20 | SELECT span_end = max_end.max from pg_tracing_peek_spans, max_end 21 | where span_type='Select query' AND parameters IS NOT NULL; 22 | ?column? 23 | ---------- 24 | t 25 | (1 row) 26 | 27 | -- Cleanup 28 | CALL reset_settings(); 29 | CALL clean_spans(); 30 | -------------------------------------------------------------------------------- /regress/16/expected/select_multistatement.out: -------------------------------------------------------------------------------- 1 | -- Check multi statement query 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Test multiple statements in a single query 4 | /*dddbs='postgres.db',traceparent='00-0000000000000000000000000000000c-000000000000000c-01'*/ select 1; select 2; 5 | ?column? 6 | ---------- 7 | 1 8 | (1 row) 9 | 10 | ?column? 11 | ---------- 12 | 2 13 | (1 row) 14 | 15 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='0000000000000000000000000000000c'; 16 | span_operation | parameters | lvl 17 | ----------------+------------+----- 18 | select $1 | {1} | 0 19 | Planner | | 1 20 | ExecutorRun | | 1 21 | Result | | 2 22 | (4 rows) 23 | 24 | CALL clean_spans(); 25 | -- Force a multi-query statement with \; 26 | SELECT 1\; SELECT 1, 2; 27 | ?column? 28 | ---------- 29 | 1 30 | (1 row) 31 | 32 | ?column? | ?column? 33 | ----------+---------- 34 | 1 | 2 35 | (1 row) 36 | 37 | SELECT span_type, span_operation, parameters, lvl from peek_ordered_spans; 38 | span_type | span_operation | parameters | lvl 39 | --------------+----------------+------------+----- 40 | Select query | SELECT $1 | {1} | 0 41 | Planner | Planner | | 1 42 | ExecutorRun | ExecutorRun | | 1 43 | Result | Result | | 2 44 | Select query | SELECT $1, $2 | {1,2} | 0 45 | Planner | Planner | | 1 46 | ExecutorRun | ExecutorRun | | 1 47 | Result | Result | | 2 48 | (8 rows) 49 | 50 | CALL clean_spans(); 51 | -- Cleanup 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /regress/16/expected/setup.out: -------------------------------------------------------------------------------- 1 | -- Some helper functions 2 | CREATE OR REPLACE FUNCTION get_epoch(ts timestamptz) RETURNS float AS 3 | $BODY$ 4 | SELECT extract(epoch from ts); 5 | $BODY$ 6 | LANGUAGE sql; 7 | CREATE OR REPLACE PROCEDURE clean_spans() AS $$ 8 | BEGIN 9 | PERFORM count(*) from pg_tracing_consume_spans; 10 | END; 11 | $$ LANGUAGE plpgsql; 12 | CREATE OR REPLACE PROCEDURE reset_settings() 13 | LANGUAGE SQL 14 | AS $$ 15 | SET pg_tracing.filter_query_ids TO DEFAULT; 16 | SET pg_tracing.sample_rate TO DEFAULT; 17 | SET pg_tracing.caller_sample_rate TO DEFAULT; 18 | SET pg_tracing.track_utility TO DEFAULT; 19 | SET pg_tracing.max_parameter_size TO DEFAULT; 20 | SET parallel_setup_cost TO DEFAULT; 21 | SET parallel_tuple_cost TO DEFAULT; 22 | SET min_parallel_table_scan_size TO DEFAULT; 23 | SET max_parallel_workers_per_gather TO DEFAULT; 24 | $$; 25 | CREATE OR REPLACE PROCEDURE reset_pg_tracing_test_table() AS $$ 26 | BEGIN 27 | DROP TABLE IF EXISTS pg_tracing_test; 28 | CREATE TABLE pg_tracing_test (a int, b char(20), c int); 29 | COMMIT; 30 | CREATE INDEX pg_tracing_index_a ON pg_tracing_test (a); 31 | CREATE INDEX pg_tracing_index_b ON pg_tracing_test (b); 32 | CREATE INDEX pg_tracing_index_c ON pg_tracing_test (c); 33 | INSERT INTO pg_tracing_test SELECT *, *, * FROM generate_series(1, 10000); 34 | ANALYZE pg_tracing_test; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -- Create test tables with data 38 | CALL reset_pg_tracing_test_table(); 39 | NOTICE: table "pg_tracing_test" does not exist, skipping 40 | -- Create test table to test modifications 41 | CREATE TABLE test_modifications (a int, b char(20)); 42 | CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; 43 | ALTER TABLE m ADD UNIQUE (k); 44 | -------------------------------------------------------------------------------- /regress/16/expected/subxact.out: -------------------------------------------------------------------------------- 1 | -- Enable full sampling 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Start a transaction with subxaction 4 | BEGIN; 5 | SAVEPOINT s1; 6 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 7 | SAVEPOINT s2; 8 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 9 | SAVEPOINT s3; 10 | SELECT 1; 11 | ?column? 12 | ---------- 13 | 1 14 | (1 row) 15 | 16 | COMMIT; 17 | -- Check that subxact_count is correctly reported 18 | select span_operation, parameters, subxact_count, lvl FROM peek_ordered_spans WHERE span_operation NOT LIKE 'SAVEPOINT%'; 19 | span_operation | parameters | subxact_count | lvl 20 | -----------------------------------------------------------------+-------------+---------------+----- 21 | TransactionBlock | | 0 | 0 22 | BEGIN; | | 0 | 1 23 | ProcessUtility | | 0 | 2 24 | ProcessUtility | | 0 | 2 25 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 0 | 1 26 | Planner | | 0 | 2 27 | ExecutorRun | | 0 | 2 28 | Insert on pg_tracing_test | | 1 | 3 29 | ProjectSet | | 1 | 4 30 | Result | | 1 | 5 31 | ProcessUtility | | 1 | 2 32 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 1 | 1 33 | Planner | | 1 | 2 34 | ExecutorRun | | 1 | 2 35 | Insert on pg_tracing_test | | 2 | 3 36 | ProjectSet | | 2 | 4 37 | Result | | 2 | 5 38 | ProcessUtility | | 2 | 2 39 | SELECT $1 | {1} | 2 | 1 40 | Planner | | 2 | 2 41 | ExecutorRun | | 2 | 2 42 | Result | | 2 | 3 43 | COMMIT; | | 2 | 1 44 | ProcessUtility | | 2 | 2 45 | TransactionCommit | | 2 | 1 46 | (25 rows) 47 | 48 | -- Cleaning 49 | CALL clean_spans(); 50 | CALL reset_settings(); 51 | -------------------------------------------------------------------------------- /regress/16/expected/wal.out: -------------------------------------------------------------------------------- 1 | -- Generate queries with wal write 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO pg_tracing_test VALUES(generate_series(1, 10), 'aaa'); 3 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ UPDATE pg_tracing_test SET b = 'bbb' WHERE a = 7; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ DELETE FROM pg_tracing_test WHERE a = 9; 5 | -- Check WAL is generated for the above statements 6 | SELECT trace_id, span_type, span_operation, 7 | wal_records > 0 as wal_records, 8 | wal_bytes > 0 as wal_bytes 9 | FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | wal_records | wal_bytes 11 | ----------------------------------+-------------------+-----------------------------------------------------------------+-------------+----------- 12 | 00000000000000000000000000000001 | Insert query | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | t | t 13 | 00000000000000000000000000000001 | Planner | Planner | f | f 14 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 15 | 00000000000000000000000000000001 | Insert | Insert on pg_tracing_test | t | t 16 | 00000000000000000000000000000001 | ProjectSet | ProjectSet | f | f 17 | 00000000000000000000000000000001 | Result | Result | f | f 18 | 00000000000000000000000000000001 | TransactionCommit | TransactionCommit | | 19 | 00000000000000000000000000000002 | Update query | UPDATE pg_tracing_test SET b = $1 WHERE a = $2 | t | t 20 | 00000000000000000000000000000002 | Planner | Planner | f | f 21 | 00000000000000000000000000000002 | ExecutorRun | ExecutorRun | | 22 | 00000000000000000000000000000002 | Update | Update on pg_tracing_test | t | t 23 | 00000000000000000000000000000002 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | f | f 24 | 00000000000000000000000000000002 | TransactionCommit | TransactionCommit | | 25 | 00000000000000000000000000000003 | Delete query | DELETE FROM pg_tracing_test WHERE a = $1 | t | t 26 | 00000000000000000000000000000003 | Planner | Planner | f | f 27 | 00000000000000000000000000000003 | ExecutorRun | ExecutorRun | | 28 | 00000000000000000000000000000003 | Delete | Delete on pg_tracing_test | t | t 29 | 00000000000000000000000000000003 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | t | t 30 | 00000000000000000000000000000003 | TransactionCommit | TransactionCommit | | 31 | (19 rows) 32 | 33 | CALL clean_spans(); 34 | -- Cleanup 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /regress/17/expected/cleanup.out: -------------------------------------------------------------------------------- 1 | -- Drop test table and test functions 2 | DROP TABLE pg_tracing_test; 3 | DROP function test_function_project_set; 4 | DROP function test_function_result; 5 | DROP VIEW peek_ordered_spans; 6 | DROP VIEW peek_ordered_json_spans; 7 | DROP VIEW peek_ordered_spans_with_pos; 8 | DROP VIEW peek_json_spans_with_level; 9 | DROP VIEW peek_json_spans; 10 | DROP EXTENSION pg_tracing; 11 | -------------------------------------------------------------------------------- /regress/17/expected/full_buffer.out: -------------------------------------------------------------------------------- 1 | SET pg_tracing.sample_rate = 0.0; 2 | -- A simple procedure creating nested calls 3 | CREATE OR REPLACE PROCEDURE loop_select(iterations int) AS 4 | $BODY$ 5 | BEGIN 6 | FOR i IN 1..iterations LOOP 7 | PERFORM 'SELECT 1;'; 8 | END LOOP; 9 | END; 10 | $BODY$ 11 | LANGUAGE plpgsql; 12 | -- Clear stats and spans 13 | CALL clean_spans(); 14 | select * from pg_tracing_reset(); 15 | pg_tracing_reset 16 | ------------------ 17 | 18 | (1 row) 19 | 20 | -- Check initial stats after reset 21 | select processed_traces, processed_spans, dropped_traces, dropped_spans from pg_tracing_info(); 22 | processed_traces | processed_spans | dropped_traces | dropped_spans 23 | ------------------+-----------------+----------------+--------------- 24 | 0 | 0 | 0 | 0 25 | (1 row) 26 | 27 | -- Saturate the span buffer. Each call should create at least 2 spans 28 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CALL loop_select(20); 29 | -- Check that we have dropped spans. The trace was still partially processed 30 | select processed_traces = 1, processed_spans = 50, dropped_traces = 0, dropped_spans > 0 from pg_tracing_info(); 31 | ?column? | ?column? | ?column? | ?column? 32 | ----------+----------+----------+---------- 33 | t | t | t | t 34 | (1 row) 35 | 36 | -- Try to create new traces while buffer is full 37 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1; 38 | ?column? 39 | ---------- 40 | 1 41 | (1 row) 42 | 43 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-00'*/ SELECT 1; 44 | ?column? 45 | ---------- 46 | 1 47 | (1 row) 48 | 49 | -- We should have only one additional dropped trace 50 | select processed_traces = 1, processed_spans = 50, dropped_traces = 1 from pg_tracing_info(); 51 | ?column? | ?column? | ?column? 52 | ----------+----------+---------- 53 | t | t | t 54 | (1 row) 55 | 56 | -- Clean current spans 57 | CALL clean_spans(); 58 | -------------------------------------------------------------------------------- /regress/17/expected/insert.out: -------------------------------------------------------------------------------- 1 | -- Only trace queries with sample flag 2 | SET pg_tracing.sample_rate = 0.0; 3 | SET pg_tracing.caller_sample_rate = 1.0; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); 5 | SELECT span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 6 | span_type | span_operation | lvl 7 | -------------------+-----------------------------------------------------------------------------------------------------------------------------------+----- 8 | Utility query | CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); | 0 9 | ProcessUtility | ProcessUtility | 1 10 | Utility query | CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); | 2 11 | ProcessUtility | ProcessUtility | 3 12 | TransactionCommit | TransactionCommit | 0 13 | (5 rows) 14 | 15 | -- Simple insertion 16 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 17 | SELECT span_type, span_operation from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 18 | span_type | span_operation 19 | -------------------+------------------------------------------------------------------ 20 | Insert query | INSERT INTO pg_tracing_test_table_with_constraint VALUES($1, $2) 21 | Planner | Planner 22 | ExecutorRun | ExecutorRun 23 | Insert | Insert on pg_tracing_test_table_with_constraint 24 | Result | Result 25 | TransactionCommit | TransactionCommit 26 | (6 rows) 27 | 28 | -- Trigger constraint violation 29 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 30 | ERROR: duplicate key value violates unique constraint "pk_tracing_test" 31 | DETAIL: Key (a)=(1) already exists. 32 | SELECT span_type, span_operation, sql_error_code, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 33 | span_type | span_operation | sql_error_code | lvl 34 | --------------+------------------------------------------------------------------+----------------+----- 35 | Insert query | INSERT INTO pg_tracing_test_table_with_constraint VALUES($1, $2) | 23505 | 0 36 | Planner | Planner | 00000 | 1 37 | ExecutorRun | ExecutorRun | 23505 | 1 38 | Insert | Insert on pg_tracing_test_table_with_constraint | 23505 | 2 39 | Result | Result | 23505 | 3 40 | (5 rows) 41 | 42 | -- Trigger an error while calling pg_tracing_peek_spans which resets tracing, nothing should be generated 43 | CALL clean_spans(); 44 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(length((select sql_error_code from public.pg_tracing_peek_spans)), 'aaa'); 45 | ERROR: null value in column "a" of relation "pg_tracing_test_table_with_constraint" violates not-null constraint 46 | DETAIL: Failing row contains (null, aaa ). 47 | SELECT span_type, span_operation, sql_error_code from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 48 | span_type | span_operation | sql_error_code 49 | -----------+----------------+---------------- 50 | (0 rows) 51 | 52 | -- Cleanup 53 | CALL clean_spans(); 54 | -------------------------------------------------------------------------------- /regress/17/expected/nested_17.out: -------------------------------------------------------------------------------- 1 | -- Test error thrown within a nested function 2 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 3 | eval_expr(format('date %L + interval %L', '-infinity', 'infinity')), 4 | eval_expr(format('date %L - interval %L', '-infinity', 'infinity')); 5 | eval_expr | eval_expr 6 | ------------------------+----------- 7 | timestamp out of range | -infinity 8 | (1 row) 9 | 10 | select span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001'; 11 | span_operation | lvl 12 | ------------------------------------+----- 13 | SELECT +| 0 14 | eval_expr(format($1, $2, $3)),+| 15 | eval_expr(format($4, $5, $6)) | 16 | Planner | 1 17 | ExecutorRun | 1 18 | Result | 2 19 | $2||expr | 3 20 | Planner | 4 21 | Planner | 4 22 | select date $1 - interval $2 | 3 23 | Planner | 4 24 | ExecutorRun | 4 25 | Result | 5 26 | (11 rows) 27 | 28 | -- Cleanup 29 | CALL clean_spans(); 30 | -------------------------------------------------------------------------------- /regress/17/expected/parameters_extended.out: -------------------------------------------------------------------------------- 1 | -- Saturate the parameter buffer with extended protocol 2 | SET pg_tracing.max_parameter_size=1; 3 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select $1, $2, $3 \bind 1 2 3 \g 4 | ?column? | ?column? | ?column? 5 | ----------+----------+---------- 6 | 1 | 2 | 3 7 | (1 row) 8 | 9 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | parameters | lvl 11 | -------------------+-------------+----- 12 | select $1, $2, $3 | {1,...,...} | 0 13 | Planner | | 1 14 | ExecutorRun | | 1 15 | Result | | 2 16 | (4 rows) 17 | 18 | SET pg_tracing.max_parameter_size=2; 19 | /*traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ select $1, $2, $3 \bind 1 2 3 \g 20 | ?column? | ?column? | ?column? 21 | ----------+----------+---------- 22 | 1 | 2 | 3 23 | (1 row) 24 | 25 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 26 | span_operation | parameters | lvl 27 | -------------------+-------------+----- 28 | select $1, $2, $3 | {1,...,...} | 0 29 | Planner | | 1 30 | ExecutorRun | | 1 31 | Result | | 2 32 | (4 rows) 33 | 34 | SET pg_tracing.max_parameter_size=3; 35 | /*traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ select $1, $2, $3 \bind 1 2 3 \g 36 | ?column? | ?column? | ?column? 37 | ----------+----------+---------- 38 | 1 | 2 | 3 39 | (1 row) 40 | 41 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 42 | span_operation | parameters | lvl 43 | -------------------+------------+----- 44 | select $1, $2, $3 | {1,2,...} | 0 45 | Planner | | 1 46 | ExecutorRun | | 1 47 | Result | | 2 48 | (4 rows) 49 | 50 | -- Cleanup 51 | CALL clean_spans(); 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /regress/17/expected/planstate_bitmap.out: -------------------------------------------------------------------------------- 1 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * from pg_tracing_test where a=1 OR b='2' OR c=3; 2 | a | b | c 3 | ---+----------------------+--- 4 | 1 | 1 | 1 5 | 2 | 2 | 2 6 | 3 | 3 | 3 7 | (3 rows) 8 | 9 | -- PG < 18 10 | -- +-----------------------------------------------------------------------------------------+ 11 | -- | A: BitmapOr | 12 | -- ++-----------------------------+-------------------------------+--------------------------+ 13 | -- |B: Bitmap Index Scan (a=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (c=3)| 14 | -- +-----------------------------+-------------------------------+--------------------------+ 15 | -- PG >= 18 16 | -- +-----------------------------------------------------------------------------------------+ 17 | -- | A: BitmapOr | 18 | -- ++-----------------------------+-------------------------------+--------------------------+ 19 | -- |B: Bitmap Index Scan (c=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (a=1)| 20 | -- +-----------------------------+-------------------------------+--------------------------+ 21 | SELECT span_operation, deparse_info, parameters, pos_start, pos_end, lvl from peek_ordered_spans_with_pos where trace_id='00000000000000000000000000000001'; 22 | span_operation | deparse_info | parameters | pos_start | pos_end | lvl 23 | ----------------------------------------------------------+---------------------------------------------------------+------------+-----------+---------+----- 24 | SELECT * from pg_tracing_test where a=$1 OR b=$2 OR c=$3 | | {1,'2',3} | 1 | 11 | 0 25 | Planner | | | 2 | 3 | 1 26 | ExecutorRun | | | 4 | 10 | 1 27 | BitmapHeapScan on pg_tracing_test | Recheck Cond: ((a = 1) OR (b = '2'::bpchar) OR (c = 3)) | | 5 | 9 | 2 28 | BitmapOr | | | 5 | 8 | 3 29 | BitmapIndexScan on pg_tracing_index_a | Index Cond: (a = 1) | | 5 | 6 | 4 30 | BitmapIndexScan on pg_tracing_index_b | Index Cond: (b = '2'::bpchar) | | 6 | 7 | 4 31 | BitmapIndexScan on pg_tracing_index_c | Index Cond: (c = 3) | | 7 | 8 | 4 32 | (8 rows) 33 | 34 | -- Clean created spans 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /regress/17/expected/planstate_hash.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select count(*) from pg_tracing_test r join pg_tracing_test s using (a); 2 | count 3 | ------- 4 | 10000 5 | (1 row) 6 | 7 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 8 | span_operation | deparse_info | parameters | lvl 9 | --------------------------------------------------------------------------+--------------------+------------+----- 10 | select count(*) from pg_tracing_test r join pg_tracing_test s using (a); | | | 0 11 | Planner | | | 1 12 | ExecutorRun | | | 1 13 | Aggregate | | | 2 14 | Hash Join | Hash Cond: (a = a) | | 3 15 | SeqScan on pg_tracing_test r | | | 4 16 | Hash | | | 4 17 | SeqScan on pg_tracing_test s | | | 5 18 | (8 rows) 19 | 20 | -- +-----------------------------------------+ 21 | -- | A: HashJoin | 22 | -- ++-----------------+----------------------+ 23 | -- | B: SeqScan | 24 | -- +-----------------+ 25 | -- +--------------------+ 26 | -- | C: Hash | 27 | -- +--------------------+ 28 | -- | D: SeqScan | 29 | -- +--------------+ 30 | SELECT span_id AS span_a_id, 31 | get_epoch(span_start) as span_a_start, 32 | get_epoch(span_end) as span_a_end 33 | from pg_tracing_peek_spans 34 | where trace_id='00000000000000000000000000000001' AND span_operation='Hash Join' \gset 35 | SELECT span_id AS span_b_id, 36 | get_epoch(span_start) as span_b_start, 37 | get_epoch(span_end) as span_b_end 38 | from pg_tracing_peek_spans 39 | where parent_id =:'span_a_id' and span_operation='SeqScan on pg_tracing_test r' \gset 40 | SELECT span_id AS span_c_id, 41 | get_epoch(span_start) as span_c_start, 42 | get_epoch(span_end) as span_c_end 43 | from pg_tracing_peek_spans 44 | where parent_id =:'span_a_id' and span_operation='Hash' \gset 45 | SELECT span_id AS span_d_id, 46 | get_epoch(span_start) as span_d_start, 47 | get_epoch(span_end) as span_d_end 48 | from pg_tracing_peek_spans 49 | where parent_id =:'span_c_id' and span_operation='SeqScan on pg_tracing_test s' \gset 50 | SELECT :span_a_end >= :span_c_end as root_ends_last, 51 | :span_c_start >= :span_b_start as hash_start_after_seqscan, 52 | :span_c_start = :span_d_start as hash_start_same_as_child_seqscan, 53 | :span_d_end <= :span_c_end as nested_seq_scan_end_before_parent; 54 | root_ends_last | hash_start_after_seqscan | hash_start_same_as_child_seqscan | nested_seq_scan_end_before_parent 55 | ----------------+--------------------------+----------------------------------+----------------------------------- 56 | t | t | t | t 57 | (1 row) 58 | 59 | -- Clean created spans 60 | CALL clean_spans(); 61 | -------------------------------------------------------------------------------- /regress/17/expected/planstate_projectset.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select information_schema._pg_expandarray('{0,1,2}'::int[]); 2 | _pg_expandarray 3 | ----------------- 4 | (0,1) 5 | (1,2) 6 | (2,3) 7 | (3 rows) 8 | 9 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | deparse_info | parameters | lvl 11 | ------------------------------------------------------+--------------+---------------+----- 12 | select information_schema._pg_expandarray($1::int[]) | | {"'{0,1,2}'"} | 0 13 | Planner | | | 1 14 | ExecutorRun | | | 1 15 | ProjectSet | | | 2 16 | Result | | | 3 17 | SELECT * FROM pg_catalog.unnest($1) WITH ORDINALITY | | | 3 18 | Planner | | | 4 19 | (7 rows) 20 | 21 | -- +---------------------------------------------+ 22 | -- | A: ProjectSet | 23 | -- ++-----------------+--+----------------+------+ 24 | -- | B: Result | | C: TopSpan | 25 | -- +-----------------+ +-+------------+-+ 26 | -- | D: Planner | 27 | -- +------------+ 28 | SELECT span_id AS span_a_id, 29 | get_epoch(span_start) as span_a_start, 30 | get_epoch(span_end) as span_a_end 31 | from pg_tracing_peek_spans 32 | where trace_id='00000000000000000000000000000001' AND span_operation='ProjectSet' \gset 33 | SELECT span_id AS span_b_id, 34 | get_epoch(span_start) as span_b_start, 35 | get_epoch(span_end) as span_b_end 36 | from pg_tracing_peek_spans 37 | where parent_id =:'span_a_id' and span_operation='Result' \gset 38 | SELECT span_id AS span_c_id, 39 | get_epoch(span_start) as span_c_start, 40 | get_epoch(span_end) as span_c_end 41 | from pg_tracing_peek_spans 42 | where parent_id =:'span_a_id' and span_type='Select query' \gset 43 | SELECT span_id AS span_d_id, 44 | get_epoch(span_start) as span_d_start, 45 | get_epoch(span_end) as span_d_end 46 | from pg_tracing_peek_spans 47 | where parent_id =:'span_c_id' and span_type='Planner' \gset 48 | SELECT :span_a_end >= :span_c_end as project_set_ends_after_nested_top_span; 49 | project_set_ends_after_nested_top_span 50 | ---------------------------------------- 51 | t 52 | (1 row) 53 | 54 | -- Clean created spans 55 | CALL clean_spans(); 56 | -------------------------------------------------------------------------------- /regress/17/expected/planstate_union.out: -------------------------------------------------------------------------------- 1 | -- Test simple append 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ 3 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10 4 | UNION ALL 5 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10000; 6 | count 7 | ------- 8 | 9 9 | 9999 10 | (2 rows) 11 | 12 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 13 | span_operation | deparse_info | parameters | lvl 14 | --------------------------------------------------------+----------------------------+----------------+----- 15 | SELECT count(*) FROM pg_tracing_test WHERE a + $1 < $2+| | {0,10,0,10000} | 0 16 | UNION ALL +| | | 17 | SELECT count(*) FROM pg_tracing_test WHERE a + $3 < $4 | | | 18 | Planner | | | 1 19 | ExecutorRun | | | 1 20 | Append | | | 2 21 | Aggregate | | | 3 22 | SeqScan on pg_tracing_test | Filter : ((a + 0) < 10) | | 4 23 | Aggregate | | | 3 24 | SeqScan on pg_tracing_test pg_tracing_test_1 | Filter : ((a + 0) < 10000) | | 4 25 | (8 rows) 26 | 27 | -- +------------------------------------------+ 28 | -- | A: Append | 29 | -- +-+-----------------+-+-----------------+--+ 30 | -- | B: Aggregate | | C: Aggregate | 31 | -- +-----------------+ +-----------------+ 32 | -- | D: IndexScan | | E: IndexScan | 33 | -- +-----------------+ +-----------------+ 34 | SELECT span_id AS span_a_id, 35 | get_epoch(span_start) as span_a_start, 36 | get_epoch(span_end) as span_a_end 37 | from pg_tracing_peek_spans 38 | where trace_id='00000000000000000000000000000001' AND span_operation='Append' \gset 39 | SELECT span_id AS span_b_id, 40 | get_epoch(span_start) as span_b_start, 41 | get_epoch(span_end) as span_b_end 42 | from pg_tracing_peek_spans 43 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 0 \gset 44 | SELECT span_id AS span_c_id, 45 | get_epoch(span_start) as span_c_start, 46 | get_epoch(span_end) as span_c_end 47 | from pg_tracing_peek_spans 48 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 1 \gset 49 | SELECT span_id AS span_d_id, 50 | get_epoch(span_start) as span_d_start, 51 | get_epoch(span_end) as span_d_end 52 | from pg_tracing_peek_spans 53 | where parent_id =:'span_b_id' \gset 54 | SELECT span_id AS span_e_id, 55 | get_epoch(span_start) as span_e_start, 56 | get_epoch(span_end) as span_e_end 57 | from pg_tracing_peek_spans 58 | where parent_id =:'span_c_id' \gset 59 | SELECT :span_a_end >= GREATEST(:span_c_end, :span_e_end) as root_ends_last, 60 | :span_c_start >= :span_b_start as second_index_start_after_first; 61 | root_ends_last | second_index_start_after_first 62 | ----------------+-------------------------------- 63 | t | t 64 | (1 row) 65 | 66 | -- Cleanup 67 | CALL clean_spans(); 68 | CALL reset_settings(); 69 | -------------------------------------------------------------------------------- /regress/17/expected/select_multistatement.out: -------------------------------------------------------------------------------- 1 | -- Check multi statement query 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Test multiple statements in a single query 4 | /*dddbs='postgres.db',traceparent='00-0000000000000000000000000000000c-000000000000000c-01'*/ select 1; select 2; 5 | ?column? 6 | ---------- 7 | 1 8 | (1 row) 9 | 10 | ?column? 11 | ---------- 12 | 2 13 | (1 row) 14 | 15 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='0000000000000000000000000000000c'; 16 | span_operation | parameters | lvl 17 | ----------------+------------+----- 18 | select $1 | {1} | 0 19 | Planner | | 1 20 | ExecutorRun | | 1 21 | Result | | 2 22 | (4 rows) 23 | 24 | CALL clean_spans(); 25 | -- Force a multi-query statement with \; 26 | SELECT 1\; SELECT 1, 2; 27 | ?column? 28 | ---------- 29 | 1 30 | (1 row) 31 | 32 | ?column? | ?column? 33 | ----------+---------- 34 | 1 | 2 35 | (1 row) 36 | 37 | SELECT span_type, span_operation, parameters, lvl from peek_ordered_spans; 38 | span_type | span_operation | parameters | lvl 39 | --------------+----------------+------------+----- 40 | Select query | SELECT $1 | {1} | 0 41 | Planner | Planner | | 1 42 | ExecutorRun | ExecutorRun | | 1 43 | Result | Result | | 2 44 | Select query | SELECT $1, $2 | {1,2} | 0 45 | Planner | Planner | | 1 46 | ExecutorRun | ExecutorRun | | 1 47 | Result | Result | | 2 48 | (8 rows) 49 | 50 | CALL clean_spans(); 51 | -- Cleanup 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /regress/17/expected/setup.out: -------------------------------------------------------------------------------- 1 | -- Some helper functions 2 | CREATE OR REPLACE FUNCTION get_epoch(ts timestamptz) RETURNS float AS 3 | $BODY$ 4 | SELECT extract(epoch from ts); 5 | $BODY$ 6 | LANGUAGE sql; 7 | CREATE OR REPLACE PROCEDURE clean_spans() AS $$ 8 | BEGIN 9 | PERFORM count(*) from pg_tracing_consume_spans; 10 | END; 11 | $$ LANGUAGE plpgsql; 12 | CREATE OR REPLACE PROCEDURE reset_settings() 13 | LANGUAGE SQL 14 | AS $$ 15 | SET pg_tracing.filter_query_ids TO DEFAULT; 16 | SET pg_tracing.sample_rate TO DEFAULT; 17 | SET pg_tracing.caller_sample_rate TO DEFAULT; 18 | SET pg_tracing.track_utility TO DEFAULT; 19 | SET pg_tracing.max_parameter_size TO DEFAULT; 20 | SET parallel_setup_cost TO DEFAULT; 21 | SET parallel_tuple_cost TO DEFAULT; 22 | SET min_parallel_table_scan_size TO DEFAULT; 23 | SET max_parallel_workers_per_gather TO DEFAULT; 24 | $$; 25 | CREATE OR REPLACE PROCEDURE reset_pg_tracing_test_table() AS $$ 26 | BEGIN 27 | DROP TABLE IF EXISTS pg_tracing_test; 28 | CREATE TABLE pg_tracing_test (a int, b char(20), c int); 29 | COMMIT; 30 | CREATE INDEX pg_tracing_index_a ON pg_tracing_test (a); 31 | CREATE INDEX pg_tracing_index_b ON pg_tracing_test (b); 32 | CREATE INDEX pg_tracing_index_c ON pg_tracing_test (c); 33 | INSERT INTO pg_tracing_test SELECT *, *, * FROM generate_series(1, 10000); 34 | ANALYZE pg_tracing_test; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -- Create test tables with data 38 | CALL reset_pg_tracing_test_table(); 39 | NOTICE: table "pg_tracing_test" does not exist, skipping 40 | -- Create test table to test modifications 41 | CREATE TABLE test_modifications (a int, b char(20)); 42 | CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; 43 | ALTER TABLE m ADD UNIQUE (k); 44 | -------------------------------------------------------------------------------- /regress/17/expected/subxact.out: -------------------------------------------------------------------------------- 1 | -- Enable full sampling 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Start a transaction with subxaction 4 | BEGIN; 5 | SAVEPOINT s1; 6 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 7 | SAVEPOINT s2; 8 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 9 | SAVEPOINT s3; 10 | SELECT 1; 11 | ?column? 12 | ---------- 13 | 1 14 | (1 row) 15 | 16 | COMMIT; 17 | -- Check that subxact_count is correctly reported 18 | select span_operation, parameters, subxact_count, lvl FROM peek_ordered_spans WHERE span_operation NOT LIKE 'SAVEPOINT%'; 19 | span_operation | parameters | subxact_count | lvl 20 | -----------------------------------------------------------------+-------------+---------------+----- 21 | TransactionBlock | | 0 | 0 22 | BEGIN; | | 0 | 1 23 | ProcessUtility | | 0 | 2 24 | ProcessUtility | | 0 | 2 25 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 0 | 1 26 | Planner | | 0 | 2 27 | ExecutorRun | | 0 | 2 28 | Insert on pg_tracing_test | | 1 | 3 29 | ProjectSet | | 1 | 4 30 | Result | | 1 | 5 31 | ProcessUtility | | 1 | 2 32 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 1 | 1 33 | Planner | | 1 | 2 34 | ExecutorRun | | 1 | 2 35 | Insert on pg_tracing_test | | 2 | 3 36 | ProjectSet | | 2 | 4 37 | Result | | 2 | 5 38 | ProcessUtility | | 2 | 2 39 | SELECT $1 | {1} | 2 | 1 40 | Planner | | 2 | 2 41 | ExecutorRun | | 2 | 2 42 | Result | | 2 | 3 43 | COMMIT; | | 2 | 1 44 | ProcessUtility | | 2 | 2 45 | TransactionCommit | | 2 | 1 46 | (25 rows) 47 | 48 | -- Cleaning 49 | CALL clean_spans(); 50 | CALL reset_settings(); 51 | -------------------------------------------------------------------------------- /regress/17/expected/wal.out: -------------------------------------------------------------------------------- 1 | -- Generate queries with wal write 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO pg_tracing_test VALUES(generate_series(1, 10), 'aaa'); 3 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ UPDATE pg_tracing_test SET b = 'bbb' WHERE a = 7; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ DELETE FROM pg_tracing_test WHERE a = 9; 5 | -- Check WAL is generated for the above statements 6 | SELECT trace_id, span_type, span_operation, 7 | wal_records > 0 as wal_records, 8 | wal_bytes > 0 as wal_bytes 9 | FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | wal_records | wal_bytes 11 | ----------------------------------+-------------------+-----------------------------------------------------------------+-------------+----------- 12 | 00000000000000000000000000000001 | Insert query | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | t | t 13 | 00000000000000000000000000000001 | Planner | Planner | f | f 14 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 15 | 00000000000000000000000000000001 | Insert | Insert on pg_tracing_test | t | t 16 | 00000000000000000000000000000001 | ProjectSet | ProjectSet | f | f 17 | 00000000000000000000000000000001 | Result | Result | f | f 18 | 00000000000000000000000000000001 | TransactionCommit | TransactionCommit | | 19 | 00000000000000000000000000000002 | Update query | UPDATE pg_tracing_test SET b = $1 WHERE a = $2 | t | t 20 | 00000000000000000000000000000002 | Planner | Planner | f | f 21 | 00000000000000000000000000000002 | ExecutorRun | ExecutorRun | | 22 | 00000000000000000000000000000002 | Update | Update on pg_tracing_test | t | t 23 | 00000000000000000000000000000002 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | f | f 24 | 00000000000000000000000000000002 | TransactionCommit | TransactionCommit | | 25 | 00000000000000000000000000000003 | Delete query | DELETE FROM pg_tracing_test WHERE a = $1 | t | t 26 | 00000000000000000000000000000003 | Planner | Planner | f | f 27 | 00000000000000000000000000000003 | ExecutorRun | ExecutorRun | | 28 | 00000000000000000000000000000003 | Delete | Delete on pg_tracing_test | t | t 29 | 00000000000000000000000000000003 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | t | t 30 | 00000000000000000000000000000003 | TransactionCommit | TransactionCommit | | 31 | (19 rows) 32 | 33 | CALL clean_spans(); 34 | -- Cleanup 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /regress/18/expected/cleanup.out: -------------------------------------------------------------------------------- 1 | -- Drop test table and test functions 2 | DROP TABLE pg_tracing_test; 3 | DROP function test_function_project_set; 4 | DROP function test_function_result; 5 | DROP VIEW peek_ordered_spans; 6 | DROP VIEW peek_ordered_json_spans; 7 | DROP VIEW peek_ordered_spans_with_pos; 8 | DROP VIEW peek_json_spans_with_level; 9 | DROP VIEW peek_json_spans; 10 | DROP EXTENSION pg_tracing; 11 | -------------------------------------------------------------------------------- /regress/18/expected/fk_failure.out: -------------------------------------------------------------------------------- 1 | CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); 2 | CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY); 3 | CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent); 4 | INSERT INTO enumtest_parent VALUES ('red'); 5 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO enumtest_child VALUES ('blue'); 6 | ERROR: insert or update on table "enumtest_child" violates foreign key constraint "enumtest_child_parent_fkey" 7 | DETAIL: Key (parent)=(blue) is not present in table "enumtest_parent". 8 | -- Cleanup 9 | CALL reset_settings(); 10 | CALL clean_spans(); 11 | -------------------------------------------------------------------------------- /regress/18/expected/full_buffer.out: -------------------------------------------------------------------------------- 1 | SET pg_tracing.sample_rate = 0.0; 2 | -- A simple procedure creating nested calls 3 | CREATE OR REPLACE PROCEDURE loop_select(iterations int) AS 4 | $BODY$ 5 | BEGIN 6 | FOR i IN 1..iterations LOOP 7 | PERFORM 'SELECT 1;'; 8 | END LOOP; 9 | END; 10 | $BODY$ 11 | LANGUAGE plpgsql; 12 | -- Clear stats and spans 13 | CALL clean_spans(); 14 | select * from pg_tracing_reset(); 15 | pg_tracing_reset 16 | ------------------ 17 | 18 | (1 row) 19 | 20 | -- Check initial stats after reset 21 | select processed_traces, processed_spans, dropped_traces, dropped_spans from pg_tracing_info(); 22 | processed_traces | processed_spans | dropped_traces | dropped_spans 23 | ------------------+-----------------+----------------+--------------- 24 | 0 | 0 | 0 | 0 25 | (1 row) 26 | 27 | -- Saturate the span buffer. Each call should create at least 2 spans 28 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CALL loop_select(20); 29 | -- Check that we have dropped spans. The trace was still partially processed 30 | select processed_traces = 1, processed_spans = 50, dropped_traces = 0, dropped_spans > 0 from pg_tracing_info(); 31 | ?column? | ?column? | ?column? | ?column? 32 | ----------+----------+----------+---------- 33 | t | t | t | t 34 | (1 row) 35 | 36 | -- Try to create new traces while buffer is full 37 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1; 38 | ?column? 39 | ---------- 40 | 1 41 | (1 row) 42 | 43 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-00'*/ SELECT 1; 44 | ?column? 45 | ---------- 46 | 1 47 | (1 row) 48 | 49 | -- We should have only one additional dropped trace 50 | select processed_traces = 1, processed_spans = 50, dropped_traces = 1 from pg_tracing_info(); 51 | ?column? | ?column? | ?column? 52 | ----------+----------+---------- 53 | t | t | t 54 | (1 row) 55 | 56 | -- Clean current spans 57 | CALL clean_spans(); 58 | -------------------------------------------------------------------------------- /regress/18/expected/nested_17.out: -------------------------------------------------------------------------------- 1 | -- Test error thrown within a nested function 2 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 3 | eval_expr(format('date %L + interval %L', '-infinity', 'infinity')), 4 | eval_expr(format('date %L - interval %L', '-infinity', 'infinity')); 5 | eval_expr | eval_expr 6 | ------------------------+----------- 7 | timestamp out of range | -infinity 8 | (1 row) 9 | 10 | select span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001'; 11 | span_operation | lvl 12 | ------------------------------------+----- 13 | SELECT +| 0 14 | eval_expr(format($1, $2, $3)),+| 15 | eval_expr(format($4, $5, $6)) | 16 | Planner | 1 17 | ExecutorRun | 1 18 | Result | 2 19 | $2||expr | 3 20 | Planner | 4 21 | Planner | 4 22 | select date $1 - interval $2 | 3 23 | Planner | 4 24 | ExecutorRun | 4 25 | Result | 5 26 | (11 rows) 27 | 28 | -- Cleanup 29 | CALL clean_spans(); 30 | -------------------------------------------------------------------------------- /regress/18/expected/parameters_extended.out: -------------------------------------------------------------------------------- 1 | -- Saturate the parameter buffer with extended protocol 2 | SET pg_tracing.max_parameter_size=1; 3 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select $1, $2, $3 \bind 1 2 3 \g 4 | ?column? | ?column? | ?column? 5 | ----------+----------+---------- 6 | 1 | 2 | 3 7 | (1 row) 8 | 9 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | parameters | lvl 11 | -------------------+-------------+----- 12 | select $1, $2, $3 | {1,...,...} | 0 13 | Planner | | 1 14 | ExecutorRun | | 1 15 | Result | | 2 16 | (4 rows) 17 | 18 | SET pg_tracing.max_parameter_size=2; 19 | /*traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ select $1, $2, $3 \bind 1 2 3 \g 20 | ?column? | ?column? | ?column? 21 | ----------+----------+---------- 22 | 1 | 2 | 3 23 | (1 row) 24 | 25 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 26 | span_operation | parameters | lvl 27 | -------------------+-------------+----- 28 | select $1, $2, $3 | {1,...,...} | 0 29 | Planner | | 1 30 | ExecutorRun | | 1 31 | Result | | 2 32 | (4 rows) 33 | 34 | SET pg_tracing.max_parameter_size=3; 35 | /*traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ select $1, $2, $3 \bind 1 2 3 \g 36 | ?column? | ?column? | ?column? 37 | ----------+----------+---------- 38 | 1 | 2 | 3 39 | (1 row) 40 | 41 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 42 | span_operation | parameters | lvl 43 | -------------------+------------+----- 44 | select $1, $2, $3 | {1,2,...} | 0 45 | Planner | | 1 46 | ExecutorRun | | 1 47 | Result | | 2 48 | (4 rows) 49 | 50 | -- Cleanup 51 | CALL clean_spans(); 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /regress/18/expected/planstate_bitmap.out: -------------------------------------------------------------------------------- 1 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * from pg_tracing_test where a=1 OR b='2' OR c=3; 2 | a | b | c 3 | ---+----------------------+--- 4 | 1 | 1 | 1 5 | 2 | 2 | 2 6 | 3 | 3 | 3 7 | (3 rows) 8 | 9 | -- PG < 18 10 | -- +-----------------------------------------------------------------------------------------+ 11 | -- | A: BitmapOr | 12 | -- ++-----------------------------+-------------------------------+--------------------------+ 13 | -- |B: Bitmap Index Scan (a=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (c=3)| 14 | -- +-----------------------------+-------------------------------+--------------------------+ 15 | -- PG >= 18 16 | -- +-----------------------------------------------------------------------------------------+ 17 | -- | A: BitmapOr | 18 | -- ++-----------------------------+-------------------------------+--------------------------+ 19 | -- |B: Bitmap Index Scan (c=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (a=1)| 20 | -- +-----------------------------+-------------------------------+--------------------------+ 21 | SELECT span_operation, deparse_info, parameters, pos_start, pos_end, lvl from peek_ordered_spans_with_pos where trace_id='00000000000000000000000000000001'; 22 | span_operation | deparse_info | parameters | pos_start | pos_end | lvl 23 | ----------------------------------------------------------+---------------------------------------------------------+------------+-----------+---------+----- 24 | SELECT * from pg_tracing_test where a=$1 OR b=$2 OR c=$3 | | {1,'2',3} | 1 | 11 | 0 25 | Planner | | | 2 | 3 | 1 26 | ExecutorRun | | | 4 | 10 | 1 27 | BitmapHeapScan on pg_tracing_test | Recheck Cond: ((c = 3) OR (b = '2'::bpchar) OR (a = 1)) | | 5 | 9 | 2 28 | BitmapOr | | | 5 | 8 | 3 29 | BitmapIndexScan on pg_tracing_index_c | Index Cond: (c = 3) | | 5 | 6 | 4 30 | BitmapIndexScan on pg_tracing_index_b | Index Cond: (b = '2'::bpchar) | | 6 | 7 | 4 31 | BitmapIndexScan on pg_tracing_index_a | Index Cond: (a = 1) | | 7 | 8 | 4 32 | (8 rows) 33 | 34 | -- Clean created spans 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /regress/18/expected/planstate_hash.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select count(*) from pg_tracing_test r join pg_tracing_test s using (a); 2 | count 3 | ------- 4 | 10000 5 | (1 row) 6 | 7 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 8 | span_operation | deparse_info | parameters | lvl 9 | --------------------------------------------------------------------------+--------------------+------------+----- 10 | select count(*) from pg_tracing_test r join pg_tracing_test s using (a); | | | 0 11 | Planner | | | 1 12 | ExecutorRun | | | 1 13 | Aggregate | | | 2 14 | Hash Join | Hash Cond: (a = a) | | 3 15 | SeqScan on pg_tracing_test r | | | 4 16 | Hash | | | 4 17 | SeqScan on pg_tracing_test s | | | 5 18 | (8 rows) 19 | 20 | -- +-----------------------------------------+ 21 | -- | A: HashJoin | 22 | -- ++-----------------+----------------------+ 23 | -- | B: SeqScan | 24 | -- +-----------------+ 25 | -- +--------------------+ 26 | -- | C: Hash | 27 | -- +--------------------+ 28 | -- | D: SeqScan | 29 | -- +--------------+ 30 | SELECT span_id AS span_a_id, 31 | get_epoch(span_start) as span_a_start, 32 | get_epoch(span_end) as span_a_end 33 | from pg_tracing_peek_spans 34 | where trace_id='00000000000000000000000000000001' AND span_operation='Hash Join' \gset 35 | SELECT span_id AS span_b_id, 36 | get_epoch(span_start) as span_b_start, 37 | get_epoch(span_end) as span_b_end 38 | from pg_tracing_peek_spans 39 | where parent_id =:'span_a_id' and span_operation='SeqScan on pg_tracing_test r' \gset 40 | SELECT span_id AS span_c_id, 41 | get_epoch(span_start) as span_c_start, 42 | get_epoch(span_end) as span_c_end 43 | from pg_tracing_peek_spans 44 | where parent_id =:'span_a_id' and span_operation='Hash' \gset 45 | SELECT span_id AS span_d_id, 46 | get_epoch(span_start) as span_d_start, 47 | get_epoch(span_end) as span_d_end 48 | from pg_tracing_peek_spans 49 | where parent_id =:'span_c_id' and span_operation='SeqScan on pg_tracing_test s' \gset 50 | SELECT :span_a_end >= :span_c_end as root_ends_last, 51 | :span_c_start >= :span_b_start as hash_start_after_seqscan, 52 | :span_c_start = :span_d_start as hash_start_same_as_child_seqscan, 53 | :span_d_end <= :span_c_end as nested_seq_scan_end_before_parent; 54 | root_ends_last | hash_start_after_seqscan | hash_start_same_as_child_seqscan | nested_seq_scan_end_before_parent 55 | ----------------+--------------------------+----------------------------------+----------------------------------- 56 | t | t | t | t 57 | (1 row) 58 | 59 | -- Clean created spans 60 | CALL clean_spans(); 61 | -------------------------------------------------------------------------------- /regress/18/expected/planstate_projectset.out: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select information_schema._pg_expandarray('{0,1,2}'::int[]); 2 | _pg_expandarray 3 | ----------------- 4 | (0,1) 5 | (1,2) 6 | (2,3) 7 | (3 rows) 8 | 9 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | span_operation | deparse_info | parameters | lvl 11 | ------------------------------------------------------+--------------+---------------+----- 12 | select information_schema._pg_expandarray($1::int[]) | | {"'{0,1,2}'"} | 0 13 | Planner | | | 1 14 | ExecutorRun | | | 1 15 | ProjectSet | | | 2 16 | Result | | | 3 17 | SELECT * FROM pg_catalog.unnest($1) WITH ORDINALITY | | | 3 18 | Planner | | | 4 19 | (7 rows) 20 | 21 | -- +---------------------------------------------+ 22 | -- | A: ProjectSet | 23 | -- ++-----------------+--+----------------+------+ 24 | -- | B: Result | | C: TopSpan | 25 | -- +-----------------+ +-+------------+-+ 26 | -- | D: Planner | 27 | -- +------------+ 28 | SELECT span_id AS span_a_id, 29 | get_epoch(span_start) as span_a_start, 30 | get_epoch(span_end) as span_a_end 31 | from pg_tracing_peek_spans 32 | where trace_id='00000000000000000000000000000001' AND span_operation='ProjectSet' \gset 33 | SELECT span_id AS span_b_id, 34 | get_epoch(span_start) as span_b_start, 35 | get_epoch(span_end) as span_b_end 36 | from pg_tracing_peek_spans 37 | where parent_id =:'span_a_id' and span_operation='Result' \gset 38 | SELECT span_id AS span_c_id, 39 | get_epoch(span_start) as span_c_start, 40 | get_epoch(span_end) as span_c_end 41 | from pg_tracing_peek_spans 42 | where parent_id =:'span_a_id' and span_type='Select query' \gset 43 | SELECT span_id AS span_d_id, 44 | get_epoch(span_start) as span_d_start, 45 | get_epoch(span_end) as span_d_end 46 | from pg_tracing_peek_spans 47 | where parent_id =:'span_c_id' and span_type='Planner' \gset 48 | SELECT :span_a_end >= :span_c_end as project_set_ends_after_nested_top_span; 49 | project_set_ends_after_nested_top_span 50 | ---------------------------------------- 51 | t 52 | (1 row) 53 | 54 | -- Clean created spans 55 | CALL clean_spans(); 56 | -------------------------------------------------------------------------------- /regress/18/expected/planstate_union.out: -------------------------------------------------------------------------------- 1 | -- Test simple append 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ 3 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10 4 | UNION ALL 5 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10000; 6 | count 7 | ------- 8 | 9 9 | 9999 10 | (2 rows) 11 | 12 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 13 | span_operation | deparse_info | parameters | lvl 14 | --------------------------------------------------------+----------------------------+----------------+----- 15 | SELECT count(*) FROM pg_tracing_test WHERE a + $1 < $2+| | {0,10,0,10000} | 0 16 | UNION ALL +| | | 17 | SELECT count(*) FROM pg_tracing_test WHERE a + $3 < $4 | | | 18 | Planner | | | 1 19 | ExecutorRun | | | 1 20 | Append | | | 2 21 | Aggregate | | | 3 22 | SeqScan on pg_tracing_test | Filter : ((a + 0) < 10) | | 4 23 | Aggregate | | | 3 24 | SeqScan on pg_tracing_test pg_tracing_test_1 | Filter : ((a + 0) < 10000) | | 4 25 | (8 rows) 26 | 27 | -- +------------------------------------------+ 28 | -- | A: Append | 29 | -- +-+-----------------+-+-----------------+--+ 30 | -- | B: Aggregate | | C: Aggregate | 31 | -- +-----------------+ +-----------------+ 32 | -- | D: IndexScan | | E: IndexScan | 33 | -- +-----------------+ +-----------------+ 34 | SELECT span_id AS span_a_id, 35 | get_epoch(span_start) as span_a_start, 36 | get_epoch(span_end) as span_a_end 37 | from pg_tracing_peek_spans 38 | where trace_id='00000000000000000000000000000001' AND span_operation='Append' \gset 39 | SELECT span_id AS span_b_id, 40 | get_epoch(span_start) as span_b_start, 41 | get_epoch(span_end) as span_b_end 42 | from pg_tracing_peek_spans 43 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 0 \gset 44 | SELECT span_id AS span_c_id, 45 | get_epoch(span_start) as span_c_start, 46 | get_epoch(span_end) as span_c_end 47 | from pg_tracing_peek_spans 48 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 1 \gset 49 | SELECT span_id AS span_d_id, 50 | get_epoch(span_start) as span_d_start, 51 | get_epoch(span_end) as span_d_end 52 | from pg_tracing_peek_spans 53 | where parent_id =:'span_b_id' \gset 54 | SELECT span_id AS span_e_id, 55 | get_epoch(span_start) as span_e_start, 56 | get_epoch(span_end) as span_e_end 57 | from pg_tracing_peek_spans 58 | where parent_id =:'span_c_id' \gset 59 | SELECT :span_a_end >= GREATEST(:span_c_end, :span_e_end) as root_ends_last, 60 | :span_c_start >= :span_b_start as second_index_start_after_first; 61 | root_ends_last | second_index_start_after_first 62 | ----------------+-------------------------------- 63 | t | t 64 | (1 row) 65 | 66 | -- Cleanup 67 | CALL clean_spans(); 68 | CALL reset_settings(); 69 | -------------------------------------------------------------------------------- /regress/18/expected/psql_extended.out: -------------------------------------------------------------------------------- 1 | -- Select with extended protocol 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT $1, $2 \parse stmt1 3 | \bind_named stmt1 1 2 \g 4 | ?column? | ?column? 5 | ----------+---------- 6 | 1 | 2 7 | (1 row) 8 | 9 | SELECT trace_id, span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | parameters | lvl 11 | ----------------------------------+--------------+----------------+------------+----- 12 | 00000000000000000000000000000001 | Select query | SELECT $1, $2 | | 0 13 | 00000000000000000000000000000001 | Select query | SELECT $1, $2 | {1,2} | 0 14 | 00000000000000000000000000000001 | Planner | Planner | | 1 15 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 1 16 | 00000000000000000000000000000001 | Result | Result | | 2 17 | (5 rows) 18 | 19 | WITH max_end AS (select max(span_end) from pg_tracing_peek_spans) 20 | SELECT span_end = max_end.max from pg_tracing_peek_spans, max_end 21 | where span_type='Select query' AND parameters IS NOT NULL; 22 | ?column? 23 | ---------- 24 | t 25 | (1 row) 26 | 27 | -- Cleanup 28 | CALL reset_settings(); 29 | CALL clean_spans(); 30 | -------------------------------------------------------------------------------- /regress/18/expected/psql_extended_18.out: -------------------------------------------------------------------------------- 1 | -- Select with extended protocol 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT $1, $2 \parse stmt1 3 | \bind_named stmt1 1 2 \g 4 | ?column? | ?column? 5 | ----------+---------- 6 | 1 | 2 7 | (1 row) 8 | 9 | SELECT trace_id, span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | parameters | lvl 11 | ----------------------------------+--------------+----------------+------------+----- 12 | 00000000000000000000000000000001 | Select query | SELECT $1, $2 | | 0 13 | 00000000000000000000000000000001 | Select query | SELECT $1, $2 | {1,2} | 0 14 | 00000000000000000000000000000001 | Planner | Planner | | 1 15 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 1 16 | 00000000000000000000000000000001 | Result | Result | | 2 17 | (5 rows) 18 | 19 | WITH max_end AS (select max(span_end) from pg_tracing_peek_spans) 20 | SELECT span_end = max_end.max from pg_tracing_peek_spans, max_end 21 | where span_type='Select query' AND parameters IS NOT NULL; 22 | ?column? 23 | ---------- 24 | t 25 | (1 row) 26 | 27 | -- Cleanup 28 | CALL reset_settings(); 29 | CALL clean_spans(); 30 | -------------------------------------------------------------------------------- /regress/18/expected/reset.out: -------------------------------------------------------------------------------- 1 | -- Check reset is working 2 | SELECT pg_tracing_reset(); 3 | pg_tracing_reset 4 | ------------------ 5 | 6 | (1 row) 7 | 8 | SELECT traces from pg_tracing_info; 9 | traces 10 | -------- 11 | 0 12 | (1 row) 13 | 14 | -------------------------------------------------------------------------------- /regress/18/expected/select_multistatement.out: -------------------------------------------------------------------------------- 1 | -- Check multi statement query 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Test multiple statements in a single query 4 | /*dddbs='postgres.db',traceparent='00-0000000000000000000000000000000c-000000000000000c-01'*/ select 1; select 2; 5 | ?column? 6 | ---------- 7 | 1 8 | (1 row) 9 | 10 | ?column? 11 | ---------- 12 | 2 13 | (1 row) 14 | 15 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='0000000000000000000000000000000c'; 16 | span_operation | parameters | lvl 17 | ----------------+------------+----- 18 | select $1 | {1} | 0 19 | Planner | | 1 20 | ExecutorRun | | 1 21 | Result | | 2 22 | (4 rows) 23 | 24 | CALL clean_spans(); 25 | -- Force a multi-query statement with \; 26 | SELECT 1\; SELECT 1, 2; 27 | ?column? 28 | ---------- 29 | 1 30 | (1 row) 31 | 32 | ?column? | ?column? 33 | ----------+---------- 34 | 1 | 2 35 | (1 row) 36 | 37 | SELECT span_type, span_operation, parameters, lvl from peek_ordered_spans; 38 | span_type | span_operation | parameters | lvl 39 | --------------+----------------+------------+----- 40 | Select query | SELECT $1 | {1} | 0 41 | Planner | Planner | | 1 42 | ExecutorRun | ExecutorRun | | 1 43 | Result | Result | | 2 44 | Select query | SELECT $1, $2 | {1,2} | 0 45 | Planner | Planner | | 1 46 | ExecutorRun | ExecutorRun | | 1 47 | Result | Result | | 2 48 | (8 rows) 49 | 50 | CALL clean_spans(); 51 | -- Cleanup 52 | CALL reset_settings(); 53 | -------------------------------------------------------------------------------- /regress/18/expected/setup.out: -------------------------------------------------------------------------------- 1 | -- Some helper functions 2 | CREATE OR REPLACE FUNCTION get_epoch(ts timestamptz) RETURNS float AS 3 | $BODY$ 4 | SELECT extract(epoch from ts); 5 | $BODY$ 6 | LANGUAGE sql; 7 | CREATE OR REPLACE PROCEDURE clean_spans() AS $$ 8 | BEGIN 9 | PERFORM count(*) from pg_tracing_consume_spans; 10 | END; 11 | $$ LANGUAGE plpgsql; 12 | CREATE OR REPLACE PROCEDURE reset_settings() 13 | LANGUAGE SQL 14 | AS $$ 15 | SET pg_tracing.filter_query_ids TO DEFAULT; 16 | SET pg_tracing.sample_rate TO DEFAULT; 17 | SET pg_tracing.caller_sample_rate TO DEFAULT; 18 | SET pg_tracing.track_utility TO DEFAULT; 19 | SET pg_tracing.max_parameter_size TO DEFAULT; 20 | SET parallel_setup_cost TO DEFAULT; 21 | SET parallel_tuple_cost TO DEFAULT; 22 | SET min_parallel_table_scan_size TO DEFAULT; 23 | SET max_parallel_workers_per_gather TO DEFAULT; 24 | $$; 25 | CREATE OR REPLACE PROCEDURE reset_pg_tracing_test_table() AS $$ 26 | BEGIN 27 | DROP TABLE IF EXISTS pg_tracing_test; 28 | CREATE TABLE pg_tracing_test (a int, b char(20), c int); 29 | COMMIT; 30 | CREATE INDEX pg_tracing_index_a ON pg_tracing_test (a); 31 | CREATE INDEX pg_tracing_index_b ON pg_tracing_test (b); 32 | CREATE INDEX pg_tracing_index_c ON pg_tracing_test (c); 33 | INSERT INTO pg_tracing_test SELECT *, *, * FROM generate_series(1, 10000); 34 | ANALYZE pg_tracing_test; 35 | END; 36 | $$ LANGUAGE plpgsql; 37 | -- Create test tables with data 38 | CALL reset_pg_tracing_test_table(); 39 | NOTICE: table "pg_tracing_test" does not exist, skipping 40 | -- Create test table to test modifications 41 | CREATE TABLE test_modifications (a int, b char(20)); 42 | CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; 43 | ALTER TABLE m ADD UNIQUE (k); 44 | -------------------------------------------------------------------------------- /regress/18/expected/subxact.out: -------------------------------------------------------------------------------- 1 | -- Enable full sampling 2 | SET pg_tracing.sample_rate = 1.0; 3 | -- Start a transaction with subxaction 4 | BEGIN; 5 | SAVEPOINT s1; 6 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 7 | SAVEPOINT s2; 8 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 9 | SAVEPOINT s3; 10 | SELECT 1; 11 | ?column? 12 | ---------- 13 | 1 14 | (1 row) 15 | 16 | COMMIT; 17 | -- Check that subxact_count is correctly reported 18 | select span_operation, parameters, subxact_count, lvl FROM peek_ordered_spans WHERE span_operation NOT LIKE 'SAVEPOINT%'; 19 | span_operation | parameters | subxact_count | lvl 20 | -----------------------------------------------------------------+-------------+---------------+----- 21 | TransactionBlock | | 0 | 0 22 | BEGIN; | | 0 | 1 23 | ProcessUtility | | 0 | 2 24 | ProcessUtility | | 0 | 2 25 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 0 | 1 26 | Planner | | 0 | 2 27 | ExecutorRun | | 0 | 2 28 | Insert on pg_tracing_test | | 1 | 3 29 | ProjectSet | | 1 | 4 30 | Result | | 1 | 5 31 | ProcessUtility | | 1 | 2 32 | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | {1,2,'aaa'} | 1 | 1 33 | Planner | | 1 | 2 34 | ExecutorRun | | 1 | 2 35 | Insert on pg_tracing_test | | 2 | 3 36 | ProjectSet | | 2 | 4 37 | Result | | 2 | 5 38 | ProcessUtility | | 2 | 2 39 | SELECT $1 | {1} | 2 | 1 40 | Planner | | 2 | 2 41 | ExecutorRun | | 2 | 2 42 | Result | | 2 | 3 43 | COMMIT; | | 2 | 1 44 | ProcessUtility | | 2 | 2 45 | TransactionCommit | | 2 | 1 46 | (25 rows) 47 | 48 | -- Cleaning 49 | CALL clean_spans(); 50 | CALL reset_settings(); 51 | -------------------------------------------------------------------------------- /regress/18/expected/wal.out: -------------------------------------------------------------------------------- 1 | -- Generate queries with wal write 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO pg_tracing_test VALUES(generate_series(1, 10), 'aaa'); 3 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ UPDATE pg_tracing_test SET b = 'bbb' WHERE a = 7; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ DELETE FROM pg_tracing_test WHERE a = 9; 5 | -- Check WAL is generated for the above statements 6 | SELECT trace_id, span_type, span_operation, 7 | wal_records > 0 as wal_records, 8 | wal_bytes > 0 as wal_bytes 9 | FROM peek_ordered_spans; 10 | trace_id | span_type | span_operation | wal_records | wal_bytes 11 | ----------------------------------+-------------------+-----------------------------------------------------------------+-------------+----------- 12 | 00000000000000000000000000000001 | Insert query | INSERT INTO pg_tracing_test VALUES(generate_series($1, $2), $3) | t | t 13 | 00000000000000000000000000000001 | Planner | Planner | f | f 14 | 00000000000000000000000000000001 | ExecutorRun | ExecutorRun | | 15 | 00000000000000000000000000000001 | Insert | Insert on pg_tracing_test | t | t 16 | 00000000000000000000000000000001 | ProjectSet | ProjectSet | f | f 17 | 00000000000000000000000000000001 | Result | Result | f | f 18 | 00000000000000000000000000000001 | TransactionCommit | TransactionCommit | | 19 | 00000000000000000000000000000002 | Update query | UPDATE pg_tracing_test SET b = $1 WHERE a = $2 | t | t 20 | 00000000000000000000000000000002 | Planner | Planner | f | f 21 | 00000000000000000000000000000002 | ExecutorRun | ExecutorRun | | 22 | 00000000000000000000000000000002 | Update | Update on pg_tracing_test | t | t 23 | 00000000000000000000000000000002 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | f | f 24 | 00000000000000000000000000000002 | TransactionCommit | TransactionCommit | | 25 | 00000000000000000000000000000003 | Delete query | DELETE FROM pg_tracing_test WHERE a = $1 | t | t 26 | 00000000000000000000000000000003 | Planner | Planner | f | f 27 | 00000000000000000000000000000003 | ExecutorRun | ExecutorRun | | 28 | 00000000000000000000000000000003 | Delete | Delete on pg_tracing_test | t | t 29 | 00000000000000000000000000000003 | IndexScan | IndexScan using pg_tracing_index_a on pg_tracing_test | t | t 30 | 00000000000000000000000000000003 | TransactionCommit | TransactionCommit | | 31 | (19 rows) 32 | 33 | CALL clean_spans(); 34 | -- Cleanup 35 | CALL clean_spans(); 36 | -------------------------------------------------------------------------------- /sql/cleanup.sql: -------------------------------------------------------------------------------- 1 | -- Drop test table and test functions 2 | DROP TABLE pg_tracing_test; 3 | DROP function test_function_project_set; 4 | DROP function test_function_result; 5 | DROP VIEW peek_ordered_spans; 6 | DROP VIEW peek_ordered_json_spans; 7 | DROP VIEW peek_ordered_spans_with_pos; 8 | DROP VIEW peek_json_spans_with_level; 9 | DROP VIEW peek_json_spans; 10 | DROP EXTENSION pg_tracing; 11 | -------------------------------------------------------------------------------- /sql/extended.sql: -------------------------------------------------------------------------------- 1 | -- Trace everything 2 | SET pg_tracing.sample_rate = 1.0; 3 | 4 | -- Simple query with extended protocol 5 | SELECT $1, $2 \bind 1 2 \g 6 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 7 | CALL clean_spans(); 8 | 9 | -- Trigger an error due to mismatching number of parameters 10 | BEGIN; select $1 \bind \g 11 | ROLLBACK; 12 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 13 | CALL clean_spans(); 14 | 15 | -- Execute queries with extended protocol within an explicit transaction 16 | BEGIN; 17 | SELECT $1 \bind 1 \g 18 | SELECT $1, $2 \bind 2 3 \g 19 | COMMIT; 20 | 21 | -- Send begin through extended protocol 22 | BEGIN \bind \g 23 | SELECT $1 \bind 1 \g 24 | COMMIT; 25 | 26 | -- Spans within the same transaction should have been generated with the same trace_id 27 | SELECT count(distinct(trace_id)) = 1 FROM pg_tracing_peek_spans; 28 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 29 | CALL clean_spans(); 30 | 31 | -- Mix extended protocol and simple protocol 32 | BEGIN; 33 | SELECT $1 \bind 1 \g 34 | SELECT 5, 6, 7; 35 | SELECT $1, $2 \bind 2 3 \g 36 | COMMIT; 37 | 38 | -- Spans within the same transaction should have been generated with the same trace_id 39 | SELECT count(distinct(trace_id)) = 1 FROM pg_tracing_peek_spans; 40 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 41 | CALL clean_spans(); 42 | 43 | -- gdesc calls a single parse command then execute a query. Make sure we handle this case 44 | SELECT 1 \gdesc 45 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 46 | CALL clean_spans(); 47 | 48 | -- Trace only sampled statements 49 | SET pg_tracing.sample_rate = 0.0; 50 | 51 | -- Test tracing the whole transaction with extended protocol 52 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ BEGIN; 53 | SELECT $1 \bind 1 \g 54 | SELECT $1, $2 \bind 1 2 \g 55 | SELECT $1, $2, $3 \bind 1 2 3 \g 56 | COMMIT; 57 | 58 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans WHERE trace_id='00000000000000000000000000000001'; 59 | 60 | -- Test tracing only individual statements with extended protocol 61 | BEGIN; 62 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000001-01'*/ SELECT $1 \bind 1 \g 63 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT $1, $2 \bind 1 2 \g 64 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000003-01'*/ SELECT $1, $2, $3 \bind 1 2 3 \g 65 | COMMIT; 66 | 67 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans WHERE trace_id='00000000000000000000000000000002'; 68 | 69 | -- Test tracing only subset of individual statements with extended protocol 70 | BEGIN; 71 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000001-01'*/ SELECT $1 \bind 1 \g 72 | SELECT $1, $2 \bind 1 2 \g 73 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ SELECT $1, $2, $3 \bind 1 2 3 \g 74 | SELECT $1 \bind 1 \g 75 | COMMIT; 76 | 77 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans WHERE trace_id='00000000000000000000000000000003'; 78 | 79 | -- Test tracing the whole transaction with extended protocol with BEGIN sent through extended protocol 80 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ BEGIN \bind \g 81 | SELECT $1 \bind 1 \g 82 | COMMIT; 83 | SELECT span_type, span_operation, parameters, lvl FROM peek_ordered_spans WHERE trace_id='00000000000000000000000000000004'; 84 | 85 | -- Cleanup 86 | CALL clean_spans(); 87 | CALL reset_settings(); 88 | -------------------------------------------------------------------------------- /sql/full_buffer.sql: -------------------------------------------------------------------------------- 1 | SET pg_tracing.sample_rate = 0.0; 2 | 3 | -- A simple procedure creating nested calls 4 | CREATE OR REPLACE PROCEDURE loop_select(iterations int) AS 5 | $BODY$ 6 | BEGIN 7 | FOR i IN 1..iterations LOOP 8 | PERFORM 'SELECT 1;'; 9 | END LOOP; 10 | END; 11 | $BODY$ 12 | LANGUAGE plpgsql; 13 | 14 | -- Clear stats and spans 15 | CALL clean_spans(); 16 | select * from pg_tracing_reset(); 17 | -- Check initial stats after reset 18 | select processed_traces, processed_spans, dropped_traces, dropped_spans from pg_tracing_info(); 19 | 20 | -- Saturate the span buffer. Each call should create at least 2 spans 21 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CALL loop_select(20); 22 | -- Check that we have dropped spans. The trace was still partially processed 23 | select processed_traces = 1, processed_spans = 50, dropped_traces = 0, dropped_spans > 0 from pg_tracing_info(); 24 | -- Try to create new traces while buffer is full 25 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1; 26 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-00'*/ SELECT 1; 27 | 28 | -- We should have only one additional dropped trace 29 | select processed_traces = 1, processed_spans = 50, dropped_traces = 1 from pg_tracing_info(); 30 | 31 | -- Clean current spans 32 | CALL clean_spans(); 33 | -------------------------------------------------------------------------------- /sql/guc.sql: -------------------------------------------------------------------------------- 1 | -- Test trace context propagation through GUCs 2 | SET pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00-00000000000000000000000000000004-0000000000000004-01'''; 3 | SELECT 1; 4 | 5 | -- Test trace context propagation through a local GUCs 6 | BEGIN; 7 | SET LOCAL pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00-00000000000000000000000000000005-0000000000000005-01'''; 8 | SELECT 1; 9 | COMMIT; 10 | 11 | -- Test multiple statements 12 | SET pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00-fffffffffffffffffffffffffffffff5-0000000000000005-01'''; 13 | SELECT 2; 14 | SELECT 3; 15 | 16 | -- Check results for GUC propagation with simple and multiple statements 17 | select trace_id, span_operation, parameters, lvl from peek_ordered_spans; 18 | CALL clean_spans(); 19 | 20 | -- Mix SQLCommenter and GUC propagation 21 | SET pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00-fffffffffffffffffffffffffffffff6-0000000000000006-01'''; 22 | SELECT 2; 23 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1, 1; 24 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000009-0000000000000009-00'*/ SELECT 1, 2, 3; 25 | SELECT 3; 26 | 27 | -- Check mix SQLCommenter and GUC propagation 28 | select trace_id, span_operation, parameters, lvl from peek_ordered_spans; 29 | CALL clean_spans(); 30 | 31 | -- Test statement after reset 32 | SET pg_tracing.trace_context TO default; 33 | SELECT 4; 34 | 35 | -- Test no traceparent field 36 | SET pg_tracing.trace_context='dddbs=''postgres.db'',taceparent=''00-fffffffffffffffffffffffffffffff5-0000000000000005-01'''; 37 | -- Test incorrect trace id 38 | SET pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00-ffffffffffffffffffffffffffffff5-0000000000000005-01'''; 39 | -- Test wrong format 40 | SET pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00f-ffffffffffffffffffffffffffffff5-0000000000000005-01'''; 41 | -- Test missing end quote 42 | SET pg_tracing.trace_context='dddbs=''postgres.db'',traceparent=''00-fffffffffffffffffffffffffffffff6-0000000000000006-01'; 43 | 44 | -- GUC errors and no GUC tracecontext should not generate spans 45 | select count(*) = 0 from peek_ordered_spans; 46 | 47 | -- Cleaning 48 | CALL clean_spans(); 49 | -------------------------------------------------------------------------------- /sql/insert.sql: -------------------------------------------------------------------------------- 1 | -- Only trace queries with sample flag 2 | SET pg_tracing.sample_rate = 0.0; 3 | SET pg_tracing.caller_sample_rate = 1.0; 4 | 5 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ CREATE TABLE IF NOT EXISTS pg_tracing_test_table_with_constraint (a int, b char(20), CONSTRAINT PK_tracing_test PRIMARY KEY (a)); 6 | SELECT span_type, span_operation, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 7 | 8 | -- Simple insertion 9 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 10 | SELECT span_type, span_operation from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 11 | 12 | -- Trigger constraint violation 13 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(1, 'aaa'); 14 | SELECT span_type, span_operation, sql_error_code, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 15 | 16 | -- Trigger an error while calling pg_tracing_peek_spans which resets tracing, nothing should be generated 17 | CALL clean_spans(); 18 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ INSERT INTO pg_tracing_test_table_with_constraint VALUES(length((select sql_error_code from public.pg_tracing_peek_spans)), 'aaa'); 19 | SELECT span_type, span_operation, sql_error_code from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 20 | 21 | -- Cleanup 22 | CALL clean_spans(); 23 | -------------------------------------------------------------------------------- /sql/json.sql: -------------------------------------------------------------------------------- 1 | -- Check generated json for simple and multi line select query 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1; 3 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 4 | 1, 5 | 2; 6 | -- Check json generated spans for simple and multi line query 7 | SELECT trace_id, name, kind, lvl FROM peek_ordered_json_spans; 8 | -- Test plan attributes with json export 9 | SELECT trace_id, name, plan_startup_cost, plan_total_cost, plan_rows, plan_width, parameters, lvl FROM peek_ordered_json_spans; 10 | CALL clean_spans(); 11 | 12 | -- Test error code with json export 13 | set statement_timeout=200; 14 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select * from pg_sleep(1); 15 | -- Check json generated spans with sql error code 16 | set statement_timeout TO DEFAULT; 17 | SELECT trace_id, name, sql_error_code, status_code, status_message, lvl FROM peek_ordered_json_spans; 18 | CALL clean_spans(); 19 | 20 | -- Test subxact_count and node counters with json export 21 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ BEGIN; 22 | SAVEPOINT s1; 23 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 24 | SAVEPOINT s2; 25 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 26 | ROLLBACK; 27 | SELECT trace_id, name, 28 | subxact_count, 29 | shared_blks_hit > 0 as has_shared_hit, 30 | wal_records > 0 as has_wal_records, 31 | wal_bytes > 0 as has_wal_bytes, 32 | startup > 0 as has_startup, 33 | lvl FROM peek_ordered_json_spans 34 | WHERE name LIKE 'INSERT%'; 35 | CALL clean_spans(); 36 | 37 | -- Test parameters and deparse_info 38 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * FROM pg_tracing_test WHERE a=1; 39 | SELECT trace_id, name, parameters, deparse_info, lvl FROM peek_ordered_json_spans; 40 | CALL clean_spans(); 41 | 42 | -- Test service name 43 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * FROM pg_tracing_test WHERE a=1; 44 | SELECT trace_id, name, service_name FROM peek_ordered_json_spans; 45 | 46 | -- Cleanup 47 | CALL reset_settings(); 48 | CALL clean_spans(); 49 | -------------------------------------------------------------------------------- /sql/nested_17.sql: -------------------------------------------------------------------------------- 1 | -- Test error thrown within a nested function 2 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 3 | eval_expr(format('date %L + interval %L', '-infinity', 'infinity')), 4 | eval_expr(format('date %L - interval %L', '-infinity', 'infinity')); 5 | select span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001'; 6 | 7 | -- Cleanup 8 | CALL clean_spans(); 9 | -------------------------------------------------------------------------------- /sql/parallel.sql: -------------------------------------------------------------------------------- 1 | begin; 2 | -- encourage use of parallel plans 3 | set local parallel_setup_cost=0; 4 | set local parallel_tuple_cost=0; 5 | set local min_parallel_table_scan_size=0; 6 | set local max_parallel_workers_per_gather=2; 7 | 8 | -- Trace parallel queries 9 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select 1 from pg_class limit 1; 10 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000002-01'*/ select 2 from pg_class limit 1; 11 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000003-00'*/ select 3 from pg_class limit 1; 12 | 13 | -- Try with parallel tracing disabled 14 | set local pg_tracing.trace_parallel_workers = false; 15 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000004-01'*/ select 4 from pg_class limit 1; 16 | commit; 17 | 18 | -- get tx block 19 | select span_id as tx_block_id from pg_tracing_peek_spans where span_type='TransactionBlock' and trace_id='00000000000000000000000000000001' and parent_id='0000000000000001' \gset 20 | -- get root top span id 21 | select span_id as root_span_id from pg_tracing_peek_spans where span_type='Select query' and trace_id='00000000000000000000000000000001' and parent_id=:'tx_block_id' limit 1 \gset 22 | -- Get executor top span id 23 | SELECT span_id as executor_span_id from pg_tracing_peek_spans where span_operation='ExecutorRun' and trace_id='00000000000000000000000000000001' and parent_id=:'root_span_id' \gset 24 | -- Get Limit span id 25 | SELECT span_id as limit_span_id from pg_tracing_peek_spans where span_operation='Limit' and trace_id='00000000000000000000000000000001' and parent_id=:'executor_span_id' \gset 26 | -- Get Gather span id 27 | SELECT span_id as gather_span_id from pg_tracing_peek_spans where span_operation='Gather' and trace_id='00000000000000000000000000000001' and parent_id=:'limit_span_id' \gset 28 | 29 | -- Check the select spans that are attached to the root top span 30 | SELECT trace_id, span_type, span_operation from pg_tracing_peek_spans where span_type='Select query' and parent_id=:'gather_span_id' order by span_operation; 31 | 32 | -- Check generated trace_id 33 | SELECT trace_id from pg_tracing_peek_spans group by trace_id; 34 | 35 | -- Check number of executor spans 36 | SELECT count(*) from pg_tracing_consume_spans where span_operation='ExecutorRun'; 37 | CALL clean_spans(); 38 | 39 | -- Test leaderless parallel query 40 | set parallel_setup_cost=0; 41 | set parallel_tuple_cost=0; 42 | set min_parallel_table_scan_size=0; 43 | set max_parallel_workers_per_gather=2; 44 | set parallel_leader_participation=false; 45 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select 1 from pg_class limit 1; 46 | 47 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001' ORDER BY lvl, span_operation; 48 | 49 | -- Cleanup 50 | CALL clean_spans(); 51 | CALL reset_settings(); 52 | -------------------------------------------------------------------------------- /sql/parameters.sql: -------------------------------------------------------------------------------- 1 | -- Check that parameters are not exported when disabled 2 | SET pg_tracing.max_parameter_size=0; 3 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select 1, 2, 3; 4 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 5 | 6 | -- Saturate the parameter buffer 7 | SET pg_tracing.max_parameter_size=1; 8 | /*traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ select 1, 2, 3; 9 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 10 | 11 | SET pg_tracing.max_parameter_size=2; 12 | /*traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ select 1, 2, 3; 13 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 14 | 15 | SET pg_tracing.max_parameter_size=3; 16 | /*traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ select 1, 2, 3; 17 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 18 | 19 | SET pg_tracing.max_parameter_size=4; 20 | /*traceparent='00-00000000000000000000000000000005-0000000000000005-01'*/ select 1, 2, 3; 21 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000005'; 22 | 23 | SET pg_tracing.max_parameter_size=5; 24 | /*traceparent='00-00000000000000000000000000000006-0000000000000006-01'*/ select 1, 2, 3; 25 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000006'; 26 | CALL clean_spans(); 27 | 28 | -- Test truncated string 29 | SET pg_tracing.max_parameter_size=2; 30 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select 'testtruncatedstring'; 31 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 32 | 33 | SET pg_tracing.max_parameter_size=19; 34 | /*traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ select 'testtruncatedstring'; 35 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 36 | 37 | SET pg_tracing.max_parameter_size=20; 38 | /*traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ select 'testtruncatedstring'; 39 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 40 | 41 | SET pg_tracing.max_parameter_size=21; 42 | /*traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ select 'testtruncatedstring'; 43 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 44 | 45 | -- Cleanup 46 | CALL clean_spans(); 47 | CALL reset_settings(); 48 | -------------------------------------------------------------------------------- /sql/parameters_extended.sql: -------------------------------------------------------------------------------- 1 | -- Saturate the parameter buffer with extended protocol 2 | SET pg_tracing.max_parameter_size=1; 3 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select $1, $2, $3 \bind 1 2 3 \g 4 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 5 | 6 | SET pg_tracing.max_parameter_size=2; 7 | /*traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ select $1, $2, $3 \bind 1 2 3 \g 8 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000002'; 9 | 10 | SET pg_tracing.max_parameter_size=3; 11 | /*traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ select $1, $2, $3 \bind 1 2 3 \g 12 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 13 | 14 | -- Cleanup 15 | CALL clean_spans(); 16 | CALL reset_settings(); 17 | -------------------------------------------------------------------------------- /sql/planstate.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Test with planstate_spans disabled 3 | SET pg_tracing.planstate_spans = false; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT s.relation_size + s.index_size as sum_size 5 | FROM (SELECT 6 | pg_relation_size(C.oid) as relation_size, 7 | pg_indexes_size(C.oid) as index_size 8 | FROM pg_class C) as s limit 1 \gset 9 | SELECT span_type, span_operation, deparse_info FROM peek_ordered_spans where trace_id='00000000000000000000000000000001'; 10 | 11 | -- Test with planstate_spans enabled 12 | SET pg_tracing.planstate_spans = true; 13 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT s.relation_size + s.index_size as sum_size 14 | FROM (SELECT 15 | pg_relation_size(C.oid) as relation_size, 16 | pg_indexes_size(C.oid) as index_size 17 | FROM pg_class C) as s limit 1 \gset 18 | SELECT span_type, span_operation, deparse_info FROM peek_ordered_spans where trace_id='00000000000000000000000000000002'; 19 | 20 | -- Check generated spans when deparse is disabled 21 | SET pg_tracing.deparse_plan=false; 22 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ SELECT * from pg_tracing_test where a=1; 23 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000003'; 24 | 25 | -- Check generated spans when deparse is enabled 26 | SET pg_tracing.deparse_plan=true; 27 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ SELECT * from pg_tracing_test where a=1; 28 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000004'; 29 | 30 | -- Clean created spans 31 | CALL clean_spans(); 32 | -------------------------------------------------------------------------------- /sql/planstate_bitmap.sql: -------------------------------------------------------------------------------- 1 | /*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT * from pg_tracing_test where a=1 OR b='2' OR c=3; 2 | 3 | -- PG < 18 4 | -- +-----------------------------------------------------------------------------------------+ 5 | -- | A: BitmapOr | 6 | -- ++-----------------------------+-------------------------------+--------------------------+ 7 | -- |B: Bitmap Index Scan (a=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (c=3)| 8 | -- +-----------------------------+-------------------------------+--------------------------+ 9 | -- PG >= 18 10 | -- +-----------------------------------------------------------------------------------------+ 11 | -- | A: BitmapOr | 12 | -- ++-----------------------------+-------------------------------+--------------------------+ 13 | -- |B: Bitmap Index Scan (c=1) |C: Bitmap Index Scan (b='2') |D: Bitmap Index Scan (a=1)| 14 | -- +-----------------------------+-------------------------------+--------------------------+ 15 | 16 | SELECT span_operation, deparse_info, parameters, pos_start, pos_end, lvl from peek_ordered_spans_with_pos where trace_id='00000000000000000000000000000001'; 17 | 18 | -- Clean created spans 19 | CALL clean_spans(); 20 | -------------------------------------------------------------------------------- /sql/planstate_hash.sql: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select count(*) from pg_tracing_test r join pg_tracing_test s using (a); 2 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 3 | 4 | -- +-----------------------------------------+ 5 | -- | A: HashJoin | 6 | -- ++-----------------+----------------------+ 7 | -- | B: SeqScan | 8 | -- +-----------------+ 9 | -- +--------------------+ 10 | -- | C: Hash | 11 | -- +--------------------+ 12 | -- | D: SeqScan | 13 | -- +--------------+ 14 | 15 | SELECT span_id AS span_a_id, 16 | get_epoch(span_start) as span_a_start, 17 | get_epoch(span_end) as span_a_end 18 | from pg_tracing_peek_spans 19 | where trace_id='00000000000000000000000000000001' AND span_operation='Hash Join' \gset 20 | SELECT span_id AS span_b_id, 21 | get_epoch(span_start) as span_b_start, 22 | get_epoch(span_end) as span_b_end 23 | from pg_tracing_peek_spans 24 | where parent_id =:'span_a_id' and span_operation='SeqScan on pg_tracing_test r' \gset 25 | SELECT span_id AS span_c_id, 26 | get_epoch(span_start) as span_c_start, 27 | get_epoch(span_end) as span_c_end 28 | from pg_tracing_peek_spans 29 | where parent_id =:'span_a_id' and span_operation='Hash' \gset 30 | SELECT span_id AS span_d_id, 31 | get_epoch(span_start) as span_d_start, 32 | get_epoch(span_end) as span_d_end 33 | from pg_tracing_peek_spans 34 | where parent_id =:'span_c_id' and span_operation='SeqScan on pg_tracing_test s' \gset 35 | 36 | SELECT :span_a_end >= :span_c_end as root_ends_last, 37 | :span_c_start >= :span_b_start as hash_start_after_seqscan, 38 | :span_c_start = :span_d_start as hash_start_same_as_child_seqscan, 39 | :span_d_end <= :span_c_end as nested_seq_scan_end_before_parent; 40 | 41 | -- Clean created spans 42 | CALL clean_spans(); 43 | -------------------------------------------------------------------------------- /sql/planstate_projectset.sql: -------------------------------------------------------------------------------- 1 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ select information_schema._pg_expandarray('{0,1,2}'::int[]); 2 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 3 | 4 | -- +---------------------------------------------+ 5 | -- | A: ProjectSet | 6 | -- ++-----------------+--+----------------+------+ 7 | -- | B: Result | | C: TopSpan | 8 | -- +-----------------+ +-+------------+-+ 9 | -- | D: Planner | 10 | -- +------------+ 11 | 12 | SELECT span_id AS span_a_id, 13 | get_epoch(span_start) as span_a_start, 14 | get_epoch(span_end) as span_a_end 15 | from pg_tracing_peek_spans 16 | where trace_id='00000000000000000000000000000001' AND span_operation='ProjectSet' \gset 17 | SELECT span_id AS span_b_id, 18 | get_epoch(span_start) as span_b_start, 19 | get_epoch(span_end) as span_b_end 20 | from pg_tracing_peek_spans 21 | where parent_id =:'span_a_id' and span_operation='Result' \gset 22 | SELECT span_id AS span_c_id, 23 | get_epoch(span_start) as span_c_start, 24 | get_epoch(span_end) as span_c_end 25 | from pg_tracing_peek_spans 26 | where parent_id =:'span_a_id' and span_type='Select query' \gset 27 | SELECT span_id AS span_d_id, 28 | get_epoch(span_start) as span_d_start, 29 | get_epoch(span_end) as span_d_end 30 | from pg_tracing_peek_spans 31 | where parent_id =:'span_c_id' and span_type='Planner' \gset 32 | 33 | SELECT :span_a_end >= :span_c_end as project_set_ends_after_nested_top_span; 34 | 35 | -- Clean created spans 36 | CALL clean_spans(); 37 | -------------------------------------------------------------------------------- /sql/planstate_union.sql: -------------------------------------------------------------------------------- 1 | -- Test simple append 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ 3 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10 4 | UNION ALL 5 | SELECT count(*) FROM pg_tracing_test WHERE a + 0 < 10000; 6 | SELECT span_operation, deparse_info, parameters, lvl from peek_ordered_spans where trace_id='00000000000000000000000000000001'; 7 | 8 | -- +------------------------------------------+ 9 | -- | A: Append | 10 | -- +-+-----------------+-+-----------------+--+ 11 | -- | B: Aggregate | | C: Aggregate | 12 | -- +-----------------+ +-----------------+ 13 | -- | D: IndexScan | | E: IndexScan | 14 | -- +-----------------+ +-----------------+ 15 | 16 | SELECT span_id AS span_a_id, 17 | get_epoch(span_start) as span_a_start, 18 | get_epoch(span_end) as span_a_end 19 | from pg_tracing_peek_spans 20 | where trace_id='00000000000000000000000000000001' AND span_operation='Append' \gset 21 | SELECT span_id AS span_b_id, 22 | get_epoch(span_start) as span_b_start, 23 | get_epoch(span_end) as span_b_end 24 | from pg_tracing_peek_spans 25 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 0 \gset 26 | SELECT span_id AS span_c_id, 27 | get_epoch(span_start) as span_c_start, 28 | get_epoch(span_end) as span_c_end 29 | from pg_tracing_peek_spans 30 | where parent_id =:'span_a_id' and span_operation='Aggregate' LIMIT 1 OFFSET 1 \gset 31 | 32 | SELECT span_id AS span_d_id, 33 | get_epoch(span_start) as span_d_start, 34 | get_epoch(span_end) as span_d_end 35 | from pg_tracing_peek_spans 36 | where parent_id =:'span_b_id' \gset 37 | SELECT span_id AS span_e_id, 38 | get_epoch(span_start) as span_e_start, 39 | get_epoch(span_end) as span_e_end 40 | from pg_tracing_peek_spans 41 | where parent_id =:'span_c_id' \gset 42 | 43 | SELECT :span_a_end >= GREATEST(:span_c_end, :span_e_end) as root_ends_last, 44 | :span_c_start >= :span_b_start as second_index_start_after_first; 45 | 46 | -- Cleanup 47 | CALL clean_spans(); 48 | CALL reset_settings(); 49 | -------------------------------------------------------------------------------- /sql/psql_extended_18.sql: -------------------------------------------------------------------------------- 1 | -- Select with extended protocol 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT $1, $2 \parse stmt1 3 | \bind_named stmt1 1 2 \g 4 | 5 | SELECT trace_id, span_type, span_operation, parameters, lvl FROM peek_ordered_spans; 6 | 7 | WITH max_end AS (select max(span_end) from pg_tracing_peek_spans) 8 | SELECT span_end = max_end.max from pg_tracing_peek_spans, max_end 9 | where span_type='Select query' AND parameters IS NOT NULL; 10 | 11 | -- Cleanup 12 | CALL reset_settings(); 13 | CALL clean_spans(); 14 | -------------------------------------------------------------------------------- /sql/sample.sql: -------------------------------------------------------------------------------- 1 | -- Trace nothing 2 | SET pg_tracing.sample_rate = 0.0; 3 | SET pg_tracing.caller_sample_rate = 0.0; 4 | 5 | -- Query with sampling flag 6 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1; 7 | select count(distinct(trace_id))=0 from pg_tracing_consume_spans; 8 | 9 | -- Query without trace context 10 | SELECT 1; 11 | select count(distinct(trace_id))=0 from pg_tracing_consume_spans; 12 | 13 | -- Enable full sampling 14 | SET pg_tracing.sample_rate = 1.0; 15 | 16 | -- Generate queries with sampling flag on, off and no trace context 17 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1; 18 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-00'*/ SELECT 2; 19 | SELECT 3; 20 | SELECT 4; 21 | 22 | -- Check number of generated spans 23 | select count(distinct(trace_id)) from pg_tracing_peek_spans; 24 | -- Check span order for full sampling 25 | select span_operation, parameters, lvl from peek_ordered_spans; 26 | -- Top spans should reuse generated ids and have trace_id = parent_id 27 | select span_operation, parameters from peek_ordered_spans where right(trace_id, 16) = parent_id; 28 | CALL clean_spans(); 29 | 30 | -- Only trace query with sampled flag 31 | SET pg_tracing.sample_rate = 0.0; 32 | SET pg_tracing.caller_sample_rate = 1.0; 33 | 34 | -- Clean set call spans 35 | CALL clean_spans(); 36 | 37 | -- Generate queries with sampling flag on, off, no trace context and SQLComment at the end 38 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000004-0000000000000004-01'*/ SELECT 1; 39 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000005-0000000000000005-00'*/ SELECT 2; 40 | SELECT 3 /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000006-0000000000000006-01'*/; 41 | /*dddbs='postgres.db',traceparent='00-fffffffffffffffff000000000000007-0000000000000007-01'*/ SELECT 4; 42 | SELECT 1; 43 | 44 | -- Check number of generated spans 45 | select distinct(trace_id) from pg_tracing_peek_spans order by trace_id; 46 | -- Check span order for sampled flag only 47 | select span_operation, parameters, lvl from peek_ordered_spans; 48 | -- Cleaning 49 | CALL clean_spans(); 50 | 51 | -- Test query filtering 52 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1; 53 | -- Grab query_id for select 1 54 | select distinct(query_id) as query_id_select_1 from pg_tracing_consume_spans \gset 55 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1, 2; 56 | -- Grab query_id for select 1, 2 57 | select distinct(query_id) as query_id_select_1_2 from pg_tracing_consume_spans \gset 58 | 59 | SET pg_tracing.filter_query_ids=:'query_id_select_1',:'query_id_select_1_2'; 60 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1; 61 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ SELECT 1, 2; 62 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ SELECT 1, 2, 3; 63 | 64 | select count(distinct(query_id)) from pg_tracing_consume_spans; 65 | 66 | -- Cleaning 67 | CALL reset_settings(); 68 | -------------------------------------------------------------------------------- /sql/select_multistatement.sql: -------------------------------------------------------------------------------- 1 | -- Check multi statement query 2 | SET pg_tracing.sample_rate = 1.0; 3 | 4 | -- Test multiple statements in a single query 5 | /*dddbs='postgres.db',traceparent='00-0000000000000000000000000000000c-000000000000000c-01'*/ select 1; select 2; 6 | SELECT span_operation, parameters, lvl from peek_ordered_spans where trace_id='0000000000000000000000000000000c'; 7 | CALL clean_spans(); 8 | 9 | -- Force a multi-query statement with \; 10 | SELECT 1\; SELECT 1, 2; 11 | SELECT span_type, span_operation, parameters, lvl from peek_ordered_spans; 12 | CALL clean_spans(); 13 | 14 | -- Cleanup 15 | CALL reset_settings(); 16 | -------------------------------------------------------------------------------- /sql/setup.sql: -------------------------------------------------------------------------------- 1 | -- Some helper functions 2 | CREATE OR REPLACE FUNCTION get_epoch(ts timestamptz) RETURNS float AS 3 | $BODY$ 4 | SELECT extract(epoch from ts); 5 | $BODY$ 6 | LANGUAGE sql; 7 | 8 | CREATE OR REPLACE PROCEDURE clean_spans() AS $$ 9 | BEGIN 10 | PERFORM count(*) from pg_tracing_consume_spans; 11 | END; 12 | $$ LANGUAGE plpgsql; 13 | 14 | CREATE OR REPLACE PROCEDURE reset_settings() 15 | LANGUAGE SQL 16 | AS $$ 17 | SET pg_tracing.filter_query_ids TO DEFAULT; 18 | SET pg_tracing.sample_rate TO DEFAULT; 19 | SET pg_tracing.caller_sample_rate TO DEFAULT; 20 | SET pg_tracing.track_utility TO DEFAULT; 21 | SET pg_tracing.max_parameter_size TO DEFAULT; 22 | SET parallel_setup_cost TO DEFAULT; 23 | SET parallel_tuple_cost TO DEFAULT; 24 | SET min_parallel_table_scan_size TO DEFAULT; 25 | SET max_parallel_workers_per_gather TO DEFAULT; 26 | $$; 27 | 28 | CREATE OR REPLACE PROCEDURE reset_pg_tracing_test_table() AS $$ 29 | BEGIN 30 | DROP TABLE IF EXISTS pg_tracing_test; 31 | CREATE TABLE pg_tracing_test (a int, b char(20), c int); 32 | COMMIT; 33 | CREATE INDEX pg_tracing_index_a ON pg_tracing_test (a); 34 | CREATE INDEX pg_tracing_index_b ON pg_tracing_test (b); 35 | CREATE INDEX pg_tracing_index_c ON pg_tracing_test (c); 36 | INSERT INTO pg_tracing_test SELECT *, *, * FROM generate_series(1, 10000); 37 | ANALYZE pg_tracing_test; 38 | END; 39 | $$ LANGUAGE plpgsql; 40 | 41 | -- Create test tables with data 42 | CALL reset_pg_tracing_test_table(); 43 | 44 | -- Create test table to test modifications 45 | CREATE TABLE test_modifications (a int, b char(20)); 46 | 47 | CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; 48 | ALTER TABLE m ADD UNIQUE (k); 49 | -------------------------------------------------------------------------------- /sql/subxact.sql: -------------------------------------------------------------------------------- 1 | -- Enable full sampling 2 | SET pg_tracing.sample_rate = 1.0; 3 | 4 | -- Start a transaction with subxaction 5 | BEGIN; 6 | SAVEPOINT s1; 7 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 8 | SAVEPOINT s2; 9 | INSERT INTO pg_tracing_test VALUES(generate_series(1, 2), 'aaa'); 10 | SAVEPOINT s3; 11 | SELECT 1; 12 | COMMIT; 13 | 14 | -- Check that subxact_count is correctly reported 15 | select span_operation, parameters, subxact_count, lvl FROM peek_ordered_spans WHERE span_operation NOT LIKE 'SAVEPOINT%'; 16 | 17 | -- Cleaning 18 | CALL clean_spans(); 19 | CALL reset_settings(); 20 | -------------------------------------------------------------------------------- /sql/transaction.sql: -------------------------------------------------------------------------------- 1 | -- Only trace queries with sample flag 2 | SET pg_tracing.sample_rate = 0.0; 3 | SET pg_tracing.caller_sample_rate = 1.0; 4 | 5 | -- Set tracecontext at the start of the transaction 6 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ BEGIN; SELECT 1; COMMIT; 7 | 8 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001' AND span_type!='Commit'; 9 | CALL clean_spans(); 10 | 11 | -- Test with override of the trace context in the middle of a transaction 12 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ BEGIN; 13 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ SELECT 1; COMMIT; 14 | 15 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000002' AND span_type!='Commit'; 16 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000003'; 17 | CALL clean_spans(); 18 | 19 | -- Test with a 0 traceid in the middle of a transaction 20 | BEGIN; 21 | SELECT 1; 22 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000000-0000000000000001-01'*/ SELECT 2; 23 | SELECT 3; 24 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000000-0000000000000001-01'*/ SELECT 4; 25 | END; 26 | 27 | -- Only one trace id should have been generated 28 | SELECT count(distinct(trace_id)) = 1 FROM pg_tracing_peek_spans; 29 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans; 30 | CALL clean_spans(); 31 | 32 | -- Test with a 0 parent_id in the middle of a transaction 33 | BEGIN; 34 | SELECT 1; 35 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000000-0000000000000000-01'*/ SELECT 2; 36 | SELECT 3; 37 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000000-0000000000000000-01'*/ SELECT 4; 38 | END; 39 | 40 | -- Only one trace id and parent id should have been generated 41 | SELECT count(distinct(trace_id)) = 1, count(distinct(parent_id)) = 1 FROM peek_ordered_spans WHERE lvl=1; 42 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans; 43 | CALL clean_spans(); 44 | 45 | -- Test modification within transaction block 46 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000000-01'*/ BEGIN; 47 | INSERT INTO test_modifications(a, b) VALUES (1, 1); 48 | END; 49 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans; 50 | 51 | SELECT span_id AS span_tx_block, 52 | get_epoch(span_start) AS span_tx_block_start, 53 | get_epoch(span_end) AS span_tx_block_end 54 | FROM pg_tracing_peek_spans 55 | WHERE trace_id='00000000000000000000000000000001' AND span_operation='TransactionBlock' \gset 56 | SELECT span_id AS span_commit, 57 | get_epoch(span_start) as span_commit_start, 58 | get_epoch(span_end) as span_commit_end 59 | FROM pg_tracing_peek_spans 60 | WHERE trace_id='00000000000000000000000000000001' 61 | AND parent_id = :'span_tx_block' 62 | AND span_operation='TransactionCommit' \gset 63 | -- Transaction block should end after TransactionCommit span 64 | SELECT :span_tx_block_end >= :span_commit_end; 65 | CALL clean_spans(); 66 | 67 | -- Test with transaction block created with sample rate 68 | SET pg_tracing.sample_rate = 1.0; 69 | BEGIN; 70 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ SELECT 1; 71 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000002-01'*/ SELECT 2; 72 | COMMIT; 73 | SELECT span_type, span_operation, lvl FROM peek_ordered_spans where trace_id='00000000000000000000000000000001'; 74 | 75 | CALL reset_settings(); 76 | CALL clean_spans(); 77 | -------------------------------------------------------------------------------- /sql/wal.sql: -------------------------------------------------------------------------------- 1 | -- Generate queries with wal write 2 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/ INSERT INTO pg_tracing_test VALUES(generate_series(1, 10), 'aaa'); 3 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000002-0000000000000002-01'*/ UPDATE pg_tracing_test SET b = 'bbb' WHERE a = 7; 4 | /*dddbs='postgres.db',traceparent='00-00000000000000000000000000000003-0000000000000003-01'*/ DELETE FROM pg_tracing_test WHERE a = 9; 5 | 6 | -- Check WAL is generated for the above statements 7 | SELECT trace_id, span_type, span_operation, 8 | wal_records > 0 as wal_records, 9 | wal_bytes > 0 as wal_bytes 10 | FROM peek_ordered_spans; 11 | CALL clean_spans(); 12 | 13 | -- Cleanup 14 | CALL clean_spans(); 15 | -------------------------------------------------------------------------------- /src/pg_prng.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * Pseudo-Random Number Generator 4 | * 5 | * We use Blackman and Vigna's xoroshiro128** 1.0 algorithm 6 | * to have a small, fast PRNG suitable for generating reasonably 7 | * good-quality 64-bit data. This should not be considered 8 | * cryptographically strong, however. 9 | * 10 | * About these generators: https://prng.di.unimi.it/ 11 | * See also https://en.wikipedia.org/wiki/List_of_random_number_generators 12 | * 13 | * Copyright (c) 2021-2025, PostgreSQL Global Development Group 14 | * 15 | * src/common/pg_prng.c 16 | * 17 | *------------------------------------------------------------------------- 18 | */ 19 | 20 | #include "c.h" 21 | 22 | #include 23 | 24 | #include "pg_prng.h" 25 | 26 | 27 | /* process-wide state vector */ 28 | pg_prng_state pg_global_prng_state; 29 | 30 | 31 | /* 32 | * 64-bit rotate left 33 | */ 34 | static inline uint64 35 | rotl(uint64 x, int bits) 36 | { 37 | return (x << bits) | (x >> (64 - bits)); 38 | } 39 | 40 | /* 41 | * The basic xoroshiro128** algorithm. 42 | * Generates and returns a 64-bit uniformly distributed number, 43 | * updating the state vector for next time. 44 | * 45 | * Note: the state vector must not be all-zeroes, as that is a fixed point. 46 | */ 47 | static uint64 48 | xoroshiro128ss(pg_prng_state *state) 49 | { 50 | uint64 s0 = state->s0, 51 | sx = state->s1 ^ s0, 52 | val = rotl(s0 * 5, 7) * 9; 53 | 54 | /* update state */ 55 | state->s0 = rotl(s0, 24) ^ sx ^ (sx << 16); 56 | state->s1 = rotl(sx, 37); 57 | 58 | return val; 59 | } 60 | 61 | /* 62 | * We use this generator just to fill the xoroshiro128** state vector 63 | * from a 64-bit seed. 64 | */ 65 | static uint64 66 | splitmix64(uint64 *state) 67 | { 68 | /* state update */ 69 | uint64 val = (*state += UINT64CONST(0x9E3779B97f4A7C15)); 70 | 71 | /* value extraction */ 72 | val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9); 73 | val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB); 74 | 75 | return val ^ (val >> 31); 76 | } 77 | 78 | /* 79 | * Initialize the PRNG state from a 64-bit integer, 80 | * taking care that we don't produce all-zeroes. 81 | */ 82 | void 83 | pg_prng_seed(pg_prng_state *state, uint64 seed) 84 | { 85 | state->s0 = splitmix64(&seed); 86 | state->s1 = splitmix64(&seed); 87 | /* Let's just make sure we didn't get all-zeroes */ 88 | (void) pg_prng_seed_check(state); 89 | } 90 | 91 | /* 92 | * Validate a PRNG seed value. 93 | */ 94 | bool 95 | pg_prng_seed_check(pg_prng_state *state) 96 | { 97 | /* 98 | * If the seeding mechanism chanced to produce all-zeroes, insert 99 | * something nonzero. Anything would do; use Knuth's LCG parameters. 100 | */ 101 | if (unlikely(state->s0 == 0 && state->s1 == 0)) 102 | { 103 | state->s0 = UINT64CONST(0x5851F42D4C957F2D); 104 | state->s1 = UINT64CONST(0x14057B7EF767814F); 105 | } 106 | 107 | /* As a convenience for the pg_prng_strong_seed macro, return true */ 108 | return true; 109 | } 110 | 111 | /* 112 | * Select a random uint64 uniformly from the range [0, PG_UINT64_MAX]. 113 | */ 114 | uint64 115 | pg_prng_uint64(pg_prng_state *state) 116 | { 117 | return xoroshiro128ss(state); 118 | } 119 | 120 | /* 121 | * Select a random double uniformly from the range [0.0, 1.0). 122 | * 123 | * Note: if you want a result in the range (0.0, 1.0], the standard way 124 | * to get that is "1.0 - pg_prng_double(state)". 125 | */ 126 | double 127 | pg_prng_double(pg_prng_state *state) 128 | { 129 | uint64 v = xoroshiro128ss(state); 130 | 131 | /* 132 | * As above, assume there's 52 mantissa bits in a double. This result 133 | * could round to 1.0 if double's precision is less than that; but we 134 | * assume IEEE float arithmetic elsewhere in Postgres, so this seems OK. 135 | */ 136 | return ldexp((double) (v >> (64 - 52)), -52); 137 | } 138 | -------------------------------------------------------------------------------- /src/pg_prng.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * Pseudo-Random Number Generator 4 | * 5 | * Copyright (c) 2021-2025, PostgreSQL Global Development Group 6 | * 7 | * src/include/common/pg_prng.h 8 | * 9 | *------------------------------------------------------------------------- 10 | */ 11 | #ifndef PG_PRNG_H 12 | #define PG_PRNG_H 13 | 14 | /* 15 | * State vector for PRNG generation. Callers should treat this as an 16 | * opaque typedef, but we expose its definition to allow it to be 17 | * embedded in other structs. 18 | */ 19 | typedef struct pg_prng_state 20 | { 21 | uint64 s0, 22 | s1; 23 | } pg_prng_state; 24 | 25 | /* 26 | * Callers not needing local PRNG series may use this global state vector, 27 | * after initializing it with one of the pg_prng_...seed functions. 28 | */ 29 | extern PGDLLIMPORT pg_prng_state pg_global_prng_state; 30 | 31 | extern void pg_prng_seed(pg_prng_state *state, uint64 seed); 32 | extern bool pg_prng_seed_check(pg_prng_state *state); 33 | 34 | extern uint64 pg_prng_uint64(pg_prng_state *state); 35 | extern double pg_prng_double(pg_prng_state *state); 36 | 37 | #endif /* PG_PRNG_H */ 38 | -------------------------------------------------------------------------------- /src/pg_tracing_operation_hash.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_tracing_operation_hash.c 4 | * pg_tracing plan explain functions. 5 | * 6 | * IDENTIFICATION 7 | * src/pg_tracing_operation_hash.c 8 | * 9 | *------------------------------------------------------------------------- 10 | */ 11 | #include "postgres.h" 12 | #include "utils/hsearch.h" 13 | #include "storage/shmem.h" 14 | #include "pg_tracing.h" 15 | 16 | static HTAB *operation_name_hash = NULL; 17 | 18 | typedef struct operationKey 19 | { 20 | uint64 query_id; 21 | SpanType span_type; 22 | } operationKey; 23 | 24 | typedef struct operationEntry 25 | { 26 | operationKey key; /* hash key of entry - MUST BE FIRST */ 27 | Size query_offset; /* query text offset in shared str */ 28 | } operationEntry; 29 | 30 | /* 31 | * Initialize operation hash in shared memory 32 | */ 33 | void 34 | init_operation_hash(void) 35 | { 36 | HASHCTL info; 37 | 38 | info.keysize = sizeof(operationKey); 39 | info.entrysize = sizeof(operationEntry); 40 | operation_name_hash = ShmemInitHash("pg_tracing hash", 41 | 100, 50000, &info, 42 | HASH_ELEM | HASH_BLOBS); 43 | } 44 | 45 | /* 46 | * Reset content of the operation hash table 47 | */ 48 | void 49 | reset_operation_hash(void) 50 | { 51 | HASH_SEQ_STATUS status; 52 | operationEntry *hentry; 53 | 54 | Assert(operation_name_hash != NULL); 55 | 56 | /* Currently we just flush all entries; hard to be smarter ... */ 57 | hash_seq_init(&status, operation_name_hash); 58 | 59 | while ((hentry = (operationEntry *) hash_seq_search(&status)) != NULL) 60 | { 61 | if (hash_search(operation_name_hash, 62 | &hentry->key, 63 | HASH_REMOVE, NULL) == NULL) 64 | elog(ERROR, "hash table corrupted"); 65 | } 66 | } 67 | 68 | Size 69 | lookup_operation_name(const Span * span, const char *txt) 70 | { 71 | bool found; 72 | Size offset; 73 | operationKey key; 74 | operationEntry *entry; 75 | 76 | key.query_id = span->query_id; 77 | key.span_type = span->type; 78 | if (span->query_id == 0) 79 | { 80 | /* 81 | * We may have a 0 query_id in some cases where PostgreSQL doesn't 82 | * correctly propagate queryId. In those cases, we can't use the hash 83 | * and need to fallback to write text without hash tracking 84 | */ 85 | offset = pg_tracing_shared_state->extent; 86 | append_str_to_shared_str(txt, strlen(txt) + 1); 87 | return offset; 88 | } 89 | 90 | entry = (operationEntry *) hash_search(operation_name_hash, &key, HASH_ENTER, &found); 91 | 92 | if (found) 93 | return entry->query_offset; 94 | 95 | offset = pg_tracing_shared_state->extent; 96 | append_str_to_shared_str(txt, strlen(txt) + 1); 97 | /* Update hash's entry */ 98 | entry->query_offset = offset; 99 | 100 | return offset; 101 | } 102 | -------------------------------------------------------------------------------- /src/pg_tracing_parallel.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_tracing_parallel.c 4 | * Store, retrieve and remove trace context for parallel workers. 5 | * 6 | * IDENTIFICATION 7 | * src/pg_tracing_parallel.c 8 | * 9 | *------------------------------------------------------------------------- 10 | */ 11 | #include "postgres.h" 12 | 13 | #include "storage/shmem.h" 14 | #include "storage/spin.h" 15 | #include "pg_tracing.h" 16 | 17 | /* Shared buffer storing trace context for parallel workers. */ 18 | static pgTracingParallelWorkers * pg_tracing_parallel = NULL; 19 | 20 | /* Index of the parallel worker context shared buffer if any */ 21 | static int parallel_context_index = -1; 22 | 23 | /* 24 | * Allocate share memory for propagation of trace context to parallel workers 25 | */ 26 | void 27 | pg_tracing_shmem_parallel_startup(void) 28 | { 29 | bool found_parallel; 30 | 31 | /* We won't have more than max_parallel_workers workers */ 32 | pg_tracing_parallel = ShmemInitStruct("PgTracing Parallel Workers Context", 33 | sizeof(pgTracingParallelWorkers) + max_parallel_workers * sizeof(pgTracingParallelContext), 34 | &found_parallel); 35 | if (!found_parallel) 36 | { 37 | SpinLockInit(&pg_tracing_parallel->mutex); 38 | for (int i = 0; i < max_parallel_workers; i++) 39 | pg_tracing_parallel->trace_contexts[i].leader_backend_id = INVALID_PROC_NUMBER; 40 | } 41 | } 42 | 43 | /* 44 | * Push trace context to the shared parallel worker buffer 45 | */ 46 | void 47 | add_parallel_context(const Traceparent * traceparent, uint64 parent_id) 48 | { 49 | pgTracingParallelContext *ctx = NULL; 50 | 51 | Assert(parallel_context_index == -1); 52 | SpinLockAcquire(&pg_tracing_parallel->mutex); 53 | for (int i = 0; i < max_parallel_workers; i++) 54 | { 55 | ctx = pg_tracing_parallel->trace_contexts + i; 56 | Assert(ctx->leader_backend_id != MyProcNumber); 57 | if (ctx->leader_backend_id != INVALID_PROC_NUMBER) 58 | continue; 59 | /* Slot is available */ 60 | parallel_context_index = i; 61 | ctx->leader_backend_id = MyProcNumber; 62 | /* We can do the rest outside the lock */ 63 | break; 64 | } 65 | SpinLockRelease(&pg_tracing_parallel->mutex); 66 | 67 | if (parallel_context_index > -1 && ctx != NULL) 68 | { 69 | ctx->traceparent = *traceparent; 70 | /* We don't need to propagate root span index to parallel workers */ 71 | ctx->traceparent.parent_id = parent_id; 72 | } 73 | } 74 | 75 | /* 76 | * Remove parallel context for the current leader from the shared memory. 77 | */ 78 | void 79 | remove_parallel_context(void) 80 | { 81 | if (parallel_context_index < 0) 82 | /* No tracing of parallel workers */ 83 | return; 84 | 85 | SpinLockAcquire(&pg_tracing_parallel->mutex); 86 | pg_tracing_parallel->trace_contexts[parallel_context_index].leader_backend_id = INVALID_PROC_NUMBER; 87 | SpinLockRelease(&pg_tracing_parallel->mutex); 88 | parallel_context_index = -1; 89 | } 90 | 91 | /* 92 | * If we're inside a parallel worker, check if the trace context is stored in shared memory. 93 | * If a trace context exists, it means that the query is sampled and worker tracing is enabled. 94 | */ 95 | void 96 | fetch_parallel_context(Traceparent * traceparent) 97 | { 98 | SpinLockAcquire(&pg_tracing_parallel->mutex); 99 | for (int i = 0; i < max_parallel_workers; i++) 100 | { 101 | if (pg_tracing_parallel->trace_contexts[i].leader_backend_id != ParallelLeaderProcNumber) 102 | continue; 103 | /* Found a matching a trace context, fetch it */ 104 | *traceparent = pg_tracing_parallel->trace_contexts[i].traceparent; 105 | } 106 | SpinLockRelease(&pg_tracing_parallel->mutex); 107 | } 108 | -------------------------------------------------------------------------------- /src/pg_tracing_rng.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_tracing_rng.c 4 | * pg_tracing rng functions. 5 | * 6 | * IDENTIFICATION 7 | * src/pg_tracing_rng.c 8 | * 9 | *------------------------------------------------------------------------- 10 | */ 11 | #include "postgres.h" 12 | 13 | #include "pg_prng.h" 14 | #include "pg_tracing.h" 15 | 16 | static void 17 | check_rng_seed(void) 18 | { 19 | #if (PG_VERSION_NUM < 150000) 20 | /* pid used for seeding */ 21 | static uint64 seeded_pid = 0; 22 | 23 | if (MyProcPid != seeded_pid) 24 | { 25 | /* We are in a child process, reseed prng with our new pid */ 26 | uint64 rseed; 27 | 28 | seeded_pid = MyProcPid; 29 | rseed = ((uint64) MyProcPid) ^ 30 | ((uint64) MyStartTimestamp << 12) ^ 31 | ((uint64) MyStartTimestamp >> 20); 32 | pg_prng_seed(&pg_global_prng_state, rseed); 33 | } 34 | #endif 35 | } 36 | 37 | double 38 | generate_rnd_double(void) 39 | { 40 | check_rng_seed(); 41 | return pg_prng_double(&pg_global_prng_state); 42 | } 43 | 44 | uint64 45 | generate_rnd_uint64(void) 46 | { 47 | check_rng_seed(); 48 | return pg_prng_uint64(&pg_global_prng_state); 49 | } 50 | -------------------------------------------------------------------------------- /src/pg_tracing_strinfo.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_tracing_strinfo.c 4 | * utility functions for strinfo manipulation. 5 | * 6 | * IDENTIFICATION 7 | * src/pg_tracing_strinfo.c 8 | * 9 | *------------------------------------------------------------------------- 10 | */ 11 | #include "postgres.h" 12 | 13 | #include "pg_tracing.h" 14 | 15 | /* 16 | * Append a null terminated string to provided StringInfo. 17 | * Returns the position where str was inserted 18 | */ 19 | int 20 | appendStringInfoNT(StringInfo strinfo, const char *str, int str_len) 21 | { 22 | int position = strinfo->len; 23 | 24 | Assert(str_len > 0); 25 | 26 | appendBinaryStringInfo(strinfo, str, str_len); 27 | appendStringInfoChar(strinfo, '\0'); 28 | return position; 29 | } 30 | -------------------------------------------------------------------------------- /src/version_compat.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * version_compat.c 4 | * Compatibility macros and functions for writing code agnostic to PostgreSQL versions 5 | * 6 | * IDENTIFICATION 7 | * src/version_compat.c 8 | * 9 | *------------------------------------------------------------------------- 10 | */ 11 | #include "postgres.h" 12 | 13 | #if PG_VERSION_NUM < 160000 14 | #include "version_compat.h" 15 | 16 | /* 17 | * repalloc0 18 | * Adjust the size of a previously allocated chunk and zero out the added 19 | * space. 20 | */ 21 | void * 22 | repalloc0(void *pointer, Size oldsize, Size size) 23 | { 24 | void *ret; 25 | 26 | /* catch wrong argument order */ 27 | if (unlikely(oldsize > size)) 28 | elog(ERROR, "invalid repalloc0 call: oldsize %zu, new size %zu", 29 | oldsize, size); 30 | 31 | ret = repalloc(pointer, size); 32 | memset((char *) ret + oldsize, 0, (size - oldsize)); 33 | return ret; 34 | } 35 | 36 | void * 37 | guc_malloc(int elevel, size_t size) 38 | { 39 | void *data; 40 | 41 | /* Fallback to malloc */ 42 | data = malloc(size); 43 | 44 | return data; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/version_compat.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * src/version_compat.h 4 | * Compatibility macros and functions for writing code agnostic to PostgreSQL versions 5 | * 6 | *------------------------------------------------------------------------- 7 | */ 8 | 9 | #ifndef VERSION_COMPAT_H 10 | #define VERSION_COMPAT_H 11 | 12 | #include "postgres.h" 13 | 14 | #if (PG_VERSION_NUM < 150000) 15 | 16 | #include "pg_prng.h" 17 | 18 | typedef double Cardinality; 19 | 20 | /* 21 | * Thin wrappers that convert strings to exactly 64-bit integers, matching our 22 | * definition of int64. (For the naming, compare that POSIX has 23 | * strtoimax()/strtoumax() which return intmax_t/uintmax_t.) 24 | */ 25 | #if SIZEOF_LONG == 8 26 | #define strtoi64(str, endptr, base) ((int64) strtol(str, endptr, base)) 27 | #define strtou64(str, endptr, base) ((uint64) strtoul(str, endptr, base)) 28 | #elif SIZEOF_LONG_LONG == 8 29 | #define strtoi64(str, endptr, base) ((int64) strtoll(str, endptr, base)) 30 | #define strtou64(str, endptr, base) ((uint64) strtoull(str, endptr, base)) 31 | #else 32 | #error "cannot find integer type of the same size as int64_t" 33 | #endif 34 | 35 | #endif 36 | 37 | #if (PG_VERSION_NUM < 160000) 38 | 39 | #include "utils/queryjumble.h" 40 | #define NS_PER_S INT64CONST(1000000000) 41 | 42 | /* We need to explicitly declare _PG_init */ 43 | void 44 | _PG_init(void); 45 | 46 | /* Functions that were introduced with PG 16 */ 47 | extern void *repalloc0(void *pointer, Size oldsize, Size size); 48 | extern void *guc_malloc(int elevel, size_t size); 49 | 50 | #else 51 | #include "nodes/queryjumble.h" 52 | #endif 53 | 54 | #if (PG_VERSION_NUM < 170000) 55 | 56 | #include "storage/backendid.h" 57 | /* 58 | * Compatibility with changes introduced in 59 | * https://www.postgresql.org/message-id/8171f1aa-496f-46a6-afc3-c46fe7a9b407@iki.fi 60 | */ 61 | 62 | #define INVALID_PROC_NUMBER InvalidBackendId 63 | #define MyProcNumber MyBackendId 64 | #define ProcNumber BackendId 65 | #define ParallelLeaderProcNumber ParallelLeaderBackendId 66 | 67 | #endif 68 | 69 | #if (PG_VERSION_NUM < 180000) 70 | 71 | #define UINT64_HEX_PADDED_FORMAT "%016" INT64_MODIFIER "x" 72 | #define EXECUTOR_RUN(...) \ 73 | if (prev_ExecutorRun) \ 74 | prev_ExecutorRun(queryDesc, direction, count, execute_once); \ 75 | else \ 76 | standard_ExecutorRun(queryDesc, direction, count, execute_once); 77 | #else 78 | 79 | #define UINT64_HEX_PADDED_FORMAT "%016" PRIx64 80 | #define EXECUTOR_RUN(...) \ 81 | if (prev_ExecutorRun) \ 82 | prev_ExecutorRun(queryDesc, direction, count); \ 83 | else \ 84 | standard_ExecutorRun(queryDesc, direction, count); 85 | #endif 86 | 87 | #endif /* VERSION_COMPAT_H */ 88 | -------------------------------------------------------------------------------- /t/001_basic.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use PostgreSQL::Test::Cluster; 5 | use PostgreSQL::Test::Utils; 6 | use Test::More; 7 | 8 | my $node = PostgreSQL::Test::Cluster->new('main'); 9 | 10 | # perl -MCPAN -e 'install -force HTTP::Server::Simple' 11 | { 12 | package TestWebServer; 13 | 14 | use HTTP::Server::Simple::CGI; 15 | use base qw(HTTP::Server::Simple::CGI); 16 | 17 | sub handle_request { 18 | print "HTTP/1.0 200 OK\r\n"; 19 | } 20 | } 21 | # Start test http server that will receive spans 22 | my $webserver_pid = TestWebServer->new(4318)->background(); 23 | 24 | $SIG{TERM} = $SIG{INT} = sub { 25 | kill 'INT', $webserver_pid; 26 | die "death by signal"; 27 | }; 28 | 29 | $node->init; 30 | $node->append_conf( 31 | 'postgresql.conf', 32 | qq{shared_preload_libraries = 'pg_tracing' 33 | pg_tracing.otel_naptime = 1000 34 | pg_tracing.otel_endpoint = 'http://localhost:4318' 35 | log_min_messages = info 36 | }); 37 | 38 | $node->start; 39 | 40 | # setup 41 | $node->safe_psql("postgres", 42 | "CREATE EXTENSION pg_tracing;"); 43 | 44 | # Create one span 45 | $node->safe_psql("postgres", "/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/SELECT 1;\n"); 46 | 47 | ok( $node->poll_query_until('postgres', 48 | "SELECT otel_sent_spans FROM pg_tracing_info;", 49 | 4), 50 | "Spans were sent to otel collector"); 51 | 52 | my $result = 53 | $node->safe_psql('postgres', "SELECT count(*) FROM pg_tracing_peek_spans;"); 54 | is($result, qq(0), 'All spans should have been consumed'); 55 | 56 | # Cleanup 57 | $node->stop; 58 | kill 'INT', $webserver_pid; 59 | 60 | done_testing(); 61 | -------------------------------------------------------------------------------- /t/002_connect_timeout.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use PostgreSQL::Test::Cluster; 5 | use PostgreSQL::Test::Utils; 6 | use Test::More; 7 | 8 | my $node = PostgreSQL::Test::Cluster->new('main'); 9 | 10 | $node->init; 11 | $node->append_conf( 12 | 'postgresql.conf', 13 | qq{shared_preload_libraries = 'pg_tracing' 14 | pg_tracing.otel_naptime = 1000 15 | pg_tracing.otel_endpoint = 'http://127.0.100.100:5555' 16 | log_min_messages = info 17 | }); 18 | 19 | $node->start; 20 | 21 | # setup 22 | $node->safe_psql("postgres", 23 | "CREATE EXTENSION pg_tracing;"); 24 | 25 | # Create one trace 26 | $node->safe_psql("postgres", "/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/SELECT 1"); 27 | 28 | ok( $node->poll_query_until('postgres', "SELECT otel_failures >= 1 FROM pg_tracing_info;"), 29 | "Otel failures should be reported"); 30 | 31 | my $result = 32 | $node->safe_psql('postgres', "SELECT count(*) FROM pg_tracing_peek_spans;"); 33 | is($result, qq(0), "Query's spans should have been consumed as they were copied"); 34 | 35 | # Create a second trace 36 | $node->safe_psql("postgres", "/*dddbs='postgres.db',traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/SELECT 1"); 37 | 38 | # Wait for more failures 39 | ok( $node->poll_query_until('postgres', "SELECT otel_failures >= 3 FROM pg_tracing_info;"), 40 | "Otel failures should be reported"); 41 | 42 | $result = 43 | $node->safe_psql('postgres', "SELECT count(*) FROM pg_tracing_peek_spans;"); 44 | is($result, qq(4), "Query's spans should still be present as we still attempt to send the previous payload"); 45 | 46 | # Cleanup 47 | $node->stop; 48 | 49 | done_testing(); 50 | -------------------------------------------------------------------------------- /t/003_change_endpoint.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use PostgreSQL::Test::Cluster; 5 | use PostgreSQL::Test::Utils; 6 | use Test::More; 7 | 8 | my $node = PostgreSQL::Test::Cluster->new('main'); 9 | 10 | { 11 | package TestWebServer; 12 | 13 | use HTTP::Server::Simple::CGI; 14 | use base qw(HTTP::Server::Simple::CGI); 15 | 16 | sub handle_request { 17 | print "HTTP/1.0 200 OK\r\n"; 18 | } 19 | } 20 | # Start test http server that will receive spans 21 | my $webserver_pid = TestWebServer->new(4318)->background(); 22 | 23 | $SIG{TERM} = $SIG{INT} = sub { 24 | kill 'INT', $webserver_pid; 25 | die "death by signal"; 26 | }; 27 | 28 | $node->init; 29 | $node->append_conf( 30 | 'postgresql.conf', 31 | qq{shared_preload_libraries = 'pg_tracing' 32 | pg_tracing.otel_naptime = 1000 33 | pg_tracing.otel_endpoint = 'http://127.0.100.100:5555' 34 | log_min_messages = info 35 | }); 36 | 37 | $node->start; 38 | 39 | # setup 40 | $node->safe_psql("postgres", 41 | "CREATE EXTENSION pg_tracing;"); 42 | 43 | # Create one trace 44 | $node->safe_psql("postgres", "/*traceparent='00-00000000000000000000000000000001-0000000000000001-01'*/SELECT 1"); 45 | 46 | # Sending spans should fail as endpoint is incorrect 47 | ok( $node->poll_query_until('postgres', "SELECT otel_failures >= 1 FROM pg_tracing_info;"), 48 | "Otel failures should be reported"); 49 | 50 | # Update otel endpoint 51 | $node->append_conf('postgresql.conf', "pg_tracing.otel_endpoint = 'http://localhost:4318'"); 52 | $node->reload; 53 | 54 | # Sending span should eventually succeed with the new endpoint 55 | ok( $node->poll_query_until('postgres', 56 | "SELECT otel_sent_spans FROM pg_tracing_info;", 57 | 4), 58 | "Spans were sent to otel collector"); 59 | 60 | my $result = 61 | $node->safe_psql('postgres', "SELECT count(*) FROM pg_tracing_peek_spans;"); 62 | is($result, qq(0), 'All spans should have been consumed'); 63 | 64 | # Cleanup 65 | $node->stop; 66 | kill 'INT', $webserver_pid; 67 | 68 | done_testing(); 69 | -------------------------------------------------------------------------------- /tests/postgresql.conf: -------------------------------------------------------------------------------- 1 | port = 5432 2 | max_connections = 100 3 | shared_preload_libraries = 'pg_tracing' 4 | unix_socket_directories='/var/run/postgresql' 5 | 6 | pg_tracing.sample_rate = 1.0 7 | pg_tracing.max_span = 5000 8 | --------------------------------------------------------------------------------