[
  {
    "path": ".devcontainer/Dockerfile",
    "content": "FROM mcr.microsoft.com/devcontainers/go:2-1.25-trixie\n\nRUN apt-get update && apt-get install -y postgresql-common \\\n    && yes | /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh \\\n    && apt-get install -y postgresql-client-18 \\\n    && rm -rf /var/lib/apt/lists/*\n\nUSER vscode\nRUN go install golang.org/x/tools/cmd/goimports@latest\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n\t\"name\": \"pgx\",\n\t\"dockerComposeFile\": \"docker-compose.yml\",\n\t\"service\": \"app\",\n\t\"workspaceFolder\": \"/workspaces/${localWorkspaceFolderBasename}\",\n\t\"postStartCommand\": \"base64 -d testsetup/certs/ca.pem.b64 > /tmp/ca.pem && base64 -d testsetup/certs/pgx_sslcert.crt.b64 > /tmp/pgx_sslcert.crt && base64 -d testsetup/certs/pgx_sslcert.key.b64 > /tmp/pgx_sslcert.key && chmod 600 /tmp/pgx_sslcert.key\",\n  \"postCreateCommand\": \".devcontainer/post-create.bash\"\n}\n"
  },
  {
    "path": ".devcontainer/docker-compose.yml",
    "content": "volumes:\n  postgres-14-data:\n  postgres-15-data:\n  postgres-16-data:\n  postgres-17-data:\n  postgres-18-data:\n  pg-sockets:\n\nservices:\n  app:\n    build:\n      context: .\n      dockerfile: Dockerfile\n\n    volumes:\n      - ../..:/workspaces:cached\n      - pg-sockets:/var/run/postgresql\n\n    environment:\n      PGUSER: postgres\n      PGPASSWORD: postgres\n      PGDATABASE: pgx_test\n      PGHOST: localhost\n      PGCLIENTENCODING: utf8\n\n      # PGX test env vars target PG18 (port 5432) by default.\n      # test.sh overrides these per-target.\n      PGX_TEST_DATABASE: \"host=localhost port=5432 user=postgres password=postgres dbname=pgx_test\"\n      PGX_TEST_UNIX_SOCKET_CONN_STRING: \"host=/var/run/postgresql port=5432 user=postgres dbname=pgx_test\"\n      PGX_TEST_TCP_CONN_STRING: \"host=127.0.0.1 port=5432 user=pgx_md5 password=secret dbname=pgx_test\"\n      PGX_TEST_MD5_PASSWORD_CONN_STRING: \"host=127.0.0.1 port=5432 user=pgx_md5 password=secret dbname=pgx_test\"\n      PGX_TEST_SCRAM_PASSWORD_CONN_STRING: \"host=localhost port=5432 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\"\n      PGX_TEST_SCRAM_PLUS_CONN_STRING: \"host=127.0.0.1 port=5432 user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\"\n      PGX_TEST_PLAIN_PASSWORD_CONN_STRING: \"host=127.0.0.1 port=5432 user=pgx_pw password=secret dbname=pgx_test\"\n      PGX_TEST_TLS_CONN_STRING: \"host=localhost port=5432 user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\"\n      PGX_TEST_TLS_CLIENT_CONN_STRING: \"host=localhost port=5432 user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\"\n      PGX_SSL_PASSWORD: certpw\n\n    # Overrides default command so things don't shut down after the process ends.\n    command: sleep infinity\n\n  postgres-14:\n    image: postgres:14\n    restart: unless-stopped\n    volumes:\n      - postgres-14-data:/var/lib/postgresql\n      - ../testsetup/postgresql_setup.sql:/docker-entrypoint-initdb.d/01-setup.sql:ro\n      - ../testsetup/pg_ssl_init.sh:/docker-entrypoint-initdb.d/02-ssl-init.sh:ro\n      - ../testsetup/pg_hba_devcontainer.conf:/etc/postgresql/pg_hba.conf:ro\n      - ../testsetup/certs:/etc/postgresql/ssl:ro\n      - ../testsetup/postgresql_ssl.conf:/etc/postgresql/postgresql_ssl.conf:ro\n      - pg-sockets:/var/run/postgresql\n    network_mode: service:app\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_DB: pgx_test\n      POSTGRES_HOSTNAME: localhost\n      PGPORT: 5414\n    command: postgres -c port=5414 -c hba_file=/etc/postgresql/pg_hba.conf -c unix_socket_directories=/var/run/postgresql\n\n  postgres-15:\n    image: postgres:15\n    restart: unless-stopped\n    volumes:\n      - postgres-15-data:/var/lib/postgresql\n      - ../testsetup/postgresql_setup.sql:/docker-entrypoint-initdb.d/01-setup.sql:ro\n      - ../testsetup/pg_ssl_init.sh:/docker-entrypoint-initdb.d/02-ssl-init.sh:ro\n      - ../testsetup/pg_hba_devcontainer.conf:/etc/postgresql/pg_hba.conf:ro\n      - ../testsetup/certs:/etc/postgresql/ssl:ro\n      - ../testsetup/postgresql_ssl.conf:/etc/postgresql/postgresql_ssl.conf:ro\n      - pg-sockets:/var/run/postgresql\n    network_mode: service:app\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_DB: pgx_test\n      POSTGRES_HOSTNAME: localhost\n      PGPORT: 5415\n    command: postgres -c port=5415 -c hba_file=/etc/postgresql/pg_hba.conf -c unix_socket_directories=/var/run/postgresql\n\n  postgres-16:\n    image: postgres:16\n    restart: unless-stopped\n    volumes:\n      - postgres-16-data:/var/lib/postgresql\n      - ../testsetup/postgresql_setup.sql:/docker-entrypoint-initdb.d/01-setup.sql:ro\n      - ../testsetup/pg_ssl_init.sh:/docker-entrypoint-initdb.d/02-ssl-init.sh:ro\n      - ../testsetup/pg_hba_devcontainer.conf:/etc/postgresql/pg_hba.conf:ro\n      - ../testsetup/certs:/etc/postgresql/ssl:ro\n      - ../testsetup/postgresql_ssl.conf:/etc/postgresql/postgresql_ssl.conf:ro\n      - pg-sockets:/var/run/postgresql\n    network_mode: service:app\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_DB: pgx_test\n      POSTGRES_HOSTNAME: localhost\n      PGPORT: 5416\n    command: postgres -c port=5416 -c hba_file=/etc/postgresql/pg_hba.conf -c unix_socket_directories=/var/run/postgresql\n\n  postgres-17:\n    image: postgres:17\n    restart: unless-stopped\n    volumes:\n      - postgres-17-data:/var/lib/postgresql\n      - ../testsetup/postgresql_setup.sql:/docker-entrypoint-initdb.d/01-setup.sql:ro\n      - ../testsetup/pg_ssl_init.sh:/docker-entrypoint-initdb.d/02-ssl-init.sh:ro\n      - ../testsetup/pg_hba_devcontainer.conf:/etc/postgresql/pg_hba.conf:ro\n      - ../testsetup/certs:/etc/postgresql/ssl:ro\n      - ../testsetup/postgresql_ssl.conf:/etc/postgresql/postgresql_ssl.conf:ro\n      - pg-sockets:/var/run/postgresql\n    network_mode: service:app\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_DB: pgx_test\n      POSTGRES_HOSTNAME: localhost\n      PGPORT: 5417\n    command: postgres -c port=5417 -c hba_file=/etc/postgresql/pg_hba.conf -c unix_socket_directories=/var/run/postgresql\n\n  postgres-18:\n    image: postgres:18\n    restart: unless-stopped\n    volumes:\n      - postgres-18-data:/var/lib/postgresql\n      - ../testsetup/postgresql_setup.sql:/docker-entrypoint-initdb.d/01-setup.sql:ro\n      - ../testsetup/pg_ssl_init.sh:/docker-entrypoint-initdb.d/02-ssl-init.sh:ro\n      - ../testsetup/pg_hba_devcontainer.conf:/etc/postgresql/pg_hba.conf:ro\n      - ../testsetup/certs:/etc/postgresql/ssl:ro\n      - ../testsetup/postgresql_ssl.conf:/etc/postgresql/postgresql_ssl.conf:ro\n      - pg-sockets:/var/run/postgresql\n    network_mode: service:app\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: postgres\n      POSTGRES_DB: pgx_test\n      POSTGRES_HOSTNAME: localhost\n    command: postgres -c hba_file=/etc/postgresql/pg_hba.conf -c unix_socket_directories=/var/run/postgresql\n\n  cockroachdb:\n    image: cockroachdb/cockroach:v25.4.4\n    restart: unless-stopped\n    network_mode: service:app\n    command: start-single-node --insecure --listen-addr=127.0.0.1:26257 --store=type=mem,size=1024MiB\n"
  },
  {
    "path": ".devcontainer/post-create.bash",
    "content": "#!/bin/bash\nset -e\n\n# Run any additional setup scripts included in the shared/devcontainer directory. This is to allow for per developer or\n# per-environment customizations. These scripts are not checked into source control.\nif [ -x \"../shared/devcontainer/install\" ]; then\n  ../shared/devcontainer/install\nfi\n\n# Create a symlink to the shared .scratch directory for temporary files if it exists.\nif [ -x \"../shared/.scratch\" ]; then\n  if [ ! -e .scratch ] && [ ! -L .scratch ]; then\n    ln -s ../shared/.scratch\n  fi\nfi\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: jackc\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n\nIf possible, please provide runnable example such as:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\nfunc main() {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"DATABASE_URL\"))\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer conn.Close(context.Background())\n\n\t// Your code here...\n}\n```\n\nPlease run your example with the race detector enabled. For example, `go run -race main.go` or `go test -race`.\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Actual behavior**\nA clear and concise description of what actually happened.\n\n**Version**\n - Go: `$ go version` -> [e.g. go version go1.18.3 darwin/amd64]\n - PostgreSQL: `$ psql --no-psqlrc --tuples-only -c 'select version()'` -> [e.g. PostgreSQL 14.4 on x86_64-apple-darwin21.5.0, compiled by Apple clang version 13.1.6 (clang-1316.0.21.2.5), 64-bit]\n - pgx: `$ grep 'github.com/jackc/pgx/v[0-9]' go.mod` -> [e.g. v4.16.1]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other-issues.md",
    "content": "---\nname: Other issues\nabout: Any issue that is not a bug or a feature request\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\nPlease describe the issue in detail. If this is a question about how to use pgx please use discussions instead.\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\njobs:\n  test:\n    name: Test\n    runs-on: ubuntu-22.04\n\n    strategy:\n      matrix:\n        go-version: [\"1.24\", \"1.25\"]\n        pg-version: [14, 15, 16, 17, 18, cockroachdb]\n        include:\n          - pg-version: 14\n            pgx-test-database: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-unix-socket-conn-string: \"host=/var/run/postgresql dbname=pgx_test\"\n            pgx-test-tcp-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-scram-password-conn-string: \"host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\"\n            pgx-test-scram-plus-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\"\n            pgx-test-md5-password-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-plain-password-conn-string: \"host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test\"\n            pgx-test-tls-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\"\n            pgx-ssl-password: certpw\n            pgx-test-tls-client-conn-string: \"host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\"\n          - pg-version: 15\n            pgx-test-database: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-unix-socket-conn-string: \"host=/var/run/postgresql dbname=pgx_test\"\n            pgx-test-tcp-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-scram-password-conn-string: \"host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\"\n            pgx-test-scram-plus-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\"\n            pgx-test-md5-password-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-plain-password-conn-string: \"host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test\"\n            pgx-test-tls-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\"\n            pgx-ssl-password: certpw\n            pgx-test-tls-client-conn-string: \"host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\"\n          - pg-version: 16\n            pgx-test-database: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-unix-socket-conn-string: \"host=/var/run/postgresql dbname=pgx_test\"\n            pgx-test-tcp-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-scram-password-conn-string: \"host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\"\n            pgx-test-scram-plus-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\"\n            pgx-test-md5-password-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-plain-password-conn-string: \"host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test\"\n            pgx-test-tls-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\"\n            pgx-ssl-password: certpw\n            pgx-test-tls-client-conn-string: \"host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\"\n          - pg-version: 17\n            pgx-test-database: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-unix-socket-conn-string: \"host=/var/run/postgresql dbname=pgx_test\"\n            pgx-test-tcp-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-scram-password-conn-string: \"host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\"\n            pgx-test-scram-plus-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\"\n            pgx-test-md5-password-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-plain-password-conn-string: \"host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test\"\n            pgx-test-tls-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\"\n            pgx-ssl-password: certpw\n            pgx-test-tls-client-conn-string: \"host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\"\n          - pg-version: 18\n            pgx-test-database: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-unix-socket-conn-string: \"host=/var/run/postgresql dbname=pgx_test\"\n            pgx-test-tcp-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-scram-password-conn-string: \"host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\"\n            pgx-test-scram-plus-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\"\n            pgx-test-md5-password-conn-string: \"host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test\"\n            pgx-test-plain-password-conn-string: \"host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test\"\n            pgx-test-tls-conn-string: \"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\"\n            pgx-test-oauth: \"true\"\n            pgx-ssl-password: certpw\n            pgx-test-tls-client-conn-string: \"host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\"\n          - pg-version: cockroachdb\n            pgx-test-database: \"postgresql://root@127.0.0.1:26257/pgx_test?sslmode=disable&experimental_enable_temp_tables=on\"\n\n    steps:\n      - name: Check out code into the Go module directory\n        uses: actions/checkout@v5\n\n      - name: Set up Go ${{ matrix.go-version }}\n        uses: actions/setup-go@v6\n        with:\n          go-version: ${{ matrix.go-version }}\n\n      - name: Setup database server for testing\n        run: ci/setup_test.bash\n        env:\n          PGVERSION: ${{ matrix.pg-version }}\n\n      # - name: Setup upterm session\n      #   uses: lhotari/action-upterm@v1\n      #   with:\n      #     ## limits ssh access and adds the ssh public key for the user which triggered the workflow\n      #     limit-access-to-actor: true\n      #   env:\n      #     PGX_TEST_DATABASE: ${{ matrix.pgx-test-database }}\n      #     PGX_TEST_UNIX_SOCKET_CONN_STRING: ${{ matrix.pgx-test-unix-socket-conn-string }}\n      #     PGX_TEST_TCP_CONN_STRING: ${{ matrix.pgx-test-tcp-conn-string }}\n      #     PGX_TEST_SCRAM_PASSWORD_CONN_STRING: ${{ matrix.pgx-test-scram-password-conn-string }}\n      #     PGX_TEST_MD5_PASSWORD_CONN_STRING: ${{ matrix.pgx-test-md5-password-conn-string }}\n      #     PGX_TEST_PLAIN_PASSWORD_CONN_STRING: ${{ matrix.pgx-test-plain-password-conn-string }}\n      #     PGX_TEST_TLS_CONN_STRING: ${{ matrix.pgx-test-tls-conn-string }}\n      #     PGX_SSL_PASSWORD: ${{ matrix.pgx-ssl-password }}\n      #     PGX_TEST_TLS_CLIENT_CONN_STRING: ${{ matrix.pgx-test-tls-client-conn-string }}\n\n      - name: Check formatting\n        run: |\n          gofmt -l -s -w .\n          git status\n          git diff --exit-code\n\n      - name: Test\n        # parallel testing is disabled because somehow parallel testing causes Github Actions to kill the runner.\n        run: go test -parallel=1 -race ./...\n        env:\n          PGX_TEST_DATABASE: ${{ matrix.pgx-test-database }}\n          PGX_TEST_UNIX_SOCKET_CONN_STRING: ${{ matrix.pgx-test-unix-socket-conn-string }}\n          PGX_TEST_TCP_CONN_STRING: ${{ matrix.pgx-test-tcp-conn-string }}\n          PGX_TEST_SCRAM_PASSWORD_CONN_STRING: ${{ matrix.pgx-test-scram-password-conn-string }}\n          PGX_TEST_MD5_PASSWORD_CONN_STRING: ${{ matrix.pgx-test-md5-password-conn-string }}\n          PGX_TEST_PLAIN_PASSWORD_CONN_STRING: ${{ matrix.pgx-test-plain-password-conn-string }}\n          PGX_TEST_OAUTH: ${{ matrix.pgx-test-oauth }}\n          # TestConnectTLS fails. However, it succeeds if I connect to the CI server with upterm and run it. Give up on that test for now.\n          # PGX_TEST_TLS_CONN_STRING: ${{ matrix.pgx-test-tls-conn-string }}\n          # PGX_TEST_SCRAM_PLUS_CONN_STRING: ${{ matrix.pgx-test-scram-plus-conn-string }}\n          PGX_SSL_PASSWORD: ${{ matrix.pgx-ssl-password }}\n          PGX_TEST_TLS_CLIENT_CONN_STRING: ${{ matrix.pgx-test-tls-client-conn-string }}\n\n  test-windows:\n    name: Test Windows\n    runs-on: windows-latest\n    strategy:\n      matrix:\n        go-version: [\"1.24\", \"1.25\"]\n\n    steps:\n      - name: Setup PostgreSQL\n        id: postgres\n        uses: ikalnytskyi/action-setup-postgres@v4\n        with:\n          database: pgx_test\n\n      - name: Check out code into the Go module directory\n        uses: actions/checkout@v5\n\n      - name: Set up Go ${{ matrix.go-version }}\n        uses: actions/setup-go@v6\n        with:\n          go-version: ${{ matrix.go-version }}\n\n      - name: Initialize test database\n        run: |\n          psql -f testsetup/postgresql_setup.sql pgx_test\n        env:\n          PGSERVICE: ${{ steps.postgres.outputs.service-name }}\n        shell: bash\n\n      - name: Test\n        # Parallel testing is disabled because somehow parallel testing causes Github Actions to kill the runner.\n        # Disabling CGO because that is not working on the Windows runner. Apparently, we could install MingW and set up\n        # CGO to work, but disabling CGO takes less CI time. Without CGO, we also must not use the race detector.\n        run: go test -parallel=1 ./...\n        env:\n          CGO_ENABLED: 0\n          PGX_TEST_DATABASE: ${{ steps.postgres.outputs.connection-uri }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n\n.envrc\n/.testdb\n\n.DS_Store\n"
  },
  {
    "path": ".golangci.yml",
    "content": "# See for configurations: https://golangci-lint.run/usage/configuration/\nversion: \"2\"\n\nlinters:\n  default: none\n  enable:\n    - govet\n    - ineffassign\n\n# See: https://golangci-lint.run/usage/formatters/\nformatters:\n  enable:\n    - gofmt # https://pkg.go.dev/cmd/gofmt\n    - gofumpt # https://github.com/mvdan/gofumpt\n\n  settings:\n    gofmt:\n      simplify: true # Simplify code: gofmt with `-s` option.\n\n    gofumpt:\n      # Module path which contains the source code being formatted.\n      # Default: \"\"\n      module-path: github.com/jackc/pgx/v5 # Should match with module in go.mod\n      # Choose whether to use the extra rules.\n      # Default: false\n      extra-rules: true\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 5.8.0 (December 26, 2025)\n\n* Require Go 1.24+\n* Remove golang.org/x/crypto dependency\n* Add OptionShouldPing to control ResetSession ping behavior (ilyam8)\n* Fix: Avoid overflow when MaxConns is set to MaxInt32\n* Fix: Close batch pipeline after a query error (Anthonin Bonnefoy)\n* Faster shutdown of pgxpool.Pool background goroutines (Blake Gentry)\n* Add pgxpool ping timeout (Amirsalar Safaei)\n* Fix: Rows.FieldDescriptions for empty query\n* Scan unknown types into *any as string or []byte based on format code\n* Optimize pgtype.Numeric (Philip Dubé)\n* Add AfterNetConnect hook to pgconn.Config\n* Fix: Handle for preparing statements that fail during the Describe phase\n* Fix overflow in numeric scanning (Ilia Demianenko)\n* Fix: json/jsonb sql.Scanner source type is []byte\n* Migrate from math/rand to math/rand/v2 (Mathias Bogaert)\n* Optimize internal iobufpool (Mathias Bogaert)\n* Optimize stmtcache invalidation (Mathias Bogaert)\n* Fix: missing error case in interval parsing (Maxime Soulé)\n* Fix: invalidate statement/description cache in Exec (James Hartig)\n* ColumnTypeLength method return the type length for varbit type (DengChan)\n* Array and Composite codecs handle typed nils\n\n# 5.7.6 (September 8, 2025)\n\n* Use ParseConfigError in pgx.ParseConfig and pgxpool.ParseConfig (Yurasov Ilia)\n* Add PrepareConn hook to pgxpool (Jonathan Hall)\n* Reduce allocations in QueryContext (Dominique Lefevre)\n* Add MarshalJSON and UnmarshalJSON for pgtype.Uint32 (Panos Koutsovasilis)\n* Configure ping behavior on pgxpool with ShouldPing (Christian Kiely)\n* zeronull int types implement Int64Valuer and Int64Scanner (Li Zeghong)\n* Fix panic when receiving terminate connection message during CopyFrom (Michal Drausowski)\n* Fix statement cache not being invalidated on error during batch (Muhammadali Nazarov)\n\n# 5.7.5 (May 17, 2025)\n\n* Support sslnegotiation connection option (divyam234)\n* Update golang.org/x/crypto to v0.37.0. This placates security scanners that were unable to see that pgx did not use the behavior affected by https://pkg.go.dev/vuln/GO-2025-3487.\n* TraceLog now logs Acquire and Release at the debug level (dave sinclair)\n* Add support for PGTZ environment variable\n* Add support for PGOPTIONS environment variable\n* Unpin memory used by Rows quicker\n* Remove PlanScan memoization. This resolves a rare issue where scanning could be broken for one type by first scanning another. The problem was in the memoization system and benchmarking revealed that memoization was not providing any meaningful benefit.\n\n# 5.7.4 (March 24, 2025)\n\n* Fix / revert change to scanning JSON `null` (Felix Röhrich)\n\n# 5.7.3 (March 21, 2025)\n\n* Expose EmptyAcquireWaitTime in pgxpool.Stat (vamshiaruru32)\n* Improve SQL sanitizer performance (ninedraft)\n* Fix Scan confusion with json(b), sql.Scanner, and automatic dereferencing (moukoublen, felix-roehrich)\n* Fix Values() for xml type always returning nil instead of []byte\n* Add ability to send Flush message in pipeline mode (zenkovev)\n* Fix pgtype.Timestamp's JSON behavior to match PostgreSQL (pconstantinou)\n* Better error messages when scanning structs (logicbomb)\n* Fix handling of error on batch write (bonnefoa)\n* Match libpq's connection fallback behavior more closely (felix-roehrich)\n* Add MinIdleConns to pgxpool (djahandarie)\n\n# 5.7.2 (December 21, 2024)\n\n* Fix prepared statement already exists on batch prepare failure\n* Add commit query to tx options (Lucas Hild)\n* Fix pgtype.Timestamp json unmarshal (Shean de Montigny-Desautels)\n* Add message body size limits in frontend and backend (zene)\n* Add xid8 type\n* Ensure planning encodes and scans cannot infinitely recurse\n* Implement pgtype.UUID.String() (Konstantin Grachev)\n* Switch from ExecParams to Exec in ValidateConnectTargetSessionAttrs functions (Alexander Rumyantsev)\n* Update golang.org/x/crypto\n* Fix json(b) columns prefer sql.Scanner interface like database/sql (Ludovico Russo)\n\n# 5.7.1 (September 10, 2024)\n\n* Fix data race in tracelog.TraceLog\n* Update puddle to v2.2.2. This removes the import of nanotime via linkname.\n* Update golang.org/x/crypto and golang.org/x/text\n\n# 5.7.0 (September 7, 2024)\n\n* Add support for sslrootcert=system (Yann Soubeyrand)\n* Add LoadTypes to load multiple types in a single SQL query (Nick Farrell)\n* Add XMLCodec supports encoding + scanning XML column type like json (nickcruess-soda)\n* Add MultiTrace (Stepan Rabotkin)\n* Add TraceLogConfig with customizable TimeKey (stringintech)\n* pgx.ErrNoRows wraps sql.ErrNoRows to aid in database/sql compatibility with native pgx functions (merlin)\n* Support scanning binary formatted uint32 into string / TextScanner (jennifersp)\n* Fix interval encoding to allow 0s and avoid extra spaces (Carlos Pérez-Aradros Herce)\n* Update pgservicefile - fixes panic when parsing invalid file\n* Better error message when reading past end of batch\n* Don't print url when url.Parse returns an error (Kevin Biju)\n* Fix snake case name normalization collision in RowToStructByName with db tag (nolandseigler)\n* Fix: Scan and encode types with underlying types of arrays\n\n# 5.6.0 (May 25, 2024)\n\n* Add StrictNamedArgs (Tomas Zahradnicek)\n* Add support for macaddr8 type (Carlos Pérez-Aradros Herce)\n* Add SeverityUnlocalized field to PgError / Notice\n* Performance optimization of RowToStructByPos/Name (Zach Olstein)\n* Allow customizing context canceled behavior for pgconn\n* Add ScanLocation to pgtype.Timestamp[tz]Codec\n* Add custom data to pgconn.PgConn\n* Fix ResultReader.Read() to handle nil values\n* Do not encode interval microseconds when they are 0 (Carlos Pérez-Aradros Herce)\n* pgconn.SafeToRetry checks for wrapped errors (tjasko)\n* Failed connection attempts include all errors\n* Optimize LargeObject.Read (Mitar)\n* Add tracing for connection acquire and release from pool (ngavinsir)\n* Fix encode driver.Valuer not called when nil\n* Add support for custom JSON marshal and unmarshal (Mitar)\n* Use Go default keepalive for TCP connections (Hans-Joachim Kliemeck)\n\n# 5.5.5 (March 9, 2024)\n\nUse spaces instead of parentheses for SQL sanitization.\n\nThis still solves the problem of negative numbers creating a line comment, but this avoids breaking edge cases such as\n`set foo to $1` where the substitution is taking place in a location where an arbitrary expression is not allowed.\n\n# 5.5.4 (March 4, 2024)\n\nFix CVE-2024-27304\n\nSQL injection can occur if an attacker can cause a single query or bind message to exceed 4 GB in size. An integer\noverflow in the calculated message size can cause the one large message to be sent as multiple messages under the\nattacker's control.\n\nThanks to Paul Gerste for reporting this issue.\n\n* Fix behavior of CollectRows to return empty slice if Rows are empty (Felix)\n* Fix simple protocol encoding of json.RawMessage\n* Fix *Pipeline.getResults should close pipeline on error\n* Fix panic in TryFindUnderlyingTypeScanPlan (David Kurman)\n* Fix deallocation of invalidated cached statements in a transaction\n* Handle invalid sslkey file\n* Fix scan float4 into sql.Scanner\n* Fix pgtype.Bits not making copy of data from read buffer. This would cause the data to be corrupted by future reads.\n\n# 5.5.3 (February 3, 2024)\n\n* Fix: prepared statement already exists\n* Improve CopyFrom auto-conversion of text-ish values\n* Add ltree type support (Florent Viel)\n* Make some properties of Batch and QueuedQuery public (Pavlo Golub)\n* Add AppendRows function (Edoardo Spadolini)\n* Optimize convert UUID [16]byte to string (Kirill Malikov)\n* Fix: LargeObject Read and Write of more than ~1GB at a time (Mitar)\n\n# 5.5.2 (January 13, 2024)\n\n* Allow NamedArgs to start with underscore\n* pgproto3: Maximum message body length support (jeremy.spriet)\n* Upgrade golang.org/x/crypto to v0.17.0\n* Add snake_case support to RowToStructByName (Tikhon Fedulov)\n* Fix: update description cache after exec prepare (James Hartig)\n* Fix: pipeline checks if it is closed (James Hartig and Ryan Fowler)\n* Fix: normalize timeout / context errors during TLS startup (Samuel Stauffer)\n* Add OnPgError for easier centralized error handling (James Hartig)\n\n# 5.5.1 (December 9, 2023)\n\n* Add CopyFromFunc helper function. (robford)\n* Add PgConn.Deallocate method that uses PostgreSQL protocol Close message.\n* pgx uses new PgConn.Deallocate method. This allows deallocating statements to work in a failed transaction. This fixes a case where the prepared statement map could become invalid.\n* Fix: Prefer driver.Valuer over json.Marshaler for json fields. (Jacopo)\n* Fix: simple protocol SQL sanitizer previously panicked if an invalid $0 placeholder was used. This now returns an error instead. (maksymnevajdev)\n* Add pgtype.Numeric.ScanScientific (Eshton Robateau)\n\n# 5.5.0 (November 4, 2023)\n\n* Add CollectExactlyOneRow. (Julien GOTTELAND)\n* Add OpenDBFromPool to create *database/sql.DB from *pgxpool.Pool. (Lev Zakharov)\n* Prepare can automatically choose statement name based on sql. This makes it easier to explicitly manage prepared statements.\n* Statement cache now uses deterministic, stable statement names.\n* database/sql prepared statement names are deterministically generated.\n* Fix: SendBatch wasn't respecting context cancellation.\n* Fix: Timeout error from pipeline is now normalized.\n* Fix: database/sql encoding json.RawMessage to []byte.\n* CancelRequest: Wait for the cancel request to be acknowledged by the server. This should improve PgBouncer compatibility. (Anton Levakin)\n* stdlib: Use Ping instead of CheckConn in ResetSession\n* Add json.Marshaler and json.Unmarshaler for Float4, Float8 (Kirill Mironov)\n\n# 5.4.3 (August 5, 2023)\n\n* Fix: QCharArrayOID was defined with the wrong OID (Christoph Engelbert)\n* Fix: connect_timeout for sslmode=allow|prefer (smaher-edb)\n* Fix: pgxpool: background health check cannot overflow pool\n* Fix: Check for nil in defer when sending batch (recover properly from panic)\n* Fix: json scan of non-string pointer to pointer\n* Fix: zeronull.Timestamptz should use pgtype.Timestamptz\n* Fix: NewConnsCount was not correctly counting connections created by Acquire directly. (James Hartig)\n* RowTo(AddrOf)StructByPos ignores fields with \"-\" db tag\n* Optimization: improve text format numeric parsing (horpto)\n\n# 5.4.2 (July 11, 2023)\n\n* Fix: RowScanner errors are fatal to Rows\n* Fix: Enable failover efforts when pg_hba.conf disallows non-ssl connections (Brandon Kauffman)\n* Hstore text codec internal improvements (Evan Jones)\n* Fix: Stop timers for background reader when not in use. Fixes memory leak when closing connections (Adrian-Stefan Mares)\n* Fix: Stop background reader as soon as possible.\n* Add PgConn.SyncConn(). This combined with the above fix makes it safe to directly use the underlying net.Conn.\n\n# 5.4.1 (June 18, 2023)\n\n* Fix: concurrency bug with pgtypeDefaultMap and simple protocol (Lev Zakharov)\n* Add TxOptions.BeginQuery to allow overriding the default BEGIN query\n\n# 5.4.0 (June 14, 2023)\n\n* Replace platform specific syscalls for non-blocking IO with more traditional goroutines and deadlines. This returns to the v4 approach with some additional improvements and fixes. This restores the ability to use a pgx.Conn over an ssh.Conn as well as other non-TCP or Unix socket connections. In addition, it is a significantly simpler implementation that is less likely to have cross platform issues.\n* Optimization: The default type registrations are now shared among all connections. This saves about 100KB of memory per connection. `pgtype.Type` and `pgtype.Codec` values are now required to be immutable after registration. This was already necessary in most cases but wasn't documented until now. (Lev Zakharov)\n* Fix: Ensure pgxpool.Pool.QueryRow.Scan releases connection on panic\n* CancelRequest: don't try to read the reply (Nicola Murino)\n* Fix: correctly handle bool type aliases (Wichert Akkerman)\n* Fix: pgconn.CancelRequest: Fix unix sockets: don't use RemoteAddr()\n* Fix: pgx.Conn memory leak with prepared statement caching (Evan Jones)\n* Add BeforeClose to pgxpool.Pool (Evan Cordell)\n* Fix: various hstore fixes and optimizations (Evan Jones)\n* Fix: RowToStructByPos with embedded unexported struct\n* Support different bool string representations (Lev Zakharov)\n* Fix: error when using BatchResults.Exec on a select that returns an error after some rows.\n* Fix: pipelineBatchResults.Exec() not returning error from ResultReader\n* Fix: pipeline batch results not closing pipeline when error occurs while reading directly from results instead of using\n    a callback.\n* Fix: scanning a table type into a struct\n* Fix: scan array of record to pointer to slice of struct\n* Fix: handle null for json (Cemre Mengu)\n* Batch Query callback is called even when there is an error\n* Add RowTo(AddrOf)StructByNameLax (Audi P. Risa P)\n\n# 5.3.1 (February 27, 2023)\n\n* Fix: Support v4 and v5 stdlib in same program (Tomáš Procházka)\n* Fix: sql.Scanner not being used in certain cases\n* Add text format jsonpath support\n* Fix: fake non-blocking read adaptive wait time\n\n# 5.3.0 (February 11, 2023)\n\n* Fix: json values work with sql.Scanner\n* Fixed / improved error messages (Mark Chambers and Yevgeny Pats)\n* Fix: support scan into single dimensional arrays\n* Fix: MaxConnLifetimeJitter setting actually jitter (Ben Weintraub)\n* Fix: driver.Value representation of bytea should be []byte not string\n* Fix: better handling of unregistered OIDs\n* CopyFrom can use query cache to avoid extra round trip to get OIDs (Alejandro Do Nascimento Mora)\n* Fix: encode to json ignoring driver.Valuer\n* Support sql.Scanner on renamed base type\n* Fix: pgtype.Numeric text encoding of negative numbers (Mark Chambers)\n* Fix: connect with multiple hostnames when one can't be resolved\n* Upgrade puddle to remove dependency on uber/atomic and fix alignment issue on 32-bit platform\n* Fix: scanning json column into **string\n* Multiple reductions in memory allocations\n* Fake non-blocking read adapts its max wait time\n* Improve CopyFrom performance and reduce memory usage\n* Fix: encode []any to array\n* Fix: LoadType for composite with dropped attributes (Felix Röhrich)\n* Support v4 and v5 stdlib in same program\n* Fix: text format array decoding with string of \"NULL\"\n* Prefer binary format for arrays\n\n# 5.2.0 (December 5, 2022)\n\n* `tracelog.TraceLog` implements the pgx.PrepareTracer interface. (Vitalii Solodilov)\n* Optimize creating begin transaction SQL string (Petr Evdokimov and ksco)\n* `Conn.LoadType` supports range and multirange types (Vitalii Solodilov)\n* Fix scan `uint` and `uint64` `ScanNumeric`. This resolves a PostgreSQL `numeric` being incorrectly scanned into `uint` and `uint64`.\n\n# 5.1.1 (November 17, 2022)\n\n* Fix simple query sanitizer where query text contains a Unicode replacement character.\n* Remove erroneous `name` argument from `DeallocateAll()`. Technically, this is a breaking change, but given that method was only added 5 days ago this change was accepted. (Bodo Kaiser)\n\n# 5.1.0 (November 12, 2022)\n\n* Update puddle to v2.1.2. This resolves a race condition and a deadlock in pgxpool.\n* `QueryRewriter.RewriteQuery` now returns an error. Technically, this is a breaking change for any external implementers, but given the minimal likelihood that there are actually any external implementers this change was accepted.\n* Expose `GetSSLPassword` support to pgx.\n* Fix encode `ErrorResponse` unknown field handling. This would only affect pgproto3 being used directly as a proxy with a non-PostgreSQL server that included additional error fields.\n* Fix date text format encoding with 5 digit years.\n* Fix date values passed to a `sql.Scanner` as `string` instead of `time.Time`.\n* DateCodec.DecodeValue can return `pgtype.InfinityModifier` instead of `string` for infinite values. This now matches the behavior of the timestamp types.\n* Add domain type support to `Conn.LoadType()`.\n* Add `RowToStructByName` and `RowToAddrOfStructByName`. (Pavlo Golub)\n* Add `Conn.DeallocateAll()` to clear all prepared statements including the statement cache. (Bodo Kaiser)\n\n# 5.0.4 (October 24, 2022)\n\n* Fix: CollectOneRow prefers PostgreSQL error over pgx.ErrorNoRows\n* Fix: some reflect Kind checks to first check for nil\n* Bump golang.org/x/text dependency to placate snyk\n* Fix: RowToStructByPos on structs with multiple anonymous sub-structs (Baptiste Fontaine)\n* Fix: Exec checks if tx is closed\n\n# 5.0.3 (October 14, 2022)\n\n* Fix `driver.Valuer` handling edge cases that could cause infinite loop or crash\n\n# v5.0.2 (October 8, 2022)\n\n* Fix date encoding in text format to always use 2 digits for month and day\n* Prefer driver.Valuer over wrap plans when encoding\n* Fix scan to pointer to pointer to renamed type\n* Allow scanning NULL even if PG and Go types are incompatible\n\n# v5.0.1 (September 24, 2022)\n\n* Fix 32-bit atomic usage\n* Add MarshalJSON for Float8 (yogipristiawan)\n* Add `[` and `]` to text encoding of `Lseg`\n* Fix sqlScannerWrapper NULL handling\n\n# v5.0.0 (September 17, 2022)\n\n## Merged Packages\n\n`github.com/jackc/pgtype`, `github.com/jackc/pgconn`, and `github.com/jackc/pgproto3` are now included in the main\n`github.com/jackc/pgx` repository. Previously there was confusion as to where issues should be reported, additional\nrelease work due to releasing multiple packages, and less clear changelogs.\n\n## pgconn\n\n`CommandTag` is now an opaque type instead of directly exposing an underlying `[]byte`.\n\nThe return value `ResultReader.Values()` is no longer safe to retain a reference to after a subsequent call to `NextRow()` or `Close()`.\n\n`Trace()` method adds low level message tracing similar to the `PQtrace` function in `libpq`.\n\npgconn now uses non-blocking IO. This is a significant internal restructuring, but it should not cause any visible changes on its own. However, it is important in implementing other new features.\n\n`CheckConn()` checks a connection's liveness by doing a non-blocking read. This can be used to detect database restarts or network interruptions without executing a query or a ping.\n\npgconn now supports pipeline mode.\n\n`*PgConn.ReceiveResults` removed. Use pipeline mode instead.\n\n`Timeout()` no longer considers `context.Canceled` as a timeout error. `context.DeadlineExceeded` still is considered a timeout error.\n\n## pgxpool\n\n`Connect` and `ConnectConfig` have been renamed to `New` and `NewWithConfig` respectively. The `LazyConnect` option has been removed. Pools always lazily connect.\n\n## pgtype\n\nThe `pgtype` package has been significantly changed.\n\n### NULL Representation\n\nPreviously, types had a `Status` field that could be `Undefined`, `Null`, or `Present`. This has been changed to a\n`Valid` `bool` field to harmonize with how `database/sql` represents `NULL` and to make the zero value useable.\n\nPreviously, a type that implemented `driver.Valuer` would have the `Value` method called even on a nil pointer. All nils\nwhether typed or untyped now represent `NULL`.\n\n### Codec and Value Split\n\nPreviously, the type system combined decoding and encoding values with the value types. e.g. Type `Int8` both handled\nencoding and decoding the PostgreSQL representation and acted as a value object. This caused some difficulties when\nthere was not an exact 1 to 1 relationship between the Go types and the PostgreSQL types For example, scanning a\nPostgreSQL binary `numeric` into a Go `float64` was awkward (see https://github.com/jackc/pgtype/issues/147). This\nconcepts have been separated. A `Codec` only has responsibility for encoding and decoding values. Value types are\ngenerally defined by implementing an interface that a particular `Codec` understands (e.g. `PointScanner` and\n`PointValuer` for the PostgreSQL `point` type).\n\n### Array Types\n\nAll array types are now handled by `ArrayCodec` instead of using code generation for each new array type. This also\nmeans that less common array types such as `point[]` are now supported. `Array[T]` supports PostgreSQL multi-dimensional\narrays.\n\n### Composite Types\n\nComposite types must be registered before use. `CompositeFields` may still be used to construct and destruct composite\nvalues, but any type may now implement `CompositeIndexGetter` and `CompositeIndexScanner` to be used as a composite.\n\n### Range Types\n\nRange types are now handled with types `RangeCodec` and `Range[T]`. This allows additional user defined range types to\neasily be handled. Multirange types are handled similarly with `MultirangeCodec` and `Multirange[T]`.\n\n### pgxtype\n\n`LoadDataType` moved to `*Conn` as `LoadType`.\n\n### Bytea\n\nThe `Bytea` and `GenericBinary` types have been replaced. Use the following instead:\n\n* `[]byte` - For normal usage directly use `[]byte`.\n* `DriverBytes` - Uses driver memory only available until next database method call. Avoids a copy and an allocation.\n* `PreallocBytes` - Uses preallocated byte slice to avoid an allocation.\n* `UndecodedBytes` - Avoids any decoding. Allows working with raw bytes.\n\n### Dropped lib/pq Support\n\n`pgtype` previously supported and was tested against [lib/pq](https://github.com/lib/pq). While it will continue to work\nin most cases this is no longer supported.\n\n### database/sql Scan\n\nPreviously, most `Scan` implementations would convert `[]byte` to `string` automatically to decode a text value. Now\nonly `string` is handled. This is to allow the possibility of future binary support in `database/sql` mode by\nconsidering `[]byte` to be binary format and `string` text format. This change should have no effect for any use with\n`pgx`. The previous behavior was only necessary for `lib/pq` compatibility.\n\nAdded `*Map.SQLScanner` to create a `sql.Scanner` for types such as `[]int32` and `Range[T]` that do not implement\n`sql.Scanner` directly.\n\n### Number Type Fields Include Bit size\n\n`Int2`, `Int4`, `Int8`, `Float4`, `Float8`, and `Uint32` fields now include bit size. e.g. `Int` is renamed to `Int64`.\nThis matches the convention set by `database/sql`. In addition, for comparable types like `pgtype.Int8` and\n`sql.NullInt64` the structures are identical. This means they can be directly converted one to another.\n\n### 3rd Party Type Integrations\n\n* Extracted integrations with https://github.com/shopspring/decimal and https://github.com/gofrs/uuid to\n  https://github.com/jackc/pgx-shopspring-decimal and https://github.com/jackc/pgx-gofrs-uuid respectively. This trims\n  the pgx dependency tree.\n\n### Other Changes\n\n* `Bit` and `Varbit` are both replaced by the `Bits` type.\n* `CID`, `OID`, `OIDValue`, and `XID` are replaced by the `Uint32` type.\n* `Hstore` is now defined as `map[string]*string`.\n* `JSON` and `JSONB` types removed. Use `[]byte` or `string` directly.\n* `QChar` type removed. Use `rune` or `byte` directly.\n* `Inet` and `Cidr` types removed. Use `netip.Addr` and `netip.Prefix` directly. These types are more memory efficient than the previous `net.IPNet`.\n* `Macaddr` type removed. Use `net.HardwareAddr` directly.\n* Renamed `pgtype.ConnInfo` to `pgtype.Map`.\n* Renamed `pgtype.DataType` to `pgtype.Type`.\n* Renamed `pgtype.None` to `pgtype.Finite`.\n* `RegisterType` now accepts a `*Type` instead of `Type`.\n* Assorted array helper methods and types made private.\n\n## stdlib\n\n* Removed `AcquireConn` and `ReleaseConn` as that functionality has been built in since Go 1.13.\n\n## Reduced Memory Usage by Reusing Read Buffers\n\nPreviously, the connection read buffer would allocate large chunks of memory and never reuse them. This allowed\ntransferring ownership to anything such as scanned values without incurring an additional allocation and memory copy.\nHowever, this came at the cost of overall increased memory allocation size. But worse it was also possible to pin large\nchunks of memory by retaining a reference to a small value that originally came directly from the read buffer. Now\nownership remains with the read buffer and anything needing to retain a value must make a copy.\n\n## Query Execution Modes\n\nControl over automatic prepared statement caching and simple protocol use are now combined into query execution mode.\nSee documentation for `QueryExecMode`.\n\n## QueryRewriter Interface and NamedArgs\n\npgx now supports named arguments with the `NamedArgs` type. This is implemented via the new `QueryRewriter` interface which\nallows arbitrary rewriting of query SQL and arguments.\n\n## RowScanner Interface\n\nThe `RowScanner` interface allows a single argument to Rows.Scan to scan the entire row.\n\n## Rows Result Helpers\n\n* `CollectRows` and `RowTo*` functions simplify collecting results into a slice.\n* `CollectOneRow` collects one row using `RowTo*` functions.\n* `ForEachRow` simplifies scanning each row and executing code using the scanned values. `ForEachRow` replaces `QueryFunc`.\n\n## Tx Helpers\n\nRather than every type that implemented `Begin` or `BeginTx` methods also needing to implement `BeginFunc` and\n`BeginTxFunc` these methods have been converted to functions that take a db that implements `Begin` or `BeginTx`.\n\n## Improved Batch Query Ergonomics\n\nPreviously, the code for building a batch went in one place before the call to `SendBatch`, and the code for reading the\nresults went in one place after the call to `SendBatch`. This could make it difficult to match up the query and the code\nto handle the results. Now `Queue` returns a `QueuedQuery` which has methods `Query`, `QueryRow`, and `Exec` which can\nbe used to register a callback function that will handle the result. Callback functions are called automatically when\n`BatchResults.Close` is called.\n\n## SendBatch Uses Pipeline Mode When Appropriate\n\nPreviously, a batch with 10 unique parameterized statements executed 100 times would entail 11 network round trips. 1\nfor each prepare / describe and 1 for executing them all. Now pipeline mode is used to prepare / describe all statements\nin a single network round trip. So it would only take 2 round trips.\n\n## Tracing and Logging\n\nInternal logging support has been replaced with tracing hooks. This allows custom tracing integration with tools like OpenTelemetry. Package tracelog provides an adapter for pgx v4 loggers to act as a tracer.\n\nAll integrations with 3rd party loggers have been extracted to separate repositories. This trims the pgx dependency\ntree.\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\npgx is a PostgreSQL driver and toolkit for Go (`github.com/jackc/pgx/v5`). It provides both a native PostgreSQL interface and a `database/sql` compatible driver. Requires Go 1.24+ and supports PostgreSQL 14+ and CockroachDB.\n\n## Build & Test Commands\n\n```bash\n# Run all tests (requires PGX_TEST_DATABASE to be set)\ngo test ./...\n\n# Run a specific test\ngo test -run TestFunctionName ./...\n\n# Run tests for a specific package\ngo test ./pgconn/...\n\n# Run tests with race detector\ngo test -race ./...\n\n# DevContainer: run tests against specific PostgreSQL versions\n./test.sh pg18                      # Default: PostgreSQL 18\n./test.sh pg16 -run TestConnect     # Specific test against PG16\n./test.sh crdb                      # CockroachDB\n./test.sh all                       # All targets (pg14-18 + crdb)\n\n# Format (always run after making changes)\ngoimports -w .\n\n# Lint\ngolangci-lint run ./...\n```\n\n## Test Database Setup\n\nTests require `PGX_TEST_DATABASE` environment variable. In the devcontainer, `test.sh` handles this. For local development:\n\n```bash\nexport PGX_TEST_DATABASE=\"host=localhost user=postgres password=postgres dbname=pgx_test\"\n```\n\nThe test database needs extensions: `hstore`, `ltree`, and a `uint64` domain. See `testsetup/postgresql_setup.sql` for full setup. Many tests are skipped unless additional `PGX_TEST_*` env vars are set (for TLS, SCRAM, MD5, unix socket, PgBouncer testing).\n\n## Architecture\n\nThe codebase is a layered architecture, bottom-up:\n\n- **pgproto3/** — PostgreSQL wire protocol v3 encoder/decoder. Defines `FrontendMessage` and `BackendMessage` types for every protocol message.\n- **pgconn/** — Low-level connection layer (roughly libpq-equivalent). Handles authentication, TLS, query execution, COPY protocol, and notifications. `PgConn` is the core type.\n- **pgx** (root package) — High-level query interface built on `pgconn`. Provides `Conn`, `Rows`, `Tx`, `Batch`, `CopyFrom`, and generic helpers like `CollectRows`/`ForEachRow`. Includes automatic statement caching (LRU).\n- **pgtype/** — Type system mapping between Go and PostgreSQL types (70+ types). Key interfaces: `Codec`, `Type`, `TypeMap`. Custom types (enums, composites, domains) are registered through `TypeMap`.\n- **pgxpool/** — Concurrency-safe connection pool built on `puddle/v2`. `Pool` is the main type; wraps `pgx.Conn`.\n- **stdlib/** — `database/sql` compatibility adapter.\n\nSupporting packages:\n- **internal/stmtcache/** — Prepared statement cache with LRU eviction\n- **internal/sanitize/** — SQL query sanitization\n- **tracelog/** — Logging adapter that implements tracer interfaces\n- **multitracer/** — Composes multiple tracers into one\n- **pgxtest/** — Test helpers for running tests across connection types\n\n## Key Design Conventions\n\n- **Semantic versioning** — strictly followed. Do not break the public API (no removing or renaming exported types, functions, methods, or fields; no changing function signatures).\n- **Minimal dependencies** — adding new dependencies is strongly discouraged (see CONTRIBUTING.md).\n- **Context-based** — all blocking operations take `context.Context`.\n- **Tracer interfaces** — observability via `QueryTracer`, `BatchTracer`, `CopyFromTracer`, `PrepareTracer` on `ConnConfig.Tracer`.\n- **Formatting** — always run `goimports -w .` after making changes to ensure code is properly formatted. CI checks formatting via `gofmt -l -s -w . && git diff --exit-code`. `gofumpt` with extra rules is also enforced via `golangci-lint`.\n- **Linters** — `govet` and `ineffassign` only (configured in `.golangci.yml`).\n- **CI matrix** — tests run against Go 1.24/1.25 × PostgreSQL 14-18 + CockroachDB, on Linux and Windows. Race detector enabled on Linux only.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Discuss Significant Changes\n\nBefore you invest a significant amount of time on a change, please create a discussion or issue describing your\nproposal. This will help to ensure your proposed change has a reasonable chance of being merged.\n\n## Avoid Dependencies\n\nAdding a dependency is a big deal. While on occasion a new dependency may be accepted, the default answer to any change\nthat adds a dependency is no.\n\n## AI\n\nUsing AI is acceptable (not that it can really be stopped) under one the following conditions.\n\n* AI was used, but you deeply understand the code and you can answer questions regarding your change. You are not going\n  to answer questions with \"I don't know\", AI did it. You are not going to \"answer\" questions by relaying them to your\n  agent. This is wasteful of the code reviewer's time.\n* AI was used to solve a problem without your deep understanding. This can still be a good starting point for a fix or\n  feature. But you need to clearly state that this is an AI proposal. You should include additional information such as\n  the AI used and what prompts were used. You should also be aware that large, complicated, or subtle changes may be\n  rejected simply because the reviewer is not confident in a change that no human understands.\n\n## Development Environment Setup\n\npgx tests naturally require a PostgreSQL database. It will connect to the database specified in the `PGX_TEST_DATABASE`\nenvironment variable. The `PGX_TEST_DATABASE` environment variable can either be a URL or key-value pairs. In addition,\nthe standard `PG*` environment variables will be respected. Consider using [direnv](https://github.com/direnv/direnv) to\nsimplify environment variable handling.\n\n### Devcontainer\n\nThe easiest way to start development is with the included devcontainer. It includes containers for each supported\nPostgreSQL version as well as CockroachDB. `./test.sh all` will run the tests against all database types.\n\n### Using an Existing PostgreSQL Cluster Outside of a Devcontainer\n\nIf you already have a PostgreSQL development server this is the quickest way to start and run the majority of the pgx\ntest suite. Some tests will be skipped that require server configuration changes (e.g. those testing different\nauthentication methods).\n\nCreate and setup a test database:\n\n```\nexport PGDATABASE=pgx_test\ncreatedb\npsql -c 'create extension hstore;'\npsql -c 'create extension ltree;'\npsql -c 'create domain uint64 as numeric(20,0);'\n```\n\nEnsure a `postgres` user exists. This happens by default in normal PostgreSQL installs, but some installation methods\nsuch as Homebrew do not.\n\n```\ncreateuser -s postgres\n```\n\nEnsure your `PGX_TEST_DATABASE` environment variable points to the database you just created and run the tests.\n\n```\nexport PGX_TEST_DATABASE=\"host=/private/tmp database=pgx_test\"\ngo test ./...\n```\n\nThis will run the vast majority of the tests, but some tests will be skipped (e.g. those testing different connection methods).\n\n### Creating a New PostgreSQL Cluster Exclusively for Testing Outside of a Devcontainer\n\nThe following environment variables need to be set both for initial setup and whenever the tests are run. (direnv is\nhighly recommended). Depending on your platform, you may need to change the host for `PGX_TEST_UNIX_SOCKET_CONN_STRING`.\n\n```\nexport PGPORT=5015\nexport PGUSER=postgres\nexport PGDATABASE=pgx_test\nexport POSTGRESQL_DATA_DIR=postgresql\n\nexport PGX_TEST_DATABASE=\"host=127.0.0.1 database=pgx_test user=pgx_md5 password=secret\"\nexport PGX_TEST_UNIX_SOCKET_CONN_STRING=\"host=/private/tmp database=pgx_test\"\nexport PGX_TEST_TCP_CONN_STRING=\"host=127.0.0.1 database=pgx_test user=pgx_md5 password=secret\"\nexport PGX_TEST_SCRAM_PASSWORD_CONN_STRING=\"host=127.0.0.1 user=pgx_scram password=secret database=pgx_test channel_binding=disable\"\nexport PGX_TEST_SCRAM_PLUS_CONN_STRING=\"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem database=pgx_test channel_binding=require\"\nexport PGX_TEST_MD5_PASSWORD_CONN_STRING=\"host=127.0.0.1 database=pgx_test user=pgx_md5 password=secret\"\nexport PGX_TEST_PLAIN_PASSWORD_CONN_STRING=\"host=127.0.0.1 user=pgx_pw password=secret\"\nexport PGX_TEST_TLS_CONN_STRING=\"host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem channel_binding=disable\"\nexport PGX_SSL_PASSWORD=certpw\nexport PGX_TEST_TLS_CLIENT_CONN_STRING=\"host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem database=pgx_test sslcert=`pwd`/.testdb/pgx_sslcert.crt sslkey=`pwd`/.testdb/pgx_sslcert.key\"\n```\n\nCreate a new database cluster.\n\n```\ninitdb --locale=en_US -E UTF-8 --username=postgres .testdb/$POSTGRESQL_DATA_DIR\n\necho \"listen_addresses = '127.0.0.1'\" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf\necho \"port = $PGPORT\" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf\ncat testsetup/postgresql_ssl.conf >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf\ncp testsetup/pg_hba.conf .testdb/$POSTGRESQL_DATA_DIR/pg_hba.conf\n\ncd .testdb\n\n# Generate CA, server, and encrypted client certificates.\ngo run ../testsetup/generate_certs.go\n\n# Copy certificates to server directory and set permissions.\ncp ca.pem $POSTGRESQL_DATA_DIR/root.crt\ncp localhost.key $POSTGRESQL_DATA_DIR/server.key\nchmod 600 $POSTGRESQL_DATA_DIR/server.key\ncp localhost.crt $POSTGRESQL_DATA_DIR/server.crt\n\ncd ..\n```\n\n\nStart the new cluster. This will be necessary whenever you are running pgx tests.\n\n```\npostgres -D .testdb/$POSTGRESQL_DATA_DIR\n```\n\nSetup the test database in the new cluster.\n\n```\ncreatedb\npsql --no-psqlrc -f testsetup/postgresql_setup.sql\n```\n\n### PgBouncer\n\nThere are tests specific for PgBouncer that will be executed if `PGX_TEST_PGBOUNCER_CONN_STRING` is set.\n\n### Optional Tests\n\npgx supports multiple connection types and means of authentication. These tests are optional. They will only run if the\nappropriate environment variables are set. In addition, there may be tests specific to particular PostgreSQL versions,\nnon-PostgreSQL servers (e.g. CockroachDB), or connection poolers (e.g. PgBouncer). `go test ./... -v | grep SKIP` to see\nif any tests are being skipped.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2013-2021 Jack Christensen\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![Go Reference](https://pkg.go.dev/badge/github.com/jackc/pgx/v5.svg)](https://pkg.go.dev/github.com/jackc/pgx/v5)\n[![Build Status](https://github.com/jackc/pgx/actions/workflows/ci.yml/badge.svg)](https://github.com/jackc/pgx/actions/workflows/ci.yml)\n\n# pgx - PostgreSQL Driver and Toolkit\n\npgx is a pure Go driver and toolkit for PostgreSQL.\n\nThe pgx driver is a low-level, high performance interface that exposes PostgreSQL-specific features such as `LISTEN` /\n`NOTIFY` and `COPY`. It also includes an adapter for the standard `database/sql` interface.\n\nThe toolkit component is a related set of packages that implement PostgreSQL functionality such as parsing the wire protocol\nand type mapping between PostgreSQL and Go. These underlying packages can be used to implement alternative drivers,\nproxies, load balancers, logical replication clients, etc.\n\n## Example Usage\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\nfunc main() {\n\t// urlExample := \"postgres://username:password@localhost:5432/database_name\"\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"DATABASE_URL\"))\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Unable to connect to database: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer conn.Close(context.Background())\n\n\tvar name string\n\tvar weight int64\n\terr = conn.QueryRow(context.Background(), \"select name, weight from widgets where id=$1\", 42).Scan(&name, &weight)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"QueryRow failed: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tfmt.Println(name, weight)\n}\n```\n\nSee the [getting started guide](https://github.com/jackc/pgx/wiki/Getting-started-with-pgx) for more information.\n\n## Features\n\n* Support for approximately 70 different PostgreSQL types\n* Automatic statement preparation and caching\n* Batch queries\n* Single-round trip query mode\n* Full TLS connection control\n* Binary format support for custom types (allows for much quicker encoding/decoding)\n* `COPY` protocol support for faster bulk data loads\n* Tracing and logging support\n* Connection pool with after-connect hook for arbitrary connection setup\n* `LISTEN` / `NOTIFY`\n* Conversion of PostgreSQL arrays to Go slice mappings for integers, floats, and strings\n* `hstore` support\n* `json` and `jsonb` support\n* Maps `inet` and `cidr` PostgreSQL types to `netip.Addr` and `netip.Prefix`\n* Large object support\n* NULL mapping to pointer to pointer\n* Supports `database/sql.Scanner` and `database/sql/driver.Valuer` interfaces for custom types\n* Notice response handling\n* Simulated nested transactions with savepoints\n\n## Choosing Between the pgx and database/sql Interfaces\n\nThe pgx interface is faster. Many PostgreSQL specific features such as `LISTEN` / `NOTIFY` and `COPY` are not available\nthrough the `database/sql` interface.\n\nThe pgx interface is recommended when:\n\n1. The application only targets PostgreSQL.\n2. No other libraries that require `database/sql` are in use.\n\nIt is also possible to use the `database/sql` interface and convert a connection to the lower-level pgx interface as needed.\n\n## Testing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions.\n\n## Architecture\n\nSee the presentation at Golang Estonia, [PGX Top to Bottom](https://www.youtube.com/watch?v=sXMSWhcHCf8) for a description of pgx architecture.\n\n## Supported Go and PostgreSQL Versions\n\npgx supports the same versions of Go and PostgreSQL that are supported by their respective teams. For [Go](https://golang.org/doc/devel/release.html#policy) that is the two most recent major releases and for [PostgreSQL](https://www.postgresql.org/support/versioning/) the major releases in the last 5 years. This means pgx supports Go 1.24 and higher and PostgreSQL 14 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).\n\n## Version Policy\n\npgx follows semantic versioning for the documented public API on stable releases. `v5` is the latest stable major version.\n\n## PGX Family Libraries\n\n### [github.com/jackc/pglogrepl](https://github.com/jackc/pglogrepl)\n\npglogrepl provides functionality to act as a client for PostgreSQL logical replication.\n\n### [github.com/jackc/pgmock](https://github.com/jackc/pgmock)\n\npgmock offers the ability to create a server that mocks the PostgreSQL wire protocol. This is used internally to test pgx by purposely inducing unusual errors. pgproto3 and pgmock together provide most of the foundational tooling required to implement a PostgreSQL proxy or MitM (such as for a custom connection pooler).\n\n### [github.com/jackc/tern](https://github.com/jackc/tern)\n\ntern is a stand-alone SQL migration system.\n\n### [github.com/jackc/pgerrcode](https://github.com/jackc/pgerrcode)\n\npgerrcode contains constants for the PostgreSQL error codes.\n\n## Adapters for 3rd Party Types\n\n* [github.com/jackc/pgx-gofrs-uuid](https://github.com/jackc/pgx-gofrs-uuid)\n* [github.com/jackc/pgx-shopspring-decimal](https://github.com/jackc/pgx-shopspring-decimal)\n* [github.com/ColeBurch/pgx-govalues-decimal](https://github.com/ColeBurch/pgx-govalues-decimal)\n* [github.com/twpayne/pgx-geos](https://github.com/twpayne/pgx-geos) ([PostGIS](https://postgis.net/) and [GEOS](https://libgeos.org/) via [go-geos](https://github.com/twpayne/go-geos))\n* [github.com/vgarvardt/pgx-google-uuid](https://github.com/vgarvardt/pgx-google-uuid)\n\n\n## Adapters for 3rd Party Tracers\n\n* [github.com/jackhopner/pgx-xray-tracer](https://github.com/jackhopner/pgx-xray-tracer)\n* [github.com/exaring/otelpgx](https://github.com/exaring/otelpgx)\n\n## Adapters for 3rd Party Loggers\n\nThese adapters can be used with the tracelog package.\n\n* [github.com/jackc/pgx-go-kit-log](https://github.com/jackc/pgx-go-kit-log)\n* [github.com/jackc/pgx-log15](https://github.com/jackc/pgx-log15)\n* [github.com/jackc/pgx-logrus](https://github.com/jackc/pgx-logrus)\n* [github.com/jackc/pgx-zap](https://github.com/jackc/pgx-zap)\n* [github.com/jackc/pgx-zerolog](https://github.com/jackc/pgx-zerolog)\n* [github.com/mcosta74/pgx-slog](https://github.com/mcosta74/pgx-slog)\n* [github.com/kataras/pgx-golog](https://github.com/kataras/pgx-golog)\n\n## 3rd Party Libraries with PGX Support\n\n### [github.com/pashagolub/pgxmock](https://github.com/pashagolub/pgxmock)\n\npgxmock is a mock library implementing pgx interfaces.\npgxmock has one and only purpose - to simulate pgx behavior in tests, without needing a real database connection.\n\n### [github.com/georgysavva/scany](https://github.com/georgysavva/scany)\n\nLibrary for scanning data from a database into Go structs and more.\n\n### [github.com/vingarcia/ksql](https://github.com/vingarcia/ksql)\n\nA carefully designed SQL client for making using SQL easier,\nmore productive, and less error-prone on Golang.\n\n### [github.com/otan/gopgkrb5](https://github.com/otan/gopgkrb5)\n\nAdds GSSAPI / Kerberos authentication support.\n\n### [github.com/wcamarao/pmx](https://github.com/wcamarao/pmx)\n\nExplicit data mapping and scanning library for Go structs and slices.\n\n### [github.com/stephenafamo/scan](https://github.com/stephenafamo/scan)\n\nType safe and flexible package for scanning database data into Go types.\nSupports, structs, maps, slices and custom mapping functions.\n\n### [github.com/z0ne-dev/mgx](https://github.com/z0ne-dev/mgx)\n\nCode first migration library for native pgx (no database/sql abstraction).\n\n### [github.com/amirsalarsafaei/sqlc-pgx-monitoring](https://github.com/amirsalarsafaei/sqlc-pgx-monitoring)\n\nA database monitoring/metrics library for pgx and sqlc. Trace, log and monitor your sqlc query performance using OpenTelemetry.\n\n### [https://github.com/nikolayk812/pgx-outbox](https://github.com/nikolayk812/pgx-outbox)\n\nSimple Golang implementation for transactional outbox pattern for PostgreSQL using jackc/pgx driver.\n\n### [https://github.com/Arlandaren/pgxWrappy](https://github.com/Arlandaren/pgxWrappy)\n\nSimplifies working with the pgx library, providing convenient scanning of nested structures.\n\n### [https://github.com/KoNekoD/pgx-colon-query-rewriter](https://github.com/KoNekoD/pgx-colon-query-rewriter)\n\nImplementation of the pgx query rewriter to use ':' instead of '@' in named query parameters.\n"
  },
  {
    "path": "Rakefile",
    "content": "require \"erb\"\n\nrule '.go' => '.go.erb' do |task|\n  erb = ERB.new(File.read(task.source))\n  File.write(task.name, \"// Code generated from #{task.source}. DO NOT EDIT.\\n\\n\" + erb.result(binding))\n  sh \"goimports\", \"-w\", task.name\nend\n\ngenerated_code_files = [\n  \"pgtype/int.go\",\n  \"pgtype/int_test.go\",\n  \"pgtype/integration_benchmark_test.go\",\n  \"pgtype/zeronull/int.go\",\n  \"pgtype/zeronull/int_test.go\"\n]\n\ndesc \"Generate code\"\ntask generate: generated_code_files\n"
  },
  {
    "path": "batch.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// QueuedQuery is a query that has been queued for execution via a Batch.\ntype QueuedQuery struct {\n\tSQL       string\n\tArguments []any\n\tFn        batchItemFunc\n\tsd        *pgconn.StatementDescription\n}\n\ntype batchItemFunc func(br BatchResults) error\n\n// Query sets fn to be called when the response to qq is received.\nfunc (qq *QueuedQuery) Query(fn func(rows Rows) error) {\n\tqq.Fn = func(br BatchResults) error {\n\t\trows, _ := br.Query()\n\t\tdefer rows.Close()\n\n\t\terr := fn(rows)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trows.Close()\n\n\t\treturn rows.Err()\n\t}\n}\n\n// Query sets fn to be called when the response to qq is received.\nfunc (qq *QueuedQuery) QueryRow(fn func(row Row) error) {\n\tqq.Fn = func(br BatchResults) error {\n\t\trow := br.QueryRow()\n\t\treturn fn(row)\n\t}\n}\n\n// Exec sets fn to be called when the response to qq is received.\n//\n// Note: for simple batch insert uses where it is not required to handle\n// each potential error individually, it's sufficient to not set any callbacks,\n// and just handle the return value of BatchResults.Close.\nfunc (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {\n\tqq.Fn = func(br BatchResults) error {\n\t\tct, err := br.Exec()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn fn(ct)\n\t}\n}\n\n// Batch queries are a way of bundling multiple queries together to avoid\n// unnecessary network round trips. A Batch must only be sent once.\ntype Batch struct {\n\tQueuedQueries []*QueuedQuery\n}\n\n// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement. The only pgx option\n// argument that is supported is QueryRewriter. Queries are executed using the connection's DefaultQueryExecMode.\n//\n// While query can contain multiple statements if the connection's DefaultQueryExecMode is QueryModeSimple, this should\n// be avoided. QueuedQuery.Fn must not be set as it will only be called for the first query. That is, QueuedQuery.Query,\n// QueuedQuery.QueryRow, and QueuedQuery.Exec must not be called. In addition, any error messages or tracing that\n// include the current query may reference the wrong query.\nfunc (b *Batch) Queue(query string, arguments ...any) *QueuedQuery {\n\tqq := &QueuedQuery{\n\t\tSQL:       query,\n\t\tArguments: arguments,\n\t}\n\tb.QueuedQueries = append(b.QueuedQueries, qq)\n\treturn qq\n}\n\n// Len returns number of queries that have been queued so far.\nfunc (b *Batch) Len() int {\n\treturn len(b.QueuedQueries)\n}\n\ntype BatchResults interface {\n\t// Exec reads the results from the next query in the batch as if the query has been sent with Conn.Exec. Prefer\n\t// calling Exec on the QueuedQuery, or just calling Close.\n\tExec() (pgconn.CommandTag, error)\n\n\t// Query reads the results from the next query in the batch as if the query has been sent with Conn.Query. Prefer\n\t// calling Query on the QueuedQuery.\n\tQuery() (Rows, error)\n\n\t// QueryRow reads the results from the next query in the batch as if the query has been sent with Conn.QueryRow.\n\t// Prefer calling QueryRow on the QueuedQuery.\n\tQueryRow() Row\n\n\t// Close closes the batch operation. All unread results are read and any callback functions registered with\n\t// QueuedQuery.Query, QueuedQuery.QueryRow, or QueuedQuery.Exec will be called. If a callback function returns an\n\t// error or the batch encounters an error subsequent callback functions will not be called.\n\t//\n\t// For simple batch inserts inside a transaction or similar queries, it's sufficient to not set any callbacks,\n\t// and just handle the return value of Close.\n\t//\n\t// Close must be called before the underlying connection can be used again. Any error that occurred during a batch\n\t// operation may have made it impossible to resyncronize the connection with the server. In this case the underlying\n\t// connection will have been closed.\n\t//\n\t// Close is safe to call multiple times. If it returns an error subsequent calls will return the same error. Callback\n\t// functions will not be rerun.\n\tClose() error\n}\n\ntype batchResults struct {\n\tctx       context.Context\n\tconn      *Conn\n\tmrr       *pgconn.MultiResultReader\n\terr       error\n\tb         *Batch\n\tqqIdx     int\n\tclosed    bool\n\tendTraced bool\n}\n\n// Exec reads the results from the next query in the batch as if the query has been sent with Exec.\nfunc (br *batchResults) Exec() (pgconn.CommandTag, error) {\n\tif br.err != nil {\n\t\treturn pgconn.CommandTag{}, br.err\n\t}\n\tif br.closed {\n\t\treturn pgconn.CommandTag{}, fmt.Errorf(\"batch already closed\")\n\t}\n\n\tquery, arguments, _ := br.nextQueryAndArgs()\n\n\tif !br.mrr.NextResult() {\n\t\terr := br.mrr.Close()\n\t\tif err == nil {\n\t\t\terr = errors.New(\"no more results in batch\")\n\t\t}\n\t\tif br.conn.batchTracer != nil {\n\t\t\tbr.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{\n\t\t\t\tSQL:  query,\n\t\t\t\tArgs: arguments,\n\t\t\t\tErr:  err,\n\t\t\t})\n\t\t}\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\tcommandTag, err := br.mrr.ResultReader().Close()\n\tif err != nil {\n\t\tbr.err = err\n\t\tbr.mrr.Close()\n\t}\n\n\tif br.conn.batchTracer != nil {\n\t\tbr.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{\n\t\t\tSQL:        query,\n\t\t\tArgs:       arguments,\n\t\t\tCommandTag: commandTag,\n\t\t\tErr:        br.err,\n\t\t})\n\t}\n\n\treturn commandTag, br.err\n}\n\n// Query reads the results from the next query in the batch as if the query has been sent with Query.\nfunc (br *batchResults) Query() (Rows, error) {\n\tquery, arguments, ok := br.nextQueryAndArgs()\n\tif !ok {\n\t\tquery = \"batch query\"\n\t}\n\n\tif br.err != nil {\n\t\treturn &baseRows{err: br.err, closed: true}, br.err\n\t}\n\n\tif br.closed {\n\t\talreadyClosedErr := fmt.Errorf(\"batch already closed\")\n\t\treturn &baseRows{err: alreadyClosedErr, closed: true}, alreadyClosedErr\n\t}\n\n\trows := br.conn.getRows(br.ctx, query, arguments)\n\trows.batchTracer = br.conn.batchTracer\n\n\tif !br.mrr.NextResult() {\n\t\trows.err = br.mrr.Close()\n\t\tif rows.err == nil {\n\t\t\trows.err = errors.New(\"no more results in batch\")\n\t\t}\n\t\trows.closed = true\n\n\t\tif br.conn.batchTracer != nil {\n\t\t\tbr.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{\n\t\t\t\tSQL:  query,\n\t\t\t\tArgs: arguments,\n\t\t\t\tErr:  rows.err,\n\t\t\t})\n\t\t}\n\n\t\treturn rows, rows.err\n\t}\n\n\trows.resultReader = br.mrr.ResultReader()\n\treturn rows, nil\n}\n\n// QueryRow reads the results from the next query in the batch as if the query has been sent with QueryRow.\nfunc (br *batchResults) QueryRow() Row {\n\trows, _ := br.Query()\n\treturn (*connRow)(rows.(*baseRows))\n}\n\n// Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to\n// resyncronize the connection with the server. In this case the underlying connection will have been closed.\nfunc (br *batchResults) Close() error {\n\tdefer func() {\n\t\tif !br.endTraced {\n\t\t\tif br.conn != nil && br.conn.batchTracer != nil {\n\t\t\t\tbr.conn.batchTracer.TraceBatchEnd(br.ctx, br.conn, TraceBatchEndData{Err: br.err})\n\t\t\t}\n\t\t\tbr.endTraced = true\n\t\t}\n\n\t\tinvalidateCachesOnBatchResultsError(br.conn, br.b, br.err)\n\t}()\n\n\tif br.err != nil {\n\t\treturn br.err\n\t}\n\n\tif br.closed {\n\t\treturn nil\n\t}\n\n\t// Read and run fn for all remaining items\n\tfor br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {\n\t\tif br.b.QueuedQueries[br.qqIdx].Fn != nil {\n\t\t\terr := br.b.QueuedQueries[br.qqIdx].Fn(br)\n\t\t\tif err != nil {\n\t\t\t\tbr.err = err\n\t\t\t}\n\t\t} else {\n\t\t\tbr.Exec()\n\t\t}\n\t}\n\n\tbr.closed = true\n\n\terr := br.mrr.Close()\n\tif br.err == nil {\n\t\tbr.err = err\n\t}\n\n\treturn br.err\n}\n\nfunc (br *batchResults) earlyError() error {\n\treturn br.err\n}\n\nfunc (br *batchResults) nextQueryAndArgs() (query string, args []any, ok bool) {\n\tif br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {\n\t\tbi := br.b.QueuedQueries[br.qqIdx]\n\t\tquery = bi.SQL\n\t\targs = bi.Arguments\n\t\tok = true\n\t\tbr.qqIdx++\n\t}\n\treturn query, args, ok\n}\n\ntype pipelineBatchResults struct {\n\tctx       context.Context\n\tconn      *Conn\n\tpipeline  *pgconn.Pipeline\n\tlastRows  *baseRows\n\terr       error\n\tb         *Batch\n\tqqIdx     int\n\tclosed    bool\n\tendTraced bool\n}\n\n// Exec reads the results from the next query in the batch as if the query has been sent with Exec.\nfunc (br *pipelineBatchResults) Exec() (pgconn.CommandTag, error) {\n\tif br.err != nil {\n\t\treturn pgconn.CommandTag{}, br.err\n\t}\n\tif br.closed {\n\t\treturn pgconn.CommandTag{}, fmt.Errorf(\"batch already closed\")\n\t}\n\tif br.lastRows != nil && br.lastRows.err != nil {\n\t\treturn pgconn.CommandTag{}, br.err\n\t}\n\n\tquery, arguments, err := br.nextQueryAndArgs()\n\tif err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\tresults, err := br.pipeline.GetResults()\n\tif err != nil {\n\t\tbr.err = err\n\t\treturn pgconn.CommandTag{}, br.err\n\t}\n\tvar commandTag pgconn.CommandTag\n\tswitch results := results.(type) {\n\tcase *pgconn.ResultReader:\n\t\tcommandTag, br.err = results.Close()\n\tdefault:\n\t\treturn pgconn.CommandTag{}, fmt.Errorf(\"unexpected pipeline result: %T\", results)\n\t}\n\n\tif br.conn.batchTracer != nil {\n\t\tbr.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{\n\t\t\tSQL:        query,\n\t\t\tArgs:       arguments,\n\t\t\tCommandTag: commandTag,\n\t\t\tErr:        br.err,\n\t\t})\n\t}\n\n\treturn commandTag, br.err\n}\n\n// Query reads the results from the next query in the batch as if the query has been sent with Query.\nfunc (br *pipelineBatchResults) Query() (Rows, error) {\n\tif br.err != nil {\n\t\treturn &baseRows{err: br.err, closed: true}, br.err\n\t}\n\n\tif br.closed {\n\t\talreadyClosedErr := fmt.Errorf(\"batch already closed\")\n\t\treturn &baseRows{err: alreadyClosedErr, closed: true}, alreadyClosedErr\n\t}\n\n\tif br.lastRows != nil && br.lastRows.err != nil {\n\t\tbr.err = br.lastRows.err\n\t\treturn &baseRows{err: br.err, closed: true}, br.err\n\t}\n\n\tquery, arguments, err := br.nextQueryAndArgs()\n\tif err != nil {\n\t\treturn &baseRows{err: err, closed: true}, err\n\t}\n\n\trows := br.conn.getRows(br.ctx, query, arguments)\n\trows.batchTracer = br.conn.batchTracer\n\tbr.lastRows = rows\n\n\tresults, err := br.pipeline.GetResults()\n\tif err != nil {\n\t\tbr.err = err\n\t\trows.err = err\n\t\trows.closed = true\n\n\t\tif br.conn.batchTracer != nil {\n\t\t\tbr.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{\n\t\t\t\tSQL:  query,\n\t\t\t\tArgs: arguments,\n\t\t\t\tErr:  err,\n\t\t\t})\n\t\t}\n\t} else {\n\t\tswitch results := results.(type) {\n\t\tcase *pgconn.ResultReader:\n\t\t\trows.resultReader = results\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"unexpected pipeline result: %T\", results)\n\t\t\tbr.err = err\n\t\t\trows.err = err\n\t\t\trows.closed = true\n\t\t}\n\t}\n\n\treturn rows, rows.err\n}\n\n// QueryRow reads the results from the next query in the batch as if the query has been sent with QueryRow.\nfunc (br *pipelineBatchResults) QueryRow() Row {\n\trows, _ := br.Query()\n\treturn (*connRow)(rows.(*baseRows))\n}\n\n// Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to\n// resyncronize the connection with the server. In this case the underlying connection will have been closed.\nfunc (br *pipelineBatchResults) Close() error {\n\tdefer func() {\n\t\tif !br.endTraced {\n\t\t\tif br.conn.batchTracer != nil {\n\t\t\t\tbr.conn.batchTracer.TraceBatchEnd(br.ctx, br.conn, TraceBatchEndData{Err: br.err})\n\t\t\t}\n\t\t\tbr.endTraced = true\n\t\t}\n\n\t\tinvalidateCachesOnBatchResultsError(br.conn, br.b, br.err)\n\t}()\n\n\tif br.err == nil && br.lastRows != nil && br.lastRows.err != nil {\n\t\tbr.err = br.lastRows.err\n\t}\n\n\tif br.closed {\n\t\treturn br.err\n\t}\n\n\t// Read and run fn for all remaining items\n\tfor br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {\n\t\tif br.b.QueuedQueries[br.qqIdx].Fn != nil {\n\t\t\terr := br.b.QueuedQueries[br.qqIdx].Fn(br)\n\t\t\tif err != nil {\n\t\t\t\tbr.err = err\n\t\t\t}\n\t\t} else {\n\t\t\tbr.Exec()\n\t\t}\n\t}\n\n\tbr.closed = true\n\n\terr := br.pipeline.Close()\n\tif br.err == nil {\n\t\tbr.err = err\n\t}\n\n\treturn br.err\n}\n\nfunc (br *pipelineBatchResults) earlyError() error {\n\treturn br.err\n}\n\nfunc (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, err error) {\n\tif br.b == nil {\n\t\treturn \"\", nil, errors.New(\"no reference to batch\")\n\t}\n\n\tif br.qqIdx >= len(br.b.QueuedQueries) {\n\t\treturn \"\", nil, errors.New(\"no more results in batch\")\n\t}\n\n\tbi := br.b.QueuedQueries[br.qqIdx]\n\tbr.qqIdx++\n\treturn bi.SQL, bi.Arguments, nil\n}\n\ntype emptyBatchResults struct {\n\tconn   *Conn\n\tclosed bool\n}\n\n// Exec reads the results from the next query in the batch as if the query has been sent with Exec.\nfunc (br *emptyBatchResults) Exec() (pgconn.CommandTag, error) {\n\tif br.closed {\n\t\treturn pgconn.CommandTag{}, fmt.Errorf(\"batch already closed\")\n\t}\n\treturn pgconn.CommandTag{}, errors.New(\"no more results in batch\")\n}\n\n// Query reads the results from the next query in the batch as if the query has been sent with Query.\nfunc (br *emptyBatchResults) Query() (Rows, error) {\n\tif br.closed {\n\t\talreadyClosedErr := fmt.Errorf(\"batch already closed\")\n\t\treturn &baseRows{err: alreadyClosedErr, closed: true}, alreadyClosedErr\n\t}\n\n\trows := br.conn.getRows(context.Background(), \"\", nil)\n\trows.err = errors.New(\"no more results in batch\")\n\trows.closed = true\n\treturn rows, rows.err\n}\n\n// QueryRow reads the results from the next query in the batch as if the query has been sent with QueryRow.\nfunc (br *emptyBatchResults) QueryRow() Row {\n\trows, _ := br.Query()\n\treturn (*connRow)(rows.(*baseRows))\n}\n\n// Close closes the batch operation. Any error that occurred during a batch operation may have made it impossible to\n// resyncronize the connection with the server. In this case the underlying connection will have been closed.\nfunc (br *emptyBatchResults) Close() error {\n\tbr.closed = true\n\treturn nil\n}\n\n// invalidates statement and description caches on batch results error\nfunc invalidateCachesOnBatchResultsError(conn *Conn, b *Batch, err error) {\n\tif err != nil && conn != nil && b != nil {\n\t\tif sc := conn.statementCache; sc != nil {\n\t\t\tfor _, bi := range b.QueuedQueries {\n\t\t\t\tsc.Invalidate(bi.SQL)\n\t\t\t}\n\t\t}\n\n\t\tif sc := conn.descriptionCache; sc != nil {\n\t\t\tfor _, bi := range b.QueuedQueries {\n\t\t\t\tsc.Invalidate(bi.SQL)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ErrPreprocessingBatch occurs when an error is encountered while preprocessing a batch.\n// The two preprocessing steps are \"prepare\" (server-side SQL parse/plan) and\n// \"build\" (client-side argument encoding).\ntype ErrPreprocessingBatch struct {\n\tstep string // \"prepare\" or \"build\"\n\tsql  string\n\terr  error\n}\n\nfunc newErrPreprocessingBatch(step, sql string, err error) ErrPreprocessingBatch {\n\treturn ErrPreprocessingBatch{step: step, sql: sql, err: err}\n}\n\nfunc (e ErrPreprocessingBatch) Error() string {\n\t// intentionally not including the SQL query in the error message\n\t// to avoid leaking potentially sensitive information into logs.\n\t// If the user wants the SQL, they can call SQL().\n\treturn fmt.Sprintf(\"error preprocessing batch (%s): %v\", e.step, e.err)\n}\n\nfunc (e ErrPreprocessingBatch) Unwrap() error {\n\treturn e.err\n}\n\nfunc (e ErrPreprocessingBatch) SQL() string {\n\treturn e.sql\n}\n"
  },
  {
    "path": "batch_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/internal/faultyconn\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnSendBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server serial type is incompatible with test\")\n\n\t\tsql := `create temporary table ledger(\n\t  id serial primary key,\n\t  description varchar not null,\n\t  amount int not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q1\", 1)\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q2\", 2)\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q3\", 3)\n\t\tbatch.Queue(\"select id, description, amount from ledger order by id\")\n\t\tbatch.Queue(\"select id, description, amount from ledger order by id\")\n\t\tbatch.Queue(\"select * from ledger where false\")\n\t\tbatch.Queue(\"select sum(amount) from ledger\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\tct, err := br.Exec()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif ct.RowsAffected() != 1 {\n\t\t\tt.Errorf(\"ct.RowsAffected() => %v, want %v\", ct.RowsAffected(), 1)\n\t\t}\n\n\t\tct, err = br.Exec()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif ct.RowsAffected() != 1 {\n\t\t\tt.Errorf(\"ct.RowsAffected() => %v, want %v\", ct.RowsAffected(), 1)\n\t\t}\n\n\t\tct, err = br.Exec()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif ct.RowsAffected() != 1 {\n\t\t\tt.Errorf(\"ct.RowsAffected() => %v, want %v\", ct.RowsAffected(), 1)\n\t\t}\n\n\t\tselectFromLedgerExpectedRows := []struct {\n\t\t\tid          int32\n\t\t\tdescription string\n\t\t\tamount      int32\n\t\t}{\n\t\t\t{1, \"q1\", 1},\n\t\t\t{2, \"q2\", 2},\n\t\t\t{3, \"q3\", 3},\n\t\t}\n\n\t\trows, err := br.Query()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tvar id int32\n\t\tvar description string\n\t\tvar amount int32\n\t\trowCount := 0\n\n\t\tfor rows.Next() {\n\t\t\tif rowCount >= len(selectFromLedgerExpectedRows) {\n\t\t\t\tt.Fatalf(\"got too many rows: %d\", rowCount)\n\t\t\t}\n\n\t\t\tif err := rows.Scan(&id, &description, &amount); err != nil {\n\t\t\t\tt.Fatalf(\"row %d: %v\", rowCount, err)\n\t\t\t}\n\n\t\t\tif id != selectFromLedgerExpectedRows[rowCount].id {\n\t\t\t\tt.Errorf(\"id => %v, want %v\", id, selectFromLedgerExpectedRows[rowCount].id)\n\t\t\t}\n\t\t\tif description != selectFromLedgerExpectedRows[rowCount].description {\n\t\t\t\tt.Errorf(\"description => %v, want %v\", description, selectFromLedgerExpectedRows[rowCount].description)\n\t\t\t}\n\t\t\tif amount != selectFromLedgerExpectedRows[rowCount].amount {\n\t\t\t\tt.Errorf(\"amount => %v, want %v\", amount, selectFromLedgerExpectedRows[rowCount].amount)\n\t\t\t}\n\n\t\t\trowCount++\n\t\t}\n\n\t\tif rows.Err() != nil {\n\t\t\tt.Fatal(rows.Err())\n\t\t}\n\n\t\trowCount = 0\n\t\trows, _ = br.Query()\n\t\t_, err = pgx.ForEachRow(rows, []any{&id, &description, &amount}, func() error {\n\t\t\tif id != selectFromLedgerExpectedRows[rowCount].id {\n\t\t\t\tt.Errorf(\"id => %v, want %v\", id, selectFromLedgerExpectedRows[rowCount].id)\n\t\t\t}\n\t\t\tif description != selectFromLedgerExpectedRows[rowCount].description {\n\t\t\t\tt.Errorf(\"description => %v, want %v\", description, selectFromLedgerExpectedRows[rowCount].description)\n\t\t\t}\n\t\t\tif amount != selectFromLedgerExpectedRows[rowCount].amount {\n\t\t\t\tt.Errorf(\"amount => %v, want %v\", amount, selectFromLedgerExpectedRows[rowCount].amount)\n\t\t\t}\n\n\t\t\trowCount++\n\n\t\t\treturn nil\n\t\t})\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\terr = br.QueryRow().Scan(&id, &description, &amount)\n\t\tif !errors.Is(err, pgx.ErrNoRows) {\n\t\t\tt.Errorf(\"expected pgx.ErrNoRows but got: %v\", err)\n\t\t}\n\n\t\terr = br.QueryRow().Scan(&amount)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif amount != 6 {\n\t\t\tt.Errorf(\"amount => %v, want %v\", amount, 6)\n\t\t}\n\n\t\terr = br.Close()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n}\n\nfunc TestConnSendBatchQueuedQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server serial type is incompatible with test\")\n\n\t\tsql := `create temporary table ledger(\n\t  id serial primary key,\n\t  description varchar not null,\n\t  amount int not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\tbatch := &pgx.Batch{}\n\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q1\", 1).Exec(func(ct pgconn.CommandTag) error {\n\t\t\tassert.EqualValues(t, 1, ct.RowsAffected())\n\t\t\treturn nil\n\t\t})\n\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q2\", 2).Exec(func(ct pgconn.CommandTag) error {\n\t\t\tassert.EqualValues(t, 1, ct.RowsAffected())\n\t\t\treturn nil\n\t\t})\n\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q3\", 3).Exec(func(ct pgconn.CommandTag) error {\n\t\t\tassert.EqualValues(t, 1, ct.RowsAffected())\n\t\t\treturn nil\n\t\t})\n\n\t\tselectFromLedgerExpectedRows := []struct {\n\t\t\tid          int32\n\t\t\tdescription string\n\t\t\tamount      int32\n\t\t}{\n\t\t\t{1, \"q1\", 1},\n\t\t\t{2, \"q2\", 2},\n\t\t\t{3, \"q3\", 3},\n\t\t}\n\n\t\tbatch.Queue(\"select id, description, amount from ledger order by id\").Query(func(rows pgx.Rows) error {\n\t\t\trowCount := 0\n\t\t\tvar id int32\n\t\t\tvar description string\n\t\t\tvar amount int32\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&id, &description, &amount}, func() error {\n\t\t\t\tassert.Equal(t, selectFromLedgerExpectedRows[rowCount].id, id)\n\t\t\t\tassert.Equal(t, selectFromLedgerExpectedRows[rowCount].description, description)\n\t\t\t\tassert.Equal(t, selectFromLedgerExpectedRows[rowCount].amount, amount)\n\t\t\t\trowCount++\n\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tassert.NoError(t, err)\n\t\t\treturn nil\n\t\t})\n\n\t\tbatch.Queue(\"select id, description, amount from ledger order by id\").Query(func(rows pgx.Rows) error {\n\t\t\trowCount := 0\n\t\t\tvar id int32\n\t\t\tvar description string\n\t\t\tvar amount int32\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&id, &description, &amount}, func() error {\n\t\t\t\tassert.Equal(t, selectFromLedgerExpectedRows[rowCount].id, id)\n\t\t\t\tassert.Equal(t, selectFromLedgerExpectedRows[rowCount].description, description)\n\t\t\t\tassert.Equal(t, selectFromLedgerExpectedRows[rowCount].amount, amount)\n\t\t\t\trowCount++\n\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tassert.NoError(t, err)\n\t\t\treturn nil\n\t\t})\n\n\t\tbatch.Queue(\"select * from ledger where false\").QueryRow(func(row pgx.Row) error {\n\t\t\terr := row.Scan(nil, nil, nil)\n\t\t\tassert.ErrorIs(t, err, pgx.ErrNoRows)\n\t\t\treturn nil\n\t\t})\n\n\t\tbatch.Queue(\"select sum(amount) from ledger\").QueryRow(func(row pgx.Row) error {\n\t\t\tvar sumAmount int32\n\t\t\terr := row.Scan(&sumAmount)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.EqualValues(t, 6, sumAmount)\n\t\t\treturn nil\n\t\t})\n\n\t\terr := conn.SendBatch(ctx, batch).Close()\n\t\tassert.NoError(t, err)\n\t})\n}\n\nfunc TestConnSendBatchMany(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := `create temporary table ledger(\n\t  id serial primary key,\n\t  description varchar not null,\n\t  amount int not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\tbatch := &pgx.Batch{}\n\n\t\tnumInserts := 1000\n\n\t\tfor range numInserts {\n\t\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2)\", \"q1\", 1)\n\t\t}\n\t\tbatch.Queue(\"select count(*) from ledger\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\tfor range numInserts {\n\t\t\tct, err := br.Exec()\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.EqualValues(t, 1, ct.RowsAffected())\n\t\t}\n\n\t\tvar actualInserts int\n\t\terr := br.QueryRow().Scan(&actualInserts)\n\t\tassert.NoError(t, err)\n\t\tassert.EqualValues(t, numInserts, actualInserts)\n\n\t\terr = br.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1801#issuecomment-2203784178\nfunc TestConnSendBatchReadResultsWhenNothingQueued(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbr := conn.SendBatch(ctx, batch)\n\t\tcommandTag, err := br.Exec()\n\t\trequire.Equal(t, \"\", commandTag.String())\n\t\trequire.EqualError(t, err, \"no more results in batch\")\n\t\terr = br.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnSendBatchReadMoreResultsThanQueriesSent(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 1\")\n\t\tbr := conn.SendBatch(ctx, batch)\n\t\tcommandTag, err := br.Exec()\n\t\trequire.Equal(t, \"SELECT 1\", commandTag.String())\n\t\trequire.NoError(t, err)\n\t\tcommandTag, err = br.Exec()\n\t\trequire.Equal(t, \"\", commandTag.String())\n\t\trequire.EqualError(t, err, \"no more results in batch\")\n\t\terr = br.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnSendBatchWithPreparedStatement(t *testing.T) {\n\tt.Parallel()\n\n\tmodes := []pgx.QueryExecMode{\n\t\tpgx.QueryExecModeCacheStatement,\n\t\tpgx.QueryExecModeCacheDescribe,\n\t\tpgx.QueryExecModeDescribeExec,\n\t\tpgx.QueryExecModeExec,\n\t\t// Don't test simple mode with prepared statements.\n\t}\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, modes, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\t\t_, err := conn.Prepare(ctx, \"ps1\", \"select n from generate_series(0,$1::int) n\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tbatch := &pgx.Batch{}\n\n\t\tqueryCount := 3\n\t\tfor range queryCount {\n\t\t\tbatch.Queue(\"ps1\", 5)\n\t\t}\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\tfor range queryCount {\n\t\t\trows, err := br.Query()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tfor k := 0; rows.Next(); k++ {\n\t\t\t\tvar n int\n\t\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif n != k {\n\t\t\t\t\tt.Fatalf(\"n => %v, want %v\", n, k)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif rows.Err() != nil {\n\t\t\t\tt.Fatal(rows.Err())\n\t\t\t}\n\t\t}\n\n\t\terr = br.Close()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n}\n\nfunc TestConnSendBatchWithQueryRewriter(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"something to be replaced\", &testQueryRewriter{sql: \"select $1::int\", args: []any{1}})\n\t\tbatch.Queue(\"something else to be replaced\", &testQueryRewriter{sql: \"select $1::text\", args: []any{\"hello\"}})\n\t\tbatch.Queue(\"more to be replaced\", &testQueryRewriter{sql: \"select $1::int\", args: []any{3}})\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\tvar n int32\n\t\terr := br.QueryRow().Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, n)\n\n\t\tvar s string\n\t\terr = br.QueryRow().Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"hello\", s)\n\n\t\terr = br.QueryRow().Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 3, n)\n\n\t\terr = br.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/856\nfunc TestConnSendBatchWithPreparedStatementAndStatementCacheDisabled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeDescribeExec\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\n\t_, err = conn.Prepare(ctx, \"ps1\", \"select n from generate_series(0,$1::int) n\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbatch := &pgx.Batch{}\n\n\tqueryCount := 3\n\tfor range queryCount {\n\t\tbatch.Queue(\"ps1\", 5)\n\t}\n\n\tbr := conn.SendBatch(ctx, batch)\n\n\tfor range queryCount {\n\t\trows, err := br.Query()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tfor k := 0; rows.Next(); k++ {\n\t\t\tvar n int\n\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif n != k {\n\t\t\t\tt.Fatalf(\"n => %v, want %v\", n, k)\n\t\t\t}\n\t\t}\n\n\t\tif rows.Err() != nil {\n\t\t\tt.Fatal(rows.Err())\n\t\t}\n\t}\n\n\terr = br.Close()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnSendBatchCloseRowsPartiallyRead(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select n from generate_series(0,5) n\")\n\t\tbatch.Queue(\"select n from generate_series(0,5) n\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\trows, err := br.Query()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tfor i := range 3 {\n\t\t\tif !rows.Next() {\n\t\t\t\tt.Error(\"expected a row to be available\")\n\t\t\t}\n\n\t\t\tvar n int\n\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif n != i {\n\t\t\t\tt.Errorf(\"n => %v, want %v\", n, i)\n\t\t\t}\n\t\t}\n\n\t\trows.Close()\n\n\t\trows, err = br.Query()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tfor i := 0; rows.Next(); i++ {\n\t\t\tvar n int\n\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif n != i {\n\t\t\t\tt.Errorf(\"n => %v, want %v\", n, i)\n\t\t\t}\n\t\t}\n\n\t\tif rows.Err() != nil {\n\t\t\tt.Error(rows.Err())\n\t\t}\n\n\t\terr = br.Close()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n}\n\nfunc TestConnSendBatchQueryError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select n from generate_series(0,5) n where 100/(5-n) > 0\")\n\t\tbatch.Queue(\"select n from generate_series(0,5) n\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\trows, err := br.Query()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tfor i := 0; rows.Next(); i++ {\n\t\t\tvar n int\n\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif n != i {\n\t\t\t\tt.Errorf(\"n => %v, want %v\", n, i)\n\t\t\t}\n\t\t}\n\n\t\tif pgErr, ok := rows.Err().(*pgconn.PgError); !(ok && pgErr.Code == \"22012\") {\n\t\t\tt.Errorf(\"rows.Err() => %v, want error code %v\", rows.Err(), 22012)\n\t\t}\n\n\t\terr = br.Close()\n\t\tif pgErr, ok := err.(*pgconn.PgError); !(ok && pgErr.Code == \"22012\") {\n\t\t\tt.Errorf(\"br.Close() => %v, want error code %v\", err, 22012)\n\t\t}\n\t})\n}\n\nfunc TestConnSendBatchQuerySyntaxError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 1 1\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\tvar n int32\n\t\terr := br.QueryRow().Scan(&n)\n\t\tvar pgErr *pgconn.PgError\n\t\tif !(errors.As(err, &pgErr) && pgErr.Code == \"42601\") {\n\t\t\tt.Errorf(\"rows.Err() => %v, want error code %v\", err, 42601)\n\t\t}\n\n\t\terr = br.Close()\n\t\tif err == nil {\n\t\t\tt.Error(\"Expected error\")\n\t\t}\n\t})\n}\n\nfunc TestConnSendBatchErrorReturnsErrPreprocessingBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\t// Only test exec modes that go through sendBatchExtendedWithDescription which wraps errors with ErrPreprocessingBatch.\n\tmodes := []pgx.QueryExecMode{\n\t\tpgx.QueryExecModeCacheStatement,\n\t\tpgx.QueryExecModeCacheDescribe,\n\t\tpgx.QueryExecModeDescribeExec,\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, modes, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar preprocessingErr pgx.ErrPreprocessingBatch\n\n\t\t// Test prepare step failure: syntax error in a non-first statement.\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 1\")\n\t\tbatch.Queue(\"select 1 1\") // syntax error triggers prepare failure\n\n\t\terr := conn.SendBatch(ctx, batch).Close()\n\t\trequire.Error(t, err)\n\t\trequire.ErrorAs(t, err, &preprocessingErr)\n\t\tassert.Equal(t, \"select 1 1\", preprocessingErr.SQL())\n\t\tassert.NotContains(t, preprocessingErr.Error(), \"select 1 1\") // we don't want to leak the SQL query in the error message\n\t\tassert.Contains(t, preprocessingErr.Error(), \"error preprocessing batch (prepare)\")\n\n\t\t// Test build step failure: wrong number of arguments in a statement.\n\t\tbatch = &pgx.Batch{}\n\t\tbatch.Queue(\"select 1\")\n\t\tbatch.Queue(\"select $1::int\", 1, 2) // mismatched argument count triggers build failure\n\n\t\terr = conn.SendBatch(ctx, batch).Close()\n\t\trequire.Error(t, err)\n\t\trequire.ErrorAs(t, err, &preprocessingErr)\n\t\tassert.Equal(t, \"select $1::int\", preprocessingErr.SQL())\n\t\tassert.NotContains(t, preprocessingErr.Error(), \"select $1::int\") // we don't want to leak the SQL query in the error message\n\t\tassert.Contains(t, preprocessingErr.Error(), \"error preprocessing batch (build)\")\n\t})\n}\n\nfunc TestConnSendBatchQueryRowInsert(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := `create temporary table ledger(\n\t  id serial primary key,\n\t  description varchar not null,\n\t  amount int not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 1\")\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2),($1, $2)\", \"q1\", 1)\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\tvar value int\n\t\terr := br.QueryRow().Scan(&value)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tct, err := br.Exec()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif ct.RowsAffected() != 2 {\n\t\t\tt.Errorf(\"ct.RowsAffected() => %v, want %v\", ct.RowsAffected(), 2)\n\t\t}\n\n\t\tbr.Close()\n\t})\n}\n\nfunc TestConnSendBatchQueryPartialReadInsert(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := `create temporary table ledger(\n\t  id serial primary key,\n\t  description varchar not null,\n\t  amount int not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 1 union all select 2 union all select 3\")\n\t\tbatch.Queue(\"insert into ledger(description, amount) values($1, $2),($1, $2)\", \"q1\", 1)\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\trows, err := br.Query()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\trows.Close()\n\n\t\tct, err := br.Exec()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif ct.RowsAffected() != 2 {\n\t\t\tt.Errorf(\"ct.RowsAffected() => %v, want %v\", ct.RowsAffected(), 2)\n\t\t}\n\n\t\tbr.Close()\n\t})\n}\n\nfunc TestTxSendBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := `create temporary table ledger1(\n\t  id serial primary key,\n\t  description varchar not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\tsql = `create temporary table ledger2(\n\t  id int primary key,\n\t  amount int not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\ttx, _ := conn.Begin(ctx)\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"insert into ledger1(description) values($1) returning id\", \"q1\")\n\n\t\tbr := tx.SendBatch(context.Background(), batch)\n\n\t\tvar id int\n\t\terr := br.QueryRow().Scan(&id)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tbr.Close()\n\n\t\tbatch = &pgx.Batch{}\n\t\tbatch.Queue(\"insert into ledger2(id,amount) values($1, $2)\", id, 2)\n\t\tbatch.Queue(\"select amount from ledger2 where id = $1\", id)\n\n\t\tbr = tx.SendBatch(ctx, batch)\n\n\t\tct, err := br.Exec()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif ct.RowsAffected() != 1 {\n\t\t\tt.Errorf(\"ct.RowsAffected() => %v, want %v\", ct.RowsAffected(), 1)\n\t\t}\n\n\t\tvar amount int\n\t\terr = br.QueryRow().Scan(&amount)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tbr.Close()\n\t\ttx.Commit(ctx)\n\n\t\tvar count int\n\t\tconn.QueryRow(ctx, \"select count(1) from ledger1 where id = $1\", id).Scan(&count)\n\t\tif count != 1 {\n\t\t\tt.Errorf(\"count => %v, want %v\", count, 1)\n\t\t}\n\n\t\terr = br.Close()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n}\n\nfunc TestTxSendBatchRollback(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := `create temporary table ledger1(\n\t  id serial primary key,\n\t  description varchar not null\n\t);`\n\t\tmustExec(t, conn, sql)\n\n\t\ttx, _ := conn.Begin(ctx)\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"insert into ledger1(description) values($1) returning id\", \"q1\")\n\n\t\tbr := tx.SendBatch(ctx, batch)\n\n\t\tvar id int\n\t\terr := br.QueryRow().Scan(&id)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tbr.Close()\n\t\ttx.Rollback(ctx)\n\n\t\trow := conn.QueryRow(ctx, \"select count(1) from ledger1 where id = $1\", id)\n\t\tvar count int\n\t\trow.Scan(&count)\n\t\tif count != 0 {\n\t\t\tt.Errorf(\"count => %v, want %v\", count, 0)\n\t\t}\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1578\nfunc TestSendBatchErrorWhileReadingResultsWithoutCallback(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 4 / $1::int\", 0)\n\n\t\tbatchResult := conn.SendBatch(ctx, batch)\n\n\t\t_, execErr := batchResult.Exec()\n\t\trequire.Error(t, execErr)\n\n\t\tcloseErr := batchResult.Close()\n\t\trequire.Equal(t, execErr, closeErr)\n\n\t\t// Try to use the connection.\n\t\t_, err := conn.Exec(ctx, \"select 1\")\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestSendBatchErrorWhileReadingResultsWithExecWhereSomeRowsAreReturned(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 4 / n from generate_series(-2, 2) n\")\n\n\t\tbatchResult := conn.SendBatch(ctx, batch)\n\n\t\t_, execErr := batchResult.Exec()\n\t\trequire.Error(t, execErr)\n\n\t\tcloseErr := batchResult.Close()\n\t\trequire.Equal(t, execErr, closeErr)\n\n\t\t// Try to use the connection.\n\t\t_, err := conn.Exec(ctx, \"select 1\")\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnBeginBatchDeferredError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\n\t\tmustExec(t, conn, `create temporary table t (\n\t\tid text primary key,\n\t\tn int not null,\n\t\tunique (n) deferrable initially deferred\n\t);\n\n\tinsert into t (id, n) values ('a', 1), ('b', 2), ('c', 3);`)\n\n\t\tbatch := &pgx.Batch{}\n\n\t\tbatch.Queue(`update t set n=n+1 where id='b' returning *`)\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\trows, err := br.Query()\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\n\t\tfor rows.Next() {\n\t\t\tvar id string\n\t\t\tvar n int32\n\t\t\terr = rows.Scan(&id, &n)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\n\t\terr = br.Close()\n\t\tif err == nil {\n\t\t\tt.Fatal(\"expected error 23505 but got none\")\n\t\t}\n\n\t\tif err, ok := err.(*pgconn.PgError); !ok || err.Code != \"23505\" {\n\t\t\tt.Fatalf(\"expected error 23505, got %v\", err)\n\t\t}\n\t})\n}\n\nfunc TestConnSendBatchNoStatementCache(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeDescribeExec\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\ttestConnSendBatch(t, ctx, conn, 3)\n}\n\nfunc TestConnSendBatchPrepareStatementCache(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tconfig.StatementCacheCapacity = 32\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\ttestConnSendBatch(t, ctx, conn, 3)\n}\n\nfunc TestConnSendBatchDescribeStatementCache(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheDescribe\n\tconfig.DescriptionCacheCapacity = 32\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\ttestConnSendBatch(t, ctx, conn, 3)\n}\n\nfunc testConnSendBatch(t *testing.T, ctx context.Context, conn *pgx.Conn, queryCount int) {\n\tbatch := &pgx.Batch{}\n\tfor range queryCount {\n\t\tbatch.Queue(\"select n from generate_series(0,5) n\")\n\t}\n\n\tbr := conn.SendBatch(ctx, batch)\n\n\tfor range queryCount {\n\t\trows, err := br.Query()\n\t\trequire.NoError(t, err)\n\n\t\tfor k := 0; rows.Next(); k++ {\n\t\t\tvar n int\n\t\t\terr := rows.Scan(&n)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, k, n)\n\t\t}\n\n\t\trequire.NoError(t, rows.Err())\n\t}\n\n\terr := br.Close()\n\trequire.NoError(t, err)\n}\n\nfunc TestSendBatchSimpleProtocol(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\tvar batch pgx.Batch\n\tbatch.Queue(\"SELECT 1::int\")\n\tbatch.Queue(\"SELECT 2::int; SELECT $1::int\", 3)\n\tresults := conn.SendBatch(ctx, &batch)\n\trows, err := results.Query()\n\tassert.NoError(t, err)\n\tassert.True(t, rows.Next())\n\tvalues, err := rows.Values()\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 1, values[0])\n\tassert.False(t, rows.Next())\n\n\trows, err = results.Query()\n\tassert.NoError(t, err)\n\tassert.True(t, rows.Next())\n\tvalues, err = rows.Values()\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 2, values[0])\n\tassert.False(t, rows.Next())\n\n\trows, err = results.Query()\n\tassert.NoError(t, err)\n\tassert.True(t, rows.Next())\n\tvalues, err = rows.Values()\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 3, values[0])\n\tassert.False(t, rows.Next())\n}\n\n// https://github.com/jackc/pgx/issues/1847#issuecomment-2347858887\nfunc TestConnSendBatchErrorDoesNotLeaveOrphanedPreparedStatement(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server serial type is incompatible with test\")\n\n\t\tmustExec(t, conn, `create temporary table foo(col1 text primary key);`)\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select col1 from foo\")\n\t\tbatch.Queue(\"select col1 from baz\")\n\t\terr := conn.SendBatch(ctx, batch).Close()\n\t\trequire.ErrorContains(t, err, `relation \"baz\" does not exist (SQLSTATE 42P01)`)\n\n\t\tmustExec(t, conn, `create temporary table baz(col1 text primary key);`)\n\n\t\t// Since table baz now exists, the batch should succeed.\n\n\t\tbatch = &pgx.Batch{}\n\t\tbatch.Queue(\"select col1 from foo\")\n\t\tbatch.Queue(\"select col1 from baz\")\n\t\terr = conn.SendBatch(ctx, batch).Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestSendBatchStatementTimeout(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"CockroachDB does not recover connection after batch statement timeout\")\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"SET statement_timeout='1ms'\")\n\t\tbatch.Queue(\"SELECT pg_sleep(10)\")\n\n\t\tbr := conn.SendBatch(context.Background(), batch)\n\t\t// set statement_timeout\n\t\t_, err := br.Exec()\n\t\tassert.NoError(t, err)\n\n\t\t// get pg_sleep results\n\t\trows, _ := br.Query()\n\n\t\t// Consume rows and check error\n\t\trows.Close()\n\t\terr = rows.Err()\n\t\tassert.ErrorContains(t, err, \"(SQLSTATE 57014)\")\n\n\t\t// The last error should be repeated when closing the batch\n\t\terr = br.Close()\n\t\tassert.ErrorContains(t, err, \"(SQLSTATE 57014)\")\n\n\t\t// Connection should be usable after the statement timeout in pipeline\n\t\t_, err = conn.Exec(context.Background(), \"Select 1\")\n\t\tassert.NoError(t, err)\n\t})\n}\n\nfunc TestSendBatchHandlesTimeoutBetweenParseAndDescribe(t *testing.T) {\n\t// Not parallel because it is a timing sensitive test.\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tvar faultyConn *faultyconn.Conn\n\tfaultyConnTestRunner := pgxtest.DefaultConnTestRunner()\n\tfaultyConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tconfig.AfterNetConnect = func(ctx context.Context, config *pgconn.Config, conn net.Conn) (net.Conn, error) {\n\t\t\tfaultyConn = faultyconn.New(conn)\n\t\t\treturn faultyConn, nil\n\t\t}\n\t\treturn config\n\t}\n\n\t// Only need to test modes that use Parse/Describe.\n\textendedQueryModes := []pgx.QueryExecMode{\n\t\tpgx.QueryExecModeCacheStatement,\n\t\tpgx.QueryExecModeCacheDescribe,\n\t\tpgx.QueryExecModeDescribeExec,\n\t\tpgx.QueryExecModeExec,\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, faultyConnTestRunner, extendedQueryModes, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Induced error does not occur on CockroachDB\")\n\n\t\t_, err := conn.Exec(ctx, \"set statement_timeout = '100ms'\")\n\t\trequire.NoError(t, err)\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select 1\")\n\t\tbatch.Queue(\"select 2\")\n\n\t\tfaultyConn.HandleFrontendMessage = func(backendWriter io.Writer, msg pgproto3.FrontendMessage) error {\n\t\t\tif _, ok := msg.(*pgproto3.Describe); ok {\n\t\t\t\ttime.Sleep(200 * time.Millisecond)\n\t\t\t}\n\t\t\tbuf, err := msg.Encode(nil)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t_, err = backendWriter.Write(buf)\n\t\t\treturn err\n\t\t}\n\n\t\terr = conn.SendBatch(ctx, batch).Close()\n\t\trequire.Error(t, err)\n\t\tvar pgErr *pgconn.PgError\n\t\trequire.True(t, errors.As(err, &pgErr))\n\t\trequire.Equal(t, \"57014\", pgErr.Code)\n\n\t\tfaultyConn.HandleFrontendMessage = nil\n\n\t\t_, err = conn.Exec(ctx, \"set statement_timeout = default\")\n\t\trequire.NoError(t, err)\n\n\t\tbatch = &pgx.Batch{}\n\t\tbatch.Queue(\"select 1\")\n\t\tbatch.Queue(\"select 2\")\n\t\terr = conn.SendBatch(ctx, batch).Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestBatchNetworkUsage(t *testing.T) {\n\tt.Parallel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tvar counterConn *byteCounterConn\n\tconfig.AfterNetConnect = func(ctx context.Context, config *pgconn.Config, conn net.Conn) (net.Conn, error) {\n\t\tcounterConn = &byteCounterConn{conn: conn}\n\t\treturn counterConn, nil\n\t}\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server uses different number of bytes for same operations\")\n\n\tcounterConn.bytesWritten = 0\n\tcounterConn.bytesRead = 0\n\n\tbatch := &pgx.Batch{}\n\n\tfor range 10 {\n\t\tbatch.Queue(\n\t\t\t\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\",\n\t\t\t1,\n\t\t)\n\t}\n\n\terr := conn.SendBatch(context.Background(), batch).Close()\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, 1736, counterConn.bytesRead)\n\tassert.Equal(t, 1408, counterConn.bytesWritten)\n\n\tensureConnValid(t, conn)\n}\n\nfunc ExampleConn_SendBatch() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\tbatch := &pgx.Batch{}\n\tbatch.Queue(\"select 1 + 1\").QueryRow(func(row pgx.Row) error {\n\t\tvar n int32\n\t\terr := row.Scan(&n)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfmt.Println(n)\n\n\t\treturn err\n\t})\n\n\tbatch.Queue(\"select 1 + 2\").QueryRow(func(row pgx.Row) error {\n\t\tvar n int32\n\t\terr := row.Scan(&n)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfmt.Println(n)\n\n\t\treturn err\n\t})\n\n\tbatch.Queue(\"select 2 + 3\").QueryRow(func(row pgx.Row) error {\n\t\tvar n int32\n\t\terr := row.Scan(&n)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfmt.Println(n)\n\n\t\treturn err\n\t})\n\n\terr = conn.SendBatch(ctx, batch).Close()\n\tif err != nil {\n\t\tfmt.Printf(\"SendBatch error: %v\", err)\n\t\treturn\n\t}\n\n\t// Output:\n\t// 2\n\t// 3\n\t// 5\n}\n"
  },
  {
    "path": "bench_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc BenchmarkConnectClose(b *testing.B) {\n\tfor b.Loop() {\n\t\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\terr = conn.Close(context.Background())\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalUnpreparedSelectWithoutStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeDescribeExec\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tvar n int64\n\n\tfor i := 0; b.Loop(); i++ {\n\t\terr := conn.QueryRow(context.Background(), \"select $1::int8\", i).Scan(&n)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif n != int64(i) {\n\t\t\tb.Fatalf(\"expected %d, got %d\", i, n)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalUnpreparedSelectWithStatementCacheModeDescribe(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheDescribe\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 32\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tvar n int64\n\n\tfor i := 0; b.Loop(); i++ {\n\t\terr := conn.QueryRow(context.Background(), \"select $1::int8\", i).Scan(&n)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif n != int64(i) {\n\t\t\tb.Fatalf(\"expected %d, got %d\", i, n)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalUnpreparedSelectWithStatementCacheModePrepare(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tconfig.StatementCacheCapacity = 32\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tvar n int64\n\n\tfor i := 0; b.Loop(); i++ {\n\t\terr := conn.QueryRow(context.Background(), \"select $1::int8\", i).Scan(&n)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif n != int64(i) {\n\t\t\tb.Fatalf(\"expected %d, got %d\", i, n)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalPreparedSelect(b *testing.B) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\t_, err := conn.Prepare(context.Background(), \"ps1\", \"select $1::int8\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tvar n int64\n\n\tfor i := 0; b.Loop(); i++ {\n\t\terr = conn.QueryRow(context.Background(), \"ps1\", i).Scan(&n)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif n != int64(i) {\n\t\t\tb.Fatalf(\"expected %d, got %d\", i, n)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalPgConnPreparedSelect(b *testing.B) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\tpgConn := conn.PgConn()\n\n\t_, err := pgConn.Prepare(context.Background(), \"ps1\", \"select $1::int8\", nil)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tencodedBytes := make([]byte, 8)\n\n\tfor b.Loop() {\n\n\t\trr := pgConn.ExecPrepared(context.Background(), \"ps1\", [][]byte{encodedBytes}, []int16{1}, []int16{1})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tfor rr.NextRow() {\n\t\t\tfor i := range rr.Values() {\n\t\t\t\tif !bytes.Equal(rr.Values()[0], encodedBytes) {\n\t\t\t\t\tb.Fatalf(\"unexpected values: %s %s\", rr.Values()[i], encodedBytes)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_, err = rr.Close()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalPgConnPreparedStatementDescriptionSelect(b *testing.B) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\tpgConn := conn.PgConn()\n\n\tpsd, err := pgConn.Prepare(context.Background(), \"ps1\", \"select $1::int8\", nil)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tencodedBytes := make([]byte, 8)\n\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\n\t\trr := pgConn.ExecStatement(context.Background(), psd, [][]byte{encodedBytes}, []int16{1}, []int16{1})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tfor rr.NextRow() {\n\t\t\tfor i := range rr.Values() {\n\t\t\t\tif !bytes.Equal(rr.Values()[0], encodedBytes) {\n\t\t\t\t\tb.Fatalf(\"unexpected values: %s %s\", rr.Values()[i], encodedBytes)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_, err = rr.Close()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkPointerPointerWithNullValues(b *testing.B) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\t_, err := conn.Prepare(context.Background(), \"selectNulls\", \"select 1::int4, 'johnsmith', null::text, null::text, null::text, null::date, null::timestamptz\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tvar record struct {\n\t\t\tid            int32\n\t\t\tuserName      string\n\t\t\temail         *string\n\t\t\tname          *string\n\t\t\tsex           *string\n\t\t\tbirthDate     *time.Time\n\t\t\tlastLoginTime *time.Time\n\t\t}\n\n\t\terr = conn.QueryRow(context.Background(), \"selectNulls\").Scan(\n\t\t\t&record.id,\n\t\t\t&record.userName,\n\t\t\t&record.email,\n\t\t\t&record.name,\n\t\t\t&record.sex,\n\t\t\t&record.birthDate,\n\t\t\t&record.lastLoginTime,\n\t\t)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\t// These checks both ensure that the correct data was returned\n\t\t// and provide a benchmark of accessing the returned values.\n\t\tif record.id != 1 {\n\t\t\tb.Fatalf(\"bad value for id: %v\", record.id)\n\t\t}\n\t\tif record.userName != \"johnsmith\" {\n\t\t\tb.Fatalf(\"bad value for userName: %v\", record.userName)\n\t\t}\n\t\tif record.email != nil {\n\t\t\tb.Fatalf(\"bad value for email: %v\", record.email)\n\t\t}\n\t\tif record.name != nil {\n\t\t\tb.Fatalf(\"bad value for name: %v\", record.name)\n\t\t}\n\t\tif record.sex != nil {\n\t\t\tb.Fatalf(\"bad value for sex: %v\", record.sex)\n\t\t}\n\t\tif record.birthDate != nil {\n\t\t\tb.Fatalf(\"bad value for birthDate: %v\", record.birthDate)\n\t\t}\n\t\tif record.lastLoginTime != nil {\n\t\t\tb.Fatalf(\"bad value for lastLoginTime: %v\", record.lastLoginTime)\n\t\t}\n\t}\n}\n\nfunc BenchmarkPointerPointerWithPresentValues(b *testing.B) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\t_, err := conn.Prepare(context.Background(), \"selectNulls\", \"select 1::int4, 'johnsmith', 'johnsmith@example.com', 'John Smith', 'male', '1970-01-01'::date, '2015-01-01 00:00:00'::timestamptz\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tvar record struct {\n\t\t\tid            int32\n\t\t\tuserName      string\n\t\t\temail         *string\n\t\t\tname          *string\n\t\t\tsex           *string\n\t\t\tbirthDate     *time.Time\n\t\t\tlastLoginTime *time.Time\n\t\t}\n\n\t\terr = conn.QueryRow(context.Background(), \"selectNulls\").Scan(\n\t\t\t&record.id,\n\t\t\t&record.userName,\n\t\t\t&record.email,\n\t\t\t&record.name,\n\t\t\t&record.sex,\n\t\t\t&record.birthDate,\n\t\t\t&record.lastLoginTime,\n\t\t)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\t// These checks both ensure that the correct data was returned\n\t\t// and provide a benchmark of accessing the returned values.\n\t\tif record.id != 1 {\n\t\t\tb.Fatalf(\"bad value for id: %v\", record.id)\n\t\t}\n\t\tif record.userName != \"johnsmith\" {\n\t\t\tb.Fatalf(\"bad value for userName: %v\", record.userName)\n\t\t}\n\t\tif record.email == nil || *record.email != \"johnsmith@example.com\" {\n\t\t\tb.Fatalf(\"bad value for email: %v\", record.email)\n\t\t}\n\t\tif record.name == nil || *record.name != \"John Smith\" {\n\t\t\tb.Fatalf(\"bad value for name: %v\", record.name)\n\t\t}\n\t\tif record.sex == nil || *record.sex != \"male\" {\n\t\t\tb.Fatalf(\"bad value for sex: %v\", record.sex)\n\t\t}\n\t\tif record.birthDate == nil || *record.birthDate != time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) {\n\t\t\tb.Fatalf(\"bad value for birthDate: %v\", record.birthDate)\n\t\t}\n\t\tif record.lastLoginTime == nil || *record.lastLoginTime != time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local) {\n\t\t\tb.Fatalf(\"bad value for lastLoginTime: %v\", record.lastLoginTime)\n\t\t}\n\t}\n}\n\nconst benchmarkWriteTableCreateSQL = `drop table if exists t;\n\ncreate table t(\n\tvarchar_1 varchar not null,\n\tvarchar_2 varchar not null,\n\tvarchar_null_1 varchar,\n\tdate_1 date not null,\n\tdate_null_1 date,\n\tint4_1 int4 not null,\n\tint4_2 int4 not null,\n\tint4_null_1 int4,\n\ttstz_1 timestamptz not null,\n\ttstz_2 timestamptz,\n\tbool_1 bool not null,\n\tbool_2 bool not null,\n\tbool_3 bool not null\n);\n`\n\nconst benchmarkWriteTableInsertSQL = `insert into t(\n\tvarchar_1,\n\tvarchar_2,\n\tvarchar_null_1,\n\tdate_1,\n\tdate_null_1,\n\tint4_1,\n\tint4_2,\n\tint4_null_1,\n\ttstz_1,\n\ttstz_2,\n\tbool_1,\n\tbool_2,\n\tbool_3\n) values (\n\t$1::varchar,\n\t$2::varchar,\n\t$3::varchar,\n\t$4::date,\n\t$5::date,\n\t$6::int4,\n\t$7::int4,\n\t$8::int4,\n\t$9::timestamptz,\n\t$10::timestamptz,\n\t$11::bool,\n\t$12::bool,\n\t$13::bool\n)`\n\ntype benchmarkWriteTableCopyFromSrc struct {\n\tcount int\n\tidx   int\n\trow   []any\n}\n\nfunc (s *benchmarkWriteTableCopyFromSrc) Next() bool {\n\tnext := s.idx < s.count\n\ts.idx++\n\treturn next\n}\n\nfunc (s *benchmarkWriteTableCopyFromSrc) Values() ([]any, error) {\n\treturn s.row, nil\n}\n\nfunc (s *benchmarkWriteTableCopyFromSrc) Err() error {\n\treturn nil\n}\n\nfunc newBenchmarkWriteTableCopyFromSrc(count int) pgx.CopyFromSource {\n\treturn &benchmarkWriteTableCopyFromSrc{\n\t\tcount: count,\n\t\trow: []any{\n\t\t\t\"varchar_1\",\n\t\t\t\"varchar_2\",\n\t\t\t&pgtype.Text{},\n\t\t\ttime.Date(2000, 1, 1, 0, 0, 0, 0, time.Local),\n\t\t\t&pgtype.Date{},\n\t\t\t1,\n\t\t\t2,\n\t\t\t&pgtype.Int4{},\n\t\t\ttime.Date(2001, 1, 1, 0, 0, 0, 0, time.Local),\n\t\t\ttime.Date(2002, 1, 1, 0, 0, 0, 0, time.Local),\n\t\t\ttrue,\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t},\n\t}\n}\n\nfunc benchmarkWriteNRowsViaInsert(b *testing.B, n int) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\tmustExec(b, conn, benchmarkWriteTableCreateSQL)\n\t_, err := conn.Prepare(context.Background(), \"insert_t\", benchmarkWriteTableInsertSQL)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tsrc := newBenchmarkWriteTableCopyFromSrc(n)\n\n\t\ttx, err := conn.Begin(context.Background())\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tfor src.Next() {\n\t\t\tvalues, _ := src.Values()\n\t\t\tif _, err = tx.Exec(context.Background(), \"insert_t\", values...); err != nil {\n\t\t\t\tb.Fatalf(\"Exec unexpectedly failed with: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\terr = tx.Commit(context.Background())\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc benchmarkWriteNRowsViaBatchInsert(b *testing.B, n int) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\tmustExec(b, conn, benchmarkWriteTableCreateSQL)\n\t_, err := conn.Prepare(context.Background(), \"insert_t\", benchmarkWriteTableInsertSQL)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tsrc := newBenchmarkWriteTableCopyFromSrc(n)\n\n\t\tbatch := &pgx.Batch{}\n\t\tfor src.Next() {\n\t\t\tvalues, _ := src.Values()\n\t\t\tbatch.Queue(\"insert_t\", values...)\n\t\t}\n\n\t\terr = conn.SendBatch(context.Background(), batch).Close()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\ntype queryArgs []any\n\nfunc (qa *queryArgs) Append(v any) string {\n\t*qa = append(*qa, v)\n\treturn \"$\" + strconv.Itoa(len(*qa))\n}\n\n// note this function is only used for benchmarks -- it doesn't escape tableName\n// or columnNames\nfunc multiInsert(conn *pgx.Conn, tableName string, columnNames []string, rowSrc pgx.CopyFromSource) (int, error) {\n\tmaxRowsPerInsert := 65535 / len(columnNames)\n\trowsThisInsert := 0\n\trowCount := 0\n\n\tsqlBuf := &bytes.Buffer{}\n\targs := make(queryArgs, 0)\n\n\tresetQuery := func() {\n\t\tsqlBuf.Reset()\n\t\tfmt.Fprintf(sqlBuf, \"insert into %s(%s) values\", tableName, strings.Join(columnNames, \", \"))\n\n\t\targs = args[0:0]\n\n\t\trowsThisInsert = 0\n\t}\n\tresetQuery()\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer tx.Rollback(context.Background())\n\n\tfor rowSrc.Next() {\n\t\tif rowsThisInsert > 0 {\n\t\t\tsqlBuf.WriteByte(',')\n\t\t}\n\n\t\tsqlBuf.WriteByte('(')\n\n\t\tvalues, err := rowSrc.Values()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tfor i, val := range values {\n\t\t\tif i > 0 {\n\t\t\t\tsqlBuf.WriteByte(',')\n\t\t\t}\n\t\t\tsqlBuf.WriteString(args.Append(val))\n\t\t}\n\n\t\tsqlBuf.WriteByte(')')\n\n\t\trowsThisInsert++\n\n\t\tif rowsThisInsert == maxRowsPerInsert {\n\t\t\t_, err := tx.Exec(context.Background(), sqlBuf.String(), args...)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\n\t\t\trowCount += rowsThisInsert\n\t\t\tresetQuery()\n\t\t}\n\t}\n\n\tif rowsThisInsert > 0 {\n\t\t_, err := tx.Exec(context.Background(), sqlBuf.String(), args...)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\trowCount += rowsThisInsert\n\t}\n\n\tif err := tx.Commit(context.Background()); err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn rowCount, nil\n}\n\nfunc benchmarkWriteNRowsViaMultiInsert(b *testing.B, n int) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\tmustExec(b, conn, benchmarkWriteTableCreateSQL)\n\t_, err := conn.Prepare(context.Background(), \"insert_t\", benchmarkWriteTableInsertSQL)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor b.Loop() {\n\t\tsrc := newBenchmarkWriteTableCopyFromSrc(n)\n\n\t\t_, err := multiInsert(conn, \"t\",\n\t\t\t[]string{\n\t\t\t\t\"varchar_1\",\n\t\t\t\t\"varchar_2\",\n\t\t\t\t\"varchar_null_1\",\n\t\t\t\t\"date_1\",\n\t\t\t\t\"date_null_1\",\n\t\t\t\t\"int4_1\",\n\t\t\t\t\"int4_2\",\n\t\t\t\t\"int4_null_1\",\n\t\t\t\t\"tstz_1\",\n\t\t\t\t\"tstz_2\",\n\t\t\t\t\"bool_1\",\n\t\t\t\t\"bool_2\",\n\t\t\t\t\"bool_3\",\n\t\t\t},\n\t\t\tsrc)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc benchmarkWriteNRowsViaCopy(b *testing.B, n int) {\n\tconn := mustConnect(b, mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\")))\n\tdefer closeConn(b, conn)\n\n\tmustExec(b, conn, benchmarkWriteTableCreateSQL)\n\n\tfor b.Loop() {\n\t\tsrc := newBenchmarkWriteTableCopyFromSrc(n)\n\n\t\t_, err := conn.CopyFrom(context.Background(),\n\t\t\tpgx.Identifier{\"t\"},\n\t\t\t[]string{\n\t\t\t\t\"varchar_1\",\n\t\t\t\t\"varchar_2\",\n\t\t\t\t\"varchar_null_1\",\n\t\t\t\t\"date_1\",\n\t\t\t\t\"date_null_1\",\n\t\t\t\t\"int4_1\",\n\t\t\t\t\"int4_2\",\n\t\t\t\t\"int4_null_1\",\n\t\t\t\t\"tstz_1\",\n\t\t\t\t\"tstz_2\",\n\t\t\t\t\"bool_1\",\n\t\t\t\t\"bool_2\",\n\t\t\t\t\"bool_3\",\n\t\t\t},\n\t\t\tsrc)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkWrite2RowsViaInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaInsert(b, 2)\n}\n\nfunc BenchmarkWrite2RowsViaMultiInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaMultiInsert(b, 2)\n}\n\nfunc BenchmarkWrite2RowsViaBatchInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaBatchInsert(b, 2)\n}\n\nfunc BenchmarkWrite2RowsViaCopy(b *testing.B) {\n\tbenchmarkWriteNRowsViaCopy(b, 2)\n}\n\nfunc BenchmarkWrite5RowsViaInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaInsert(b, 5)\n}\n\nfunc BenchmarkWrite5RowsViaMultiInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaMultiInsert(b, 5)\n}\n\nfunc BenchmarkWrite5RowsViaBatchInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaBatchInsert(b, 5)\n}\n\nfunc BenchmarkWrite5RowsViaCopy(b *testing.B) {\n\tbenchmarkWriteNRowsViaCopy(b, 5)\n}\n\nfunc BenchmarkWrite10RowsViaInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaInsert(b, 10)\n}\n\nfunc BenchmarkWrite10RowsViaMultiInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaMultiInsert(b, 10)\n}\n\nfunc BenchmarkWrite10RowsViaBatchInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaBatchInsert(b, 10)\n}\n\nfunc BenchmarkWrite10RowsViaCopy(b *testing.B) {\n\tbenchmarkWriteNRowsViaCopy(b, 10)\n}\n\nfunc BenchmarkWrite100RowsViaInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaInsert(b, 100)\n}\n\nfunc BenchmarkWrite100RowsViaMultiInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaMultiInsert(b, 100)\n}\n\nfunc BenchmarkWrite100RowsViaBatchInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaBatchInsert(b, 100)\n}\n\nfunc BenchmarkWrite100RowsViaCopy(b *testing.B) {\n\tbenchmarkWriteNRowsViaCopy(b, 100)\n}\n\nfunc BenchmarkWrite1000RowsViaInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaInsert(b, 1_000)\n}\n\nfunc BenchmarkWrite1000RowsViaMultiInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaMultiInsert(b, 1_000)\n}\n\nfunc BenchmarkWrite1000RowsViaBatchInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaBatchInsert(b, 1_000)\n}\n\nfunc BenchmarkWrite1000RowsViaCopy(b *testing.B) {\n\tbenchmarkWriteNRowsViaCopy(b, 1_000)\n}\n\nfunc BenchmarkWrite10000RowsViaInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaInsert(b, 10_000)\n}\n\nfunc BenchmarkWrite10000RowsViaMultiInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaMultiInsert(b, 10_000)\n}\n\nfunc BenchmarkWrite10000RowsViaBatchInsert(b *testing.B) {\n\tbenchmarkWriteNRowsViaBatchInsert(b, 10_000)\n}\n\nfunc BenchmarkWrite10000RowsViaCopy(b *testing.B) {\n\tbenchmarkWriteNRowsViaCopy(b, 10_000)\n}\n\nfunc BenchmarkMultipleQueriesNonBatchNoStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeDescribeExec\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tbenchmarkMultipleQueriesNonBatch(b, conn, 3)\n}\n\nfunc BenchmarkMultipleQueriesNonBatchPrepareStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tconfig.StatementCacheCapacity = 32\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tbenchmarkMultipleQueriesNonBatch(b, conn, 3)\n}\n\nfunc BenchmarkMultipleQueriesNonBatchDescribeStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheDescribe\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 32\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tbenchmarkMultipleQueriesNonBatch(b, conn, 3)\n}\n\nfunc benchmarkMultipleQueriesNonBatch(b *testing.B, conn *pgx.Conn, queryCount int) {\n\tfor b.Loop() {\n\t\tfor range queryCount {\n\t\t\trows, err := conn.Query(context.Background(), \"select n from generate_series(0, 5) n\")\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\n\t\t\tfor k := 0; rows.Next(); k++ {\n\t\t\t\tvar n int\n\t\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif n != k {\n\t\t\t\t\tb.Fatalf(\"n => %v, want %v\", n, k)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif rows.Err() != nil {\n\t\t\t\tb.Fatal(rows.Err())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkMultipleQueriesBatchNoStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeDescribeExec\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tbenchmarkMultipleQueriesBatch(b, conn, 3)\n}\n\nfunc BenchmarkMultipleQueriesBatchPrepareStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tconfig.StatementCacheCapacity = 32\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tbenchmarkMultipleQueriesBatch(b, conn, 3)\n}\n\nfunc BenchmarkMultipleQueriesBatchDescribeStatementCache(b *testing.B) {\n\tconfig := mustParseConfig(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheDescribe\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 32\n\n\tconn := mustConnect(b, config)\n\tdefer closeConn(b, conn)\n\n\tbenchmarkMultipleQueriesBatch(b, conn, 3)\n}\n\nfunc benchmarkMultipleQueriesBatch(b *testing.B, conn *pgx.Conn, queryCount int) {\n\tfor b.Loop() {\n\t\tbatch := &pgx.Batch{}\n\t\tfor range queryCount {\n\t\t\tbatch.Queue(\"select n from generate_series(0,5) n\")\n\t\t}\n\n\t\tbr := conn.SendBatch(context.Background(), batch)\n\n\t\tfor range queryCount {\n\t\t\trows, err := br.Query()\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\n\t\t\tfor k := 0; rows.Next(); k++ {\n\t\t\t\tvar n int\n\t\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif n != k {\n\t\t\t\t\tb.Fatalf(\"n => %v, want %v\", n, k)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif rows.Err() != nil {\n\t\t\t\tb.Fatal(rows.Err())\n\t\t\t}\n\t\t}\n\n\t\terr := br.Close()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSelectManyUnknownEnum(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\tctx := context.Background()\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(b, err)\n\tdefer tx.Rollback(ctx)\n\n\t_, err = tx.Exec(context.Background(), \"drop type if exists color;\")\n\trequire.NoError(b, err)\n\n\t_, err = tx.Exec(ctx, `create type color as enum ('blue', 'green', 'orange')`)\n\trequire.NoError(b, err)\n\n\tvar x, y, z string\n\tfor b.Loop() {\n\t\trows, err := conn.Query(ctx, \"select 'blue'::color, 'green'::color, 'orange'::color from generate_series(1,10)\")\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tfor rows.Next() {\n\t\t\terr = rows.Scan(&x, &y, &z)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\n\t\t\tif x != \"blue\" {\n\t\t\t\tb.Fatal(\"unexpected result\")\n\t\t\t}\n\t\t\tif y != \"green\" {\n\t\t\t\tb.Fatal(\"unexpected result\")\n\t\t\t}\n\t\t\tif z != \"orange\" {\n\t\t\t\tb.Fatal(\"unexpected result\")\n\t\t\t}\n\t\t}\n\n\t\tif rows.Err() != nil {\n\t\t\tb.Fatal(rows.Err())\n\t\t}\n\t}\n}\n\nfunc BenchmarkSelectManyRegisteredEnum(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\tctx := context.Background()\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(b, err)\n\tdefer tx.Rollback(ctx)\n\n\t_, err = tx.Exec(context.Background(), \"drop type if exists color;\")\n\trequire.NoError(b, err)\n\n\t_, err = tx.Exec(ctx, `create type color as enum ('blue', 'green', 'orange')`)\n\trequire.NoError(b, err)\n\n\tvar oid uint32\n\terr = conn.QueryRow(context.Background(), \"select oid from pg_type where typname=$1;\", \"color\").Scan(&oid)\n\trequire.NoError(b, err)\n\n\tconn.TypeMap().RegisterType(&pgtype.Type{Name: \"color\", OID: oid, Codec: &pgtype.EnumCodec{}})\n\n\tvar x, y, z string\n\tfor b.Loop() {\n\t\trows, err := conn.Query(ctx, \"select 'blue'::color, 'green'::color, 'orange'::color from generate_series(1,10)\")\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tfor rows.Next() {\n\t\t\terr = rows.Scan(&x, &y, &z)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\n\t\t\tif x != \"blue\" {\n\t\t\t\tb.Fatal(\"unexpected result\")\n\t\t\t}\n\t\t\tif y != \"green\" {\n\t\t\t\tb.Fatal(\"unexpected result\")\n\t\t\t}\n\t\t\tif z != \"orange\" {\n\t\t\t\tb.Fatal(\"unexpected result\")\n\t\t\t}\n\t\t}\n\n\t\tif rows.Err() != nil {\n\t\t\tb.Fatal(rows.Err())\n\t\t}\n\t}\n}\n\nfunc getSelectRowsCounts(b *testing.B) []int64 {\n\tvar rowCounts []int64\n\t{\n\t\ts := os.Getenv(\"PGX_BENCH_SELECT_ROWS_COUNTS\")\n\t\tif s != \"\" {\n\t\t\tfor p := range strings.SplitSeq(s, \" \") {\n\t\t\t\tn, err := strconv.ParseInt(p, 10, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatalf(\"Bad PGX_BENCH_SELECT_ROWS_COUNTS value: %v\", err)\n\t\t\t\t}\n\t\t\t\trowCounts = append(rowCounts, n)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(rowCounts) == 0 {\n\t\trowCounts = []int64{1, 10, 100, 1000}\n\t}\n\n\treturn rowCounts\n}\n\ntype BenchRowSimple struct {\n\tID         int32\n\tFirstName  string\n\tLastName   string\n\tSex        string\n\tBirthDate  time.Time\n\tWeight     int32\n\tHeight     int32\n\tTags       []string\n\tUpdateTime time.Time\n}\n\nfunc BenchmarkSelectRowsScanSimple(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tbr := &BenchRowSimple{}\n\t\t\tfor b.Loop() {\n\t\t\t\trows, err := conn.Query(context.Background(), \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", rowCount)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\trows.Scan(&br.ID, &br.FirstName, &br.LastName, &br.Sex, &br.BirthDate, &br.Weight, &br.Height, &br.Tags, &br.UpdateTime)\n\t\t\t\t}\n\n\t\t\t\tif rows.Err() != nil {\n\t\t\t\t\tb.Fatal(rows.Err())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype BenchRowStringBytes struct {\n\tID         int32\n\tFirstName  []byte\n\tLastName   []byte\n\tSex        []byte\n\tBirthDate  time.Time\n\tWeight     int32\n\tHeight     int32\n\tTags       []string\n\tUpdateTime time.Time\n}\n\nfunc BenchmarkSelectRowsScanStringBytes(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tbr := &BenchRowStringBytes{}\n\t\t\tfor b.Loop() {\n\t\t\t\trows, err := conn.Query(context.Background(), \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", rowCount)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\trows.Scan(&br.ID, &br.FirstName, &br.LastName, &br.Sex, &br.BirthDate, &br.Weight, &br.Height, &br.Tags, &br.UpdateTime)\n\t\t\t\t}\n\n\t\t\t\tif rows.Err() != nil {\n\t\t\t\t\tb.Fatal(rows.Err())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype BenchRowDecoder struct {\n\tID         pgtype.Int4\n\tFirstName  pgtype.Text\n\tLastName   pgtype.Text\n\tSex        pgtype.Text\n\tBirthDate  pgtype.Date\n\tWeight     pgtype.Int4\n\tHeight     pgtype.Int4\n\tTags       pgtype.FlatArray[string]\n\tUpdateTime pgtype.Timestamptz\n}\n\nfunc BenchmarkSelectRowsScanDecoder(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tformats := []struct {\n\t\t\t\tname string\n\t\t\t\tcode int16\n\t\t\t}{\n\t\t\t\t{\"text\", pgx.TextFormatCode},\n\t\t\t\t{\"binary\", pgx.BinaryFormatCode},\n\t\t\t}\n\t\t\tfor _, format := range formats {\n\t\t\t\tb.Run(format.name, func(b *testing.B) {\n\t\t\t\t\tbr := &BenchRowDecoder{}\n\t\t\t\t\tfor b.Loop() {\n\t\t\t\t\t\trows, err := conn.Query(\n\t\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\t\t\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\",\n\t\t\t\t\t\t\tpgx.QueryResultFormats{format.code},\n\t\t\t\t\t\t\trowCount,\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor rows.Next() {\n\t\t\t\t\t\t\trows.Scan(&br.ID, &br.FirstName, &br.LastName, &br.Sex, &br.BirthDate, &br.Weight, &br.Height, &br.Tags, &br.UpdateTime)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif rows.Err() != nil {\n\t\t\t\t\t\t\tb.Fatal(rows.Err())\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsPgConnExecText(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\tmrr := conn.PgConn().Exec(context.Background(), fmt.Sprintf(\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + %d) n\", rowCount))\n\t\t\t\tfor mrr.NextResult() {\n\t\t\t\t\trr := mrr.ResultReader()\n\t\t\t\t\tfor rr.NextRow() {\n\t\t\t\t\t\trr.Values()\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\terr := mrr.Close()\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsPgConnExecParams(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tformats := []struct {\n\t\t\t\tname string\n\t\t\t\tcode int16\n\t\t\t}{\n\t\t\t\t{\"text\", pgx.TextFormatCode},\n\t\t\t\t{\"binary - mostly\", pgx.BinaryFormatCode},\n\t\t\t}\n\t\t\tfor _, format := range formats {\n\t\t\t\tb.Run(format.name, func(b *testing.B) {\n\t\t\t\t\tfor b.Loop() {\n\t\t\t\t\t\trr := conn.PgConn().ExecParams(\n\t\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\t\t\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\",\n\t\t\t\t\t\t\t[][]byte{[]byte(strconv.FormatInt(rowCount, 10))},\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\t[]int16{format.code, pgx.TextFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, format.code, format.code, format.code, format.code, format.code},\n\t\t\t\t\t\t)\n\t\t\t\t\t\tfor rr.NextRow() {\n\t\t\t\t\t\t\trr.Values()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_, err := rr.Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsSimpleCollectRowsRowToStructByPos(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\trows, _ := conn.Query(context.Background(), \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", rowCount)\n\t\t\t\tbenchRows, err := pgx.CollectRows(rows, pgx.RowToStructByPos[BenchRowSimple])\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif len(benchRows) != int(rowCount) {\n\t\t\t\t\tb.Fatalf(\"Expected %d rows, got %d\", rowCount, len(benchRows))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsSimpleAppendRowsRowToStructByPos(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tbenchRows := make([]BenchRowSimple, 0, rowCount)\n\t\t\tfor b.Loop() {\n\t\t\t\tbenchRows = benchRows[:0]\n\t\t\t\trows, _ := conn.Query(context.Background(), \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", rowCount)\n\t\t\t\tvar err error\n\t\t\t\tbenchRows, err = pgx.AppendRows(benchRows, rows, pgx.RowToStructByPos[BenchRowSimple])\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif len(benchRows) != int(rowCount) {\n\t\t\t\t\tb.Fatalf(\"Expected %d rows, got %d\", rowCount, len(benchRows))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsSimpleCollectRowsRowToStructByName(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\trows, _ := conn.Query(context.Background(), \"select n as id, 'Adam' as first_name, 'Smith ' || n as last_name, 'male' as sex, '1952-06-16'::date as birth_date, 258 as weight, 72 as height, '{foo,bar,baz}'::text[] as tags, '2001-01-28 01:02:03-05'::timestamptz as update_time from generate_series(100001, 100000 + $1) n\", rowCount)\n\t\t\t\tbenchRows, err := pgx.CollectRows(rows, pgx.RowToStructByName[BenchRowSimple])\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif len(benchRows) != int(rowCount) {\n\t\t\t\t\tb.Fatalf(\"Expected %d rows, got %d\", rowCount, len(benchRows))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsSimpleAppendRowsRowToStructByName(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tbenchRows := make([]BenchRowSimple, 0, rowCount)\n\t\t\tfor b.Loop() {\n\t\t\t\tbenchRows = benchRows[:0]\n\t\t\t\trows, _ := conn.Query(context.Background(), \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", rowCount)\n\t\t\t\tvar err error\n\t\t\t\tbenchRows, err = pgx.AppendRows(benchRows, rows, pgx.RowToStructByPos[BenchRowSimple])\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif len(benchRows) != int(rowCount) {\n\t\t\t\t\tb.Fatalf(\"Expected %d rows, got %d\", rowCount, len(benchRows))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsPgConnExecPrepared(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\t_, err := conn.PgConn().Prepare(context.Background(), \"ps1\", \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", nil)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tformats := []struct {\n\t\t\t\tname string\n\t\t\t\tcode int16\n\t\t\t}{\n\t\t\t\t{\"text\", pgx.TextFormatCode},\n\t\t\t\t{\"binary - mostly\", pgx.BinaryFormatCode},\n\t\t\t}\n\t\t\tfor _, format := range formats {\n\t\t\t\tb.Run(format.name, func(b *testing.B) {\n\t\t\t\t\tfor b.Loop() {\n\t\t\t\t\t\trr := conn.PgConn().ExecPrepared(\n\t\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\t\t\"ps1\",\n\t\t\t\t\t\t\t[][]byte{[]byte(strconv.FormatInt(rowCount, 10))},\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\t[]int16{format.code, pgx.TextFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, format.code, format.code, format.code, format.code, format.code},\n\t\t\t\t\t\t)\n\t\t\t\t\t\tfor rr.NextRow() {\n\t\t\t\t\t\t\trr.Values()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_, err := rr.Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelectRowsPgConnExecStatement(b *testing.B) {\n\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(b, conn)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tpsd, err := conn.PgConn().Prepare(context.Background(), \"ps1\", \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", nil)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tformats := []struct {\n\t\t\t\tname string\n\t\t\t\tcode int16\n\t\t\t}{\n\t\t\t\t{\"text\", pgx.TextFormatCode},\n\t\t\t\t{\"binary - mostly\", pgx.BinaryFormatCode},\n\t\t\t}\n\t\t\tfor _, format := range formats {\n\t\t\t\tb.Run(format.name, func(b *testing.B) {\n\t\t\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t\t\trr := conn.PgConn().ExecStatement(\n\t\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\t\tpsd,\n\t\t\t\t\t\t\t[][]byte{[]byte(strconv.FormatInt(rowCount, 10))},\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\t[]int16{format.code, pgx.TextFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, format.code, format.code, format.code, format.code, format.code},\n\t\t\t\t\t\t)\n\t\t\t\t\t\tfor rr.NextRow() {\n\t\t\t\t\t\t\trr.Values()\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_, err := rr.Close()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype queryRecorder struct {\n\tconn      net.Conn\n\twriteBuf  []byte\n\treadCount int\n}\n\nfunc (qr *queryRecorder) Read(b []byte) (n int, err error) {\n\tn, err = qr.conn.Read(b)\n\tqr.readCount += n\n\treturn n, err\n}\n\nfunc (qr *queryRecorder) Write(b []byte) (n int, err error) {\n\tqr.writeBuf = append(qr.writeBuf, b...)\n\treturn qr.conn.Write(b)\n}\n\nfunc (qr *queryRecorder) Close() error {\n\treturn qr.conn.Close()\n}\n\nfunc (qr *queryRecorder) LocalAddr() net.Addr {\n\treturn qr.conn.LocalAddr()\n}\n\nfunc (qr *queryRecorder) RemoteAddr() net.Addr {\n\treturn qr.conn.RemoteAddr()\n}\n\nfunc (qr *queryRecorder) SetDeadline(t time.Time) error {\n\treturn qr.conn.SetDeadline(t)\n}\n\nfunc (qr *queryRecorder) SetReadDeadline(t time.Time) error {\n\treturn qr.conn.SetReadDeadline(t)\n}\n\nfunc (qr *queryRecorder) SetWriteDeadline(t time.Time) error {\n\treturn qr.conn.SetWriteDeadline(t)\n}\n\n// BenchmarkSelectRowsRawPrepared hijacks a pgconn connection and inserts a queryRecorder. It then executes the query\n// once. The benchmark is simply sending the exact query bytes over the wire to the server and reading the expected\n// number of bytes back. It does nothing else. This should be the theoretical maximum performance a Go application\n// could achieve.\nfunc BenchmarkSelectRowsRawPrepared(b *testing.B) {\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tformats := []struct {\n\t\t\t\tname string\n\t\t\t\tcode int16\n\t\t\t}{\n\t\t\t\t{\"text\", pgx.TextFormatCode},\n\t\t\t\t{\"binary - mostly\", pgx.BinaryFormatCode},\n\t\t\t}\n\t\t\tfor _, format := range formats {\n\t\t\t\tb.Run(format.name, func(b *testing.B) {\n\t\t\t\t\tconn := mustConnectString(b, os.Getenv(\"PGX_TEST_DATABASE\")).PgConn()\n\t\t\t\t\tdefer conn.Close(context.Background())\n\n\t\t\t\t\t_, err := conn.Prepare(context.Background(), \"ps1\", \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", nil)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\n\t\t\t\t\thijackedConn, err := conn.Hijack()\n\t\t\t\t\trequire.NoError(b, err)\n\n\t\t\t\t\tqr := &queryRecorder{\n\t\t\t\t\t\tconn: hijackedConn.Conn,\n\t\t\t\t\t}\n\n\t\t\t\t\thijackedConn.Conn = qr\n\t\t\t\t\thijackedConn.Frontend = hijackedConn.Config.BuildFrontend(qr, qr)\n\t\t\t\t\tconn, err = pgconn.Construct(hijackedConn)\n\t\t\t\t\trequire.NoError(b, err)\n\n\t\t\t\t\t{\n\t\t\t\t\t\trr := conn.ExecPrepared(\n\t\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\t\t\"ps1\",\n\t\t\t\t\t\t\t[][]byte{[]byte(strconv.FormatInt(rowCount, 10))},\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\t[]int16{format.code, pgx.TextFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, format.code, format.code, format.code, format.code, format.code},\n\t\t\t\t\t\t)\n\t\t\t\t\t\t_, err := rr.Close()\n\t\t\t\t\t\trequire.NoError(b, err)\n\t\t\t\t\t}\n\n\t\t\t\t\tbuf := make([]byte, qr.readCount)\n\n\t\t\t\t\tb.ResetTimer()\n\t\t\t\t\tfor b.Loop() {\n\t\t\t\t\t\t_, err := qr.conn.Write(qr.writeBuf)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t_, err = io.ReadFull(qr.conn, buf)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "ci/setup_test.bash",
    "content": "#!/usr/bin/env bash\nset -eux\n\nif [[ \"${PGVERSION-}\" =~ ^[0-9.]+$ ]]\nthen\n  sudo apt-get remove -y --purge postgresql libpq-dev libpq5 postgresql-client-common postgresql-common\n  sudo rm -rf /var/lib/postgresql\n  wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -\n  sudo sh -c \"echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list\"\n  sudo apt-get update -qq\n  sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=\"--force-confnew\" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION\n\n  sudo cp testsetup/pg_hba.conf /etc/postgresql/$PGVERSION/main/pg_hba.conf\n  sudo sh -c \"echo \\\"listen_addresses = '127.0.0.1'\\\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf\"\n  sudo sh -c \"cat testsetup/postgresql_ssl.conf >> /etc/postgresql/$PGVERSION/main/postgresql.conf\"\n\n  if [ \"$PGVERSION\" -ge 18 ]; then\n    # Configure and Install OAuth validator for PostgreSQL 18+\n    sudo sh -c \"cat testsetup/oauth_validator_module/postgresql.conf >> /etc/postgresql/$PGVERSION/main/postgresql.conf\"\n    sudo sh -c \"cat testsetup/oauth_validator_module/pg_hba.conf >> /etc/postgresql/$PGVERSION/main/pg_hba.conf\"\n    (\n      cd testsetup/oauth_validator_module\n      sudo apt-get install -y gcc make libkrb5-dev\n      make && sudo make install\n    )\n  fi\n\n  cd testsetup\n\n  # Generate CA, server, and encrypted client certificates.\n  go run generate_certs.go\n\n  # Copy certificates to server directory and set permissions.\n  sudo cp ca.pem /var/lib/postgresql/$PGVERSION/main/root.crt\n  sudo chown postgres:postgres /var/lib/postgresql/$PGVERSION/main/root.crt\n  sudo cp localhost.key /var/lib/postgresql/$PGVERSION/main/server.key\n  sudo chown postgres:postgres /var/lib/postgresql/$PGVERSION/main/server.key\n  sudo chmod 600 /var/lib/postgresql/$PGVERSION/main/server.key\n  sudo cp localhost.crt /var/lib/postgresql/$PGVERSION/main/server.crt\n  sudo chown postgres:postgres /var/lib/postgresql/$PGVERSION/main/server.crt\n\n  cp ca.pem /tmp\n  cp pgx_sslcert.key /tmp\n  cp pgx_sslcert.crt /tmp\n\n  cd ..\n\n  sudo /etc/init.d/postgresql restart\n\n  createdb -U postgres pgx_test\n  psql -U postgres -f testsetup/postgresql_setup.sql pgx_test\nfi\n\nif [[ \"${PGVERSION-}\" =~ ^cockroach ]]\nthen\n  wget -qO- https://binaries.cockroachdb.com/cockroach-v25.4.4.linux-amd64.tgz | tar xvz\n  sudo mv cockroach-v25.4.4.linux-amd64/cockroach /usr/local/bin/\n  cockroach start-single-node --insecure --background --listen-addr=localhost\n  cockroach sql --insecure -e 'create database pgx_test'\nfi\n\nif [ \"${CRATEVERSION-}\" != \"\" ]\nthen\n  docker run \\\n    -p \"6543:5432\" \\\n    -d \\\n    crate:\"$CRATEVERSION\" \\\n    crate \\\n      -Cnetwork.host=0.0.0.0 \\\n      -Ctransport.host=localhost \\\n      -Clicense.enterprise=false\nfi\n"
  },
  {
    "path": "conn.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/sanitize\"\n\t\"github.com/jackc/pgx/v5/internal/stmtcache\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// ConnConfig contains all the options used to establish a connection. It must be created by ParseConfig and\n// then it can be modified. A manually initialized ConnConfig will cause ConnectConfig to panic.\ntype ConnConfig struct {\n\tpgconn.Config\n\n\tTracer QueryTracer\n\n\t// Original connection string that was parsed into config.\n\tconnString string\n\n\t// StatementCacheCapacity is maximum size of the statement cache used when executing a query with \"cache_statement\"\n\t// query exec mode.\n\tStatementCacheCapacity int\n\n\t// DescriptionCacheCapacity is the maximum size of the description cache used when executing a query with\n\t// \"cache_describe\" query exec mode.\n\tDescriptionCacheCapacity int\n\n\t// DefaultQueryExecMode controls the default mode for executing queries. By default pgx uses the extended protocol\n\t// and automatically prepares and caches prepared statements. However, this may be incompatible with proxies such as\n\t// PGBouncer. In this case it may be preferable to use QueryExecModeExec or QueryExecModeSimpleProtocol. The same\n\t// functionality can be controlled on a per query basis by passing a QueryExecMode as the first query argument.\n\tDefaultQueryExecMode QueryExecMode\n\n\tcreatedByParseConfig bool // Used to enforce created by ParseConfig rule.\n}\n\n// ParseConfigOptions contains options that control how a config is built such as getsslpassword.\ntype ParseConfigOptions struct {\n\tpgconn.ParseConfigOptions\n}\n\n// Copy returns a deep copy of the config that is safe to use and modify.\n// The only exception is the tls.Config:\n// according to the tls.Config docs it must not be modified after creation.\nfunc (cc *ConnConfig) Copy() *ConnConfig {\n\tnewConfig := new(ConnConfig)\n\t*newConfig = *cc\n\tnewConfig.Config = *newConfig.Config.Copy()\n\treturn newConfig\n}\n\n// ConnString returns the connection string as parsed by pgx.ParseConfig into pgx.ConnConfig.\nfunc (cc *ConnConfig) ConnString() string { return cc.connString }\n\n// Conn is a PostgreSQL connection handle. It is not safe for concurrent usage. Use a connection pool to manage access\n// to multiple database connections from multiple goroutines.\ntype Conn struct {\n\tpgConn                  *pgconn.PgConn\n\tconfig                  *ConnConfig // config used when establishing this connection\n\tpreparedStatements      map[string]*pgconn.StatementDescription\n\tfailedDescribeStatement string\n\tstatementCache          stmtcache.Cache\n\tdescriptionCache        stmtcache.Cache\n\n\tqueryTracer    QueryTracer\n\tbatchTracer    BatchTracer\n\tcopyFromTracer CopyFromTracer\n\tprepareTracer  PrepareTracer\n\n\tnotifications []*pgconn.Notification\n\n\tdoneChan   chan struct{}\n\tclosedChan chan error\n\n\ttypeMap *pgtype.Map\n\n\twbuf []byte\n\teqb  ExtendedQueryBuilder\n}\n\n// Identifier a PostgreSQL identifier or name. Identifiers can be composed of\n// multiple parts such as [\"schema\", \"table\"] or [\"table\", \"column\"].\ntype Identifier []string\n\n// Sanitize returns a sanitized string safe for SQL interpolation.\nfunc (ident Identifier) Sanitize() string {\n\tparts := make([]string, len(ident))\n\tfor i := range ident {\n\t\ts := strings.ReplaceAll(ident[i], string([]byte{0}), \"\")\n\t\tparts[i] = `\"` + strings.ReplaceAll(s, `\"`, `\"\"`) + `\"`\n\t}\n\treturn strings.Join(parts, \".\")\n}\n\nvar (\n\t// ErrNoRows occurs when rows are expected but none are returned.\n\tErrNoRows = newProxyErr(sql.ErrNoRows, \"no rows in result set\")\n\t// ErrTooManyRows occurs when more rows than expected are returned.\n\tErrTooManyRows = errors.New(\"too many rows in result set\")\n)\n\nfunc newProxyErr(background error, msg string) error {\n\treturn &proxyError{\n\t\tmsg:        msg,\n\t\tbackground: background,\n\t}\n}\n\ntype proxyError struct {\n\tmsg        string\n\tbackground error\n}\n\nfunc (err *proxyError) Error() string { return err.msg }\n\nfunc (err *proxyError) Unwrap() error { return err.background }\n\nvar (\n\terrDisabledStatementCache   = fmt.Errorf(\"cannot use QueryExecModeCacheStatement with disabled statement cache\")\n\terrDisabledDescriptionCache = fmt.Errorf(\"cannot use QueryExecModeCacheDescribe with disabled description cache\")\n)\n\n// Connect establishes a connection with a PostgreSQL server with a connection string. See\n// pgconn.Connect for details.\nfunc Connect(ctx context.Context, connString string) (*Conn, error) {\n\tconnConfig, err := ParseConfig(connString)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn connect(ctx, connConfig)\n}\n\n// ConnectWithOptions behaves exactly like Connect with the addition of options. At the present options is only used to\n// provide a GetSSLPassword function.\nfunc ConnectWithOptions(ctx context.Context, connString string, options ParseConfigOptions) (*Conn, error) {\n\tconnConfig, err := ParseConfigWithOptions(connString, options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn connect(ctx, connConfig)\n}\n\n// ConnectConfig establishes a connection with a PostgreSQL server with a configuration struct.\n// connConfig must have been created by ParseConfig.\nfunc ConnectConfig(ctx context.Context, connConfig *ConnConfig) (*Conn, error) {\n\t// In general this improves safety. In particular avoid the config.Config.OnNotification mutation from affecting other\n\t// connections with the same config. See https://github.com/jackc/pgx/issues/618.\n\tconnConfig = connConfig.Copy()\n\n\treturn connect(ctx, connConfig)\n}\n\n// ParseConfigWithOptions behaves exactly as ParseConfig does with the addition of options. At the present options is\n// only used to provide a GetSSLPassword function.\nfunc ParseConfigWithOptions(connString string, options ParseConfigOptions) (*ConnConfig, error) {\n\tconfig, err := pgconn.ParseConfigWithOptions(connString, options.ParseConfigOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstatementCacheCapacity := 512\n\tif s, ok := config.RuntimeParams[\"statement_cache_capacity\"]; ok {\n\t\tdelete(config.RuntimeParams, \"statement_cache_capacity\")\n\t\tn, err := strconv.ParseInt(s, 10, 32)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse statement_cache_capacity\", err)\n\t\t}\n\t\tstatementCacheCapacity = int(n)\n\t}\n\n\tdescriptionCacheCapacity := 512\n\tif s, ok := config.RuntimeParams[\"description_cache_capacity\"]; ok {\n\t\tdelete(config.RuntimeParams, \"description_cache_capacity\")\n\t\tn, err := strconv.ParseInt(s, 10, 32)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse description_cache_capacity\", err)\n\t\t}\n\t\tdescriptionCacheCapacity = int(n)\n\t}\n\n\tdefaultQueryExecMode := QueryExecModeCacheStatement\n\tif s, ok := config.RuntimeParams[\"default_query_exec_mode\"]; ok {\n\t\tdelete(config.RuntimeParams, \"default_query_exec_mode\")\n\t\tswitch s {\n\t\tcase \"cache_statement\":\n\t\t\tdefaultQueryExecMode = QueryExecModeCacheStatement\n\t\tcase \"cache_describe\":\n\t\t\tdefaultQueryExecMode = QueryExecModeCacheDescribe\n\t\tcase \"describe_exec\":\n\t\t\tdefaultQueryExecMode = QueryExecModeDescribeExec\n\t\tcase \"exec\":\n\t\t\tdefaultQueryExecMode = QueryExecModeExec\n\t\tcase \"simple_protocol\":\n\t\t\tdefaultQueryExecMode = QueryExecModeSimpleProtocol\n\t\tdefault:\n\t\t\treturn nil, pgconn.NewParseConfigError(\n\t\t\t\tconnString, \"invalid default_query_exec_mode\", fmt.Errorf(\"unknown value %q\", s),\n\t\t\t)\n\t\t}\n\t}\n\n\tconnConfig := &ConnConfig{\n\t\tConfig:                   *config,\n\t\tcreatedByParseConfig:     true,\n\t\tStatementCacheCapacity:   statementCacheCapacity,\n\t\tDescriptionCacheCapacity: descriptionCacheCapacity,\n\t\tDefaultQueryExecMode:     defaultQueryExecMode,\n\t\tconnString:               connString,\n\t}\n\n\treturn connConfig, nil\n}\n\n// ParseConfig creates a ConnConfig from a connection string. ParseConfig handles all options that [pgconn.ParseConfig]\n// does. In addition, it accepts the following options:\n//\n//   - default_query_exec_mode.\n//     Possible values: \"cache_statement\", \"cache_describe\", \"describe_exec\", \"exec\", and \"simple_protocol\". See\n//     QueryExecMode constant documentation for the meaning of these values. Default: \"cache_statement\".\n//\n//   - statement_cache_capacity.\n//     The maximum size of the statement cache used when executing a query with \"cache_statement\" query exec mode.\n//     Default: 512.\n//\n//   - description_cache_capacity.\n//     The maximum size of the description cache used when executing a query with \"cache_describe\" query exec mode.\n//     Default: 512.\nfunc ParseConfig(connString string) (*ConnConfig, error) {\n\treturn ParseConfigWithOptions(connString, ParseConfigOptions{})\n}\n\n// connect connects to a database. connect takes ownership of config. The caller must not use or access it again.\nfunc connect(ctx context.Context, config *ConnConfig) (c *Conn, err error) {\n\tif connectTracer, ok := config.Tracer.(ConnectTracer); ok {\n\t\tctx = connectTracer.TraceConnectStart(ctx, TraceConnectStartData{ConnConfig: config})\n\t\tdefer func() {\n\t\t\tconnectTracer.TraceConnectEnd(ctx, TraceConnectEndData{Conn: c, Err: err})\n\t\t}()\n\t}\n\n\t// Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from\n\t// zero values.\n\tif !config.createdByParseConfig {\n\t\tpanic(\"config must be created by ParseConfig\")\n\t}\n\n\tc = &Conn{\n\t\tconfig:      config,\n\t\ttypeMap:     pgtype.NewMap(),\n\t\tqueryTracer: config.Tracer,\n\t}\n\n\tif t, ok := c.queryTracer.(BatchTracer); ok {\n\t\tc.batchTracer = t\n\t}\n\tif t, ok := c.queryTracer.(CopyFromTracer); ok {\n\t\tc.copyFromTracer = t\n\t}\n\tif t, ok := c.queryTracer.(PrepareTracer); ok {\n\t\tc.prepareTracer = t\n\t}\n\n\t// Only install pgx notification system if no other callback handler is present.\n\tif config.Config.OnNotification == nil {\n\t\tconfig.Config.OnNotification = c.bufferNotifications\n\t}\n\n\tc.pgConn, err = pgconn.ConnectConfig(ctx, &config.Config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.preparedStatements = make(map[string]*pgconn.StatementDescription)\n\tc.doneChan = make(chan struct{})\n\tc.closedChan = make(chan error)\n\tc.wbuf = make([]byte, 0, 1024)\n\n\tif c.config.StatementCacheCapacity > 0 {\n\t\tc.statementCache = stmtcache.NewLRUCache(c.config.StatementCacheCapacity)\n\t}\n\n\tif c.config.DescriptionCacheCapacity > 0 {\n\t\tc.descriptionCache = stmtcache.NewLRUCache(c.config.DescriptionCacheCapacity)\n\t}\n\n\treturn c, nil\n}\n\n// Close closes a connection. It is safe to call Close on an already closed\n// connection.\nfunc (c *Conn) Close(ctx context.Context) error {\n\tif c.IsClosed() {\n\t\treturn nil\n\t}\n\n\terr := c.pgConn.Close(ctx)\n\treturn err\n}\n\n// Prepare creates a prepared statement with name and sql. sql can contain placeholders for bound parameters. These\n// placeholders are referenced positionally as $1, $2, etc. name can be used instead of sql with Query, QueryRow, and\n// Exec to execute the statement. It can also be used with Batch.Queue.\n//\n// The underlying PostgreSQL identifier for the prepared statement will be name if name != sql or a digest of sql if\n// name == sql.\n//\n// Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same name and sql arguments. This\n// allows a code path to Prepare and Query/Exec without concern for if the statement has already been prepared.\nfunc (c *Conn) Prepare(ctx context.Context, name, sql string) (sd *pgconn.StatementDescription, err error) {\n\tif c.failedDescribeStatement != \"\" {\n\t\terr = c.Deallocate(ctx, c.failedDescribeStatement)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to deallocate previously failed statement %q: %w\", c.failedDescribeStatement, err)\n\t\t}\n\t\tc.failedDescribeStatement = \"\"\n\t}\n\n\tif c.prepareTracer != nil {\n\t\tctx = c.prepareTracer.TracePrepareStart(ctx, c, TracePrepareStartData{Name: name, SQL: sql})\n\t}\n\n\tif name != \"\" {\n\t\tvar ok bool\n\t\tif sd, ok = c.preparedStatements[name]; ok && sd.SQL == sql {\n\t\t\tif c.prepareTracer != nil {\n\t\t\t\tc.prepareTracer.TracePrepareEnd(ctx, c, TracePrepareEndData{AlreadyPrepared: true})\n\t\t\t}\n\t\t\treturn sd, nil\n\t\t}\n\t}\n\n\tif c.prepareTracer != nil {\n\t\tdefer func() {\n\t\t\tc.prepareTracer.TracePrepareEnd(ctx, c, TracePrepareEndData{Err: err})\n\t\t}()\n\t}\n\n\tvar psName, psKey string\n\tif name == sql {\n\t\tdigest := sha256.Sum256([]byte(sql))\n\t\tpsName = \"stmt_\" + hex.EncodeToString(digest[0:24])\n\t\tpsKey = sql\n\t} else {\n\t\tpsName = name\n\t\tpsKey = name\n\t}\n\n\tsd, err = c.pgConn.Prepare(ctx, psName, sql, nil)\n\tif err != nil {\n\t\tvar pErr *pgconn.PrepareError\n\t\tif errors.As(err, &pErr) {\n\t\t\tc.failedDescribeStatement = psKey\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tif psKey != \"\" {\n\t\tc.preparedStatements[psKey] = sd\n\t}\n\n\treturn sd, nil\n}\n\n// Deallocate releases a prepared statement. Calling Deallocate on a non-existent prepared statement will succeed.\nfunc (c *Conn) Deallocate(ctx context.Context, name string) error {\n\tvar psName string\n\tsd := c.preparedStatements[name]\n\tif sd != nil {\n\t\tpsName = sd.Name\n\t} else {\n\t\tpsName = name\n\t}\n\n\terr := c.pgConn.Deallocate(ctx, psName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif sd != nil {\n\t\tdelete(c.preparedStatements, name)\n\t}\n\n\treturn nil\n}\n\n// DeallocateAll releases all previously prepared statements from the server and client, where it also resets the statement and description cache.\nfunc (c *Conn) DeallocateAll(ctx context.Context) error {\n\tc.preparedStatements = map[string]*pgconn.StatementDescription{}\n\tif c.config.StatementCacheCapacity > 0 {\n\t\tc.statementCache = stmtcache.NewLRUCache(c.config.StatementCacheCapacity)\n\t}\n\tif c.config.DescriptionCacheCapacity > 0 {\n\t\tc.descriptionCache = stmtcache.NewLRUCache(c.config.DescriptionCacheCapacity)\n\t}\n\t_, err := c.pgConn.Exec(ctx, \"deallocate all\").ReadAll()\n\treturn err\n}\n\nfunc (c *Conn) bufferNotifications(_ *pgconn.PgConn, n *pgconn.Notification) {\n\tc.notifications = append(c.notifications, n)\n}\n\n// WaitForNotification waits for a PostgreSQL notification. It wraps the underlying pgconn notification system in a\n// slightly more convenient form.\nfunc (c *Conn) WaitForNotification(ctx context.Context) (*pgconn.Notification, error) {\n\tvar n *pgconn.Notification\n\n\t// Return already received notification immediately\n\tif len(c.notifications) > 0 {\n\t\tn = c.notifications[0]\n\t\tc.notifications = c.notifications[1:]\n\t\treturn n, nil\n\t}\n\n\terr := c.pgConn.WaitForNotification(ctx)\n\tif len(c.notifications) > 0 {\n\t\tn = c.notifications[0]\n\t\tc.notifications = c.notifications[1:]\n\t}\n\treturn n, err\n}\n\n// IsClosed reports if the connection has been closed.\nfunc (c *Conn) IsClosed() bool {\n\treturn c.pgConn.IsClosed()\n}\n\nfunc (c *Conn) die() {\n\tif c.IsClosed() {\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tcancel() // force immediate hard cancel\n\tc.pgConn.Close(ctx)\n}\n\nfunc quoteIdentifier(s string) string {\n\treturn `\"` + strings.ReplaceAll(s, `\"`, `\"\"`) + `\"`\n}\n\n// Ping delegates to the underlying *pgconn.PgConn.Ping.\nfunc (c *Conn) Ping(ctx context.Context) error {\n\treturn c.pgConn.Ping(ctx)\n}\n\n// PgConn returns the underlying *pgconn.PgConn. This is an escape hatch method that allows lower level access to the\n// PostgreSQL connection than pgx exposes.\n//\n// It is strongly recommended that the connection be idle (no in-progress queries) before the underlying *pgconn.PgConn\n// is used and the connection must be returned to the same state before any *pgx.Conn methods are again used.\nfunc (c *Conn) PgConn() *pgconn.PgConn { return c.pgConn }\n\n// TypeMap returns the connection info used for this connection.\nfunc (c *Conn) TypeMap() *pgtype.Map { return c.typeMap }\n\n// Config returns a copy of config that was used to establish this connection.\nfunc (c *Conn) Config() *ConnConfig { return c.config.Copy() }\n\n// Exec executes sql. sql can be either a prepared statement name or an SQL string. arguments should be referenced\n// positionally from the sql string as $1, $2, etc.\nfunc (c *Conn) Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) {\n\tif c.queryTracer != nil {\n\t\tctx = c.queryTracer.TraceQueryStart(ctx, c, TraceQueryStartData{SQL: sql, Args: arguments})\n\t}\n\n\tif err := c.deallocateInvalidatedCachedStatements(ctx); err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\tcommandTag, err := c.exec(ctx, sql, arguments...)\n\n\tif c.queryTracer != nil {\n\t\tc.queryTracer.TraceQueryEnd(ctx, c, TraceQueryEndData{CommandTag: commandTag, Err: err})\n\t}\n\n\treturn commandTag, err\n}\n\nfunc (c *Conn) exec(ctx context.Context, sql string, arguments ...any) (commandTag pgconn.CommandTag, err error) {\n\tmode := c.config.DefaultQueryExecMode\n\tvar queryRewriter QueryRewriter\n\noptionLoop:\n\tfor len(arguments) > 0 {\n\t\tswitch arg := arguments[0].(type) {\n\t\tcase QueryExecMode:\n\t\t\tmode = arg\n\t\t\targuments = arguments[1:]\n\t\tcase QueryRewriter:\n\t\t\tqueryRewriter = arg\n\t\t\targuments = arguments[1:]\n\t\tdefault:\n\t\t\tbreak optionLoop\n\t\t}\n\t}\n\n\tif queryRewriter != nil {\n\t\tsql, arguments, err = queryRewriter.RewriteQuery(ctx, c, sql, arguments)\n\t\tif err != nil {\n\t\t\treturn pgconn.CommandTag{}, fmt.Errorf(\"rewrite query failed: %w\", err)\n\t\t}\n\t}\n\n\t// Always use simple protocol when there are no arguments.\n\tif len(arguments) == 0 {\n\t\tmode = QueryExecModeSimpleProtocol\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tif sc := c.statementCache; sc != nil {\n\t\t\t\tsc.Invalidate(sql)\n\t\t\t}\n\n\t\t\tif sc := c.descriptionCache; sc != nil {\n\t\t\t\tsc.Invalidate(sql)\n\t\t\t}\n\t\t}\n\t}()\n\n\tif sd, ok := c.preparedStatements[sql]; ok {\n\t\treturn c.execPrepared(ctx, sd, arguments)\n\t}\n\n\tswitch mode {\n\tcase QueryExecModeCacheStatement:\n\t\tif c.statementCache == nil {\n\t\t\treturn pgconn.CommandTag{}, errDisabledStatementCache\n\t\t}\n\t\tsd := c.statementCache.Get(sql)\n\t\tif sd == nil {\n\t\t\tsd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)\n\t\t\tif err != nil {\n\t\t\t\treturn pgconn.CommandTag{}, err\n\t\t\t}\n\t\t\tc.statementCache.Put(sd)\n\t\t}\n\n\t\treturn c.execPrepared(ctx, sd, arguments)\n\tcase QueryExecModeCacheDescribe:\n\t\tif c.descriptionCache == nil {\n\t\t\treturn pgconn.CommandTag{}, errDisabledDescriptionCache\n\t\t}\n\t\tsd := c.descriptionCache.Get(sql)\n\t\tif sd == nil {\n\t\t\tsd, err = c.Prepare(ctx, \"\", sql)\n\t\t\tif err != nil {\n\t\t\t\treturn pgconn.CommandTag{}, err\n\t\t\t}\n\t\t\tc.descriptionCache.Put(sd)\n\t\t}\n\n\t\treturn c.execParams(ctx, sd, arguments)\n\tcase QueryExecModeDescribeExec:\n\t\tsd, err := c.Prepare(ctx, \"\", sql)\n\t\tif err != nil {\n\t\t\treturn pgconn.CommandTag{}, err\n\t\t}\n\t\treturn c.execPrepared(ctx, sd, arguments)\n\tcase QueryExecModeExec:\n\t\treturn c.execSQLParams(ctx, sql, arguments)\n\tcase QueryExecModeSimpleProtocol:\n\t\treturn c.execSimpleProtocol(ctx, sql, arguments)\n\tdefault:\n\t\treturn pgconn.CommandTag{}, fmt.Errorf(\"unknown QueryExecMode: %v\", mode)\n\t}\n}\n\nfunc (c *Conn) execSimpleProtocol(ctx context.Context, sql string, arguments []any) (commandTag pgconn.CommandTag, err error) {\n\tif len(arguments) > 0 {\n\t\tsql, err = c.sanitizeForSimpleQuery(sql, arguments...)\n\t\tif err != nil {\n\t\t\treturn pgconn.CommandTag{}, err\n\t\t}\n\t}\n\n\tmrr := c.pgConn.Exec(ctx, sql)\n\tfor mrr.NextResult() {\n\t\tcommandTag, _ = mrr.ResultReader().Close()\n\t}\n\terr = mrr.Close()\n\treturn commandTag, err\n}\n\nfunc (c *Conn) execParams(ctx context.Context, sd *pgconn.StatementDescription, arguments []any) (pgconn.CommandTag, error) {\n\terr := c.eqb.Build(c.typeMap, sd, arguments)\n\tif err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\tresult := c.pgConn.ExecParams(ctx, sd.SQL, c.eqb.ParamValues, sd.ParamOIDs, c.eqb.ParamFormats, c.eqb.ResultFormats).Read()\n\tc.eqb.reset() // Allow c.eqb internal memory to be GC'ed as soon as possible.\n\treturn result.CommandTag, result.Err\n}\n\nfunc (c *Conn) execPrepared(ctx context.Context, sd *pgconn.StatementDescription, arguments []any) (pgconn.CommandTag, error) {\n\terr := c.eqb.Build(c.typeMap, sd, arguments)\n\tif err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\tresult := c.pgConn.ExecStatement(ctx, sd, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats).Read()\n\tc.eqb.reset() // Allow c.eqb internal memory to be GC'ed as soon as possible.\n\treturn result.CommandTag, result.Err\n}\n\nfunc (c *Conn) execSQLParams(ctx context.Context, sql string, args []any) (pgconn.CommandTag, error) {\n\terr := c.eqb.Build(c.typeMap, nil, args)\n\tif err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\tresult := c.pgConn.ExecParams(ctx, sql, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats).Read()\n\tc.eqb.reset() // Allow c.eqb internal memory to be GC'ed as soon as possible.\n\treturn result.CommandTag, result.Err\n}\n\nfunc (c *Conn) getRows(ctx context.Context, sql string, args []any) *baseRows {\n\tr := &baseRows{}\n\n\tr.ctx = ctx\n\tr.queryTracer = c.queryTracer\n\tr.typeMap = c.typeMap\n\tr.startTime = time.Now()\n\tr.sql = sql\n\tr.args = args\n\tr.conn = c\n\n\treturn r\n}\n\ntype QueryExecMode int32\n\nconst (\n\t_ QueryExecMode = iota\n\n\t// Automatically prepare and cache statements. This uses the extended protocol. Queries are executed in a single round\n\t// trip after the statement is cached. This is the default. If the database schema is modified or the search_path is\n\t// changed after a statement is cached then the first execution of a previously cached query may fail. e.g. If the\n\t// number of columns returned by a \"SELECT *\" changes or the type of a column is changed.\n\tQueryExecModeCacheStatement\n\n\t// Cache statement descriptions (i.e. argument and result types) and assume they do not change. This uses the extended\n\t// protocol. Queries are executed in a single round trip after the description is cached. If the database schema is\n\t// modified or the search_path is changed after a statement is cached then the first execution of a previously cached\n\t// query may fail. e.g. If the number of columns returned by a \"SELECT *\" changes or the type of a column is changed.\n\tQueryExecModeCacheDescribe\n\n\t// Get the statement description on every execution. This uses the extended protocol. Queries require two round trips\n\t// to execute. It does not use named prepared statements. But it does use the unnamed prepared statement to get the\n\t// statement description on the first round trip and then uses it to execute the query on the second round trip. This\n\t// may cause problems with connection poolers that switch the underlying connection between round trips. It is safe\n\t// even when the database schema is modified concurrently.\n\tQueryExecModeDescribeExec\n\n\t// Assume the PostgreSQL query parameter types based on the Go type of the arguments. This uses the extended protocol\n\t// with text formatted parameters and results. Queries are executed in a single round trip. Type mappings can be\n\t// registered with pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are\n\t// unregistered or ambiguous. e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know\n\t// the PostgreSQL type can use a map[string]string directly as an argument. This mode cannot.\n\t//\n\t// On rare occasions user defined types may behave differently when encoded in the text format instead of the binary\n\t// format. For example, this could happen if a \"type RomanNumeral int32\" implements fmt.Stringer to format integers as\n\t// Roman numerals (e.g. 7 is VII). The binary format would properly encode the integer 7 as the binary value for 7.\n\t// But the text format would encode the integer 7 as the string \"VII\". As QueryExecModeExec uses the text format, it\n\t// is possible that changing query mode from another mode to QueryExecModeExec could change the behavior of the query.\n\t// This should not occur with types pgx supports directly and can be avoided by registering the types with\n\t// pgtype.Map.RegisterDefaultPgType and implementing the appropriate type interfaces. In the cas of RomanNumeral, it\n\t// should implement pgtype.Int64Valuer.\n\tQueryExecModeExec\n\n\t// Use the simple protocol. Assume the PostgreSQL query parameter types based on the Go type of the arguments. This is\n\t// especially significant for []byte values. []byte values are encoded as PostgreSQL bytea. string must be used\n\t// instead for text type values including json and jsonb. Type mappings can be registered with\n\t// pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are unregistered or ambiguous.\n\t// e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know the PostgreSQL type can use a\n\t// map[string]string directly as an argument. This mode cannot. Queries are executed in a single round trip.\n\t//\n\t// QueryExecModeSimpleProtocol should have the user application visible behavior as QueryExecModeExec. This includes\n\t// the warning regarding differences in text format and binary format encoding with user defined types. There may be\n\t// other minor exceptions such as behavior when multiple result returning queries are erroneously sent in a single\n\t// string.\n\t//\n\t// QueryExecModeSimpleProtocol uses client side parameter interpolation. All values are quoted and escaped. Prefer\n\t// QueryExecModeExec over QueryExecModeSimpleProtocol whenever possible. In general QueryExecModeSimpleProtocol should\n\t// only be used if connecting to a proxy server, connection pool server, or non-PostgreSQL server that does not\n\t// support the extended protocol.\n\tQueryExecModeSimpleProtocol\n)\n\nfunc (m QueryExecMode) String() string {\n\tswitch m {\n\tcase QueryExecModeCacheStatement:\n\t\treturn \"cache statement\"\n\tcase QueryExecModeCacheDescribe:\n\t\treturn \"cache describe\"\n\tcase QueryExecModeDescribeExec:\n\t\treturn \"describe exec\"\n\tcase QueryExecModeExec:\n\t\treturn \"exec\"\n\tcase QueryExecModeSimpleProtocol:\n\t\treturn \"simple protocol\"\n\tdefault:\n\t\treturn \"invalid\"\n\t}\n}\n\n// QueryResultFormats controls the result format (text=0, binary=1) of a query by result column position.\ntype QueryResultFormats []int16\n\n// QueryResultFormatsByOID controls the result format (text=0, binary=1) of a query by the result column OID.\ntype QueryResultFormatsByOID map[uint32]int16\n\n// QueryRewriter rewrites a query when used as the first arguments to a query method.\ntype QueryRewriter interface {\n\tRewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error)\n}\n\n// Query sends a query to the server and returns a Rows to read the results. Only errors encountered sending the query\n// and initializing Rows will be returned. Err() on the returned Rows must be checked after the Rows is closed to\n// determine if the query executed successfully.\n//\n// The returned Rows must be closed before the connection can be used again. It is safe to attempt to read from the\n// returned Rows even if an error is returned. The error will be the available in rows.Err() after rows are closed. It\n// is allowed to ignore the error returned from Query and handle it in Rows.\n//\n// It is possible for a call of FieldDescriptions on the returned Rows to return nil even if the Query call did not\n// return an error.\n//\n// It is possible for a query to return one or more rows before encountering an error. In most cases the rows should be\n// collected before processing rather than processed while receiving each row. This avoids the possibility of the\n// application processing rows from a query that the server rejected. The CollectRows function is useful here.\n//\n// An implementor of QueryRewriter may be passed as the first element of args. It can rewrite the sql and change or\n// replace args. For example, NamedArgs is QueryRewriter that implements named arguments.\n//\n// For extra control over how the query is executed, the types QueryExecMode, QueryResultFormats, and\n// QueryResultFormatsByOID may be used as the first args to control exactly how the query is executed. This is rarely\n// needed. See the documentation for those types for details.\nfunc (c *Conn) Query(ctx context.Context, sql string, args ...any) (Rows, error) {\n\tif c.queryTracer != nil {\n\t\tctx = c.queryTracer.TraceQueryStart(ctx, c, TraceQueryStartData{SQL: sql, Args: args})\n\t}\n\n\tif err := c.deallocateInvalidatedCachedStatements(ctx); err != nil {\n\t\tif c.queryTracer != nil {\n\t\t\tc.queryTracer.TraceQueryEnd(ctx, c, TraceQueryEndData{Err: err})\n\t\t}\n\t\treturn &baseRows{err: err, closed: true}, err\n\t}\n\n\tvar resultFormats QueryResultFormats\n\tvar resultFormatsByOID QueryResultFormatsByOID\n\tmode := c.config.DefaultQueryExecMode\n\tvar queryRewriter QueryRewriter\n\noptionLoop:\n\tfor len(args) > 0 {\n\t\tswitch arg := args[0].(type) {\n\t\tcase QueryResultFormats:\n\t\t\tresultFormats = arg\n\t\t\targs = args[1:]\n\t\tcase QueryResultFormatsByOID:\n\t\t\tresultFormatsByOID = arg\n\t\t\targs = args[1:]\n\t\tcase QueryExecMode:\n\t\t\tmode = arg\n\t\t\targs = args[1:]\n\t\tcase QueryRewriter:\n\t\t\tqueryRewriter = arg\n\t\t\targs = args[1:]\n\t\tdefault:\n\t\t\tbreak optionLoop\n\t\t}\n\t}\n\n\tif queryRewriter != nil {\n\t\tvar err error\n\t\toriginalSQL := sql\n\t\toriginalArgs := args\n\t\tsql, args, err = queryRewriter.RewriteQuery(ctx, c, sql, args)\n\t\tif err != nil {\n\t\t\trows := c.getRows(ctx, originalSQL, originalArgs)\n\t\t\terr = fmt.Errorf(\"rewrite query failed: %w\", err)\n\t\t\trows.fatal(err)\n\t\t\treturn rows, err\n\t\t}\n\t}\n\n\t// Bypass any statement caching.\n\tif sql == \"\" {\n\t\tmode = QueryExecModeSimpleProtocol\n\t}\n\n\tc.eqb.reset()\n\trows := c.getRows(ctx, sql, args)\n\n\tvar err error\n\tsd, explicitPreparedStatement := c.preparedStatements[sql]\n\tif sd != nil || mode == QueryExecModeCacheStatement || mode == QueryExecModeCacheDescribe || mode == QueryExecModeDescribeExec {\n\t\tif sd == nil {\n\t\t\tsd, err = c.getStatementDescription(ctx, mode, sql)\n\t\t\tif err != nil {\n\t\t\t\trows.fatal(err)\n\t\t\t\treturn rows, err\n\t\t\t}\n\t\t}\n\n\t\tif len(sd.ParamOIDs) != len(args) {\n\t\t\trows.fatal(fmt.Errorf(\"expected %d arguments, got %d\", len(sd.ParamOIDs), len(args)))\n\t\t\treturn rows, rows.err\n\t\t}\n\n\t\trows.sql = sd.SQL\n\n\t\terr = c.eqb.Build(c.typeMap, sd, args)\n\t\tif err != nil {\n\t\t\trows.fatal(err)\n\t\t\treturn rows, rows.err\n\t\t}\n\n\t\tif resultFormatsByOID != nil {\n\t\t\tresultFormats = make([]int16, len(sd.Fields))\n\t\t\tfor i := range resultFormats {\n\t\t\t\tresultFormats[i] = resultFormatsByOID[uint32(sd.Fields[i].DataTypeOID)]\n\t\t\t}\n\t\t}\n\n\t\tif resultFormats == nil {\n\t\t\tresultFormats = c.eqb.ResultFormats\n\t\t}\n\n\t\tif !explicitPreparedStatement && mode == QueryExecModeCacheDescribe {\n\t\t\trows.resultReader = c.pgConn.ExecParams(ctx, sql, c.eqb.ParamValues, sd.ParamOIDs, c.eqb.ParamFormats, resultFormats)\n\t\t} else {\n\t\t\trows.resultReader = c.pgConn.ExecStatement(ctx, sd, c.eqb.ParamValues, c.eqb.ParamFormats, resultFormats)\n\t\t}\n\t} else if mode == QueryExecModeExec {\n\t\terr := c.eqb.Build(c.typeMap, nil, args)\n\t\tif err != nil {\n\t\t\trows.fatal(err)\n\t\t\treturn rows, rows.err\n\t\t}\n\n\t\trows.resultReader = c.pgConn.ExecParams(ctx, sql, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats)\n\t} else if mode == QueryExecModeSimpleProtocol {\n\t\tsql, err = c.sanitizeForSimpleQuery(sql, args...)\n\t\tif err != nil {\n\t\t\trows.fatal(err)\n\t\t\treturn rows, err\n\t\t}\n\n\t\tmrr := c.pgConn.Exec(ctx, sql)\n\t\tif mrr.NextResult() {\n\t\t\trows.resultReader = mrr.ResultReader()\n\t\t\trows.multiResultReader = mrr\n\t\t} else {\n\t\t\terr = mrr.Close()\n\t\t\trows.fatal(err)\n\t\t\treturn rows, err\n\t\t}\n\n\t\treturn rows, nil\n\t} else {\n\t\terr = fmt.Errorf(\"unknown QueryExecMode: %v\", mode)\n\t\trows.fatal(err)\n\t\treturn rows, rows.err\n\t}\n\n\tc.eqb.reset() // Allow c.eqb internal memory to be GC'ed as soon as possible.\n\n\treturn rows, rows.err\n}\n\n// getStatementDescription returns the statement description of the sql query\n// according to the given mode.\n//\n// If the mode is one that doesn't require to know the param and result OIDs\n// then nil is returned without error.\nfunc (c *Conn) getStatementDescription(\n\tctx context.Context,\n\tmode QueryExecMode,\n\tsql string,\n) (sd *pgconn.StatementDescription, err error) {\n\tswitch mode {\n\tcase QueryExecModeCacheStatement:\n\t\tif c.statementCache == nil {\n\t\t\treturn nil, errDisabledStatementCache\n\t\t}\n\t\tsd = c.statementCache.Get(sql)\n\t\tif sd == nil {\n\t\t\tsd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tc.statementCache.Put(sd)\n\t\t}\n\tcase QueryExecModeCacheDescribe:\n\t\tif c.descriptionCache == nil {\n\t\t\treturn nil, errDisabledDescriptionCache\n\t\t}\n\t\tsd = c.descriptionCache.Get(sql)\n\t\tif sd == nil {\n\t\t\tsd, err = c.Prepare(ctx, \"\", sql)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tc.descriptionCache.Put(sd)\n\t\t}\n\tcase QueryExecModeDescribeExec:\n\t\treturn c.Prepare(ctx, \"\", sql)\n\t}\n\treturn sd, err\n}\n\n// QueryRow is a convenience wrapper over Query. Any error that occurs while\n// querying is deferred until calling Scan on the returned Row. That Row will\n// error with ErrNoRows if no rows are returned.\nfunc (c *Conn) QueryRow(ctx context.Context, sql string, args ...any) Row {\n\trows, _ := c.Query(ctx, sql, args...)\n\treturn (*connRow)(rows.(*baseRows))\n}\n\n// SendBatch sends all queued queries to the server at once. All queries are run in an implicit transaction unless\n// explicit transaction control statements are executed. The returned BatchResults must be closed before the connection\n// is used again.\n//\n// Depending on the QueryExecMode, all queries may be prepared before any are executed. This means that creating a table\n// and using it in a subsequent query in the same batch can fail.\nfunc (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {\n\tif len(b.QueuedQueries) == 0 {\n\t\treturn &emptyBatchResults{conn: c}\n\t}\n\n\tif c.batchTracer != nil {\n\t\tctx = c.batchTracer.TraceBatchStart(ctx, c, TraceBatchStartData{Batch: b})\n\t\tdefer func() {\n\t\t\terr := br.(interface{ earlyError() error }).earlyError()\n\t\t\tif err != nil {\n\t\t\t\tc.batchTracer.TraceBatchEnd(ctx, c, TraceBatchEndData{Err: err})\n\t\t\t}\n\t\t}()\n\t}\n\n\tif err := c.deallocateInvalidatedCachedStatements(ctx); err != nil {\n\t\treturn &batchResults{ctx: ctx, conn: c, err: err}\n\t}\n\n\tfor _, bi := range b.QueuedQueries {\n\t\tvar queryRewriter QueryRewriter\n\t\tsql := bi.SQL\n\t\targuments := bi.Arguments\n\n\toptionLoop:\n\t\tfor len(arguments) > 0 {\n\t\t\t// Update Batch.Queue function comment when additional options are implemented\n\t\t\tswitch arg := arguments[0].(type) {\n\t\t\tcase QueryRewriter:\n\t\t\t\tqueryRewriter = arg\n\t\t\t\targuments = arguments[1:]\n\t\t\tdefault:\n\t\t\t\tbreak optionLoop\n\t\t\t}\n\t\t}\n\n\t\tif queryRewriter != nil {\n\t\t\tvar err error\n\t\t\tsql, arguments, err = queryRewriter.RewriteQuery(ctx, c, sql, arguments)\n\t\t\tif err != nil {\n\t\t\t\treturn &batchResults{ctx: ctx, conn: c, err: fmt.Errorf(\"rewrite query failed: %w\", err)}\n\t\t\t}\n\t\t}\n\n\t\tbi.SQL = sql\n\t\tbi.Arguments = arguments\n\t}\n\n\t// TODO: changing mode per batch? Update Batch.Queue function comment when implemented\n\tmode := c.config.DefaultQueryExecMode\n\tif mode == QueryExecModeSimpleProtocol {\n\t\treturn c.sendBatchQueryExecModeSimpleProtocol(ctx, b)\n\t}\n\n\t// All other modes use extended protocol and thus can use prepared statements.\n\tfor _, bi := range b.QueuedQueries {\n\t\tif sd, ok := c.preparedStatements[bi.SQL]; ok {\n\t\t\tbi.sd = sd\n\t\t}\n\t}\n\n\tswitch mode {\n\tcase QueryExecModeExec:\n\t\treturn c.sendBatchQueryExecModeExec(ctx, b)\n\tcase QueryExecModeCacheStatement:\n\t\treturn c.sendBatchQueryExecModeCacheStatement(ctx, b)\n\tcase QueryExecModeCacheDescribe:\n\t\treturn c.sendBatchQueryExecModeCacheDescribe(ctx, b)\n\tcase QueryExecModeDescribeExec:\n\t\treturn c.sendBatchQueryExecModeDescribeExec(ctx, b)\n\tdefault:\n\t\tpanic(\"unknown QueryExecMode\")\n\t}\n}\n\nfunc (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batch) *batchResults {\n\tvar sb strings.Builder\n\tfor i, bi := range b.QueuedQueries {\n\t\tif i > 0 {\n\t\t\tsb.WriteByte(';')\n\t\t}\n\t\tsql, err := c.sanitizeForSimpleQuery(bi.SQL, bi.Arguments...)\n\t\tif err != nil {\n\t\t\treturn &batchResults{ctx: ctx, conn: c, err: err}\n\t\t}\n\t\tsb.WriteString(sql)\n\t}\n\tmrr := c.pgConn.Exec(ctx, sb.String())\n\treturn &batchResults{\n\t\tctx:   ctx,\n\t\tconn:  c,\n\t\tmrr:   mrr,\n\t\tb:     b,\n\t\tqqIdx: 0,\n\t}\n}\n\nfunc (c *Conn) sendBatchQueryExecModeExec(ctx context.Context, b *Batch) *batchResults {\n\tbatch := &pgconn.Batch{}\n\n\tfor _, bi := range b.QueuedQueries {\n\t\tsd := bi.sd\n\t\tif sd != nil {\n\t\t\terr := c.eqb.Build(c.typeMap, sd, bi.Arguments)\n\t\t\tif err != nil {\n\t\t\t\treturn &batchResults{ctx: ctx, conn: c, err: err}\n\t\t\t}\n\n\t\t\tbatch.ExecPrepared(sd.Name, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats)\n\t\t} else {\n\t\t\terr := c.eqb.Build(c.typeMap, nil, bi.Arguments)\n\t\t\tif err != nil {\n\t\t\t\treturn &batchResults{ctx: ctx, conn: c, err: err}\n\t\t\t}\n\t\t\tbatch.ExecParams(bi.SQL, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats)\n\t\t}\n\t}\n\n\tc.eqb.reset() // Allow c.eqb internal memory to be GC'ed as soon as possible.\n\n\tmrr := c.pgConn.ExecBatch(ctx, batch)\n\n\treturn &batchResults{\n\t\tctx:   ctx,\n\t\tconn:  c,\n\t\tmrr:   mrr,\n\t\tb:     b,\n\t\tqqIdx: 0,\n\t}\n}\n\nfunc (c *Conn) sendBatchQueryExecModeCacheStatement(ctx context.Context, b *Batch) (pbr *pipelineBatchResults) {\n\tif c.statementCache == nil {\n\t\treturn &pipelineBatchResults{ctx: ctx, conn: c, err: errDisabledStatementCache, closed: true}\n\t}\n\n\tdistinctNewQueries := []*pgconn.StatementDescription{}\n\tdistinctNewQueriesIdxMap := make(map[string]int)\n\n\tfor _, bi := range b.QueuedQueries {\n\t\tif bi.sd == nil {\n\t\t\tsd := c.statementCache.Get(bi.SQL)\n\t\t\tif sd != nil {\n\t\t\t\tbi.sd = sd\n\t\t\t} else {\n\t\t\t\tif idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {\n\t\t\t\t\tbi.sd = distinctNewQueries[idx]\n\t\t\t\t} else {\n\t\t\t\t\tsd = &pgconn.StatementDescription{\n\t\t\t\t\t\tName: stmtcache.StatementName(bi.SQL),\n\t\t\t\t\t\tSQL:  bi.SQL,\n\t\t\t\t\t}\n\t\t\t\t\tdistinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)\n\t\t\t\t\tdistinctNewQueries = append(distinctNewQueries, sd)\n\t\t\t\t\tbi.sd = sd\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn c.sendBatchExtendedWithDescription(ctx, b, distinctNewQueries, c.statementCache)\n}\n\nfunc (c *Conn) sendBatchQueryExecModeCacheDescribe(ctx context.Context, b *Batch) (pbr *pipelineBatchResults) {\n\tif c.descriptionCache == nil {\n\t\treturn &pipelineBatchResults{ctx: ctx, conn: c, err: errDisabledDescriptionCache, closed: true}\n\t}\n\n\tdistinctNewQueries := []*pgconn.StatementDescription{}\n\tdistinctNewQueriesIdxMap := make(map[string]int)\n\n\tfor _, bi := range b.QueuedQueries {\n\t\tif bi.sd == nil {\n\t\t\tsd := c.descriptionCache.Get(bi.SQL)\n\t\t\tif sd != nil {\n\t\t\t\tbi.sd = sd\n\t\t\t} else {\n\t\t\t\tif idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {\n\t\t\t\t\tbi.sd = distinctNewQueries[idx]\n\t\t\t\t} else {\n\t\t\t\t\tsd = &pgconn.StatementDescription{\n\t\t\t\t\t\tSQL: bi.SQL,\n\t\t\t\t\t}\n\t\t\t\t\tdistinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)\n\t\t\t\t\tdistinctNewQueries = append(distinctNewQueries, sd)\n\t\t\t\t\tbi.sd = sd\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn c.sendBatchExtendedWithDescription(ctx, b, distinctNewQueries, c.descriptionCache)\n}\n\nfunc (c *Conn) sendBatchQueryExecModeDescribeExec(ctx context.Context, b *Batch) (pbr *pipelineBatchResults) {\n\tdistinctNewQueries := []*pgconn.StatementDescription{}\n\tdistinctNewQueriesIdxMap := make(map[string]int)\n\n\tfor _, bi := range b.QueuedQueries {\n\t\tif bi.sd == nil {\n\t\t\tif idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {\n\t\t\t\tbi.sd = distinctNewQueries[idx]\n\t\t\t} else {\n\t\t\t\tsd := &pgconn.StatementDescription{\n\t\t\t\t\tSQL: bi.SQL,\n\t\t\t\t}\n\t\t\t\tdistinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)\n\t\t\t\tdistinctNewQueries = append(distinctNewQueries, sd)\n\t\t\t\tbi.sd = sd\n\t\t\t}\n\t\t}\n\t}\n\n\treturn c.sendBatchExtendedWithDescription(ctx, b, distinctNewQueries, nil)\n}\n\nfunc (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, distinctNewQueries []*pgconn.StatementDescription, sdCache stmtcache.Cache) (pbr *pipelineBatchResults) {\n\tpipeline := c.pgConn.StartPipeline(ctx)\n\tdefer func() {\n\t\tif pbr != nil && pbr.err != nil {\n\t\t\tpipeline.Close()\n\t\t}\n\t}()\n\n\t// Prepare any needed queries\n\tif len(distinctNewQueries) > 0 {\n\t\terr := func() (err error) {\n\t\t\tfor _, sd := range distinctNewQueries {\n\t\t\t\tpipeline.SendPrepare(sd.Name, sd.SQL, nil)\n\t\t\t}\n\n\t\t\t// Store all statements we are preparing into the cache. It's fine if it overflows because HandleInvalidated will\n\t\t\t// clean them up later.\n\t\t\tif sdCache != nil {\n\t\t\t\tfor _, sd := range distinctNewQueries {\n\t\t\t\t\tsdCache.Put(sd)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If something goes wrong preparing the statements, we need to invalidate the cache entries we just added.\n\t\t\tdefer func() {\n\t\t\t\tif err != nil && sdCache != nil {\n\t\t\t\t\tfor _, sd := range distinctNewQueries {\n\t\t\t\t\t\tsdCache.Invalidate(sd.SQL)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\terr = pipeline.Sync()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tfor _, sd := range distinctNewQueries {\n\t\t\t\tresults, err := pipeline.GetResults()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn newErrPreprocessingBatch(\"prepare\", sd.SQL, err)\n\t\t\t\t}\n\n\t\t\t\tresultSD, ok := results.(*pgconn.StatementDescription)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn fmt.Errorf(\"expected statement description, got %T\", results)\n\t\t\t\t}\n\n\t\t\t\t// Fill in the previously empty / pending statement descriptions.\n\t\t\t\tsd.ParamOIDs = resultSD.ParamOIDs\n\t\t\t\tsd.Fields = resultSD.Fields\n\t\t\t}\n\n\t\t\tresults, err := pipeline.GetResults()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t_, ok := results.(*pgconn.PipelineSync)\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"expected sync, got %T\", results)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}()\n\t\tif err != nil {\n\t\t\treturn &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}\n\t\t}\n\t}\n\n\t// Queue the queries.\n\tfor _, bi := range b.QueuedQueries {\n\t\terr := c.eqb.Build(c.typeMap, bi.sd, bi.Arguments)\n\t\tif err != nil {\n\t\t\terr = newErrPreprocessingBatch(\"build\", bi.SQL, err)\n\t\t\treturn &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}\n\t\t}\n\n\t\tif bi.sd.Name == \"\" {\n\t\t\tpipeline.SendQueryParams(bi.sd.SQL, c.eqb.ParamValues, bi.sd.ParamOIDs, c.eqb.ParamFormats, c.eqb.ResultFormats)\n\t\t} else {\n\t\t\tpipeline.SendQueryStatement(bi.sd, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats)\n\t\t}\n\t}\n\n\terr := pipeline.Sync()\n\tif err != nil {\n\t\treturn &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}\n\t}\n\n\treturn &pipelineBatchResults{\n\t\tctx:      ctx,\n\t\tconn:     c,\n\t\tpipeline: pipeline,\n\t\tb:        b,\n\t}\n}\n\nfunc (c *Conn) sanitizeForSimpleQuery(sql string, args ...any) (string, error) {\n\tif c.pgConn.ParameterStatus(\"standard_conforming_strings\") != \"on\" {\n\t\treturn \"\", errors.New(\"simple protocol queries must be run with standard_conforming_strings=on\")\n\t}\n\n\tif c.pgConn.ParameterStatus(\"client_encoding\") != \"UTF8\" {\n\t\treturn \"\", errors.New(\"simple protocol queries must be run with client_encoding=UTF8\")\n\t}\n\n\tvar err error\n\tvalueArgs := make([]any, len(args))\n\tfor i, a := range args {\n\t\tvalueArgs[i], err = convertSimpleArgument(c.typeMap, a)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\treturn sanitize.SanitizeSQL(sql, valueArgs...)\n}\n\n// LoadType inspects the database for typeName and produces a pgtype.Type suitable for registration. typeName must be\n// the name of a type where the underlying type(s) is already understood by pgx. It is for derived types. In particular,\n// typeName must be one of the following:\n//   - An array type name of a type that is already registered. e.g. \"_foo\" when \"foo\" is registered.\n//   - A composite type name where all field types are already registered.\n//   - A domain type name where the base type is already registered.\n//   - An enum type name.\n//   - A range type name where the element type is already registered.\n//   - A multirange type name where the element type is already registered.\nfunc (c *Conn) LoadType(ctx context.Context, typeName string) (*pgtype.Type, error) {\n\tvar oid uint32\n\n\terr := c.QueryRow(ctx, \"select $1::text::regtype::oid;\", typeName).Scan(&oid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar typtype string\n\tvar typbasetype uint32\n\n\terr = c.QueryRow(ctx, \"select typtype::text, typbasetype from pg_type where oid=$1\", oid).Scan(&typtype, &typbasetype)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch typtype {\n\tcase \"b\": // array\n\t\telementOID, err := c.getArrayElementOID(ctx, oid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdt, ok := c.TypeMap().TypeForOID(elementOID)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"array element OID not registered\")\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.ArrayCodec{ElementType: dt}}, nil\n\tcase \"c\": // composite\n\t\tfields, err := c.getCompositeFields(ctx, oid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.CompositeCodec{Fields: fields}}, nil\n\tcase \"d\": // domain\n\t\tdt, ok := c.TypeMap().TypeForOID(typbasetype)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"domain base type OID not registered\")\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: dt.Codec}, nil\n\tcase \"e\": // enum\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.EnumCodec{}}, nil\n\tcase \"r\": // range\n\t\telementOID, err := c.getRangeElementOID(ctx, oid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdt, ok := c.TypeMap().TypeForOID(elementOID)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"range element OID not registered\")\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.RangeCodec{ElementType: dt}}, nil\n\tcase \"m\": // multirange\n\t\telementOID, err := c.getMultiRangeElementOID(ctx, oid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdt, ok := c.TypeMap().TypeForOID(elementOID)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"multirange element OID not registered\")\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.MultirangeCodec{ElementType: dt}}, nil\n\tdefault:\n\t\treturn &pgtype.Type{}, errors.New(\"unknown typtype\")\n\t}\n}\n\nfunc (c *Conn) getArrayElementOID(ctx context.Context, oid uint32) (uint32, error) {\n\tvar typelem uint32\n\n\terr := c.QueryRow(ctx, \"select typelem from pg_type where oid=$1\", oid).Scan(&typelem)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn typelem, nil\n}\n\nfunc (c *Conn) getRangeElementOID(ctx context.Context, oid uint32) (uint32, error) {\n\tvar typelem uint32\n\n\terr := c.QueryRow(ctx, \"select rngsubtype from pg_range where rngtypid=$1\", oid).Scan(&typelem)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn typelem, nil\n}\n\nfunc (c *Conn) getMultiRangeElementOID(ctx context.Context, oid uint32) (uint32, error) {\n\tvar typelem uint32\n\n\terr := c.QueryRow(ctx, \"select rngtypid from pg_range where rngmultitypid=$1\", oid).Scan(&typelem)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn typelem, nil\n}\n\nfunc (c *Conn) getCompositeFields(ctx context.Context, oid uint32) ([]pgtype.CompositeCodecField, error) {\n\tvar typrelid uint32\n\n\terr := c.QueryRow(ctx, \"select typrelid from pg_type where oid=$1\", oid).Scan(&typrelid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar fields []pgtype.CompositeCodecField\n\tvar fieldName string\n\tvar fieldOID uint32\n\trows, _ := c.Query(ctx, `select attname, atttypid\nfrom pg_attribute\nwhere attrelid=$1\n\tand not attisdropped\n\tand attnum > 0\norder by attnum`,\n\t\ttyprelid,\n\t)\n\t_, err = ForEachRow(rows, []any{&fieldName, &fieldOID}, func() error {\n\t\tdt, ok := c.TypeMap().TypeForOID(fieldOID)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"unknown composite type field OID: %v\", fieldOID)\n\t\t}\n\t\tfields = append(fields, pgtype.CompositeCodecField{Name: fieldName, Type: dt})\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn fields, nil\n}\n\nfunc (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error {\n\tif txStatus := c.pgConn.TxStatus(); txStatus != 'I' && txStatus != 'T' {\n\t\treturn nil\n\t}\n\n\tif c.descriptionCache != nil {\n\t\tc.descriptionCache.RemoveInvalidated()\n\t}\n\n\tvar invalidatedStatements []*pgconn.StatementDescription\n\tif c.statementCache != nil {\n\t\tinvalidatedStatements = c.statementCache.GetInvalidated()\n\t}\n\n\tif len(invalidatedStatements) == 0 {\n\t\treturn nil\n\t}\n\n\tpipeline := c.pgConn.StartPipeline(ctx)\n\tdefer pipeline.Close()\n\n\tfor _, sd := range invalidatedStatements {\n\t\tpipeline.SendDeallocate(sd.Name)\n\t}\n\n\terr := pipeline.Sync()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to deallocate cached statement(s): %w\", err)\n\t}\n\n\terr = pipeline.Close()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to deallocate cached statement(s): %w\", err)\n\t}\n\n\tc.statementCache.RemoveInvalidated()\n\tfor _, sd := range invalidatedStatements {\n\t\tdelete(c.preparedStatements, sd.Name)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "conn_internal_test.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc mustParseConfig(t testing.TB, connString string) *ConnConfig {\n\tconfig, err := ParseConfig(connString)\n\trequire.Nil(t, err)\n\treturn config\n}\n\nfunc mustConnect(t testing.TB, config *ConnConfig) *Conn {\n\tconn, err := ConnectConfig(context.Background(), config)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to establish connection: %v\", err)\n\t}\n\treturn conn\n}\n\n// Ensures the connection limits the size of its cached objects.\n// This test examines the internals of *Conn so must be in the same package.\nfunc TestStmtCacheSizeLimit(t *testing.T) {\n\tconst cacheLimit = 16\n\n\tconnConfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconnConfig.StatementCacheCapacity = cacheLimit\n\tconn := mustConnect(t, connConfig)\n\tdefer func() {\n\t\terr := conn.Close(context.Background())\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}()\n\n\t// run a set of unique queries that should overflow the cache\n\tctx := context.Background()\n\tfor i := range cacheLimit * 2 {\n\t\tuniqueString := fmt.Sprintf(\"unique %d\", i)\n\t\tuniqueSQL := fmt.Sprintf(\"select '%s'\", uniqueString)\n\t\tvar output string\n\t\terr := conn.QueryRow(ctx, uniqueSQL).Scan(&output)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, uniqueString, output)\n\t}\n\t// preparedStatements contains cacheLimit+1 because deallocation happens before the query\n\tassert.Len(t, conn.preparedStatements, cacheLimit+1)\n\tassert.Equal(t, cacheLimit, conn.statementCache.Len())\n}\n"
  },
  {
    "path": "conn_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/internal/faultyconn\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCrateDBConnect(t *testing.T) {\n\tt.Parallel()\n\n\tconnString := os.Getenv(\"PGX_TEST_CRATEDB_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_CRATEDB_CONN_STRING\")\n\t}\n\n\tconn, err := pgx.Connect(context.Background(), connString)\n\trequire.Nil(t, err)\n\tdefer closeConn(t, conn)\n\n\tassert.Equal(t, connString, conn.Config().ConnString())\n\n\tvar result int\n\terr = conn.QueryRow(context.Background(), \"select 1 +1\").Scan(&result)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan unexpectedly failed: %v\", err)\n\t}\n\tif result != 2 {\n\t\tt.Errorf(\"bad result: %d\", result)\n\t}\n}\n\nfunc TestConnect(t *testing.T) {\n\tt.Parallel()\n\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\tconfig := mustParseConfig(t, connString)\n\n\tconn, err := pgx.ConnectConfig(context.Background(), config)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to establish connection: %v\", err)\n\t}\n\n\tassertConfigsEqual(t, config, conn.Config(), \"Conn.Config() returns original config\")\n\n\tvar currentDB string\n\terr = conn.QueryRow(context.Background(), \"select current_database()\").Scan(&currentDB)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan unexpectedly failed: %v\", err)\n\t}\n\tif currentDB != config.Config.Database {\n\t\tt.Errorf(\"Did not connect to specified database (%v)\", config.Config.Database)\n\t}\n\n\tvar user string\n\terr = conn.QueryRow(context.Background(), \"select current_user\").Scan(&user)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan unexpectedly failed: %v\", err)\n\t}\n\tif user != config.Config.User {\n\t\tt.Errorf(\"Did not connect as specified user (%v)\", config.Config.User)\n\t}\n\n\terr = conn.Close(context.Background())\n\tif err != nil {\n\t\tt.Fatal(\"Unable to close connection\")\n\t}\n}\n\nfunc TestConnectWithPreferSimpleProtocol(t *testing.T) {\n\tt.Parallel()\n\n\tconnConfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconnConfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol\n\n\tconn := mustConnect(t, connConfig)\n\tdefer closeConn(t, conn)\n\n\t// If simple protocol is used we should be able to correctly scan the result\n\t// into a pgtype.Text as the integer will have been encoded in text.\n\n\tvar s pgtype.Text\n\terr := conn.QueryRow(context.Background(), \"select $1::int4\", 42).Scan(&s)\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgtype.Text{String: \"42\", Valid: true}, s)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnectConfigRequiresConnConfigFromParseConfig(t *testing.T) {\n\tconfig := &pgx.ConnConfig{}\n\trequire.PanicsWithValue(t, \"config must be created by ParseConfig\", func() {\n\t\tpgx.ConnectConfig(context.Background(), config)\n\t})\n}\n\nfunc TestConfigContainsConnStr(t *testing.T) {\n\tconnStr := os.Getenv(\"PGX_TEST_DATABASE\")\n\tconfig, err := pgx.ParseConfig(connStr)\n\trequire.NoError(t, err)\n\tassert.Equal(t, connStr, config.ConnString())\n}\n\nfunc TestConfigCopyReturnsEqualConfig(t *testing.T) {\n\tconnString := \"postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5\"\n\toriginal, err := pgx.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\tassertConfigsEqual(t, original, copied, t.Name())\n}\n\nfunc TestConfigCopyCanBeUsedToConnect(t *testing.T) {\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\toriginal, err := pgx.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\tassert.NotPanics(t, func() {\n\t\t_, err = pgx.ConnectConfig(context.Background(), copied)\n\t})\n\tassert.NoError(t, err)\n}\n\nfunc TestParseConfigExtractsStatementCacheOptions(t *testing.T) {\n\tt.Parallel()\n\n\tconfig, err := pgx.ParseConfig(\"statement_cache_capacity=0\")\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 0, config.StatementCacheCapacity)\n\n\tconfig, err = pgx.ParseConfig(\"statement_cache_capacity=42\")\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 42, config.StatementCacheCapacity)\n\n\tconfig, err = pgx.ParseConfig(\"description_cache_capacity=0\")\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 0, config.DescriptionCacheCapacity)\n\n\tconfig, err = pgx.ParseConfig(\"description_cache_capacity=42\")\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 42, config.DescriptionCacheCapacity)\n\n\t//\tdefault_query_exec_mode\n\t//\t\tPossible values: \"cache_statement\", \"cache_describe\", \"describe_exec\", \"exec\", and \"simple_protocol\". See\n\n\tconfig, err = pgx.ParseConfig(\"default_query_exec_mode=cache_statement\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgx.QueryExecModeCacheStatement, config.DefaultQueryExecMode)\n\n\tconfig, err = pgx.ParseConfig(\"default_query_exec_mode=cache_describe\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgx.QueryExecModeCacheDescribe, config.DefaultQueryExecMode)\n\n\tconfig, err = pgx.ParseConfig(\"default_query_exec_mode=describe_exec\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgx.QueryExecModeDescribeExec, config.DefaultQueryExecMode)\n\n\tconfig, err = pgx.ParseConfig(\"default_query_exec_mode=exec\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgx.QueryExecModeExec, config.DefaultQueryExecMode)\n\n\tconfig, err = pgx.ParseConfig(\"default_query_exec_mode=simple_protocol\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgx.QueryExecModeSimpleProtocol, config.DefaultQueryExecMode)\n}\n\nfunc TestParseConfigExtractsDefaultQueryExecMode(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tt := range []struct {\n\t\tconnString           string\n\t\tdefaultQueryExecMode pgx.QueryExecMode\n\t}{\n\t\t{\"\", pgx.QueryExecModeCacheStatement},\n\t\t{\"default_query_exec_mode=cache_statement\", pgx.QueryExecModeCacheStatement},\n\t\t{\"default_query_exec_mode=cache_describe\", pgx.QueryExecModeCacheDescribe},\n\t\t{\"default_query_exec_mode=describe_exec\", pgx.QueryExecModeDescribeExec},\n\t\t{\"default_query_exec_mode=exec\", pgx.QueryExecModeExec},\n\t\t{\"default_query_exec_mode=simple_protocol\", pgx.QueryExecModeSimpleProtocol},\n\t} {\n\t\tconfig, err := pgx.ParseConfig(tt.connString)\n\t\trequire.NoError(t, err)\n\t\trequire.Equalf(t, tt.defaultQueryExecMode, config.DefaultQueryExecMode, \"connString: `%s`\", tt.connString)\n\t\trequire.Empty(t, config.RuntimeParams[\"default_query_exec_mode\"])\n\t}\n}\n\nfunc TestParseConfigErrors(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tt := range []struct {\n\t\tconnString           string\n\t\texpectedErrSubstring string\n\t}{\n\t\t{\"default_query_exec_mode=does_not_exist\", \"does_not_exist\"},\n\t} {\n\t\tconfig, err := pgx.ParseConfig(tt.connString)\n\t\trequire.Nil(t, config)\n\t\trequire.ErrorContains(t, err, tt.expectedErrSubstring)\n\t}\n}\n\nfunc TestExec(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tif results := mustExec(t, conn, \"create temporary table foo(id integer primary key);\"); results.String() != \"CREATE TABLE\" {\n\t\t\tt.Error(\"Unexpected results from Exec\")\n\t\t}\n\n\t\t// Accept parameters\n\t\tif results := mustExec(t, conn, \"insert into foo(id) values($1)\", 1); results.String() != \"INSERT 0 1\" {\n\t\t\tt.Errorf(\"Unexpected results from Exec: %v\", results)\n\t\t}\n\n\t\tif results := mustExec(t, conn, \"drop table foo;\"); results.String() != \"DROP TABLE\" {\n\t\t\tt.Error(\"Unexpected results from Exec\")\n\t\t}\n\n\t\t// Multiple statements can be executed -- last command tag is returned\n\t\tif results := mustExec(t, conn, \"create temporary table foo(id serial primary key); drop table foo;\"); results.String() != \"DROP TABLE\" {\n\t\t\tt.Error(\"Unexpected results from Exec\")\n\t\t}\n\n\t\t// Can execute longer SQL strings than sharedBufferSize\n\t\tif results := mustExec(t, conn, strings.Repeat(\"select 42; \", 1000)); results.String() != \"SELECT 1\" {\n\t\t\tt.Errorf(\"Unexpected results from Exec: %v\", results)\n\t\t}\n\n\t\t// Exec no-op which does not return a command tag\n\t\tif results := mustExec(t, conn, \"--;\"); results.String() != \"\" {\n\t\t\tt.Errorf(\"Unexpected results from Exec: %v\", results)\n\t\t}\n\t})\n}\n\ntype testQueryRewriter struct {\n\tsql  string\n\targs []any\n}\n\nfunc (qr *testQueryRewriter) RewriteQuery(ctx context.Context, conn *pgx.Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {\n\treturn qr.sql, qr.args, nil\n}\n\nfunc TestExecWithQueryRewriter(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tqr := testQueryRewriter{sql: \"select $1::int\", args: []any{42}}\n\t\t_, err := conn.Exec(ctx, \"should be replaced\", &qr)\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestExecFailure(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tif _, err := conn.Exec(context.Background(), \"selct;\"); err == nil {\n\t\t\tt.Fatal(\"Expected SQL syntax error\")\n\t\t}\n\n\t\trows, _ := conn.Query(context.Background(), \"select 1\")\n\t\trows.Close()\n\t\tif rows.Err() != nil {\n\t\t\tt.Fatalf(\"Exec failure appears to have broken connection: %v\", rows.Err())\n\t\t}\n\t})\n}\n\nfunc TestExecFailureWithArguments(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(context.Background(), \"selct $1;\", 1)\n\t\tif err == nil {\n\t\t\tt.Fatal(\"Expected SQL syntax error\")\n\t\t}\n\t\tassert.False(t, pgconn.SafeToRetry(err))\n\n\t\t_, err = conn.Exec(context.Background(), \"select $1::varchar(1);\", \"1\", \"2\")\n\t\trequire.Error(t, err)\n\t})\n}\n\nfunc TestExecContextWithoutCancelation(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tctx, cancelFunc := context.WithCancel(ctx)\n\t\tdefer cancelFunc()\n\n\t\tcommandTag, err := conn.Exec(ctx, \"create temporary table foo(id integer primary key);\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif commandTag.String() != \"CREATE TABLE\" {\n\t\t\tt.Fatalf(\"Unexpected results from Exec: %v\", commandTag)\n\t\t}\n\t\tassert.False(t, pgconn.SafeToRetry(err))\n\t})\n}\n\nfunc TestExecContextFailureWithoutCancelation(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tctx, cancelFunc := context.WithCancel(ctx)\n\t\tdefer cancelFunc()\n\n\t\t_, err := conn.Exec(ctx, \"selct;\")\n\t\tif err == nil {\n\t\t\tt.Fatal(\"Expected SQL syntax error\")\n\t\t}\n\t\tassert.False(t, pgconn.SafeToRetry(err))\n\n\t\trows, _ := conn.Query(context.Background(), \"select 1\")\n\t\trows.Close()\n\t\tif rows.Err() != nil {\n\t\t\tt.Fatalf(\"ExecEx failure appears to have broken connection: %v\", rows.Err())\n\t\t}\n\t\tassert.False(t, pgconn.SafeToRetry(err))\n\t})\n}\n\nfunc TestExecContextFailureWithoutCancelationWithArguments(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tctx, cancelFunc := context.WithCancel(ctx)\n\t\tdefer cancelFunc()\n\n\t\t_, err := conn.Exec(ctx, \"selct $1;\", 1)\n\t\tif err == nil {\n\t\t\tt.Fatal(\"Expected SQL syntax error\")\n\t\t}\n\t\tassert.False(t, pgconn.SafeToRetry(err))\n\t})\n}\n\nfunc TestExecFailureCloseBefore(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tcloseConn(t, conn)\n\n\t_, err := conn.Exec(context.Background(), \"select 1\")\n\trequire.Error(t, err)\n\tassert.True(t, pgconn.SafeToRetry(err))\n}\n\nfunc TestExecPerQuerySimpleProtocol(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tctx := t.Context()\n\n\tcommandTag, err := conn.Exec(ctx, \"create temporary table foo(name varchar primary key);\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif commandTag.String() != \"CREATE TABLE\" {\n\t\tt.Fatalf(\"Unexpected results from Exec: %v\", commandTag)\n\t}\n\n\tcommandTag, err = conn.Exec(ctx,\n\t\t\"insert into foo(name) values($1);\",\n\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\"bar'; drop table foo;--\",\n\t)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif commandTag.String() != \"INSERT 0 1\" {\n\t\tt.Fatalf(\"Unexpected results from Exec: %v\", commandTag)\n\t}\n}\n\nfunc TestPrepare(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t_, err := conn.Prepare(context.Background(), \"test\", \"select $1::varchar\")\n\tif err != nil {\n\t\tt.Errorf(\"Unable to prepare statement: %v\", err)\n\t\treturn\n\t}\n\n\tvar s string\n\terr = conn.QueryRow(context.Background(), \"test\", \"hello\").Scan(&s)\n\tif err != nil {\n\t\tt.Errorf(\"Executing prepared statement failed: %v\", err)\n\t}\n\n\tif s != \"hello\" {\n\t\tt.Errorf(\"Prepared statement did not return expected value: %v\", s)\n\t}\n\n\terr = conn.Deallocate(context.Background(), \"test\")\n\tif err != nil {\n\t\tt.Errorf(\"conn.Deallocate failed: %v\", err)\n\t}\n\n\t// Create another prepared statement to ensure Deallocate left the connection\n\t// in a working state and that we can reuse the prepared statement name.\n\n\t_, err = conn.Prepare(context.Background(), \"test\", \"select $1::integer\")\n\tif err != nil {\n\t\tt.Errorf(\"Unable to prepare statement: %v\", err)\n\t\treturn\n\t}\n\n\tvar n int32\n\terr = conn.QueryRow(context.Background(), \"test\", int32(1)).Scan(&n)\n\tif err != nil {\n\t\tt.Errorf(\"Executing prepared statement failed: %v\", err)\n\t}\n\n\tif n != 1 {\n\t\tt.Errorf(\"Prepared statement did not return expected value: %v\", s)\n\t}\n\n\terr = conn.DeallocateAll(context.Background())\n\tif err != nil {\n\t\tt.Errorf(\"conn.Deallocate failed: %v\", err)\n\t}\n}\n\n// https://github.com/jackc/pgx/issues/2223\nfunc TestPrepareHandlesTimeoutBetweenParseAndDescribe(t *testing.T) {\n\t// Not parallel because it is a timing sensitive test.\n\n\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar faultyConn *faultyconn.Conn\n\tconfig.AfterNetConnect = func(ctx context.Context, config *pgconn.Config, conn net.Conn) (net.Conn, error) {\n\t\tfaultyConn = faultyconn.New(conn)\n\t\treturn faultyConn, nil\n\t}\n\n\tctx := context.Background()\n\tconn, err := pgx.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, conn)\n\trequire.NotNil(t, faultyConn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Induced error does not occur on CockroachDB\")\n\n\t_, err = conn.Exec(ctx, \"set statement_timeout = '100ms'\")\n\trequire.NoError(t, err)\n\n\tfaultyConn.HandleFrontendMessage = func(backendWriter io.Writer, msg pgproto3.FrontendMessage) error {\n\t\tif _, ok := msg.(*pgproto3.Describe); ok {\n\t\t\ttime.Sleep(200 * time.Millisecond)\n\t\t}\n\t\tbuf, err := msg.Encode(nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = backendWriter.Write(buf)\n\t\treturn err\n\t}\n\n\tpsd, err := conn.Prepare(ctx, \"test\", \"select $1::varchar\")\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, err, &pgErr)\n\trequire.Equal(t, \"57014\", pgErr.Code)\n\trequire.Nil(t, psd)\n\n\tfaultyConn.HandleFrontendMessage = nil\n\n\t_, err = conn.Exec(ctx, \"set statement_timeout = default\")\n\trequire.NoError(t, err)\n\n\tvar existsOnServer bool\n\terr = conn.QueryRow(\n\t\tctx,\n\t\t\"select exists(select 1 from pg_prepared_statements where name = 'test')\",\n\t\t// Avoid using the prepared statement cache or it will clear the broken statement before we can check for its\n\t\t// existence.\n\t\tpgx.QueryExecModeExec,\n\t).Scan(&existsOnServer)\n\trequire.NoError(t, err)\n\trequire.True(t, existsOnServer)\n\n\tpsd, err = conn.Prepare(ctx, \"test\", \"select $1::varchar\")\n\trequire.NoError(t, err)\n\trequire.NotNil(t, psd)\n}\n\nfunc TestPrepareBadSQLFailure(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tif _, err := conn.Prepare(context.Background(), \"badSQL\", \"select foo\"); err == nil {\n\t\tt.Fatal(\"Prepare should have failed with syntax error\")\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestPrepareIdempotency(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tfor i := range 2 {\n\t\t\t_, err := conn.Prepare(context.Background(), \"test\", \"select 42::integer\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"%d. Unable to prepare statement: %v\", i, err)\n\t\t\t}\n\n\t\t\tvar n int32\n\t\t\terr = conn.QueryRow(context.Background(), \"test\").Scan(&n)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Executing prepared statement failed: %v\", i, err)\n\t\t\t}\n\n\t\t\tif n != int32(42) {\n\t\t\t\tt.Errorf(\"%d. Prepared statement did not return expected value: %v\", i, n)\n\t\t\t}\n\t\t}\n\n\t\t_, err := conn.Prepare(context.Background(), \"test\", \"select 'fail'::varchar\")\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"Prepare statement with same name but different SQL should have failed but it didn't\")\n\t\t\treturn\n\t\t}\n\t})\n}\n\nfunc TestPrepareStatementCacheModes(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Prepare(context.Background(), \"test\", \"select $1::text\")\n\t\trequire.NoError(t, err)\n\n\t\tvar s string\n\t\terr = conn.QueryRow(context.Background(), \"test\", \"hello\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"hello\", s)\n\t})\n}\n\nfunc TestPrepareWithDigestedName(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := \"select $1::text\"\n\t\tsd, err := conn.Prepare(ctx, sql, sql)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"stmt_2510cc7db17de3f42758a2a29c8b9ef8305d007b997ebdd6\", sd.Name)\n\n\t\tvar s string\n\t\terr = conn.QueryRow(ctx, sql, \"hello\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"hello\", s)\n\n\t\terr = conn.Deallocate(ctx, sql)\n\t\trequire.NoError(t, err)\n\t})\n}\n\n// https://github.com/jackc/pgx/pull/1795\nfunc TestDeallocateInAbortedTransaction(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\n\t\tsql := \"select $1::text\"\n\t\tsd, err := tx.Prepare(ctx, sql, sql)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"stmt_2510cc7db17de3f42758a2a29c8b9ef8305d007b997ebdd6\", sd.Name)\n\n\t\tvar s string\n\t\terr = tx.QueryRow(ctx, sql, \"hello\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"hello\", s)\n\n\t\t_, err = tx.Exec(ctx, \"select 1/0\") // abort transaction with divide by zero error\n\t\trequire.Error(t, err)\n\n\t\terr = conn.Deallocate(ctx, sql)\n\t\trequire.NoError(t, err)\n\n\t\terr = tx.Rollback(ctx)\n\t\trequire.NoError(t, err)\n\n\t\tsd, err = conn.Prepare(ctx, sql, sql)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"stmt_2510cc7db17de3f42758a2a29c8b9ef8305d007b997ebdd6\", sd.Name)\n\t})\n}\n\nfunc TestDeallocateMissingPreparedStatementStillClearsFromPreparedStatementMap(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Prepare(ctx, \"ps\", \"select $1::text\")\n\t\trequire.NoError(t, err)\n\n\t\t_, err = conn.Exec(ctx, \"deallocate ps\")\n\t\trequire.NoError(t, err)\n\n\t\terr = conn.Deallocate(ctx, \"ps\")\n\t\trequire.NoError(t, err)\n\n\t\t_, err = conn.Prepare(ctx, \"ps\", \"select $1::text, $2::text\")\n\t\trequire.NoError(t, err)\n\n\t\tvar s1, s2 string\n\t\terr = conn.QueryRow(ctx, \"ps\", \"hello\", \"world\").Scan(&s1, &s2)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"hello\", s1)\n\t\trequire.Equal(t, \"world\", s2)\n\t})\n}\n\nfunc TestListenNotify(t *testing.T) {\n\tt.Parallel()\n\n\tlistener := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, listener)\n\n\tif listener.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support LISTEN / NOTIFY (https://github.com/cockroachdb/cockroach/issues/41522)\")\n\t}\n\n\tmustExec(t, listener, \"listen chat\")\n\n\tnotifier := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, notifier)\n\n\tmustExec(t, notifier, \"notify chat\")\n\n\t// when notification is waiting on the socket to be read\n\tnotification, err := listener.WaitForNotification(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"chat\", notification.Channel)\n\n\t// when notification has already been read during previous query\n\tmustExec(t, notifier, \"notify chat\")\n\trows, _ := listener.Query(context.Background(), \"select 1\")\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\tctx, cancelFn := context.WithCancel(context.Background())\n\tcancelFn()\n\tnotification, err = listener.WaitForNotification(ctx)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"chat\", notification.Channel)\n\n\t// when timeout occurs\n\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)\n\tdefer cancel()\n\tnotification, err = listener.WaitForNotification(ctx)\n\tassert.True(t, pgconn.Timeout(err))\n\tassert.Nil(t, notification)\n\n\t// listener can listen again after a timeout\n\tmustExec(t, notifier, \"notify chat\")\n\tnotification, err = listener.WaitForNotification(context.Background())\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"chat\", notification.Channel)\n}\n\nfunc TestListenNotifyWhileBusyIsSafe(t *testing.T) {\n\tt.Parallel()\n\n\tfunc() {\n\t\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\tdefer closeConn(t, conn)\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support LISTEN / NOTIFY (https://github.com/cockroachdb/cockroach/issues/41522)\")\n\t}()\n\n\tlistenerDone := make(chan bool)\n\tnotifierDone := make(chan bool)\n\tlistening := make(chan bool)\n\tgo func() {\n\t\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\tdefer closeConn(t, conn)\n\t\tdefer func() {\n\t\t\tlistenerDone <- true\n\t\t}()\n\n\t\tmustExec(t, conn, \"listen busysafe\")\n\t\tlistening <- true\n\n\t\tfor range 5000 {\n\t\t\tvar sum int32\n\t\t\tvar rowCount int32\n\n\t\t\trows, err := conn.Query(context.Background(), \"select generate_series(1,$1)\", 100)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"conn.Query failed: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor rows.Next() {\n\t\t\t\tvar n int32\n\t\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\t\tt.Errorf(\"Row scan failed: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tsum += n\n\t\t\t\trowCount++\n\t\t\t}\n\n\t\t\tif rows.Err() != nil {\n\t\t\t\tt.Errorf(\"conn.Query failed: %v\", rows.Err())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif sum != 5050 {\n\t\t\t\tt.Errorf(\"Wrong rows sum: %v\", sum)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif rowCount != 100 {\n\t\t\t\tt.Errorf(\"Wrong number of rows: %v\", rowCount)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\tdefer closeConn(t, conn)\n\t\tdefer func() {\n\t\t\tnotifierDone <- true\n\t\t}()\n\n\t\t<-listening\n\n\t\tfor range 100_000 {\n\t\t\tmustExec(t, conn, \"notify busysafe, 'hello'\")\n\t\t}\n\t}()\n\n\t<-listenerDone\n\t<-notifierDone\n}\n\nfunc TestListenNotifySelfNotification(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support LISTEN / NOTIFY (https://github.com/cockroachdb/cockroach/issues/41522)\")\n\n\tmustExec(t, conn, \"listen self\")\n\n\t// Notify self and WaitForNotification immediately\n\tmustExec(t, conn, \"notify self\")\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\tdefer cancel()\n\tnotification, err := conn.WaitForNotification(ctx)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"self\", notification.Channel)\n\n\t// Notify self and do something else before WaitForNotification\n\tmustExec(t, conn, \"notify self\")\n\n\trows, _ := conn.Query(context.Background(), \"select 1\")\n\trows.Close()\n\tif rows.Err() != nil {\n\t\tt.Fatalf(\"Unexpected error on Query: %v\", rows.Err())\n\t}\n\n\tctx, cncl := context.WithTimeout(context.Background(), time.Second)\n\tdefer cncl()\n\tnotification, err = conn.WaitForNotification(ctx)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"self\", notification.Channel)\n}\n\nfunc TestFatalRxError(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)\")\n\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tvar n int32\n\t\tvar s string\n\t\terr := conn.QueryRow(context.Background(), \"select 1::int4, pg_sleep(10)::varchar\").Scan(&n, &s)\n\t\tif pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Severity == \"FATAL\" {\n\t\t} else {\n\t\t\tt.Errorf(\"Expected QueryRow Scan to return fatal PgError, but instead received %v\", err)\n\t\t\treturn\n\t\t}\n\t}()\n\n\totherConn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer otherConn.Close(context.Background())\n\n\tif _, err := otherConn.Exec(context.Background(), \"select pg_terminate_backend($1)\", conn.PgConn().PID()); err != nil {\n\t\tt.Fatalf(\"Unable to kill backend PostgreSQL process: %v\", err)\n\t}\n\n\twg.Wait()\n\n\tif !conn.IsClosed() {\n\t\tt.Fatal(\"Connection should be closed\")\n\t}\n}\n\nfunc TestFatalTxError(t *testing.T) {\n\tt.Parallel()\n\n\t// Run timing sensitive test many times\n\tfor range 50 {\n\t\tfunc() {\n\t\t\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\tdefer closeConn(t, conn)\n\n\t\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)\")\n\n\t\t\totherConn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\tdefer otherConn.Close(context.Background())\n\n\t\t\t_, err := otherConn.Exec(context.Background(), \"select pg_terminate_backend($1)\", conn.PgConn().PID())\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Unable to kill backend PostgreSQL process: %v\", err)\n\t\t\t}\n\n\t\t\terr = conn.QueryRow(context.Background(), \"select 1\").Scan(nil)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected error but none occurred\")\n\t\t\t}\n\n\t\t\tif !conn.IsClosed() {\n\t\t\t\tt.Fatalf(\"Connection should be closed but isn't. Previous Query err: %v\", err)\n\t\t\t}\n\t\t}()\n\t}\n}\n\nfunc TestInsertBoolArray(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tif results := mustExec(t, conn, \"create temporary table foo(spice bool[]);\"); results.String() != \"CREATE TABLE\" {\n\t\t\tt.Error(\"Unexpected results from Exec\")\n\t\t}\n\n\t\t// Accept parameters\n\t\tif results := mustExec(t, conn, \"insert into foo(spice) values($1)\", []bool{true, false, true}); results.String() != \"INSERT 0 1\" {\n\t\t\tt.Errorf(\"Unexpected results from Exec: %v\", results)\n\t\t}\n\t})\n}\n\nfunc TestInsertTimestampArray(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tif results := mustExec(t, conn, \"create temporary table foo(spice timestamp[]);\"); results.String() != \"CREATE TABLE\" {\n\t\t\tt.Error(\"Unexpected results from Exec\")\n\t\t}\n\n\t\t// Accept parameters\n\t\tif results := mustExec(t, conn, \"insert into foo(spice) values($1)\", []time.Time{time.Unix(1419143667, 0), time.Unix(1419143672, 0)}); results.String() != \"INSERT 0 1\" {\n\t\t\tt.Errorf(\"Unexpected results from Exec: %v\", results)\n\t\t}\n\t})\n}\n\nfunc TestIdentifierSanitize(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tident    pgx.Identifier\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tident:    pgx.Identifier{`foo`},\n\t\t\texpected: `\"foo\"`,\n\t\t},\n\t\t{\n\t\t\tident:    pgx.Identifier{`select`},\n\t\t\texpected: `\"select\"`,\n\t\t},\n\t\t{\n\t\t\tident:    pgx.Identifier{`foo`, `bar`},\n\t\t\texpected: `\"foo\".\"bar\"`,\n\t\t},\n\t\t{\n\t\t\tident:    pgx.Identifier{`you should \" not do this`},\n\t\t\texpected: `\"you should \"\" not do this\"`,\n\t\t},\n\t\t{\n\t\t\tident:    pgx.Identifier{`you should \" not do this`, `please don't`},\n\t\t\texpected: `\"you should \"\" not do this\".\"please don't\"`,\n\t\t},\n\t\t{\n\t\t\tident:    pgx.Identifier{`you should ` + string([]byte{0}) + `not do this`},\n\t\t\texpected: `\"you should not do this\"`,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tqval := tt.ident.Sanitize()\n\t\tif qval != tt.expected {\n\t\t\tt.Errorf(\"%d. Expected Sanitize %v to return %v but it was %v\", i, tt.ident, tt.expected, qval)\n\t\t}\n\t}\n}\n\nfunc TestConnInitTypeMap(t *testing.T) {\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// spot check that the standard postgres type names aren't qualified\n\tnameOIDs := map[string]uint32{\n\t\t\"_int8\": pgtype.Int8ArrayOID,\n\t\t\"int8\":  pgtype.Int8OID,\n\t\t\"json\":  pgtype.JSONOID,\n\t\t\"text\":  pgtype.TextOID,\n\t}\n\tfor name, oid := range nameOIDs {\n\t\tdtByName, ok := conn.TypeMap().TypeForName(name)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"Expected type named %v to be present\", name)\n\t\t}\n\t\tdtByOID, ok := conn.TypeMap().TypeForOID(oid)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"Expected type OID %v to be present\", oid)\n\t\t}\n\t\tif dtByName != dtByOID {\n\t\t\tt.Fatalf(\"Expected type named %v to be the same as type OID %v\", name, oid)\n\t\t}\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestUnregisteredTypeUsableAsStringArgumentAndBaseResult(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does support domain types (https://github.com/cockroachdb/cockroach/issues/27796)\")\n\n\t\tvar n uint64\n\t\terr := conn.QueryRow(context.Background(), \"select $1::uint64\", \"42\").Scan(&n)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif n != 42 {\n\t\t\tt.Fatalf(\"Expected n to be 42, but was %v\", n)\n\t\t}\n\t})\n}\n\nfunc TestDomainType(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does support domain types (https://github.com/cockroachdb/cockroach/issues/27796)\")\n\n\t\t// Domain type uint64 is a PostgreSQL domain of underlying type numeric.\n\n\t\t// In the extended protocol preparing \"select $1::uint64\" appears to create a statement that expects a param OID of\n\t\t// uint64 but a result OID of the underlying numeric.\n\n\t\tvar s string\n\t\terr := conn.QueryRow(ctx, \"select $1::uint64\", \"24\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"24\", s)\n\n\t\t// Register type\n\t\tuint64Type, err := conn.LoadType(ctx, \"uint64\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(uint64Type)\n\n\t\tvar n uint64\n\t\terr = conn.QueryRow(ctx, \"select $1::uint64\", uint64(24)).Scan(&n)\n\t\trequire.NoError(t, err)\n\n\t\t// String is still an acceptable argument after registration\n\t\terr = conn.QueryRow(ctx, \"select $1::uint64\", \"7\").Scan(&n)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif n != 7 {\n\t\t\tt.Fatalf(\"Expected n to be 7, but was %v\", n)\n\t\t}\n\t})\n}\n\nfunc TestLoadTypeSameNameInDifferentSchemas(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does support composite types (https://github.com/cockroachdb/cockroach/issues/27792)\")\n\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\n\t\t_, err = tx.Exec(ctx, `create schema pgx_a;\ncreate type pgx_a.point as (a text, b text);\ncreate schema pgx_b;\ncreate type pgx_b.point as (c text);\n`)\n\t\trequire.NoError(t, err)\n\n\t\t// Register types\n\t\tfor _, typename := range []string{\"pgx_a.point\", \"pgx_b.point\"} {\n\t\t\t// Obviously using conn while a tx is in use and registering a type after the connection has been established are\n\t\t\t// really bad practices, but for the sake of convenience we do it in the test here.\n\t\t\tdt, err := conn.LoadType(ctx, typename)\n\t\t\trequire.NoError(t, err)\n\t\t\tconn.TypeMap().RegisterType(dt)\n\t\t}\n\n\t\ttype aPoint struct {\n\t\t\tA string\n\t\t\tB string\n\t\t}\n\n\t\ttype bPoint struct {\n\t\t\tC string\n\t\t}\n\n\t\tvar a aPoint\n\t\tvar b bPoint\n\t\terr = tx.QueryRow(ctx, `select '(foo,bar)'::pgx_a.point, '(baz)'::pgx_b.point`).Scan(&a, &b)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, aPoint{\"foo\", \"bar\"}, a)\n\t\trequire.Equal(t, bPoint{\"baz\"}, b)\n\t})\n}\n\nfunc TestLoadCompositeType(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does support composite types (https://github.com/cockroachdb/cockroach/issues/27792)\")\n\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\n\t\t_, err = tx.Exec(ctx, \"create type compositetype as (attr1 int, attr2 int)\")\n\t\trequire.NoError(t, err)\n\n\t\t_, err = tx.Exec(ctx, \"alter type compositetype drop attribute attr1\")\n\t\trequire.NoError(t, err)\n\n\t\t_, err = conn.LoadType(ctx, \"compositetype\")\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestLoadRangeType(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does support range types\")\n\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\n\t\t_, err = tx.Exec(ctx, \"create type examplefloatrange as range (subtype=float8, subtype_diff=float8mi)\")\n\t\trequire.NoError(t, err)\n\n\t\t// Register types\n\t\tnewRangeType, err := conn.LoadType(ctx, \"examplefloatrange\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(newRangeType)\n\t\tconn.TypeMap().RegisterDefaultPgType(pgtype.Range[float64]{}, \"examplefloatrange\")\n\n\t\tinputRangeType := pgtype.Range[float64]{\n\t\t\tLower:     1.0,\n\t\t\tUpper:     2.0,\n\t\t\tLowerType: pgtype.Inclusive,\n\t\t\tUpperType: pgtype.Inclusive,\n\t\t\tValid:     true,\n\t\t}\n\t\tvar outputRangeType pgtype.Range[float64]\n\t\terr = tx.QueryRow(ctx, \"SELECT $1::examplefloatrange\", inputRangeType).Scan(&outputRangeType)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, inputRangeType, outputRangeType)\n\t})\n}\n\nfunc TestLoadMultiRangeType(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does support range types\")\n\t\tpgxtest.SkipPostgreSQLVersionLessThan(t, conn, 14) // multirange data type was added in 14 postgresql\n\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\n\t\t_, err = tx.Exec(ctx, \"create type examplefloatrange as range (subtype=float8, subtype_diff=float8mi, multirange_type_name=examplefloatmultirange)\")\n\t\trequire.NoError(t, err)\n\n\t\t// Register types\n\t\tnewRangeType, err := conn.LoadType(ctx, \"examplefloatrange\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(newRangeType)\n\t\tconn.TypeMap().RegisterDefaultPgType(pgtype.Range[float64]{}, \"examplefloatrange\")\n\n\t\tnewMultiRangeType, err := conn.LoadType(ctx, \"examplefloatmultirange\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(newMultiRangeType)\n\t\tconn.TypeMap().RegisterDefaultPgType(pgtype.Multirange[pgtype.Range[float64]]{}, \"examplefloatmultirange\")\n\n\t\tinputMultiRangeType := pgtype.Multirange[pgtype.Range[float64]]{\n\t\t\t{\n\t\t\t\tLower:     1.0,\n\t\t\t\tUpper:     2.0,\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tUpperType: pgtype.Inclusive,\n\t\t\t\tValid:     true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tLower:     3.0,\n\t\t\t\tUpper:     4.0,\n\t\t\t\tLowerType: pgtype.Exclusive,\n\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\tValid:     true,\n\t\t\t},\n\t\t}\n\t\tvar outputMultiRangeType pgtype.Multirange[pgtype.Range[float64]]\n\t\terr = tx.QueryRow(ctx, \"SELECT $1::examplefloatmultirange\", inputMultiRangeType).Scan(&outputMultiRangeType)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, inputMultiRangeType, outputMultiRangeType)\n\t})\n}\n\nfunc TestStmtCacheInvalidationConn(t *testing.T) {\n\tctx := context.Background()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// create a table and fill it with some data\n\t_, err := conn.Exec(ctx, `\n        DROP TABLE IF EXISTS drop_cols;\n        CREATE TABLE drop_cols (\n            id SERIAL PRIMARY KEY NOT NULL,\n            f1 int NOT NULL,\n            f2 int NOT NULL\n        );\n    `)\n\trequire.NoError(t, err)\n\t_, err = conn.Exec(ctx, \"INSERT INTO drop_cols (f1, f2) VALUES (1, 2)\")\n\trequire.NoError(t, err)\n\n\tgetSQL := \"SELECT * FROM drop_cols WHERE id = $1\"\n\n\t// This query will populate the statement cache. We don't care about the result.\n\trows, err := conn.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\t// Now, change the schema of the table out from under the statement, making it invalid.\n\t_, err = conn.Exec(ctx, \"ALTER TABLE drop_cols DROP COLUMN f1\")\n\trequire.NoError(t, err)\n\n\t// We must get an error the first time we try to re-execute a bad statement.\n\t// It is up to the application to determine if it wants to try again. We punt to\n\t// the application because there is no clear recovery path in the case of failed transactions\n\t// or batch operations and because automatic retry is tricky and we don't want to get\n\t// it wrong at such an important layer of the stack.\n\trows, err = conn.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Next()\n\tnextErr := rows.Err()\n\trows.Close()\n\tfor _, err := range []error{nextErr, rows.Err()} {\n\t\tif err == nil {\n\t\t\tt.Fatal(`expected \"cached plan must not change result type\": no error`)\n\t\t}\n\t\tif !strings.Contains(err.Error(), \"cached plan must not change result type\") {\n\t\t\tt.Fatalf(`expected \"cached plan must not change result type\", got: \"%s\"`, err.Error())\n\t\t}\n\t}\n\n\t// On retry, the statement should have been flushed from the cache.\n\trows, err = conn.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Next()\n\terr = rows.Err()\n\trequire.NoError(t, err)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestStmtCacheInvalidationTx(t *testing.T) {\n\tctx := context.Background()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server has non-standard prepare in errored transaction behavior (https://github.com/cockroachdb/cockroach/issues/84140)\")\n\t}\n\n\t// create a table and fill it with some data\n\t_, err := conn.Exec(ctx, `\n        DROP TABLE IF EXISTS drop_cols;\n        CREATE TABLE drop_cols (\n            id SERIAL PRIMARY KEY NOT NULL,\n            f1 int NOT NULL,\n            f2 int NOT NULL\n        );\n    `)\n\trequire.NoError(t, err)\n\t_, err = conn.Exec(ctx, \"INSERT INTO drop_cols (f1, f2) VALUES (1, 2)\")\n\trequire.NoError(t, err)\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\n\tgetSQL := \"SELECT * FROM drop_cols WHERE id = $1\"\n\n\t// This query will populate the statement cache. We don't care about the result.\n\trows, err := tx.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\t// Now, change the schema of the table out from under the statement, making it invalid.\n\t_, err = tx.Exec(ctx, \"ALTER TABLE drop_cols DROP COLUMN f1\")\n\trequire.NoError(t, err)\n\n\t// We must get an error the first time we try to re-execute a bad statement.\n\t// It is up to the application to determine if it wants to try again. We punt to\n\t// the application because there is no clear recovery path in the case of failed transactions\n\t// or batch operations and because automatic retry is tricky and we don't want to get\n\t// it wrong at such an important layer of the stack.\n\trows, err = tx.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Next()\n\tnextErr := rows.Err()\n\trows.Close()\n\tfor _, err := range []error{nextErr, rows.Err()} {\n\t\tif err == nil {\n\t\t\tt.Fatal(`expected \"cached plan must not change result type\": no error`)\n\t\t}\n\t\tif !strings.Contains(err.Error(), \"cached plan must not change result type\") {\n\t\t\tt.Fatalf(`expected \"cached plan must not change result type\", got: \"%s\"`, err.Error())\n\t\t}\n\t}\n\n\trows, _ = tx.Query(ctx, getSQL, 1)\n\trows.Close()\n\terr = rows.Err()\n\t// Retries within the same transaction are errors (really anything except a rollback\n\t// will be an error in this transaction).\n\trequire.Error(t, err)\n\trows.Close()\n\n\terr = tx.Rollback(ctx)\n\trequire.NoError(t, err)\n\n\t// once we've rolled back, retries will work\n\trows, err = conn.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Next()\n\terr = rows.Err()\n\trequire.NoError(t, err)\n\trows.Close()\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestStmtCacheInvalidationConnWithBatch(t *testing.T) {\n\tctx := context.Background()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Test fails due to different CRDB behavior\")\n\t}\n\n\t// create a table and fill it with some data\n\t_, err := conn.Exec(ctx, `\n        DROP TABLE IF EXISTS drop_cols;\n        CREATE TABLE drop_cols (\n            id SERIAL PRIMARY KEY NOT NULL,\n            f1 int NOT NULL,\n            f2 int NOT NULL\n        );\n    `)\n\trequire.NoError(t, err)\n\t_, err = conn.Exec(ctx, \"INSERT INTO drop_cols (f1, f2) VALUES (1, 2)\")\n\trequire.NoError(t, err)\n\n\tgetSQL := \"SELECT * FROM drop_cols WHERE id = $1\"\n\n\t// This query will populate the statement cache. We don't care about the result.\n\trows, err := conn.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\t// Now, change the schema of the table out from under the statement, making it invalid.\n\t_, err = conn.Exec(ctx, \"ALTER TABLE drop_cols DROP COLUMN f1\")\n\trequire.NoError(t, err)\n\n\t// We must get an error the first time we try to re-execute a bad statement.\n\t// It is up to the application to determine if it wants to try again. We punt to\n\t// the application because there is no clear recovery path in the case of failed transactions\n\t// or batch operations and because automatic retry is tricky and we don't want to get\n\t// it wrong at such an important layer of the stack.\n\tbatch := &pgx.Batch{}\n\tbatch.Queue(getSQL, 1)\n\tbr := conn.SendBatch(ctx, batch)\n\trows, err = br.Query()\n\trequire.Error(t, err)\n\trows.Next()\n\tnextErr := rows.Err()\n\trows.Close()\n\terr = br.Close()\n\trequire.Error(t, err)\n\tfor _, err := range []error{nextErr, rows.Err()} {\n\t\tif err == nil {\n\t\t\tt.Fatal(`expected \"cached plan must not change result type\": no error`)\n\t\t}\n\t\tif !strings.Contains(err.Error(), \"cached plan must not change result type\") {\n\t\t\tt.Fatalf(`expected \"cached plan must not change result type\", got: \"%s\"`, err.Error())\n\t\t}\n\t}\n\n\t// On retry, the statement should have been flushed from the cache.\n\tbatch = &pgx.Batch{}\n\tbatch.Queue(getSQL, 1)\n\tbr = conn.SendBatch(ctx, batch)\n\trows, err = br.Query()\n\trequire.NoError(t, err)\n\trows.Next()\n\terr = rows.Err()\n\trequire.NoError(t, err)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\terr = br.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestStmtCacheInvalidationTxWithBatch(t *testing.T) {\n\tctx := context.Background()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server has non-standard prepare in errored transaction behavior (https://github.com/cockroachdb/cockroach/issues/84140)\")\n\t}\n\n\t// create a table and fill it with some data\n\t_, err := conn.Exec(ctx, `\n        DROP TABLE IF EXISTS drop_cols;\n        CREATE TABLE drop_cols (\n            id SERIAL PRIMARY KEY NOT NULL,\n            f1 int NOT NULL,\n            f2 int NOT NULL\n        );\n    `)\n\trequire.NoError(t, err)\n\t_, err = conn.Exec(ctx, \"INSERT INTO drop_cols (f1, f2) VALUES (1, 2)\")\n\trequire.NoError(t, err)\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\n\tgetSQL := \"SELECT * FROM drop_cols WHERE id = $1\"\n\n\t// This query will populate the statement cache. We don't care about the result.\n\trows, err := tx.Query(ctx, getSQL, 1)\n\trequire.NoError(t, err)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\t// Now, change the schema of the table out from under the statement, making it invalid.\n\t_, err = tx.Exec(ctx, \"ALTER TABLE drop_cols DROP COLUMN f1\")\n\trequire.NoError(t, err)\n\n\t// We must get an error the first time we try to re-execute a bad statement.\n\t// It is up to the application to determine if it wants to try again. We punt to\n\t// the application because there is no clear recovery path in the case of failed transactions\n\t// or batch operations and because automatic retry is tricky and we don't want to get\n\t// it wrong at such an important layer of the stack.\n\tbatch := &pgx.Batch{}\n\tbatch.Queue(getSQL, 1)\n\tbr := tx.SendBatch(ctx, batch)\n\trows, err = br.Query()\n\trequire.Error(t, err)\n\trows.Next()\n\tnextErr := rows.Err()\n\trows.Close()\n\terr = br.Close()\n\trequire.Error(t, err)\n\tfor _, err := range []error{nextErr, rows.Err()} {\n\t\tif err == nil {\n\t\t\tt.Fatal(`expected \"cached plan must not change result type\": no error`)\n\t\t}\n\t\tif !strings.Contains(err.Error(), \"cached plan must not change result type\") {\n\t\t\tt.Fatalf(`expected \"cached plan must not change result type\", got: \"%s\"`, err.Error())\n\t\t}\n\t}\n\n\tbatch = &pgx.Batch{}\n\tbatch.Queue(getSQL, 1)\n\tbr = tx.SendBatch(ctx, batch)\n\trows, err = br.Query()\n\trequire.Error(t, err)\n\trows.Close()\n\terr = rows.Err()\n\t// Retries within the same transaction are errors (really anything except a rollback\n\t// will be an error in this transaction).\n\trequire.Error(t, err)\n\trows.Close()\n\terr = br.Close()\n\trequire.Error(t, err)\n\n\terr = tx.Rollback(ctx)\n\trequire.NoError(t, err)\n\n\t// once we've rolled back, retries will work\n\tbatch = &pgx.Batch{}\n\tbatch.Queue(getSQL, 1)\n\tbr = conn.SendBatch(ctx, batch)\n\trows, err = br.Query()\n\trequire.NoError(t, err)\n\trows.Next()\n\terr = rows.Err()\n\trequire.NoError(t, err)\n\trows.Close()\n\terr = br.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/issues/2442\nfunc TestStmtCacheInvalidationExec(t *testing.T) {\n\tctx := context.Background()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"CockroachDB does not support column column type from int to bool\")\n\t}\n\n\t// create a table and fill it with some data\n\t_, err := conn.Exec(ctx, `\n\t\t\t\tDROP TABLE IF EXISTS drop_cols;\n        CREATE TABLE drop_cols (\n            id SERIAL PRIMARY KEY NOT NULL,\n            f1 int NOT NULL,\n            f2 int NOT NULL\n        );\n    `)\n\trequire.NoError(t, err)\n\n\tinsertSQL := \"INSERT INTO drop_cols (f1, f2) VALUES ($1, $2)\"\n\t// This query will populate the statement cache.\n\t_, err = conn.Exec(ctx, insertSQL, 1, 2)\n\trequire.NoError(t, err)\n\n\t// Now, change the schema of the table out from under the statement, making it invalid.\n\t_, err = conn.Exec(ctx, \"ALTER TABLE drop_cols ALTER COLUMN f1 TYPE boolean USING f1::boolean\")\n\trequire.NoError(t, err)\n\n\t// We must get an error the first time we try to re-execute a bad statement.\n\t// It is up to the application to determine if it wants to try again. We punt to\n\t// the application because there is no clear recovery path in the case of failed transactions\n\t// or batch operations and because automatic retry is tricky and we don't want to get\n\t// it wrong at such an important layer of the stack.\n\t_, err = conn.Exec(ctx, insertSQL, true, 2)\n\trequire.ErrorContains(t, err, \"failed to encode args[0]\")\n\n\t// On retry, the statement should have been flushed from the cache.\n\t_, err = conn.Exec(ctx, insertSQL, true, 2)\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestInsertDurationInterval(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(context.Background(), \"create temporary table t(duration INTERVAL(0) NOT NULL)\")\n\t\trequire.NoError(t, err)\n\n\t\tresult, err := conn.Exec(context.Background(), \"insert into t(duration) values($1)\", time.Minute)\n\t\trequire.NoError(t, err)\n\n\t\tn := result.RowsAffected()\n\t\trequire.EqualValues(t, 1, n)\n\t})\n}\n\nfunc TestRawValuesUnderlyingMemoryReused(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar buf []byte\n\n\t\trows, err := conn.Query(ctx, `select 1::int`)\n\t\trequire.NoError(t, err)\n\n\t\tfor rows.Next() {\n\t\t\tbuf = rows.RawValues()[0]\n\t\t}\n\n\t\trequire.NoError(t, rows.Err())\n\n\t\toriginal := make([]byte, len(buf))\n\t\tcopy(original, buf)\n\n\t\tfor i := range 1_000_000 {\n\t\t\trows, err := conn.Query(ctx, `select $1::int`, i)\n\t\t\trequire.NoError(t, err)\n\t\t\trows.Close()\n\t\t\trequire.NoError(t, rows.Err())\n\n\t\t\tif !bytes.Equal(original, buf) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tt.Fatal(\"expected buffer from RawValues to be overwritten by subsequent queries but it was not\")\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1847\nfunc TestConnDeallocateInvalidatedCachedStatementsWhenCanceled(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"CockroachDB returns decimal instead of integer for integer division\")\n\n\t\tvar n int32\n\t\terr := conn.QueryRow(ctx, \"select 1 / $1::int\", 1).Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, n)\n\n\t\t// Divide by zero causes an error. baseRows.Close() calls Invalidate on the statement cache whenever an error was\n\t\t// encountered by the query. Use this to purposely invalidate the query. If we had access to private fields of conn\n\t\t// we could call conn.statementCache.InvalidateAll() instead.\n\t\terr = conn.QueryRow(ctx, \"select 1 / $1::int\", 0).Scan(&n)\n\t\trequire.Error(t, err)\n\n\t\tctx2, cancel2 := context.WithCancel(ctx)\n\t\tcancel2()\n\t\terr = conn.QueryRow(ctx2, \"select 1 / $1::int\", 1).Scan(&n)\n\t\trequire.Error(t, err)\n\t\trequire.ErrorIs(t, err, context.Canceled)\n\n\t\terr = conn.QueryRow(ctx, \"select 1 / $1::int\", 1).Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, n)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1847\nfunc TestConnDeallocateInvalidatedCachedStatementsInTransactionWithBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx := context.Background()\n\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\tconfig := mustParseConfig(t, connString)\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tconfig.StatementCacheCapacity = 2\n\n\tconn, err := pgx.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\t_, err = tx.Exec(ctx, \"select $1::int + 1\", 1)\n\trequire.NoError(t, err)\n\n\t_, err = tx.Exec(ctx, \"select $1::int + 2\", 1)\n\trequire.NoError(t, err)\n\n\t// This should invalidate the first cached statement.\n\t_, err = tx.Exec(ctx, \"select $1::int + 3\", 1)\n\trequire.NoError(t, err)\n\n\tbatch := &pgx.Batch{}\n\tbatch.Queue(\"select $1::int + 1\", 1)\n\terr = tx.SendBatch(ctx, batch).Close()\n\trequire.NoError(t, err)\n\n\terr = tx.Rollback(ctx)\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestErrNoRows(t *testing.T) {\n\tt.Parallel()\n\n\t// ensure we preserve old error message\n\trequire.Equal(t, \"no rows in result set\", pgx.ErrNoRows.Error())\n\n\trequire.ErrorIs(t, pgx.ErrNoRows, sql.ErrNoRows, \"pgx.ErrNowRows must match sql.ErrNoRows\")\n}\n"
  },
  {
    "path": "copy_from.go",
    "content": "package pgx\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// CopyFromRows returns a CopyFromSource interface over the provided rows slice\n// making it usable by *Conn.CopyFrom.\nfunc CopyFromRows(rows [][]any) CopyFromSource {\n\treturn &copyFromRows{rows: rows, idx: -1}\n}\n\ntype copyFromRows struct {\n\trows [][]any\n\tidx  int\n}\n\nfunc (ctr *copyFromRows) Next() bool {\n\tctr.idx++\n\treturn ctr.idx < len(ctr.rows)\n}\n\nfunc (ctr *copyFromRows) Values() ([]any, error) {\n\treturn ctr.rows[ctr.idx], nil\n}\n\nfunc (ctr *copyFromRows) Err() error {\n\treturn nil\n}\n\n// CopyFromSlice returns a CopyFromSource interface over a dynamic func\n// making it usable by *Conn.CopyFrom.\nfunc CopyFromSlice(length int, next func(int) ([]any, error)) CopyFromSource {\n\treturn &copyFromSlice{next: next, idx: -1, len: length}\n}\n\ntype copyFromSlice struct {\n\tnext func(int) ([]any, error)\n\tidx  int\n\tlen  int\n\terr  error\n}\n\nfunc (cts *copyFromSlice) Next() bool {\n\tcts.idx++\n\treturn cts.idx < cts.len\n}\n\nfunc (cts *copyFromSlice) Values() ([]any, error) {\n\tvalues, err := cts.next(cts.idx)\n\tif err != nil {\n\t\tcts.err = err\n\t}\n\treturn values, err\n}\n\nfunc (cts *copyFromSlice) Err() error {\n\treturn cts.err\n}\n\n// CopyFromFunc returns a CopyFromSource interface that relies on nxtf for values.\n// nxtf returns rows until it either signals an 'end of data' by returning row=nil and err=nil,\n// or it returns an error. If nxtf returns an error, the copy is aborted.\nfunc CopyFromFunc(nxtf func() (row []any, err error)) CopyFromSource {\n\treturn &copyFromFunc{next: nxtf}\n}\n\ntype copyFromFunc struct {\n\tnext     func() ([]any, error)\n\tvalueRow []any\n\terr      error\n}\n\nfunc (g *copyFromFunc) Next() bool {\n\tg.valueRow, g.err = g.next()\n\t// only return true if valueRow exists and no error\n\treturn g.valueRow != nil && g.err == nil\n}\n\nfunc (g *copyFromFunc) Values() ([]any, error) {\n\treturn g.valueRow, g.err\n}\n\nfunc (g *copyFromFunc) Err() error {\n\treturn g.err\n}\n\n// CopyFromSource is the interface used by *Conn.CopyFrom as the source for copy data.\ntype CopyFromSource interface {\n\t// Next returns true if there is another row and makes the next row data\n\t// available to Values(). When there are no more rows available or an error\n\t// has occurred it returns false.\n\tNext() bool\n\n\t// Values returns the values for the current row.\n\tValues() ([]any, error)\n\n\t// Err returns any error that has been encountered by the CopyFromSource. If\n\t// this is not nil *Conn.CopyFrom will abort the copy.\n\tErr() error\n}\n\ntype copyFrom struct {\n\tconn          *Conn\n\ttableName     Identifier\n\tcolumnNames   []string\n\trowSrc        CopyFromSource\n\treaderErrChan chan error\n\tmode          QueryExecMode\n}\n\nfunc (ct *copyFrom) run(ctx context.Context) (int64, error) {\n\tif ct.conn.copyFromTracer != nil {\n\t\tctx = ct.conn.copyFromTracer.TraceCopyFromStart(ctx, ct.conn, TraceCopyFromStartData{\n\t\t\tTableName:   ct.tableName,\n\t\t\tColumnNames: ct.columnNames,\n\t\t})\n\t}\n\n\tquotedTableName := ct.tableName.Sanitize()\n\tcbuf := &bytes.Buffer{}\n\tfor i, cn := range ct.columnNames {\n\t\tif i != 0 {\n\t\t\tcbuf.WriteString(\", \")\n\t\t}\n\t\tcbuf.WriteString(quoteIdentifier(cn))\n\t}\n\tquotedColumnNames := cbuf.String()\n\n\tvar sd *pgconn.StatementDescription\n\tswitch ct.mode {\n\tcase QueryExecModeExec, QueryExecModeSimpleProtocol:\n\t\t// These modes don't support the binary format. Before the inclusion of the\n\t\t// QueryExecModes, Conn.Prepare was called on every COPY operation to get\n\t\t// the OIDs. These prepared statements were not cached.\n\t\t//\n\t\t// Since that's the same behavior provided by QueryExecModeDescribeExec,\n\t\t// we'll default to that mode.\n\t\tct.mode = QueryExecModeDescribeExec\n\t\tfallthrough\n\tcase QueryExecModeCacheStatement, QueryExecModeCacheDescribe, QueryExecModeDescribeExec:\n\t\tvar err error\n\t\tsd, err = ct.conn.getStatementDescription(\n\t\t\tctx,\n\t\t\tct.mode,\n\t\t\tfmt.Sprintf(\"select %s from %s\", quotedColumnNames, quotedTableName),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn 0, fmt.Errorf(\"statement description failed: %w\", err)\n\t\t}\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unknown QueryExecMode: %v\", ct.mode)\n\t}\n\n\tr, w := io.Pipe()\n\tdoneChan := make(chan struct{})\n\n\tgo func() {\n\t\tdefer close(doneChan)\n\n\t\t// Purposely NOT using defer w.Close(). See https://github.com/golang/go/issues/24283.\n\t\tbuf := ct.conn.wbuf\n\n\t\tbuf = append(buf, \"PGCOPY\\n\\377\\r\\n\\000\"...)\n\t\tbuf = pgio.AppendInt32(buf, 0)\n\t\tbuf = pgio.AppendInt32(buf, 0)\n\n\t\tmoreRows := true\n\t\tfor moreRows {\n\t\t\tvar err error\n\t\t\tmoreRows, buf, err = ct.buildCopyBuf(buf, sd)\n\t\t\tif err != nil {\n\t\t\t\tw.CloseWithError(err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif ct.rowSrc.Err() != nil {\n\t\t\t\tw.CloseWithError(ct.rowSrc.Err())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif len(buf) > 0 {\n\t\t\t\t_, err = w.Write(buf)\n\t\t\t\tif err != nil {\n\t\t\t\t\tw.Close()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbuf = buf[:0]\n\t\t}\n\n\t\tw.Close()\n\t}()\n\n\tcommandTag, err := ct.conn.pgConn.CopyFrom(ctx, r, fmt.Sprintf(\"copy %s ( %s ) from stdin binary;\", quotedTableName, quotedColumnNames))\n\n\tr.Close()\n\t<-doneChan\n\n\tif ct.conn.copyFromTracer != nil {\n\t\tct.conn.copyFromTracer.TraceCopyFromEnd(ctx, ct.conn, TraceCopyFromEndData{\n\t\t\tCommandTag: commandTag,\n\t\t\tErr:        err,\n\t\t})\n\t}\n\n\treturn commandTag.RowsAffected(), err\n}\n\nfunc (ct *copyFrom) buildCopyBuf(buf []byte, sd *pgconn.StatementDescription) (bool, []byte, error) {\n\tconst sendBufSize = 65536 - 5 // The packet has a 5-byte header\n\tlastBufLen := 0\n\tlargestRowLen := 0\n\n\tfor ct.rowSrc.Next() {\n\t\tlastBufLen = len(buf)\n\n\t\tvalues, err := ct.rowSrc.Values()\n\t\tif err != nil {\n\t\t\treturn false, nil, err\n\t\t}\n\t\tif len(values) != len(ct.columnNames) {\n\t\t\treturn false, nil, fmt.Errorf(\"expected %d values, got %d values\", len(ct.columnNames), len(values))\n\t\t}\n\n\t\tbuf = pgio.AppendInt16(buf, int16(len(ct.columnNames)))\n\t\tfor i, val := range values {\n\t\t\tbuf, err = encodeCopyValue(ct.conn.typeMap, buf, sd.Fields[i].DataTypeOID, val)\n\t\t\tif err != nil {\n\t\t\t\treturn false, nil, err\n\t\t\t}\n\t\t}\n\n\t\trowLen := len(buf) - lastBufLen\n\t\tif rowLen > largestRowLen {\n\t\t\tlargestRowLen = rowLen\n\t\t}\n\n\t\t// Try not to overflow size of the buffer PgConn.CopyFrom will be reading into. If that happens then the nature of\n\t\t// io.Pipe means that the next Read will be short. This can lead to pathological send sizes such as 65531, 13, 65531\n\t\t// 13, 65531, 13, 65531, 13.\n\t\tif len(buf) > sendBufSize-largestRowLen {\n\t\t\treturn true, buf, nil\n\t\t}\n\t}\n\n\treturn false, buf, nil\n}\n\n// CopyFrom uses the PostgreSQL copy protocol to perform bulk data insertion. It returns the number of rows copied and\n// an error.\n//\n// CopyFrom requires all values use the binary format. A pgtype.Type that supports the binary format must be registered\n// for the type of each column. Almost all types implemented by pgx support the binary format.\n//\n// Even though enum types appear to be strings they still must be registered to use with CopyFrom. This can be done with\n// Conn.LoadType and pgtype.Map.RegisterType.\nfunc (c *Conn) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) {\n\tct := &copyFrom{\n\t\tconn:          c,\n\t\ttableName:     tableName,\n\t\tcolumnNames:   columnNames,\n\t\trowSrc:        rowSrc,\n\t\treaderErrChan: make(chan error),\n\t\tmode:          c.config.DefaultQueryExecMode,\n\t}\n\n\treturn ct.run(ctx)\n}\n"
  },
  {
    "path": "copy_from_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnCopyWithAllQueryExecModes(t *testing.T) {\n\tfor _, mode := range pgxtest.AllQueryExecModes {\n\t\tt.Run(mode.String(), func(t *testing.T) {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tcfg := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\tcfg.DefaultQueryExecMode = mode\n\t\t\tconn := mustConnect(t, cfg)\n\t\t\tdefer closeConn(t, conn)\n\n\t\t\tmustExec(t, conn, `create temporary table foo(\n\t\t\ta int2,\n\t\t\tb int4,\n\t\t\tc int8,\n\t\t\td text,\n\t\t\te timestamptz\n\t\t)`)\n\n\t\t\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\t\t\tinputRows := [][]any{\n\t\t\t\t{int16(0), int32(1), int64(2), \"abc\", tzedTime},\n\t\t\t\t{nil, nil, nil, nil, nil},\n\t\t\t}\n\n\t\t\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\"}, pgx.CopyFromRows(inputRows))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Unexpected error for CopyFrom: %v\", err)\n\t\t\t}\n\t\t\tif int(copyCount) != len(inputRows) {\n\t\t\t\tt.Errorf(\"Expected CopyFrom to return %d copied rows, but got %d\", len(inputRows), copyCount)\n\t\t\t}\n\n\t\t\trows, err := conn.Query(ctx, \"select * from foo\")\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t\t\t}\n\n\t\t\tvar outputRows [][]any\n\t\t\tfor rows.Next() {\n\t\t\t\trow, err := rows.Values()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t\t\t}\n\t\t\t\toutputRows = append(outputRows, row)\n\t\t\t}\n\n\t\t\tif rows.Err() != nil {\n\t\t\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\t\t\tt.Errorf(\"Input rows and output rows do not equal: %v -> %v\", inputRows, outputRows)\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t})\n\t}\n}\n\nfunc TestConnCopyWithKnownOIDQueryExecModes(t *testing.T) {\n\tfor _, mode := range pgxtest.KnownOIDQueryExecModes {\n\t\tt.Run(mode.String(), func(t *testing.T) {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tcfg := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\tcfg.DefaultQueryExecMode = mode\n\t\t\tconn := mustConnect(t, cfg)\n\t\t\tdefer closeConn(t, conn)\n\n\t\t\tmustExec(t, conn, `create temporary table foo(\n\t\t\ta int2,\n\t\t\tb int4,\n\t\t\tc int8,\n\t\t\td varchar,\n\t\t\te text,\n\t\t\tf date,\n\t\t\tg timestamptz\n\t\t)`)\n\n\t\t\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\t\t\tinputRows := [][]any{\n\t\t\t\t{int16(0), int32(1), int64(2), \"abc\", \"efg\", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},\n\t\t\t\t{nil, nil, nil, nil, nil, nil, nil},\n\t\t\t}\n\n\t\t\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\"}, pgx.CopyFromRows(inputRows))\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Unexpected error for CopyFrom: %v\", err)\n\t\t\t}\n\t\t\tif int(copyCount) != len(inputRows) {\n\t\t\t\tt.Errorf(\"Expected CopyFrom to return %d copied rows, but got %d\", len(inputRows), copyCount)\n\t\t\t}\n\n\t\t\trows, err := conn.Query(ctx, \"select * from foo\")\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t\t\t}\n\n\t\t\tvar outputRows [][]any\n\t\t\tfor rows.Next() {\n\t\t\t\trow, err := rows.Values()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t\t\t}\n\t\t\t\toutputRows = append(outputRows, row)\n\t\t\t}\n\n\t\t\tif rows.Err() != nil {\n\t\t\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\t\t\tt.Errorf(\"Input rows and output rows do not equal: %v -> %v\", inputRows, outputRows)\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t})\n\t}\n}\n\nfunc TestConnCopyFromSmall(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int2,\n\t\tb int4,\n\t\tc int8,\n\t\td varchar,\n\t\te text,\n\t\tf date,\n\t\tg timestamptz\n\t)`)\n\n\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\tinputRows := [][]any{\n\t\t{int16(0), int32(1), int64(2), \"abc\", \"efg\", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},\n\t\t{nil, nil, nil, nil, nil, nil, nil},\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\"}, pgx.CopyFromRows(inputRows))\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for CopyFrom: %v\", err)\n\t}\n\tif int(copyCount) != len(inputRows) {\n\t\tt.Errorf(\"Expected CopyFrom to return %d copied rows, but got %d\", len(inputRows), copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\tt.Errorf(\"Input rows and output rows do not equal: %v -> %v\", inputRows, outputRows)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromSliceSmall(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int2,\n\t\tb int4,\n\t\tc int8,\n\t\td varchar,\n\t\te text,\n\t\tf date,\n\t\tg timestamptz\n\t)`)\n\n\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\tinputRows := [][]any{\n\t\t{int16(0), int32(1), int64(2), \"abc\", \"efg\", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},\n\t\t{nil, nil, nil, nil, nil, nil, nil},\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\"},\n\t\tpgx.CopyFromSlice(len(inputRows), func(i int) ([]any, error) {\n\t\t\treturn inputRows[i], nil\n\t\t}))\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for CopyFrom: %v\", err)\n\t}\n\tif int(copyCount) != len(inputRows) {\n\t\tt.Errorf(\"Expected CopyFrom to return %d copied rows, but got %d\", len(inputRows), copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\tt.Errorf(\"Input rows and output rows do not equal: %v -> %v\", inputRows, outputRows)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromLarge(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int2,\n\t\tb int4,\n\t\tc int8,\n\t\td varchar,\n\t\te text,\n\t\tf date,\n\t\tg timestamptz,\n\t\th bytea\n\t)`)\n\n\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\tinputRows := [][]any{}\n\n\tfor range 10_000 {\n\t\tinputRows = append(inputRows, []any{int16(0), int32(1), int64(2), \"abc\", \"efg\", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime, []byte{111, 111, 111, 111}})\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"}, pgx.CopyFromRows(inputRows))\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for CopyFrom: %v\", err)\n\t}\n\tif int(copyCount) != len(inputRows) {\n\t\tt.Errorf(\"Expected CopyFrom to return %d copied rows, but got %d\", len(inputRows), copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\tt.Errorf(\"Input rows and output rows do not equal\")\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromEnum(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\t_, err = tx.Exec(ctx, `drop type if exists color`)\n\trequire.NoError(t, err)\n\n\t_, err = tx.Exec(ctx, `drop type if exists fruit`)\n\trequire.NoError(t, err)\n\n\t_, err = tx.Exec(ctx, `create type color as enum ('blue', 'green', 'orange')`)\n\trequire.NoError(t, err)\n\n\t_, err = tx.Exec(ctx, `create type fruit as enum ('apple', 'orange', 'grape')`)\n\trequire.NoError(t, err)\n\n\t// Obviously using conn while a tx is in use and registering a type after the connection has been established are\n\t// really bad practices, but for the sake of convenience we do it in the test here.\n\tfor _, name := range []string{\"fruit\", \"color\"} {\n\t\ttyp, err := conn.LoadType(ctx, name)\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(typ)\n\t}\n\n\t_, err = tx.Exec(ctx, `create temporary table foo(\n\t\ta text,\n\t\tb color,\n\t\tc fruit,\n\t\td color,\n\t\te fruit,\n\t\tf text\n\t)`)\n\trequire.NoError(t, err)\n\n\tinputRows := [][]any{\n\t\t{\"abc\", \"blue\", \"grape\", \"orange\", \"orange\", \"def\"},\n\t\t{nil, nil, nil, nil, nil, nil},\n\t}\n\n\tcopyCount, err := tx.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"}, pgx.CopyFromRows(inputRows))\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, len(inputRows), copyCount)\n\n\trows, err := tx.Query(ctx, \"select * from foo\")\n\trequire.NoError(t, err)\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\trequire.NoError(t, err)\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\trequire.NoError(t, rows.Err())\n\n\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\tt.Errorf(\"Input rows and output rows do not equal: %v -> %v\", inputRows, outputRows)\n\t}\n\n\terr = tx.Rollback(ctx)\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromJSON(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tfor _, typeName := range []string{\"json\", \"jsonb\"} {\n\t\tif _, ok := conn.TypeMap().TypeForName(typeName); !ok {\n\t\t\treturn // No JSON/JSONB type -- must be running against old PostgreSQL\n\t\t}\n\t}\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta json,\n\t\tb jsonb\n\t)`)\n\n\tinputRows := [][]any{\n\t\t{map[string]any{\"foo\": \"bar\"}, map[string]any{\"bar\": \"quz\"}},\n\t\t{nil, nil},\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\"}, pgx.CopyFromRows(inputRows))\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for CopyFrom: %v\", err)\n\t}\n\tif int(copyCount) != len(inputRows) {\n\t\tt.Errorf(\"Expected CopyFrom to return %d copied rows, but got %d\", len(inputRows), copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif !reflect.DeepEqual(inputRows, outputRows) {\n\t\tt.Errorf(\"Input rows and output rows do not equal: %v -> %v\", inputRows, outputRows)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromTSVector(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"CockroachDB handles tsvector escaping differently\")\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\t_, err = tx.Exec(ctx, `create temporary table tmp_tsv (id int, t tsvector)`)\n\trequire.NoError(t, err)\n\n\tinputRows := [][]any{\n\t\t// Text format: core functionality.\n\t\t{1, `'a':1A 'cat':5 'fat':2B,4C`}, // Multiple lexemes with positions and weights.\n\t\t{2, `'bare'`},                     // Single lexeme with no positions.\n\t\t{3, `'multi':1,2,3,4,5`},          // Multiple positions (default weight D).\n\t\t{4, `'test':1A,2B,3C,4D`},         // All four weights on one lexeme.\n\t\t{5, `'word':1D`},                  // Explicit weight D (normalizes to no suffix).\n\t\t{6, `'high':16383A`},              // High position number (near 14-bit max).\n\n\t\t// Text format: escaping.\n\t\t{7, `'don''t'`}, // Quote escaping (doubled single quote).\n\t\t{8, `'don\\'t'`}, // Quote escaping (backslash).\n\t\t{9, `'ab\\\\c'`},  // Backslash in lexeme.\n\t\t{10, `'\\ foo'`}, // Escaped space.\n\n\t\t// Text format: special characters.\n\t\t{11, `'café' 'naïve'`}, // Unicode lexemes.\n\t\t{12, `'a:b' 'c,d'`},    // Delimiter-like characters (colon, comma).\n\n\t\t// Struct format: tests binary encoding path.\n\t\t{13, pgtype.TSVector{\n\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t{Word: \"alpha\", Positions: []pgtype.TSVectorPosition{{Position: 1, Weight: pgtype.TSVectorWeightA}}},\n\t\t\t\t{Word: \"beta\", Positions: []pgtype.TSVectorPosition{{Position: 2, Weight: pgtype.TSVectorWeightB}}},\n\t\t\t\t{Word: \"gamma\", Positions: nil},\n\t\t\t},\n\t\t\tValid: true,\n\t\t}},\n\t\t{14, pgtype.TSVector{Valid: true}}, // Empty valid tsvector (no lexemes).\n\n\t\t// NULL handling.\n\t\t{15, pgtype.TSVector{Valid: false}}, // Invalid (NULL) TSVector struct.\n\t\t{16, nil},                           // Nil value.\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"tmp_tsv\"}, []string{\"id\", \"t\"}, pgx.CopyFromRows(inputRows))\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, len(inputRows), copyCount)\n\n\trows, err := conn.Query(ctx, \"select id, t::text from tmp_tsv order by id nulls last\")\n\trequire.NoError(t, err)\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\trequire.NoError(t, err)\n\t\toutputRows = append(outputRows, row)\n\t}\n\trequire.NoError(t, rows.Err())\n\n\texpectedOutputRows := [][]any{\n\t\t// Text format: core functionality.\n\t\t{int32(1), `'a':1A 'cat':5 'fat':2B,4C`},\n\t\t{int32(2), `'bare'`},\n\t\t{int32(3), `'multi':1,2,3,4,5`},\n\t\t{int32(4), `'test':1A,2B,3C,4`},\n\t\t{int32(5), `'word':1`},\n\t\t{int32(6), `'high':16383A`},\n\n\t\t// Text format: escaping.\n\t\t{int32(7), `'don''t'`},\n\t\t{int32(8), `'don''t'`},\n\t\t{int32(9), `'ab\\\\c'`},\n\t\t{int32(10), `' foo'`},\n\n\t\t// Text format: special characters.\n\t\t{int32(11), `'café' 'naïve'`},\n\t\t{int32(12), `'a:b' 'c,d'`},\n\n\t\t// Struct format.\n\t\t{int32(13), `'alpha':1A 'beta':2B 'gamma'`},\n\t\t{int32(14), ``},\n\n\t\t// NULL handling.\n\t\t{int32(15), nil},\n\t\t{int32(16), nil},\n\t}\n\trequire.Equal(t, expectedOutputRows, outputRows)\n\n\tensureConnValid(t, conn)\n}\n\ntype clientFailSource struct {\n\tcount int\n\terr   error\n}\n\nfunc (cfs *clientFailSource) Next() bool {\n\tcfs.count++\n\treturn cfs.count < 100\n}\n\nfunc (cfs *clientFailSource) Values() ([]any, error) {\n\tif cfs.count == 3 {\n\t\tcfs.err = fmt.Errorf(\"client error\")\n\t\treturn nil, cfs.err\n\t}\n\treturn []any{make([]byte, 100_000)}, nil\n}\n\nfunc (cfs *clientFailSource) Err() error {\n\treturn cfs.err\n}\n\nfunc TestConnCopyFromFailServerSideMidway(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar not null\n\t)`)\n\n\tinputRows := [][]any{\n\t\t{int32(1), \"abc\"},\n\t\t{int32(2), nil}, // this row should trigger a failure\n\t\t{int32(3), \"def\"},\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\"}, pgx.CopyFromRows(inputRows))\n\tif err == nil {\n\t\tt.Errorf(\"Expected CopyFrom return error, but it did not\")\n\t}\n\tif _, ok := err.(*pgconn.PgError); !ok {\n\t\tt.Errorf(\"Expected CopyFrom return pgx.PgError, but instead it returned: %v\", err)\n\t}\n\tif copyCount != 0 {\n\t\tt.Errorf(\"Expected CopyFrom to return 0 copied rows, but got %d\", copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif len(outputRows) != 0 {\n\t\tt.Errorf(\"Expected 0 rows, but got %v\", outputRows)\n\t}\n\n\tmustExec(t, conn, \"truncate foo\")\n\n\tensureConnValid(t, conn)\n}\n\ntype failSource struct {\n\tcount int\n}\n\nfunc (fs *failSource) Next() bool {\n\ttime.Sleep(time.Millisecond * 100)\n\tfs.count++\n\treturn fs.count < 100\n}\n\nfunc (fs *failSource) Values() ([]any, error) {\n\tif fs.count == 3 {\n\t\treturn []any{nil}, nil\n\t}\n\treturn []any{make([]byte, 100_000)}, nil\n}\n\nfunc (fs *failSource) Err() error {\n\treturn nil\n}\n\nfunc TestConnCopyFromFailServerSideMidwayAbortsWithoutWaiting(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server copy error does not fail fast\")\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta bytea not null\n\t)`)\n\n\tstartTime := time.Now()\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, &failSource{})\n\tif err == nil {\n\t\tt.Errorf(\"Expected CopyFrom return error, but it did not\")\n\t}\n\tif _, ok := err.(*pgconn.PgError); !ok {\n\t\tt.Errorf(\"Expected CopyFrom return pgx.PgError, but instead it returned: %v\", err)\n\t}\n\tif copyCount != 0 {\n\t\tt.Errorf(\"Expected CopyFrom to return 0 copied rows, but got %d\", copyCount)\n\t}\n\n\tendTime := time.Now()\n\tcopyTime := endTime.Sub(startTime)\n\tif copyTime > time.Second {\n\t\tt.Errorf(\"Failing CopyFrom shouldn't have taken so long: %v\", copyTime)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif len(outputRows) != 0 {\n\t\tt.Errorf(\"Expected 0 rows, but got %v\", outputRows)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\ntype slowFailRaceSource struct {\n\tcount int\n}\n\nfunc (fs *slowFailRaceSource) Next() bool {\n\ttime.Sleep(time.Millisecond)\n\tfs.count++\n\treturn fs.count < 1000\n}\n\nfunc (fs *slowFailRaceSource) Values() ([]any, error) {\n\tif fs.count == 500 {\n\t\treturn []any{nil, nil}, nil\n\t}\n\treturn []any{1, make([]byte, 1000)}, nil\n}\n\nfunc (fs *slowFailRaceSource) Err() error {\n\treturn nil\n}\n\nfunc TestConnCopyFromSlowFailRace(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int not null,\n\t\tb bytea not null\n\t)`)\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\"}, &slowFailRaceSource{})\n\tif err == nil {\n\t\tt.Errorf(\"Expected CopyFrom return error, but it did not\")\n\t}\n\tif _, ok := err.(*pgconn.PgError); !ok {\n\t\tt.Errorf(\"Expected CopyFrom return pgx.PgError, but instead it returned: %v\", err)\n\t}\n\tif copyCount != 0 {\n\t\tt.Errorf(\"Expected CopyFrom to return 0 copied rows, but got %d\", copyCount)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromCopyFromSourceErrorMidway(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta bytea not null\n\t)`)\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, &clientFailSource{})\n\tif err == nil {\n\t\tt.Errorf(\"Expected CopyFrom return error, but it did not\")\n\t}\n\tif copyCount != 0 {\n\t\tt.Errorf(\"Expected CopyFrom to return 0 copied rows, but got %d\", copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif len(outputRows) != 0 {\n\t\tt.Errorf(\"Expected 0 rows, but got %v\", len(outputRows))\n\t}\n\n\tensureConnValid(t, conn)\n}\n\ntype clientFinalErrSource struct {\n\tcount int\n}\n\nfunc (cfs *clientFinalErrSource) Next() bool {\n\tcfs.count++\n\treturn cfs.count < 5\n}\n\nfunc (cfs *clientFinalErrSource) Values() ([]any, error) {\n\treturn []any{make([]byte, 100_000)}, nil\n}\n\nfunc (cfs *clientFinalErrSource) Err() error {\n\treturn fmt.Errorf(\"final error\")\n}\n\nfunc TestConnCopyFromCopyFromSourceErrorEnd(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta bytea not null\n\t)`)\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, &clientFinalErrSource{})\n\tif err == nil {\n\t\tt.Errorf(\"Expected CopyFrom return error, but it did not\")\n\t}\n\tif copyCount != 0 {\n\t\tt.Errorf(\"Expected CopyFrom to return 0 copied rows, but got %d\", copyCount)\n\t}\n\n\trows, err := conn.Query(ctx, \"select * from foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error for Query: %v\", err)\n\t}\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Errorf(\"Unexpected error for rows.Err(): %v\", rows.Err())\n\t}\n\n\tif len(outputRows) != 0 {\n\t\tt.Errorf(\"Expected 0 rows, but got %v\", outputRows)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnCopyFromAutomaticStringConversion(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int8\n\t)`)\n\n\tinputRows := [][]any{\n\t\t{\"42\"},\n\t\t{\"7\"},\n\t\t{8},\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, pgx.CopyFromRows(inputRows))\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, len(inputRows), copyCount)\n\n\trows, _ := conn.Query(ctx, \"select * from foo\")\n\tnums, err := pgx.CollectRows(rows, pgx.RowTo[int64])\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, []int64{42, 7, 8}, nums)\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/discussions/1891\nfunc TestConnCopyFromAutomaticStringConversionArray(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta numeric[]\n\t)`)\n\n\tinputRows := [][]any{\n\t\t{[]string{\"42\"}},\n\t\t{[]string{\"7\"}},\n\t\t{[]string{\"8\", \"9\"}},\n\t\t{[][]string{{\"10\", \"11\"}, {\"12\", \"13\"}}},\n\t}\n\n\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, pgx.CopyFromRows(inputRows))\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, len(inputRows), copyCount)\n\n\t// Test reads as int64 and flattened array for simplicity.\n\trows, _ := conn.Query(ctx, \"select * from foo\")\n\tnums, err := pgx.CollectRows(rows, pgx.RowTo[[]int64])\n\trequire.NoError(t, err)\n\trequire.Equal(t, [][]int64{{42}, {7}, {8, 9}, {10, 11, 12, 13}}, nums)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestCopyFromFunc(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, `create temporary table foo(\n\t\ta int\n\t)`)\n\n\tdataCh := make(chan int, 1)\n\n\tconst channelItems = 10\n\tgo func() {\n\t\tfor i := range channelItems {\n\t\t\tdataCh <- i\n\t\t}\n\t\tclose(dataCh)\n\t}()\n\n\tcopyCount, err := conn.CopyFrom(context.Background(), pgx.Identifier{\"foo\"}, []string{\"a\"},\n\t\tpgx.CopyFromFunc(func() ([]any, error) {\n\t\t\tv, ok := <-dataCh\n\t\t\tif !ok {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\treturn []any{v}, nil\n\t\t}))\n\n\trequire.ErrorIs(t, err, nil)\n\trequire.EqualValues(t, channelItems, copyCount)\n\n\trows, err := conn.Query(context.Background(), \"select * from foo order by a\")\n\trequire.NoError(t, err)\n\tnums, err := pgx.CollectRows(rows, pgx.RowTo[int64])\n\trequire.NoError(t, err)\n\trequire.Equal(t, []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, nums)\n\n\t// simulate a failure\n\tcopyCount, err = conn.CopyFrom(context.Background(), pgx.Identifier{\"foo\"}, []string{\"a\"},\n\t\tpgx.CopyFromFunc(func() func() ([]any, error) {\n\t\t\tx := 9\n\t\t\treturn func() ([]any, error) {\n\t\t\t\tx++\n\t\t\t\tif x > 100 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"simulated error\")\n\t\t\t\t}\n\t\t\t\treturn []any{x}, nil\n\t\t\t}\n\t\t}()))\n\trequire.NotErrorIs(t, err, nil)\n\trequire.EqualValues(t, 0, copyCount) // no change, due to error\n\n\tensureConnValid(t, conn)\n}\n"
  },
  {
    "path": "derived_types.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n/*\nbuildLoadDerivedTypesSQL generates the correct query for retrieving type information.\n\n\tpgVersion: the major version of the PostgreSQL server\n\ttypeNames: the names of the types to load. If nil, load all types.\n*/\nfunc buildLoadDerivedTypesSQL(pgVersion int64, typeNames []string) string {\n\tsupportsMultirange := (pgVersion >= 14)\n\tvar typeNamesClause string\n\n\tif typeNames == nil {\n\t\t// This should not occur; this will not return any types\n\t\ttypeNamesClause = \"= ''\"\n\t} else {\n\t\ttypeNamesClause = \"= ANY($1::text[])\"\n\t}\n\tparts := make([]string, 0, 10)\n\n\t// Each of the type names provided might be found in pg_class or pg_type.\n\t// Additionally, it may or may not include a schema portion.\n\tparts = append(parts, `\nWITH RECURSIVE\n-- find the OIDs in pg_class which match one of the provided type names\nselected_classes(oid,reltype) AS (\n    -- this query uses the namespace search path, so will match type names without a schema prefix\n    SELECT pg_class.oid, pg_class.reltype\n    FROM pg_catalog.pg_class\n        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = pg_class.relnamespace\n    WHERE pg_catalog.pg_table_is_visible(pg_class.oid)\n      AND relname `, typeNamesClause, `\nUNION ALL\n    -- this query will only match type names which include the schema prefix\n    SELECT pg_class.oid, pg_class.reltype\n    FROM pg_class\n    INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)\n    WHERE nspname || '.' || relname `, typeNamesClause, `\n),\nselected_types(oid) AS (\n    -- collect the OIDs from pg_types which correspond to the selected classes\n    SELECT reltype AS oid\n    FROM selected_classes\nUNION ALL\n    -- as well as any other type names which match our criteria\n    SELECT pg_type.oid\n    FROM pg_type\n    LEFT OUTER JOIN pg_namespace ON (pg_type.typnamespace = pg_namespace.oid)\n    WHERE typname `, typeNamesClause, `\n        OR nspname || '.' || typname `, typeNamesClause, `\n),\n-- this builds a parent/child mapping of objects, allowing us to know\n-- all the child (ie: dependent) types that a parent (type) requires\n-- As can be seen, there are 3 ways this can occur (the last of which\n-- is due to being a composite class, where the composite fields are children)\npc(parent, child) AS (\n    SELECT parent.oid, parent.typelem\n    FROM pg_type parent\n    WHERE parent.typtype = 'b' AND parent.typelem != 0\nUNION ALL\n    SELECT parent.oid, parent.typbasetype\n    FROM pg_type parent\n    WHERE parent.typtypmod = -1 AND parent.typbasetype != 0\nUNION ALL\n    SELECT pg_type.oid, atttypid\n    FROM pg_attribute\n    INNER JOIN pg_class ON (pg_class.oid = pg_attribute.attrelid)\n    INNER JOIN pg_type ON (pg_type.oid = pg_class.reltype)\n    WHERE NOT attisdropped\n      AND attnum > 0\n),\n-- Now construct a recursive query which includes a 'depth' element.\n-- This is used to ensure that the \"youngest\" children are registered before\n-- their parents.\nrelationships(parent, child, depth) AS (\n    SELECT DISTINCT 0::OID, selected_types.oid, 0\n    FROM selected_types\nUNION ALL\n    SELECT pg_type.oid AS parent, pg_attribute.atttypid AS child, 1\n    FROM selected_classes c\n    inner join pg_type ON (c.reltype = pg_type.oid)\n    inner join pg_attribute on (c.oid = pg_attribute.attrelid)\nUNION ALL\n    SELECT pc.parent, pc.child, relationships.depth + 1\n    FROM pc\n    INNER JOIN relationships ON (pc.parent = relationships.child)\n),\n-- composite fields need to be encapsulated as a couple of arrays to provide the required information for registration\ncomposite AS (\n    SELECT pg_type.oid, ARRAY_AGG(attname ORDER BY attnum) AS attnames, ARRAY_AGG(atttypid ORDER BY ATTNUM) AS atttypids\n    FROM pg_attribute\n    INNER JOIN pg_class ON (pg_class.oid = pg_attribute.attrelid)\n    INNER JOIN pg_type ON (pg_type.oid = pg_class.reltype)\n    WHERE NOT attisdropped\n      AND attnum > 0\n    GROUP BY pg_type.oid\n)\n-- Bring together this information, showing all the information which might possibly be required\n-- to complete the registration, applying filters to only show the items which relate to the selected\n-- types/classes.\nSELECT typname,\n       pg_namespace.nspname,\n       typtype,\n       typbasetype,\n       typelem,\n       pg_type.oid,`)\n\tif supportsMultirange {\n\t\tparts = append(parts, `\n       COALESCE(multirange.rngtypid, 0) AS rngtypid,`)\n\t} else {\n\t\tparts = append(parts, `\n       0 AS rngtypid,`)\n\t}\n\tparts = append(parts, `\n       COALESCE(pg_range.rngsubtype, 0) AS rngsubtype,\n       attnames, atttypids\n    FROM relationships\n    INNER JOIN pg_type ON (pg_type.oid = relationships.child)\n    LEFT OUTER JOIN pg_range ON (pg_type.oid = pg_range.rngtypid)`)\n\tif supportsMultirange {\n\t\tparts = append(parts, `\n    LEFT OUTER JOIN pg_range multirange ON (pg_type.oid = multirange.rngmultitypid)`)\n\t}\n\n\tparts = append(parts, `\n    LEFT OUTER JOIN composite USING (oid)\n    LEFT OUTER JOIN pg_namespace ON (pg_type.typnamespace = pg_namespace.oid)\n    WHERE NOT (typtype = 'b' AND typelem = 0)`)\n\tparts = append(parts, `\n    GROUP BY typname, pg_namespace.nspname, typtype, typbasetype, typelem, pg_type.oid, pg_range.rngsubtype,`)\n\tif supportsMultirange {\n\t\tparts = append(parts, `\n        multirange.rngtypid,`)\n\t}\n\tparts = append(parts, `\n        attnames, atttypids\n    ORDER BY MAX(depth) desc, typname;`)\n\treturn strings.Join(parts, \"\")\n}\n\ntype derivedTypeInfo struct {\n\tOid, Typbasetype, Typelem, Rngsubtype, Rngtypid uint32\n\tTypeName, Typtype, NspName                      string\n\tAttnames                                        []string\n\tAtttypids                                       []uint32\n}\n\n// LoadTypes performs a single (complex) query, returning all the required\n// information to register the named types, as well as any other types directly\n// or indirectly required to complete the registration.\n// The result of this call can be passed into RegisterTypes to complete the process.\nfunc (c *Conn) LoadTypes(ctx context.Context, typeNames []string) ([]*pgtype.Type, error) {\n\tm := c.TypeMap()\n\tif len(typeNames) == 0 {\n\t\treturn nil, fmt.Errorf(\"No type names were supplied.\")\n\t}\n\n\t// Disregard server version errors. This will result in\n\t// the SQL not support recent structures such as multirange\n\tserverVersion, _ := serverVersion(c)\n\tsql := buildLoadDerivedTypesSQL(serverVersion, typeNames)\n\trows, err := c.Query(ctx, sql, QueryResultFormats{TextFormatCode}, typeNames)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"While generating load types query: %w\", err)\n\t}\n\tdefer rows.Close()\n\tresult := make([]*pgtype.Type, 0, 100)\n\tfor rows.Next() {\n\t\tti := derivedTypeInfo{}\n\t\terr = rows.Scan(&ti.TypeName, &ti.NspName, &ti.Typtype, &ti.Typbasetype, &ti.Typelem, &ti.Oid, &ti.Rngtypid, &ti.Rngsubtype, &ti.Attnames, &ti.Atttypids)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"While scanning type information: %w\", err)\n\t\t}\n\t\tvar type_ *pgtype.Type\n\t\tswitch ti.Typtype {\n\t\tcase \"b\": // array\n\t\t\tdt, ok := m.TypeForOID(ti.Typelem)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"Array element OID %v not registered while loading pgtype %q\", ti.Typelem, ti.TypeName)\n\t\t\t}\n\t\t\ttype_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.ArrayCodec{ElementType: dt}}\n\t\tcase \"c\": // composite\n\t\t\tvar fields []pgtype.CompositeCodecField\n\t\t\tfor i, fieldName := range ti.Attnames {\n\t\t\t\tdt, ok := m.TypeForOID(ti.Atttypids[i])\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"Unknown field for composite type %q:  field %q (OID %v) is not already registered.\", ti.TypeName, fieldName, ti.Atttypids[i])\n\t\t\t\t}\n\t\t\t\tfields = append(fields, pgtype.CompositeCodecField{Name: fieldName, Type: dt})\n\t\t\t}\n\n\t\t\ttype_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.CompositeCodec{Fields: fields}}\n\t\tcase \"d\": // domain\n\t\t\tdt, ok := m.TypeForOID(ti.Typbasetype)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"Domain base type OID %v was not already registered, needed for %q\", ti.Typbasetype, ti.TypeName)\n\t\t\t}\n\n\t\t\ttype_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: dt.Codec}\n\t\tcase \"e\": // enum\n\t\t\ttype_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.EnumCodec{}}\n\t\tcase \"r\": // range\n\t\t\tdt, ok := m.TypeForOID(ti.Rngsubtype)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"Range element OID %v was not already registered, needed for %q\", ti.Rngsubtype, ti.TypeName)\n\t\t\t}\n\n\t\t\ttype_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.RangeCodec{ElementType: dt}}\n\t\tcase \"m\": // multirange\n\t\t\tdt, ok := m.TypeForOID(ti.Rngtypid)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"Multirange element OID %v was not already registered, needed for %q\", ti.Rngtypid, ti.TypeName)\n\t\t\t}\n\n\t\t\ttype_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.MultirangeCodec{ElementType: dt}}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"Unknown typtype %q was found while registering %q\", ti.Typtype, ti.TypeName)\n\t\t}\n\n\t\t// the type_ is impossible to be null\n\t\tm.RegisterType(type_)\n\t\tif ti.NspName != \"\" {\n\t\t\tnspType := &pgtype.Type{Name: ti.NspName + \".\" + type_.Name, OID: type_.OID, Codec: type_.Codec}\n\t\t\tm.RegisterType(nspType)\n\t\t\tresult = append(result, nspType)\n\t\t}\n\t\tresult = append(result, type_)\n\t}\n\treturn result, nil\n}\n\n// serverVersion returns the postgresql server version.\nfunc serverVersion(c *Conn) (int64, error) {\n\tserverVersionStr := c.PgConn().ParameterStatus(\"server_version\")\n\tserverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)\n\t// if not PostgreSQL do nothing\n\tif serverVersionStr == \"\" {\n\t\treturn 0, fmt.Errorf(\"Cannot identify server version in %q\", serverVersionStr)\n\t}\n\n\tversion, err := strconv.ParseInt(serverVersionStr, 10, 64)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"postgres version parsing failed: %w\", err)\n\t}\n\treturn version, nil\n}\n"
  },
  {
    "path": "derived_types_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCompositeCodecTranscodeWithLoadTypes(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `\ndrop type if exists dtype_test;\ndrop domain if exists anotheruint64;\n\ncreate domain anotheruint64 as numeric(20,0);\ncreate type dtype_test as (\n  a text,\n  b int4,\n  c anotheruint64,\n  d anotheruint64[]\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type dtype_test\")\n\t\tdefer conn.Exec(ctx, \"drop domain anotheruint64\")\n\n\t\ttypes, err := conn.LoadTypes(ctx, []string{\"dtype_test\"})\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, types, 6)\n\t\trequire.Equal(t, types[0].Name, \"public.anotheruint64\")\n\t\trequire.Equal(t, types[1].Name, \"anotheruint64\")\n\t\trequire.Equal(t, types[2].Name, \"public._anotheruint64\")\n\t\trequire.Equal(t, types[3].Name, \"_anotheruint64\")\n\t\trequire.Equal(t, types[4].Name, \"public.dtype_test\")\n\t\trequire.Equal(t, types[5].Name, \"dtype_test\")\n\t})\n}\n"
  },
  {
    "path": "doc.go",
    "content": "// Package pgx is a PostgreSQL database driver.\n/*\npgx provides a native PostgreSQL driver and can act as a database/sql driver. The native PostgreSQL interface is similar\nto the database/sql interface while providing better speed and access to PostgreSQL specific features. Use\ngithub.com/jackc/pgx/v5/stdlib to use pgx as a database/sql compatible driver. See that package's documentation for\ndetails.\n\nEstablishing a Connection\n\nThe primary way of establishing a connection is with [pgx.Connect]:\n\n    conn, err := pgx.Connect(context.Background(), os.Getenv(\"DATABASE_URL\"))\n\nThe database connection string can be in URL or key/value format. Both PostgreSQL settings and pgx settings can be\nspecified here. In addition, a config struct can be created by [ParseConfig] and modified before establishing the\nconnection with [ConnectConfig] to configure settings such as tracing that cannot be configured with a connection\nstring.\n\nConnection Pool\n\n[*pgx.Conn] represents a single connection to the database and is not concurrency safe. Use package\ngithub.com/jackc/pgx/v5/pgxpool for a concurrency safe connection pool.\n\nQuery Interface\n\npgx implements Query in the familiar database/sql style. However, pgx provides generic functions such as CollectRows and\nForEachRow that are a simpler and safer way of processing rows than manually calling defer rows.Close(), rows.Next(),\nrows.Scan, and rows.Err().\n\nCollectRows can be used collect all returned rows into a slice.\n\n    rows, _ := conn.Query(context.Background(), \"select generate_series(1,$1)\", 5)\n    numbers, err := pgx.CollectRows(rows, pgx.RowTo[int32])\n    if err != nil {\n      return err\n    }\n    // numbers => [1 2 3 4 5]\n\nForEachRow can be used to execute a callback function for every row. This is often easier than iterating over rows\ndirectly.\n\n    var sum, n int32\n    rows, _ := conn.Query(context.Background(), \"select generate_series(1,$1)\", 10)\n    _, err := pgx.ForEachRow(rows, []any{&n}, func() error {\n      sum += n\n      return nil\n    })\n    if err != nil {\n      return err\n    }\n\npgx also implements QueryRow in the same style as database/sql.\n\n    var name string\n    var weight int64\n    err := conn.QueryRow(context.Background(), \"select name, weight from widgets where id=$1\", 42).Scan(&name, &weight)\n    if err != nil {\n        return err\n    }\n\nUse Exec to execute a query that does not return a result set.\n\n    commandTag, err := conn.Exec(context.Background(), \"delete from widgets where id=$1\", 42)\n    if err != nil {\n        return err\n    }\n    if commandTag.RowsAffected() != 1 {\n        return errors.New(\"No row found to delete\")\n    }\n\nPostgreSQL Data Types\n\npgx uses the pgtype package to converting Go values to and from PostgreSQL values. It supports many PostgreSQL types\ndirectly and is customizable and extendable. User defined data types such as enums, domains,  and composite types may\nrequire type registration. See that package's documentation for details.\n\nTransactions\n\nTransactions are started by calling Begin.\n\n    tx, err := conn.Begin(context.Background())\n    if err != nil {\n        return err\n    }\n    // Rollback is safe to call even if the tx is already closed, so if\n    // the tx commits successfully, this is a no-op\n    defer tx.Rollback(context.Background())\n\n    _, err = tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n    if err != nil {\n        return err\n    }\n\n    err = tx.Commit(context.Background())\n    if err != nil {\n        return err\n    }\n\nThe Tx returned from Begin also implements the Begin method. This can be used to implement pseudo nested transactions.\nThese are internally implemented with savepoints.\n\nUse BeginTx to control the transaction mode. BeginTx also can be used to ensure a new transaction is created instead of\na pseudo nested transaction.\n\nBeginFunc and BeginTxFunc are functions that begin a transaction, execute a function, and commit or rollback the\ntransaction depending on the return value of the function. These can be simpler and less error prone to use.\n\n    err = pgx.BeginFunc(context.Background(), conn, func(tx pgx.Tx) error {\n        _, err := tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n        return err\n    })\n    if err != nil {\n        return err\n    }\n\nPrepared Statements\n\nPrepared statements can be manually created with the Prepare method. However, this is rarely necessary because pgx\nincludes an automatic statement cache by default. Queries run through the normal Query, QueryRow, and Exec functions are\nautomatically prepared on first execution and the prepared statement is reused on subsequent executions. See ParseConfig\nfor information on how to customize or disable the statement cache.\n\nCopy Protocol\n\nUse CopyFrom to efficiently insert multiple rows at a time using the PostgreSQL copy protocol. CopyFrom accepts a\nCopyFromSource interface. If the data is already in a [][]any use CopyFromRows to wrap it in a CopyFromSource interface.\nOr implement CopyFromSource to avoid buffering the entire data set in memory.\n\n    rows := [][]any{\n        {\"John\", \"Smith\", int32(36)},\n        {\"Jane\", \"Doe\", int32(29)},\n    }\n\n    copyCount, err := conn.CopyFrom(\n        context.Background(),\n        pgx.Identifier{\"people\"},\n        []string{\"first_name\", \"last_name\", \"age\"},\n        pgx.CopyFromRows(rows),\n    )\n\nWhen you already have a typed array using CopyFromSlice can be more convenient.\n\n    rows := []User{\n        {\"John\", \"Smith\", 36},\n        {\"Jane\", \"Doe\", 29},\n    }\n\n    copyCount, err := conn.CopyFrom(\n        context.Background(),\n        pgx.Identifier{\"people\"},\n        []string{\"first_name\", \"last_name\", \"age\"},\n        pgx.CopyFromSlice(len(rows), func(i int) ([]any, error) {\n            return []any{rows[i].FirstName, rows[i].LastName, rows[i].Age}, nil\n        }),\n    )\n\nCopyFrom can be faster than an insert with as few as 5 rows.\n\nListen and Notify\n\npgx can listen to the PostgreSQL notification system with the `Conn.WaitForNotification` method. It blocks until a\nnotification is received or the context is canceled.\n\n    _, err := conn.Exec(context.Background(), \"listen channelname\")\n    if err != nil {\n        return err\n    }\n\n    notification, err := conn.WaitForNotification(context.Background())\n    if err != nil {\n        return err\n    }\n    // do something with notification\n\n\nTracing and Logging\n\npgx supports tracing by setting ConnConfig.Tracer. To combine several tracers you can use the multitracer.Tracer.\n\nIn addition, the tracelog package provides the TraceLog type which lets a traditional logger act as a Tracer.\n\nFor debug tracing of the actual PostgreSQL wire protocol messages see github.com/jackc/pgx/v5/pgproto3.\n\nLower Level PostgreSQL Functionality\n\ngithub.com/jackc/pgx/v5/pgconn contains a lower level PostgreSQL driver roughly at the level of libpq. pgx.Conn is\nimplemented on top of pgconn. The Conn.PgConn() method can be used to access this lower layer.\n\nPgBouncer\n\nBy default pgx automatically uses prepared statements. Prepared statements are incompatible with PgBouncer. This can be\ndisabled by setting a different QueryExecMode in ConnConfig.DefaultQueryExecMode.\n*/\npackage pgx\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\n* chat is a command line chat program using listen/notify.\n* todo is a command line todo list that demonstrates basic CRUD actions.\n* url_shortener contains a simple example of using pgx in a web context.\n* [Tern](https://github.com/jackc/tern) is a migration tool that uses pgx.\n* [The Pithy Reader](https://github.com/jackc/tpr) is a RSS aggregator that uses pgx.\n"
  },
  {
    "path": "examples/chat/README.md",
    "content": "# Description\n\nThis is a sample chat program implemented using PostgreSQL's listen/notify\nfunctionality with pgx.\n\nStart multiple instances of this program connected to the same database to chat\nbetween them.\n\n## Connection configuration\n\nThe database connection is configured via DATABASE_URL and standard PostgreSQL environment variables (PGHOST, PGUSER, etc.)\n\nYou can either export them then run chat:\n\n    export PGHOST=/private/tmp\n    ./chat\n\nOr you can prefix the chat execution with the environment variables:\n\n    PGHOST=/private/tmp ./chat\n"
  },
  {
    "path": "examples/chat/main.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n)\n\nvar pool *pgxpool.Pool\n\nfunc main() {\n\tvar err error\n\tpool, err = pgxpool.New(context.Background(), os.Getenv(\"DATABASE_URL\"))\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Unable to connect to database:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tgo listen()\n\n\tfmt.Println(`Type a message and press enter.\n\nThis message should appear in any other chat instances connected to the same\ndatabase.\n\nType \"exit\" to quit.`)\n\n\tscanner := bufio.NewScanner(os.Stdin)\n\tfor scanner.Scan() {\n\t\tmsg := scanner.Text()\n\t\tif msg == \"exit\" {\n\t\t\tos.Exit(0)\n\t\t}\n\n\t\t_, err = pool.Exec(context.Background(), \"select pg_notify('chat', $1)\", msg)\n\t\tif err != nil {\n\t\t\tfmt.Fprintln(os.Stderr, \"Error sending notification:\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Error scanning from stdin:\", err)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc listen() {\n\tconn, err := pool.Acquire(context.Background())\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Error acquiring connection:\", err)\n\t\tos.Exit(1)\n\t}\n\tdefer conn.Release()\n\n\t_, err = conn.Exec(context.Background(), \"listen chat\")\n\tif err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Error listening to chat channel:\", err)\n\t\tos.Exit(1)\n\t}\n\n\tfor {\n\t\tnotification, err := conn.Conn().WaitForNotification(context.Background())\n\t\tif err != nil {\n\t\t\tfmt.Fprintln(os.Stderr, \"Error waiting for notification:\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\t\tfmt.Println(\"PID:\", notification.PID, \"Channel:\", notification.Channel, \"Payload:\", notification.Payload)\n\t}\n}\n"
  },
  {
    "path": "examples/todo/README.md",
    "content": "# Description\n\nThis is a sample todo list implemented using pgx as the connector to a\nPostgreSQL data store.\n\n# Usage\n\nCreate a PostgreSQL database and run structure.sql into it to create the\nnecessary data schema.\n\nExample:\n\n    createdb todo\n    psql todo < structure.sql\n\nBuild todo:\n\n    go build\n\n## Connection configuration\n\nThe database connection is configured via DATABASE_URL and standard PostgreSQL environment variables (PGHOST, PGUSER, etc.)\n\nYou can either export them then run todo:\n\n    export PGDATABASE=todo\n    ./todo list\n\nOr you can prefix the todo execution with the environment variables:\n\n    PGDATABASE=todo ./todo list\n\n## Add a todo item\n\n    ./todo add 'Learn go'\n\n## List tasks\n\n    ./todo list\n\n## Update a task\n\n    ./todo update 1 'Learn more go'\n\n## Delete a task\n\n    ./todo remove 1\n\n# Example Setup and Execution\n\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ createdb todo\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ psql todo < structure.sql\n    Expanded display is used automatically.\n    Timing is on.\n    CREATE TABLE\n    Time: 6.363 ms\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ go build\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ export PGDATABASE=todo\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo add 'Learn Go'\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list\n    1. Learn Go\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo update 1 'Learn more Go'\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list\n    1. Learn more Go\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo remove 1\n    jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list\n"
  },
  {
    "path": "examples/todo/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\nvar conn *pgx.Conn\n\nfunc main() {\n\tvar err error\n\tconn, err = pgx.Connect(context.Background(), os.Getenv(\"DATABASE_URL\"))\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"Unable to connection to database: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\tif len(os.Args) == 1 {\n\t\tprintHelp()\n\t\tos.Exit(0)\n\t}\n\n\tswitch os.Args[1] {\n\tcase \"list\":\n\t\terr = listTasks()\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable to list tasks: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\tcase \"add\":\n\t\terr = addTask(os.Args[2])\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable to add task: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\tcase \"update\":\n\t\tn, err := strconv.ParseInt(os.Args[2], 10, 32)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable convert task_num into int32: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\terr = updateTask(int32(n), os.Args[3])\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable to update task: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\tcase \"remove\":\n\t\tn, err := strconv.ParseInt(os.Args[2], 10, 32)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable convert task_num into int32: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\terr = removeTask(int32(n))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable to remove task: %v\\n\", err)\n\t\t\tos.Exit(1)\n\t\t}\n\n\tdefault:\n\t\tfmt.Fprintln(os.Stderr, \"Invalid command\")\n\t\tprintHelp()\n\t\tos.Exit(1)\n\t}\n}\n\nfunc listTasks() error {\n\trows, _ := conn.Query(context.Background(), \"select * from tasks\")\n\n\tfor rows.Next() {\n\t\tvar id int32\n\t\tvar description string\n\t\terr := rows.Scan(&id, &description)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfmt.Printf(\"%d. %s\\n\", id, description)\n\t}\n\n\treturn rows.Err()\n}\n\nfunc addTask(description string) error {\n\t_, err := conn.Exec(context.Background(), \"insert into tasks(description) values($1)\", description)\n\treturn err\n}\n\nfunc updateTask(itemNum int32, description string) error {\n\t_, err := conn.Exec(context.Background(), \"update tasks set description=$1 where id=$2\", description, itemNum)\n\treturn err\n}\n\nfunc removeTask(itemNum int32) error {\n\t_, err := conn.Exec(context.Background(), \"delete from tasks where id=$1\", itemNum)\n\treturn err\n}\n\nfunc printHelp() {\n\tfmt.Print(`Todo pgx demo\n\nUsage:\n\n  todo list\n  todo add task\n  todo update task_num item\n  todo remove task_num\n\nExample:\n\n  todo add 'Learn Go'\n  todo list\n`)\n}\n"
  },
  {
    "path": "examples/todo/structure.sql",
    "content": "create table tasks (\n  id serial primary key,\n  description text not null\n);\n"
  },
  {
    "path": "examples/url_shortener/README.md",
    "content": "# Description\n\nThis is a sample REST URL shortener service implemented using pgx as the connector to a PostgreSQL data store.\n\n# Usage\n\nCreate a PostgreSQL database and run structure.sql into it to create the necessary data schema.\n\nConfigure the database connection with `DATABASE_URL` or standard PostgreSQL (`PG*`) environment variables or\n\nRun main.go:\n\n```\ngo run main.go\n```\n\n## Create or Update a Shortened URL\n\n```\ncurl -X PUT -d 'http://www.google.com' http://localhost:8080/google\n```\n\n## Get a Shortened URL\n\n```\ncurl http://localhost:8080/google\n```\n\n## Delete a Shortened URL\n\n```\ncurl -X DELETE http://localhost:8080/google\n```\n"
  },
  {
    "path": "examples/url_shortener/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n)\n\nvar db *pgxpool.Pool\n\nfunc getUrlHandler(w http.ResponseWriter, req *http.Request) {\n\tvar url string\n\terr := db.QueryRow(context.Background(), \"select url from shortened_urls where id=$1\", req.URL.Path).Scan(&url)\n\tswitch err {\n\tcase nil:\n\t\thttp.Redirect(w, req, url, http.StatusSeeOther)\n\tcase pgx.ErrNoRows:\n\t\thttp.NotFound(w, req)\n\tdefault:\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t}\n}\n\nfunc putUrlHandler(w http.ResponseWriter, req *http.Request) {\n\tid := req.URL.Path\n\tvar url string\n\tif body, err := io.ReadAll(req.Body); err == nil {\n\t\turl = string(body)\n\t} else {\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tif _, err := db.Exec(context.Background(), `insert into shortened_urls(id, url) values ($1, $2)\n\ton conflict (id) do update set url=excluded.url`, id, url); err == nil {\n\t\tw.WriteHeader(http.StatusOK)\n\t} else {\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t}\n}\n\nfunc deleteUrlHandler(w http.ResponseWriter, req *http.Request) {\n\tif _, err := db.Exec(context.Background(), \"delete from shortened_urls where id=$1\", req.URL.Path); err == nil {\n\t\tw.WriteHeader(http.StatusOK)\n\t} else {\n\t\thttp.Error(w, \"Internal server error\", http.StatusInternalServerError)\n\t}\n}\n\nfunc urlHandler(w http.ResponseWriter, req *http.Request) {\n\tswitch req.Method {\n\tcase \"GET\":\n\t\tgetUrlHandler(w, req)\n\n\tcase \"PUT\":\n\t\tputUrlHandler(w, req)\n\n\tcase \"DELETE\":\n\t\tdeleteUrlHandler(w, req)\n\n\tdefault:\n\t\tw.Header().Add(\"Allow\", \"GET, PUT, DELETE\")\n\t\tw.WriteHeader(http.StatusMethodNotAllowed)\n\t}\n}\n\nfunc main() {\n\tpoolConfig, err := pgxpool.ParseConfig(os.Getenv(\"DATABASE_URL\"))\n\tif err != nil {\n\t\tlog.Fatalln(\"Unable to parse DATABASE_URL:\", err)\n\t}\n\n\tdb, err = pgxpool.NewWithConfig(context.Background(), poolConfig)\n\tif err != nil {\n\t\tlog.Fatalln(\"Unable to create connection pool:\", err)\n\t}\n\n\thttp.HandleFunc(\"/\", urlHandler)\n\n\tlog.Println(\"Starting URL shortener on localhost:8080\")\n\terr = http.ListenAndServe(\"localhost:8080\", nil)\n\tif err != nil {\n\t\tlog.Fatalln(\"Unable to start web server:\", err)\n\t}\n}\n"
  },
  {
    "path": "examples/url_shortener/structure.sql",
    "content": "create table shortened_urls (\n  id text primary key,\n  url text not null\n);"
  },
  {
    "path": "extended_query_builder.go",
    "content": "package pgx\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// ExtendedQueryBuilder is used to choose the parameter formats, to format the parameters and to choose the result\n// formats for an extended query.\ntype ExtendedQueryBuilder struct {\n\tParamValues     [][]byte\n\tparamValueBytes []byte\n\tParamFormats    []int16\n\tResultFormats   []int16\n}\n\n// Build sets ParamValues, ParamFormats, and ResultFormats for use with *PgConn.ExecParams or *PgConn.ExecPrepared. If\n// sd is nil then QueryExecModeExec behavior will be used.\nfunc (eqb *ExtendedQueryBuilder) Build(m *pgtype.Map, sd *pgconn.StatementDescription, args []any) error {\n\teqb.reset()\n\n\tif sd == nil {\n\t\tfor i := range args {\n\t\t\terr := eqb.appendParam(m, 0, pgtype.TextFormatCode, args[i])\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"failed to encode args[%d]: %w\", i, err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tif len(sd.ParamOIDs) != len(args) {\n\t\treturn fmt.Errorf(\"mismatched param and argument count\")\n\t}\n\n\tfor i := range args {\n\t\terr := eqb.appendParam(m, sd.ParamOIDs[i], -1, args[i])\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"failed to encode args[%d]: %w\", i, err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor i := range sd.Fields {\n\t\teqb.appendResultFormat(m.FormatCodeForOID(sd.Fields[i].DataTypeOID))\n\t}\n\n\treturn nil\n}\n\n// appendParam appends a parameter to the query. format may be -1 to automatically choose the format. If arg is nil it\n// must be an untyped nil.\nfunc (eqb *ExtendedQueryBuilder) appendParam(m *pgtype.Map, oid uint32, format int16, arg any) error {\n\tif format == -1 {\n\t\tpreferredFormat := eqb.chooseParameterFormatCode(m, oid, arg)\n\t\tpreferredErr := eqb.appendParam(m, oid, preferredFormat, arg)\n\t\tif preferredErr == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar otherFormat int16\n\t\tif preferredFormat == TextFormatCode {\n\t\t\totherFormat = BinaryFormatCode\n\t\t} else {\n\t\t\totherFormat = TextFormatCode\n\t\t}\n\n\t\totherErr := eqb.appendParam(m, oid, otherFormat, arg)\n\t\tif otherErr == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn preferredErr // return the error from the preferred format\n\t}\n\n\tv, err := eqb.encodeExtendedParamValue(m, oid, format, arg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\teqb.ParamFormats = append(eqb.ParamFormats, format)\n\teqb.ParamValues = append(eqb.ParamValues, v)\n\n\treturn nil\n}\n\n// appendResultFormat appends a result format to the query.\nfunc (eqb *ExtendedQueryBuilder) appendResultFormat(format int16) {\n\teqb.ResultFormats = append(eqb.ResultFormats, format)\n}\n\n// reset readies eqb to build another query.\nfunc (eqb *ExtendedQueryBuilder) reset() {\n\teqb.ParamValues = eqb.ParamValues[0:0]\n\teqb.paramValueBytes = eqb.paramValueBytes[0:0]\n\teqb.ParamFormats = eqb.ParamFormats[0:0]\n\teqb.ResultFormats = eqb.ResultFormats[0:0]\n\n\tif cap(eqb.ParamValues) > 64 {\n\t\teqb.ParamValues = make([][]byte, 0, 64)\n\t}\n\n\tif cap(eqb.paramValueBytes) > 256 {\n\t\teqb.paramValueBytes = make([]byte, 0, 256)\n\t}\n\n\tif cap(eqb.ParamFormats) > 64 {\n\t\teqb.ParamFormats = make([]int16, 0, 64)\n\t}\n\tif cap(eqb.ResultFormats) > 64 {\n\t\teqb.ResultFormats = make([]int16, 0, 64)\n\t}\n}\n\nfunc (eqb *ExtendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uint32, formatCode int16, arg any) ([]byte, error) {\n\tif eqb.paramValueBytes == nil {\n\t\teqb.paramValueBytes = make([]byte, 0, 128)\n\t}\n\n\tpos := len(eqb.paramValueBytes)\n\n\tbuf, err := m.Encode(oid, formatCode, arg, eqb.paramValueBytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif buf == nil {\n\t\treturn nil, nil\n\t}\n\teqb.paramValueBytes = buf\n\treturn eqb.paramValueBytes[pos:], nil\n}\n\n// chooseParameterFormatCode determines the correct format code for an\n// argument to a prepared statement. It defaults to TextFormatCode if no\n// determination can be made.\nfunc (eqb *ExtendedQueryBuilder) chooseParameterFormatCode(m *pgtype.Map, oid uint32, arg any) int16 {\n\tswitch arg.(type) {\n\tcase string, *string:\n\t\treturn TextFormatCode\n\t}\n\n\treturn m.FormatCodeForOID(oid)\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/jackc/pgx/v5\n\ngo 1.24.0\n\nrequire (\n\tgithub.com/jackc/pgpassfile v1.0.0\n\tgithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761\n\tgithub.com/jackc/puddle/v2 v2.2.2\n\tgithub.com/stretchr/testify v1.11.1\n\tgolang.org/x/sync v0.17.0\n\tgolang.org/x/text v0.29.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/kr/pretty v0.3.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngolang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=\ngolang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=\ngolang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "helper_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar defaultConnTestRunner pgxtest.ConnTestRunner\n\nfunc init() {\n\tdefaultConnTestRunner = pgxtest.DefaultConnTestRunner()\n\tdefaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\treturn config\n\t}\n}\n\nfunc mustConnectString(t testing.TB, connString string) *pgx.Conn {\n\tconn, err := pgx.Connect(context.Background(), connString)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to establish connection: %v\", err)\n\t}\n\treturn conn\n}\n\nfunc mustParseConfig(t testing.TB, connString string) *pgx.ConnConfig {\n\tconfig, err := pgx.ParseConfig(connString)\n\trequire.Nil(t, err)\n\treturn config\n}\n\nfunc mustConnect(t testing.TB, config *pgx.ConnConfig) *pgx.Conn {\n\tconn, err := pgx.ConnectConfig(context.Background(), config)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to establish connection: %v\", err)\n\t}\n\treturn conn\n}\n\nfunc closeConn(t testing.TB, conn *pgx.Conn) {\n\terr := conn.Close(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Close unexpectedly failed: %v\", err)\n\t}\n}\n\nfunc mustExec(t testing.TB, conn *pgx.Conn, sql string, arguments ...any) (commandTag pgconn.CommandTag) {\n\tvar err error\n\tif commandTag, err = conn.Exec(context.Background(), sql, arguments...); err != nil {\n\t\tt.Fatalf(\"Exec unexpectedly failed with %v: %v\", sql, err)\n\t}\n\treturn commandTag\n}\n\n// Do a simple query to ensure the connection is still usable\nfunc ensureConnValid(t testing.TB, conn *pgx.Conn) {\n\tvar sum, rowCount int32\n\n\trows, err := conn.Query(context.Background(), \"select generate_series(1,$1)\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar n int32\n\t\trows.Scan(&n)\n\t\tsum += n\n\t\trowCount++\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", rows.Err())\n\t}\n\n\tif rowCount != 10 {\n\t\tt.Error(\"Select called onDataRow wrong number of times\")\n\t}\n\tif sum != 55 {\n\t\tt.Error(\"Wrong values returned\")\n\t}\n}\n\nfunc assertConfigsEqual(t *testing.T, expected, actual *pgx.ConnConfig, testName string) {\n\tif !assert.NotNil(t, expected) {\n\t\treturn\n\t}\n\tif !assert.NotNil(t, actual) {\n\t\treturn\n\t}\n\n\tassert.Equalf(t, expected.Tracer, actual.Tracer, \"%s - Tracer\", testName)\n\tassert.Equalf(t, expected.ConnString(), actual.ConnString(), \"%s - ConnString\", testName)\n\tassert.Equalf(t, expected.StatementCacheCapacity, actual.StatementCacheCapacity, \"%s - StatementCacheCapacity\", testName)\n\tassert.Equalf(t, expected.DescriptionCacheCapacity, actual.DescriptionCacheCapacity, \"%s - DescriptionCacheCapacity\", testName)\n\tassert.Equalf(t, expected.DefaultQueryExecMode, actual.DefaultQueryExecMode, \"%s - DefaultQueryExecMode\", testName)\n\tassert.Equalf(t, expected.Host, actual.Host, \"%s - Host\", testName)\n\tassert.Equalf(t, expected.Database, actual.Database, \"%s - Database\", testName)\n\tassert.Equalf(t, expected.Port, actual.Port, \"%s - Port\", testName)\n\tassert.Equalf(t, expected.User, actual.User, \"%s - User\", testName)\n\tassert.Equalf(t, expected.Password, actual.Password, \"%s - Password\", testName)\n\tassert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, \"%s - ConnectTimeout\", testName)\n\tassert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, \"%s - RuntimeParams\", testName)\n\n\t// Can't test function equality, so just test that they are set or not.\n\tassert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, \"%s - ValidateConnect\", testName)\n\tassert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, \"%s - AfterConnect\", testName)\n\n\tif assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, \"%s - TLSConfig\", testName) {\n\t\tif expected.TLSConfig != nil {\n\t\t\tassert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, \"%s - TLSConfig InsecureSkipVerify\", testName)\n\t\t\tassert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, \"%s - TLSConfig ServerName\", testName)\n\t\t}\n\t}\n\n\tif assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), \"%s - Fallbacks\", testName) {\n\t\tfor i := range expected.Fallbacks {\n\t\t\tassert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, \"%s - Fallback %d - Host\", testName, i)\n\t\t\tassert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, \"%s - Fallback %d - Port\", testName, i)\n\n\t\t\tif assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, \"%s - Fallback %d - TLSConfig\", testName, i) {\n\t\t\t\tif expected.Fallbacks[i].TLSConfig != nil {\n\t\t\t\t\tassert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, \"%s - Fallback %d - TLSConfig InsecureSkipVerify\", testName)\n\t\t\t\t\tassert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, \"%s - Fallback %d - TLSConfig ServerName\", testName)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/faultyconn/faultyconn.go",
    "content": "package faultyconn\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n)\n\n// Conn is a wrapper for a net.Conn that allows inspection and modification of messages between a PostgreSQL client and\n// server. It is designed to be used in tests that use a *pgx.Conn or *pgconn.PgConn connected to a real PostgreSQL\n// server. Instead of mocking an entire server connection, this is used to specify and modify only the particular\n// aspects of a connection that are necessary. This can be easier to setup and is more true to real world conditions.\n//\n// It currently only supports handling frontend messages.\ntype Conn struct {\n\t// HandleFrontendMessage is called for each frontend message received. It should use backendWriter to write to the\n\t// backend.\n\tHandleFrontendMessage func(backendWriter io.Writer, msg pgproto3.FrontendMessage) error\n\n\t// TODO: Implement this if we need to handle backend messages.\n\t// HandleBackendMessage  func(w io.Writer, msg pgproto3.BackendMessage) error\n\n\tconn            net.Conn\n\tfromFrontendBuf *bytes.Buffer\n\tfromBackendBuf  *bytes.Buffer\n\tbackend         *pgproto3.Backend\n\tfrontend        *pgproto3.Frontend\n}\n\n// NewConn creates a new Conn that proxies the messages sent to and from the given net.Conn. New can be used with\n// pgconn.Config.AfterNetConnect to wrap a net.Conn for testing purposes.\nfunc New(c net.Conn) *Conn {\n\tfromFrontendBuf := &bytes.Buffer{}\n\tfromBackendBuf := &bytes.Buffer{}\n\n\treturn &Conn{\n\t\tconn:            c,\n\t\tfromFrontendBuf: fromFrontendBuf,\n\t\tfromBackendBuf:  fromBackendBuf,\n\t\tbackend:         pgproto3.NewBackend(fromFrontendBuf, c),\n\t\tfrontend:        pgproto3.NewFrontend(fromBackendBuf, c),\n\t}\n}\n\nfunc (c *Conn) Read(b []byte) (n int, err error) {\n\treturn c.conn.Read(b)\n}\n\nfunc (c *Conn) Write(b []byte) (n int, err error) {\n\tif c.HandleFrontendMessage == nil {\n\t\treturn c.conn.Write(b)\n\t}\n\n\tc.fromFrontendBuf.Write(b)\n\n\tfor {\n\t\tmsg, err := c.backend.Receive()\n\t\tif err != nil {\n\t\t\tif err == io.ErrUnexpectedEOF {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn len(b), err\n\t\t}\n\n\t\terr = c.HandleFrontendMessage(c.conn, msg)\n\t\tif err != nil {\n\t\t\treturn len(b), err\n\t\t}\n\t}\n\n\treturn len(b), nil\n}\n\nfunc (c *Conn) Close() error {\n\treturn c.conn.Close()\n}\n\nfunc (c *Conn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *Conn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\nfunc (c *Conn) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *Conn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *Conn) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "internal/iobufpool/iobufpool.go",
    "content": "// Package iobufpool implements a global segregated-fit pool of buffers for IO.\n//\n// It uses *[]byte instead of []byte to avoid the sync.Pool allocation with Put. Unfortunately, using a pointer to avoid\n// an allocation is purposely not documented. https://github.com/golang/go/issues/16323\npackage iobufpool\n\nimport (\n\t\"math/bits\"\n\t\"sync\"\n)\n\nconst minPoolExpOf2 = 8\n\nvar pools [18]*sync.Pool\n\nfunc init() {\n\tfor i := range pools {\n\t\tbufLen := 1 << (minPoolExpOf2 + i)\n\t\tpools[i] = &sync.Pool{\n\t\t\tNew: func() any {\n\t\t\t\tbuf := make([]byte, bufLen)\n\t\t\t\treturn &buf\n\t\t\t},\n\t\t}\n\t}\n}\n\n// Get gets a []byte of len size with cap <= size*2.\nfunc Get(size int) *[]byte {\n\ti := getPoolIdx(size)\n\tif i >= len(pools) {\n\t\tbuf := make([]byte, size)\n\t\treturn &buf\n\t}\n\n\tptrBuf := (pools[i].Get().(*[]byte))\n\t*ptrBuf = (*ptrBuf)[:size]\n\n\treturn ptrBuf\n}\n\nfunc getPoolIdx(size int) int {\n\tif size < 2 {\n\t\treturn 0\n\t}\n\tidx := bits.Len(uint(size-1)) - minPoolExpOf2\n\tif idx < 0 {\n\t\treturn 0\n\t}\n\treturn idx\n}\n\n// Put returns buf to the pool.\nfunc Put(buf *[]byte) {\n\ti := putPoolIdx(cap(*buf))\n\tif i < 0 {\n\t\treturn\n\t}\n\n\tpools[i].Put(buf)\n}\n\nfunc putPoolIdx(size int) int {\n\t// Only exact power-of-2 sizes match pool buckets\n\tif size&(size-1) != 0 {\n\t\treturn -1\n\t}\n\n\t// Calculate log2(size) using trailing zeros count\n\texp := bits.TrailingZeros(uint(size))\n\tidx := exp - minPoolExpOf2\n\n\tif idx < 0 || idx >= len(pools) {\n\t\treturn -1\n\t}\n\n\treturn idx\n}\n"
  },
  {
    "path": "internal/iobufpool/iobufpool_internal_test.go",
    "content": "package iobufpool\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestPoolIdx(t *testing.T) {\n\ttests := []struct {\n\t\tsize     int\n\t\texpected int\n\t}{\n\t\t{size: 0, expected: 0},\n\t\t{size: 1, expected: 0},\n\t\t{size: 255, expected: 0},\n\t\t{size: 256, expected: 0},\n\t\t{size: 257, expected: 1},\n\t\t{size: 511, expected: 1},\n\t\t{size: 512, expected: 1},\n\t\t{size: 513, expected: 2},\n\t\t{size: 1023, expected: 2},\n\t\t{size: 1024, expected: 2},\n\t\t{size: 1025, expected: 3},\n\t\t{size: 2047, expected: 3},\n\t\t{size: 2048, expected: 3},\n\t\t{size: 2049, expected: 4},\n\t\t{size: 8388607, expected: 15},\n\t\t{size: 8388608, expected: 15},\n\t\t{size: 8388609, expected: 16},\n\t}\n\tfor _, tt := range tests {\n\t\tidx := getPoolIdx(tt.size)\n\t\tassert.Equalf(t, tt.expected, idx, \"size: %d\", tt.size)\n\t}\n}\n"
  },
  {
    "path": "internal/iobufpool/iobufpool_test.go",
    "content": "package iobufpool_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/internal/iobufpool\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGetCap(t *testing.T) {\n\ttests := []struct {\n\t\trequestedLen int\n\t\texpectedCap  int\n\t}{\n\t\t{requestedLen: 0, expectedCap: 256},\n\t\t{requestedLen: 128, expectedCap: 256},\n\t\t{requestedLen: 255, expectedCap: 256},\n\t\t{requestedLen: 256, expectedCap: 256},\n\t\t{requestedLen: 257, expectedCap: 512},\n\t\t{requestedLen: 511, expectedCap: 512},\n\t\t{requestedLen: 512, expectedCap: 512},\n\t\t{requestedLen: 513, expectedCap: 1024},\n\t\t{requestedLen: 1023, expectedCap: 1024},\n\t\t{requestedLen: 1024, expectedCap: 1024},\n\t\t{requestedLen: 33554431, expectedCap: 33554432},\n\t\t{requestedLen: 33554432, expectedCap: 33554432},\n\n\t\t// Above 32 MiB skip the pool and allocate exactly the requested size.\n\t\t{requestedLen: 33554433, expectedCap: 33554433},\n\t}\n\tfor _, tt := range tests {\n\t\tbuf := iobufpool.Get(tt.requestedLen)\n\t\tassert.Equalf(t, tt.requestedLen, len(*buf), \"bad len for requestedLen: %d\", len(*buf), tt.requestedLen)\n\t\tassert.Equalf(t, tt.expectedCap, cap(*buf), \"bad cap for requestedLen: %d\", tt.requestedLen)\n\t}\n}\n\nfunc TestPutHandlesWrongSizedBuffers(t *testing.T) {\n\tfor putBufSize := range []int{0, 1, 128, 250, 256, 257, 1023, 1024, 1025, 1 << 28} {\n\t\tputBuf := make([]byte, putBufSize)\n\t\tiobufpool.Put(&putBuf)\n\n\t\ttests := []struct {\n\t\t\trequestedLen int\n\t\t\texpectedCap  int\n\t\t}{\n\t\t\t{requestedLen: 0, expectedCap: 256},\n\t\t\t{requestedLen: 128, expectedCap: 256},\n\t\t\t{requestedLen: 255, expectedCap: 256},\n\t\t\t{requestedLen: 256, expectedCap: 256},\n\t\t\t{requestedLen: 257, expectedCap: 512},\n\t\t\t{requestedLen: 511, expectedCap: 512},\n\t\t\t{requestedLen: 512, expectedCap: 512},\n\t\t\t{requestedLen: 513, expectedCap: 1024},\n\t\t\t{requestedLen: 1023, expectedCap: 1024},\n\t\t\t{requestedLen: 1024, expectedCap: 1024},\n\t\t\t{requestedLen: 33554431, expectedCap: 33554432},\n\t\t\t{requestedLen: 33554432, expectedCap: 33554432},\n\n\t\t\t// Above 32 MiB skip the pool and allocate exactly the requested size.\n\t\t\t{requestedLen: 33554433, expectedCap: 33554433},\n\t\t}\n\t\tfor _, tt := range tests {\n\t\t\tgetBuf := iobufpool.Get(tt.requestedLen)\n\t\t\tassert.Equalf(t, tt.requestedLen, len(*getBuf), \"len(putBuf): %d, requestedLen: %d\", len(putBuf), tt.requestedLen)\n\t\t\tassert.Equalf(t, tt.expectedCap, cap(*getBuf), \"cap(putBuf): %d, requestedLen: %d\", cap(putBuf), tt.requestedLen)\n\t\t}\n\t}\n}\n\nfunc TestPutGetBufferReuse(t *testing.T) {\n\t// There is no way to guarantee a buffer will be reused. It should be, but a GC between the Put and the Get will cause\n\t// it not to be. So try many times.\n\tfor range 100_000 {\n\t\tbuf := iobufpool.Get(4)\n\t\t(*buf)[0] = 1\n\t\tiobufpool.Put(buf)\n\t\tbuf = iobufpool.Get(4)\n\t\tif (*buf)[0] == 1 {\n\t\t\treturn\n\t\t}\n\t}\n\n\tt.Error(\"buffer was never reused\")\n}\n"
  },
  {
    "path": "internal/pgio/README.md",
    "content": "# pgio\n\nPackage pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.\n\npgio provides functions for appending integers to a []byte while doing byte\norder conversion.\n"
  },
  {
    "path": "internal/pgio/doc.go",
    "content": "// Package pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.\n/*\npgio provides functions for appending integers to a []byte while doing byte\norder conversion.\n*/\npackage pgio\n"
  },
  {
    "path": "internal/pgio/write.go",
    "content": "package pgio\n\nfunc AppendUint16(buf []byte, n uint16) []byte {\n\treturn append(buf, byte(n>>8), byte(n))\n}\n\nfunc AppendUint32(buf []byte, n uint32) []byte {\n\treturn append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))\n}\n\nfunc AppendUint64(buf []byte, n uint64) []byte {\n\treturn append(buf,\n\t\tbyte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32),\n\t\tbyte(n>>24), byte(n>>16), byte(n>>8), byte(n),\n\t)\n}\n\nfunc AppendInt16(buf []byte, n int16) []byte {\n\treturn AppendUint16(buf, uint16(n))\n}\n\nfunc AppendInt32(buf []byte, n int32) []byte {\n\treturn AppendUint32(buf, uint32(n))\n}\n\nfunc AppendInt64(buf []byte, n int64) []byte {\n\treturn AppendUint64(buf, uint64(n))\n}\n\nfunc SetInt32(buf []byte, n int32) {\n\t*(*[4]byte)(buf) = [4]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}\n}\n"
  },
  {
    "path": "internal/pgio/write_test.go",
    "content": "package pgio\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestAppendUint16NilBuf(t *testing.T) {\n\tbuf := AppendUint16(nil, 1)\n\tif !reflect.DeepEqual(buf, []byte{0, 1}) {\n\t\tt.Errorf(\"AppendUint16(nil, 1) => %v, want %v\", buf, []byte{0, 1})\n\t}\n}\n\nfunc TestAppendUint16EmptyBuf(t *testing.T) {\n\tbuf := []byte{}\n\tbuf = AppendUint16(buf, 1)\n\tif !reflect.DeepEqual(buf, []byte{0, 1}) {\n\t\tt.Errorf(\"AppendUint16(nil, 1) => %v, want %v\", buf, []byte{0, 1})\n\t}\n}\n\nfunc TestAppendUint16BufWithCapacityDoesNotAllocate(t *testing.T) {\n\tbuf := make([]byte, 0, 4)\n\tAppendUint16(buf, 1)\n\tbuf = buf[0:2]\n\tif !reflect.DeepEqual(buf, []byte{0, 1}) {\n\t\tt.Errorf(\"AppendUint16(nil, 1) => %v, want %v\", buf, []byte{0, 1})\n\t}\n}\n\nfunc TestAppendUint32NilBuf(t *testing.T) {\n\tbuf := AppendUint32(nil, 1)\n\tif !reflect.DeepEqual(buf, []byte{0, 0, 0, 1}) {\n\t\tt.Errorf(\"AppendUint32(nil, 1) => %v, want %v\", buf, []byte{0, 0, 0, 1})\n\t}\n}\n\nfunc TestAppendUint32EmptyBuf(t *testing.T) {\n\tbuf := []byte{}\n\tbuf = AppendUint32(buf, 1)\n\tif !reflect.DeepEqual(buf, []byte{0, 0, 0, 1}) {\n\t\tt.Errorf(\"AppendUint32(nil, 1) => %v, want %v\", buf, []byte{0, 0, 0, 1})\n\t}\n}\n\nfunc TestAppendUint32BufWithCapacityDoesNotAllocate(t *testing.T) {\n\tbuf := make([]byte, 0, 4)\n\tAppendUint32(buf, 1)\n\tbuf = buf[0:4]\n\tif !reflect.DeepEqual(buf, []byte{0, 0, 0, 1}) {\n\t\tt.Errorf(\"AppendUint32(nil, 1) => %v, want %v\", buf, []byte{0, 0, 0, 1})\n\t}\n}\n\nfunc TestAppendUint64NilBuf(t *testing.T) {\n\tbuf := AppendUint64(nil, 1)\n\tif !reflect.DeepEqual(buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) {\n\t\tt.Errorf(\"AppendUint64(nil, 1) => %v, want %v\", buf, []byte{0, 0, 0, 0, 0, 0, 0, 1})\n\t}\n}\n\nfunc TestAppendUint64EmptyBuf(t *testing.T) {\n\tbuf := []byte{}\n\tbuf = AppendUint64(buf, 1)\n\tif !reflect.DeepEqual(buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) {\n\t\tt.Errorf(\"AppendUint64(nil, 1) => %v, want %v\", buf, []byte{0, 0, 0, 0, 0, 0, 0, 1})\n\t}\n}\n\nfunc TestAppendUint64BufWithCapacityDoesNotAllocate(t *testing.T) {\n\tbuf := make([]byte, 0, 8)\n\tAppendUint64(buf, 1)\n\tbuf = buf[0:8]\n\tif !reflect.DeepEqual(buf, []byte{0, 0, 0, 0, 0, 0, 0, 1}) {\n\t\tt.Errorf(\"AppendUint64(nil, 1) => %v, want %v\", buf, []byte{0, 0, 0, 0, 0, 0, 0, 1})\n\t}\n}\n"
  },
  {
    "path": "internal/pgmock/pgmock.go",
    "content": "// Package pgmock provides the ability to mock a PostgreSQL server.\npackage pgmock\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n)\n\ntype Step interface {\n\tStep(*pgproto3.Backend) error\n}\n\ntype Script struct {\n\tSteps []Step\n}\n\nfunc (s *Script) Run(backend *pgproto3.Backend) error {\n\tfor _, step := range s.Steps {\n\t\terr := step.Step(backend)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (s *Script) Step(backend *pgproto3.Backend) error {\n\treturn s.Run(backend)\n}\n\ntype expectMessageStep struct {\n\twant pgproto3.FrontendMessage\n\tany  bool\n}\n\nfunc (e *expectMessageStep) Step(backend *pgproto3.Backend) error {\n\tmsg, err := backend.Receive()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif e.any && reflect.TypeOf(msg) == reflect.TypeOf(e.want) {\n\t\treturn nil\n\t}\n\n\tif !reflect.DeepEqual(msg, e.want) {\n\t\treturn fmt.Errorf(\"msg => %#v, e.want => %#v\", msg, e.want)\n\t}\n\n\treturn nil\n}\n\ntype expectStartupMessageStep struct {\n\twant *pgproto3.StartupMessage\n\tany  bool\n}\n\nfunc (e *expectStartupMessageStep) Step(backend *pgproto3.Backend) error {\n\tmsg, err := backend.ReceiveStartupMessage()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif e.any {\n\t\treturn nil\n\t}\n\n\tif !reflect.DeepEqual(msg, e.want) {\n\t\treturn fmt.Errorf(\"msg => %#v, e.want => %#v\", msg, e.want)\n\t}\n\n\treturn nil\n}\n\nfunc ExpectMessage(want pgproto3.FrontendMessage) Step {\n\treturn expectMessage(want, false)\n}\n\nfunc ExpectAnyMessage(want pgproto3.FrontendMessage) Step {\n\treturn expectMessage(want, true)\n}\n\nfunc expectMessage(want pgproto3.FrontendMessage, any bool) Step {\n\tif want, ok := want.(*pgproto3.StartupMessage); ok {\n\t\treturn &expectStartupMessageStep{want: want, any: any}\n\t}\n\n\treturn &expectMessageStep{want: want, any: any}\n}\n\ntype sendMessageStep struct {\n\tmsg pgproto3.BackendMessage\n}\n\nfunc (e *sendMessageStep) Step(backend *pgproto3.Backend) error {\n\tbackend.Send(e.msg)\n\treturn backend.Flush()\n}\n\nfunc SendMessage(msg pgproto3.BackendMessage) Step {\n\treturn &sendMessageStep{msg: msg}\n}\n\ntype waitForCloseMessageStep struct{}\n\nfunc (e *waitForCloseMessageStep) Step(backend *pgproto3.Backend) error {\n\tfor {\n\t\tmsg, err := backend.Receive()\n\t\tif err == io.EOF {\n\t\t\treturn nil\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif _, ok := msg.(*pgproto3.Terminate); ok {\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc WaitForClose() Step {\n\treturn &waitForCloseMessageStep{}\n}\n\nfunc AcceptUnauthenticatedConnRequestSteps() []Step {\n\treturn []Step{\n\t\tExpectAnyMessage(&pgproto3.StartupMessage{ProtocolVersion: pgproto3.ProtocolVersion30, Parameters: map[string]string{}}),\n\t\tSendMessage(&pgproto3.AuthenticationOk{}),\n\t\tSendMessage(&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: []byte{0, 0, 0, 0}}),\n\t\tSendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}),\n\t}\n}\n"
  },
  {
    "path": "internal/pgmock/pgmock_test.go",
    "content": "package pgmock_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgmock\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestScript(t *testing.T) {\n\tscript := &pgmock.Script{\n\t\tSteps: pgmock.AcceptUnauthenticatedConnRequestSteps(),\n\t}\n\tscript.Steps = append(script.Steps, pgmock.ExpectMessage(&pgproto3.Query{String: \"select 42\"}))\n\tscript.Steps = append(script.Steps, pgmock.SendMessage(&pgproto3.RowDescription{\n\t\tFields: []pgproto3.FieldDescription{\n\t\t\t{\n\t\t\t\tName:                 []byte(\"?column?\"),\n\t\t\t\tTableOID:             0,\n\t\t\t\tTableAttributeNumber: 0,\n\t\t\t\tDataTypeOID:          23,\n\t\t\t\tDataTypeSize:         4,\n\t\t\t\tTypeModifier:         -1,\n\t\t\t\tFormat:               0,\n\t\t\t},\n\t\t},\n\t}))\n\tscript.Steps = append(script.Steps, pgmock.SendMessage(&pgproto3.DataRow{\n\t\tValues: [][]byte{[]byte(\"42\")},\n\t}))\n\tscript.Steps = append(script.Steps, pgmock.SendMessage(&pgproto3.CommandComplete{CommandTag: []byte(\"SELECT 1\")}))\n\tscript.Steps = append(script.Steps, pgmock.SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}))\n\tscript.Steps = append(script.Steps, pgmock.ExpectMessage(&pgproto3.Terminate{}))\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\trequire.NoError(t, err)\n\tdefer ln.Close()\n\n\tserverErrChan := make(chan error, 1)\n\tgo func() {\n\t\tdefer close(serverErrChan)\n\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t\tdefer conn.Close()\n\n\t\terr = conn.SetDeadline(time.Now().Add(time.Second))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\n\t\terr = script.Run(pgproto3.NewBackend(conn, conn))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t}()\n\n\thost, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s\", host, port)\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\tdefer cancel()\n\tpgConn, err := pgconn.Connect(ctx, connStr)\n\trequire.NoError(t, err)\n\tresults, err := pgConn.Exec(ctx, \"select 42\").ReadAll()\n\tassert.NoError(t, err)\n\n\tassert.Len(t, results, 1)\n\tassert.Nil(t, results[0].Err)\n\tassert.Equal(t, \"SELECT 1\", results[0].CommandTag.String())\n\tassert.Len(t, results[0].Rows, 1)\n\tassert.Equal(t, \"42\", string(results[0].Rows[0][0]))\n\n\tpgConn.Close(ctx)\n\n\tassert.NoError(t, <-serverErrChan)\n}\n"
  },
  {
    "path": "internal/sanitize/benchmark.sh",
    "content": "#!/usr/bin/env bash\n\ncurrent_branch=$(git rev-parse --abbrev-ref HEAD)\nif [ \"$current_branch\" == \"HEAD\" ]; then\n    current_branch=$(git rev-parse HEAD)\nfi\n\nrestore_branch() {\n    echo \"Restoring original branch/commit: $current_branch\"\n    git checkout \"$current_branch\"\n}\ntrap restore_branch EXIT\n\n# Check if there are uncommitted changes\nif ! git diff --quiet || ! git diff --cached --quiet; then\n    echo \"There are uncommitted changes. Please commit or stash them before running this script.\"\n    exit 1\nfi\n\n# Ensure that at least one commit argument is passed\nif [ \"$#\" -lt 1 ]; then\n    echo \"Usage: $0 <commit1> <commit2> ... <commitN>\"\n    exit 1\nfi\n\ncommits=(\"$@\")\nbenchmarks_dir=benchmarks\n\nif ! mkdir -p \"${benchmarks_dir}\"; then\n    echo \"Unable to create dir for benchmarks data\"\n    exit 1\nfi\n\n# Benchmark results\nbench_files=()\n\n# Run benchmark for each listed commit\nfor i in \"${!commits[@]}\"; do\n    commit=\"${commits[i]}\"\n    git checkout \"$commit\" || {\n        echo \"Failed to checkout $commit\"\n        exit 1\n    }\n\n    # Sanitized commit message\n    commit_message=$(git log -1 --pretty=format:\"%s\" | tr -c '[:alnum:]-_' '_')\n\n    # Benchmark data will go there\n    bench_file=\"${benchmarks_dir}/${i}_${commit_message}.bench\"\n\n    if ! go test -bench=. -count=10 >\"$bench_file\"; then\n        echo \"Benchmarking failed for commit $commit\"\n        exit 1\n    fi\n\n    bench_files+=(\"$bench_file\")\ndone\n\n# go install golang.org/x/perf/cmd/benchstat[@latest]\nbenchstat \"${bench_files[@]}\"\n"
  },
  {
    "path": "internal/sanitize/sanitize.go",
    "content": "package sanitize\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\n// Part is either a string or an int. A string is raw SQL. An int is a\n// argument placeholder.\ntype Part any\n\ntype Query struct {\n\tParts []Part\n}\n\n// utf.DecodeRune returns the utf8.RuneError for errors. But that is actually rune U+FFFD -- the unicode replacement\n// character. utf8.RuneError is not an error if it is also width 3.\n//\n// https://github.com/jackc/pgx/issues/1380\nconst replacementcharacterwidth = 3\n\nconst maxBufSize = 16384 // 16 Ki\n\nvar bufPool = &pool[*bytes.Buffer]{\n\tnew: func() *bytes.Buffer {\n\t\treturn &bytes.Buffer{}\n\t},\n\treset: func(b *bytes.Buffer) bool {\n\t\tn := b.Len()\n\t\tb.Reset()\n\t\treturn n < maxBufSize\n\t},\n}\n\nvar null = []byte(\"null\")\n\nfunc (q *Query) Sanitize(args ...any) (string, error) {\n\targUse := make([]bool, len(args))\n\tbuf := bufPool.get()\n\tdefer bufPool.put(buf)\n\n\tfor _, part := range q.Parts {\n\t\tswitch part := part.(type) {\n\t\tcase string:\n\t\t\tbuf.WriteString(part)\n\t\tcase int:\n\t\t\targIdx := part - 1\n\t\t\tvar p []byte\n\t\t\tif argIdx < 0 {\n\t\t\t\treturn \"\", fmt.Errorf(\"first sql argument must be > 0\")\n\t\t\t}\n\n\t\t\tif argIdx >= len(args) {\n\t\t\t\treturn \"\", fmt.Errorf(\"insufficient arguments\")\n\t\t\t}\n\n\t\t\t// Prevent SQL injection via Line Comment Creation\n\t\t\t// https://github.com/jackc/pgx/security/advisories/GHSA-m7wr-2xf7-cm9p\n\t\t\tbuf.WriteByte(' ')\n\n\t\t\targ := args[argIdx]\n\t\t\tswitch arg := arg.(type) {\n\t\t\tcase nil:\n\t\t\t\tp = null\n\t\t\tcase int64:\n\t\t\t\tp = strconv.AppendInt(buf.AvailableBuffer(), arg, 10)\n\t\t\tcase float64:\n\t\t\t\tp = strconv.AppendFloat(buf.AvailableBuffer(), arg, 'f', -1, 64)\n\t\t\tcase bool:\n\t\t\t\tp = strconv.AppendBool(buf.AvailableBuffer(), arg)\n\t\t\tcase []byte:\n\t\t\t\tp = QuoteBytes(buf.AvailableBuffer(), arg)\n\t\t\tcase string:\n\t\t\t\tp = QuoteString(buf.AvailableBuffer(), arg)\n\t\t\tcase time.Time:\n\t\t\t\tp = arg.Truncate(time.Microsecond).\n\t\t\t\t\tAppendFormat(buf.AvailableBuffer(), \"'2006-01-02 15:04:05.999999999Z07:00:00'\")\n\t\t\tdefault:\n\t\t\t\treturn \"\", fmt.Errorf(\"invalid arg type: %T\", arg)\n\t\t\t}\n\t\t\targUse[argIdx] = true\n\n\t\t\tbuf.Write(p)\n\n\t\t\t// Prevent SQL injection via Line Comment Creation\n\t\t\t// https://github.com/jackc/pgx/security/advisories/GHSA-m7wr-2xf7-cm9p\n\t\t\tbuf.WriteByte(' ')\n\t\tdefault:\n\t\t\treturn \"\", fmt.Errorf(\"invalid Part type: %T\", part)\n\t\t}\n\t}\n\n\tfor i, used := range argUse {\n\t\tif !used {\n\t\t\treturn \"\", fmt.Errorf(\"unused argument: %d\", i)\n\t\t}\n\t}\n\treturn buf.String(), nil\n}\n\nfunc NewQuery(sql string) (*Query, error) {\n\tquery := &Query{}\n\tquery.init(sql)\n\n\treturn query, nil\n}\n\nvar sqlLexerPool = &pool[*sqlLexer]{\n\tnew: func() *sqlLexer {\n\t\treturn &sqlLexer{}\n\t},\n\treset: func(sl *sqlLexer) bool {\n\t\t*sl = sqlLexer{}\n\t\treturn true\n\t},\n}\n\nfunc (q *Query) init(sql string) {\n\tparts := q.Parts[:0]\n\tif parts == nil {\n\t\t// dirty, but fast heuristic to preallocate for ~90% usecases\n\t\tn := strings.Count(sql, \"$\") + strings.Count(sql, \"--\") + 1\n\t\tparts = make([]Part, 0, n)\n\t}\n\n\tl := sqlLexerPool.get()\n\tdefer sqlLexerPool.put(l)\n\n\tl.src = sql\n\tl.stateFn = rawState\n\tl.parts = parts\n\n\tfor l.stateFn != nil {\n\t\tl.stateFn = l.stateFn(l)\n\t}\n\n\tq.Parts = l.parts\n}\n\nfunc QuoteString(dst []byte, str string) []byte {\n\tconst quote = '\\''\n\n\t// Preallocate space for the worst case scenario\n\tdst = slices.Grow(dst, len(str)*2+2)\n\n\t// Add opening quote\n\tdst = append(dst, quote)\n\n\t// Iterate through the string without allocating\n\tfor i := 0; i < len(str); i++ {\n\t\tif str[i] == quote {\n\t\t\tdst = append(dst, quote, quote)\n\t\t} else {\n\t\t\tdst = append(dst, str[i])\n\t\t}\n\t}\n\n\t// Add closing quote\n\tdst = append(dst, quote)\n\n\treturn dst\n}\n\nfunc QuoteBytes(dst, buf []byte) []byte {\n\tif len(buf) == 0 {\n\t\treturn append(dst, `'\\x'`...)\n\t}\n\n\t// Calculate required length\n\trequiredLen := 3 + hex.EncodedLen(len(buf)) + 1\n\n\t// Ensure dst has enough capacity\n\tif cap(dst)-len(dst) < requiredLen {\n\t\tnewDst := make([]byte, len(dst), len(dst)+requiredLen)\n\t\tcopy(newDst, dst)\n\t\tdst = newDst\n\t}\n\n\t// Record original length and extend slice\n\torigLen := len(dst)\n\tdst = dst[:origLen+requiredLen]\n\n\t// Add prefix\n\tdst[origLen] = '\\''\n\tdst[origLen+1] = '\\\\'\n\tdst[origLen+2] = 'x'\n\n\t// Encode bytes directly into dst\n\thex.Encode(dst[origLen+3:len(dst)-1], buf)\n\n\t// Add suffix\n\tdst[len(dst)-1] = '\\''\n\n\treturn dst\n}\n\ntype sqlLexer struct {\n\tsrc     string\n\tstart   int\n\tpos     int\n\tnested  int // multiline comment nesting level.\n\tstateFn stateFn\n\tparts   []Part\n}\n\ntype stateFn func(*sqlLexer) stateFn\n\nfunc rawState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase 'e', 'E':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '\\'' {\n\t\t\t\tl.pos += width\n\t\t\t\treturn escapeStringState\n\t\t\t}\n\t\tcase '\\'':\n\t\t\treturn singleQuoteState\n\t\tcase '\"':\n\t\t\treturn doubleQuoteState\n\t\tcase '$':\n\t\t\tnextRune, _ := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif '0' <= nextRune && nextRune <= '9' {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos-width])\n\t\t\t\t}\n\t\t\t\tl.start = l.pos\n\t\t\t\treturn placeholderState\n\t\t\t}\n\t\tcase '-':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '-' {\n\t\t\t\tl.pos += width\n\t\t\t\treturn oneLineCommentState\n\t\t\t}\n\t\tcase '/':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '*' {\n\t\t\t\tl.pos += width\n\t\t\t\treturn multilineCommentState\n\t\t\t}\n\t\tcase utf8.RuneError:\n\t\t\tif width != replacementcharacterwidth {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\t\tl.start = l.pos\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc singleQuoteState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\\'':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '\\'' {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.pos += width\n\t\tcase utf8.RuneError:\n\t\t\tif width != replacementcharacterwidth {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\t\tl.start = l.pos\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc doubleQuoteState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\"':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '\"' {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.pos += width\n\t\tcase utf8.RuneError:\n\t\t\tif width != replacementcharacterwidth {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\t\tl.start = l.pos\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\n// placeholderState consumes a placeholder value. The $ must have already has\n// already been consumed. The first rune must be a digit.\nfunc placeholderState(l *sqlLexer) stateFn {\n\tnum := 0\n\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tif '0' <= r && r <= '9' {\n\t\t\tnum *= 10\n\t\t\tnum += int(r - '0')\n\t\t} else {\n\t\t\tl.parts = append(l.parts, num)\n\t\t\tl.pos -= width\n\t\t\tl.start = l.pos\n\t\t\treturn rawState\n\t\t}\n\t}\n}\n\nfunc escapeStringState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\t_, width = utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tl.pos += width\n\t\tcase '\\'':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '\\'' {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.pos += width\n\t\tcase utf8.RuneError:\n\t\t\tif width != replacementcharacterwidth {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\t\tl.start = l.pos\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc oneLineCommentState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\t_, width = utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tl.pos += width\n\t\tcase '\\n', '\\r':\n\t\t\treturn rawState\n\t\tcase utf8.RuneError:\n\t\t\tif width != replacementcharacterwidth {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\t\tl.start = l.pos\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc multilineCommentState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '/':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '*' {\n\t\t\t\tl.pos += width\n\t\t\t\tl.nested++\n\t\t\t}\n\t\tcase '*':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '/' {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tl.pos += width\n\t\t\tif l.nested == 0 {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.nested--\n\n\t\tcase utf8.RuneError:\n\t\t\tif width != replacementcharacterwidth {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\t\tl.start = l.pos\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar queryPool = &pool[*Query]{\n\tnew: func() *Query {\n\t\treturn &Query{}\n\t},\n\treset: func(q *Query) bool {\n\t\tn := len(q.Parts)\n\t\tq.Parts = q.Parts[:0]\n\t\treturn n < 64 // drop too large queries\n\t},\n}\n\n// SanitizeSQL replaces placeholder values with args. It quotes and escapes args\n// as necessary. This function is only safe when standard_conforming_strings is\n// on.\nfunc SanitizeSQL(sql string, args ...any) (string, error) {\n\tquery := queryPool.get()\n\tquery.init(sql)\n\tdefer queryPool.put(query)\n\n\treturn query.Sanitize(args...)\n}\n\ntype pool[E any] struct {\n\tp     sync.Pool\n\tnew   func() E\n\treset func(E) bool\n}\n\nfunc (pool *pool[E]) get() E {\n\tv, ok := pool.p.Get().(E)\n\tif !ok {\n\t\tv = pool.new()\n\t}\n\n\treturn v\n}\n\nfunc (p *pool[E]) put(v E) {\n\tif p.reset(v) {\n\t\tp.p.Put(v)\n\t}\n}\n"
  },
  {
    "path": "internal/sanitize/sanitize_bench_test.go",
    "content": "// sanitize_benchmark_test.go\npackage sanitize_test\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/sanitize\"\n)\n\nvar benchmarkSanitizeResult string\n\nconst benchmarkQuery = \"\" +\n\t`SELECT * \n   FROM \"water_containers\" \n   WHERE NOT \"id\" = $1\t\t\t-- int64\n   AND \"tags\" NOT IN $2         -- nil\n   AND \"volume\" > $3            -- float64\n   AND \"transportable\" = $4     -- bool\n   AND position($5 IN \"sign\")   -- bytes\n   AND \"label\" LIKE $6          -- string\n   AND \"created_at\" > $7;       -- time.Time`\n\nvar benchmarkArgs = []any{\n\tint64(12345),\n\tnil,\n\tfloat64(500),\n\ttrue,\n\t[]byte(\"8BADF00D\"),\n\t\"kombucha's han'dy awokowa\",\n\ttime.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC),\n}\n\nfunc BenchmarkSanitize(b *testing.B) {\n\tquery, err := sanitize.NewQuery(benchmarkQuery)\n\tif err != nil {\n\t\tb.Fatalf(\"failed to create query: %v\", err)\n\t}\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tbenchmarkSanitizeResult, err = query.Sanitize(benchmarkArgs...)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"failed to sanitize query: %v\", err)\n\t\t}\n\t}\n}\n\nvar benchmarkNewSQLResult string\n\nfunc BenchmarkSanitizeSQL(b *testing.B) {\n\tb.ReportAllocs()\n\tvar err error\n\tfor b.Loop() {\n\t\tbenchmarkNewSQLResult, err = sanitize.SanitizeSQL(benchmarkQuery, benchmarkArgs...)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"failed to sanitize SQL: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/sanitize/sanitize_fuzz_test.go",
    "content": "package sanitize_test\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/internal/sanitize\"\n)\n\nfunc FuzzQuoteString(f *testing.F) {\n\tconst prefix = \"prefix\"\n\tf.Add(\"new\\nline\")\n\tf.Add(\"sample text\")\n\tf.Add(\"sample q'u'o't'e's\")\n\tf.Add(\"select 'quoted $42', $1\")\n\n\tf.Fuzz(func(t *testing.T, input string) {\n\t\tgot := string(sanitize.QuoteString([]byte(prefix), input))\n\t\twant := oldQuoteString(input)\n\n\t\tquoted, ok := strings.CutPrefix(got, prefix)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"result has no prefix\")\n\t\t}\n\n\t\tif want != quoted {\n\t\t\tt.Errorf(\"got  %q\", got)\n\t\t\tt.Fatalf(\"want %q\", want)\n\t\t}\n\t})\n}\n\nfunc FuzzQuoteBytes(f *testing.F) {\n\tconst prefix = \"prefix\"\n\tf.Add([]byte(nil))\n\tf.Add([]byte(\"\\n\"))\n\tf.Add([]byte(\"sample text\"))\n\tf.Add([]byte(\"sample q'u'o't'e's\"))\n\tf.Add([]byte(\"select 'quoted $42', $1\"))\n\n\tf.Fuzz(func(t *testing.T, input []byte) {\n\t\tgot := string(sanitize.QuoteBytes([]byte(prefix), input))\n\t\twant := oldQuoteBytes(input)\n\n\t\tquoted, ok := strings.CutPrefix(got, prefix)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"result has no prefix\")\n\t\t}\n\n\t\tif want != quoted {\n\t\t\tt.Errorf(\"got  %q\", got)\n\t\t\tt.Fatalf(\"want %q\", want)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/sanitize/sanitize_test.go",
    "content": "package sanitize_test\n\nimport (\n\t\"encoding/hex\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/sanitize\"\n)\n\nfunc TestNewQuery(t *testing.T) {\n\tsuccessTests := []struct {\n\t\tsql      string\n\t\texpected sanitize.Query\n\t}{\n\t\t{\n\t\t\tsql:      \"select 42\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 42\"}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select $1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select 'quoted $42', $1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 'quoted $42', \", 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select \"doubled quoted $42\", $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select \"doubled quoted $42\", `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select 'foo''bar', $1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 'foo''bar', \", 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select \"foo\"\"bar\", $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select \"foo\"\"bar\", `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select '''', $1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select '''', \", 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select \"\"\"\", $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select \"\"\"\", `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select \", 1, \", \", 2, \", \", 3, \", \", 4, \", \", 5, \", \", 6, \", \", 7, \", \", 8, \", \", 9, \", \", 10, \", \", 11}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select \"adsf\"\"$1\"\"adsf\", $1, 'foo''$$12bar', $2, '$3'`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select \"adsf\"\"$1\"\"adsf\", `, 1, `, 'foo''$$12bar', `, 2, `, '$3'`}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select E'escape string\\' $42', $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select E'escape string\\' $42', `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select e'escape string\\' $42', $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select e'escape string\\' $42', `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select /* a baby's toy */ 'barbie', $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select /* a baby's toy */ 'barbie', `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select /* *_* */ $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select /* *_* */ `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      `select 42 /* /* /* 42 */ */ */, $1`,\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{`select 42 /* /* /* 42 */ */ */, `, 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select -- a baby's toy\\n'barbie', $1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select -- a baby's toy\\n'barbie', \", 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select 42 -- is a Deep Thought's favorite number\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 42 -- is a Deep Thought's favorite number\"}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select 42, -- \\\\nis a Deep Thought's favorite number\\n$1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 42, -- \\\\nis a Deep Thought's favorite number\\n\", 1}},\n\t\t},\n\t\t{\n\t\t\tsql:      \"select 42, -- \\\\nis a Deep Thought's favorite number\\r$1\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 42, -- \\\\nis a Deep Thought's favorite number\\r\", 1}},\n\t\t},\n\t\t{\n\t\t\t// https://github.com/jackc/pgx/issues/1380\n\t\t\tsql:      \"select 'hello w�rld'\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 'hello w�rld'\"}},\n\t\t},\n\t\t{\n\t\t\t// Unterminated quoted string\n\t\t\tsql:      \"select 'hello world\",\n\t\t\texpected: sanitize.Query{Parts: []sanitize.Part{\"select 'hello world\"}},\n\t\t},\n\t}\n\n\tfor i, tt := range successTests {\n\t\tquery, err := sanitize.NewQuery(tt.sql)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d. %v\", i, err)\n\t\t}\n\n\t\tif len(query.Parts) == len(tt.expected.Parts) {\n\t\t\tfor j := range query.Parts {\n\t\t\t\tif query.Parts[j] != tt.expected.Parts[j] {\n\t\t\t\t\tt.Errorf(\"%d. expected part %d to be %v but it was %v\", i, j, tt.expected.Parts[j], query.Parts[j])\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tt.Errorf(\"%d. expected query parts to be %v but it was %v\", i, tt.expected.Parts, query.Parts)\n\t\t}\n\t}\n}\n\nfunc TestQuerySanitize(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tquery    sanitize.Query\n\t\targs     []any\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select 42\"}},\n\t\t\targs:     []any{},\n\t\t\texpected: `select 42`,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{int64(42)},\n\t\t\texpected: `select  42 `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{float64(1.23)},\n\t\t\texpected: `select  1.23 `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{true},\n\t\t\texpected: `select  true `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{[]byte{0, 1, 2, 3, 255}},\n\t\t\texpected: `select  '\\x00010203ff' `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{nil},\n\t\t\texpected: `select  null `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{\"foobar\"},\n\t\t\texpected: `select  'foobar' `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{\"foo'bar\"},\n\t\t\texpected: `select  'foo''bar' `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{`foo\\'bar`},\n\t\t\texpected: `select  'foo\\''bar' `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"insert \", 1}},\n\t\t\targs:     []any{time.Date(2020, time.March, 1, 23, 59, 59, 999999999, time.UTC)},\n\t\t\texpected: `insert  '2020-03-01 23:59:59.999999Z' `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select 1-\", 1}},\n\t\t\targs:     []any{int64(-1)},\n\t\t\texpected: `select 1- -1 `,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select 1-\", 1}},\n\t\t\targs:     []any{float64(-1)},\n\t\t\texpected: `select 1- -1 `,\n\t\t},\n\t}\n\n\tfor i, tt := range successfulTests {\n\t\tactual, err := tt.query.Sanitize(tt.args...)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d. %v\", i, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif tt.expected != actual {\n\t\t\tt.Errorf(\"%d. expected %s, but got %s\", i, tt.expected, actual)\n\t\t}\n\t}\n\n\terrorTests := []struct {\n\t\tquery    sanitize.Query\n\t\targs     []any\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1, \", \", 2}},\n\t\t\targs:     []any{int64(42)},\n\t\t\texpected: `insufficient arguments`,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select 'foo'\"}},\n\t\t\targs:     []any{int64(42)},\n\t\t\texpected: `unused argument: 0`,\n\t\t},\n\t\t{\n\t\t\tquery:    sanitize.Query{Parts: []sanitize.Part{\"select \", 1}},\n\t\t\targs:     []any{42},\n\t\t\texpected: `invalid arg type: int`,\n\t\t},\n\t}\n\n\tfor i, tt := range errorTests {\n\t\t_, err := tt.query.Sanitize(tt.args...)\n\t\tif err == nil || err.Error() != tt.expected {\n\t\t\tt.Errorf(\"%d. expected error %v, got %v\", i, tt.expected, err)\n\t\t}\n\t}\n}\n\nfunc TestQuoteString(t *testing.T) {\n\ttc := func(name, input string) {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := string(sanitize.QuoteString(nil, input))\n\t\t\twant := oldQuoteString(input)\n\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"got:  %s\", got)\n\t\t\t\tt.Fatalf(\"want: %s\", want)\n\t\t\t}\n\t\t})\n\t}\n\n\ttc(\"empty\", \"\")\n\ttc(\"text\", \"abcd\")\n\ttc(\"with quotes\", `one's hat is always a cat`)\n}\n\n// This function was used before optimizations.\n// You should keep for testing purposes - we want to ensure there are no breaking changes.\nfunc oldQuoteString(str string) string {\n\treturn \"'\" + strings.ReplaceAll(str, \"'\", \"''\") + \"'\"\n}\n\nfunc TestQuoteBytes(t *testing.T) {\n\ttc := func(name string, input []byte) {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := string(sanitize.QuoteBytes(nil, input))\n\t\t\twant := oldQuoteBytes(input)\n\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"got:  %s\", got)\n\t\t\t\tt.Fatalf(\"want: %s\", want)\n\t\t\t}\n\t\t})\n\t}\n\n\ttc(\"nil\", nil)\n\ttc(\"empty\", []byte{})\n\ttc(\"text\", []byte(\"abcd\"))\n}\n\n// This function was used before optimizations.\n// You should keep for testing purposes - we want to ensure there are no breaking changes.\nfunc oldQuoteBytes(buf []byte) string {\n\treturn `'\\x` + hex.EncodeToString(buf) + \"'\"\n}\n"
  },
  {
    "path": "internal/stmtcache/lru_cache.go",
    "content": "package stmtcache\n\nimport (\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// lruNode is a typed doubly-linked list node with freelist support.\ntype lruNode struct {\n\tsd   *pgconn.StatementDescription\n\tprev *lruNode\n\tnext *lruNode\n}\n\n// LRUCache implements Cache with a Least Recently Used (LRU) cache.\ntype LRUCache struct {\n\tm    map[string]*lruNode\n\thead *lruNode\n\n\ttail     *lruNode\n\tlen      int\n\tcap      int\n\tfreelist *lruNode\n\n\tinvalidStmts []*pgconn.StatementDescription\n\tinvalidSet   map[string]struct{}\n}\n\n// NewLRUCache creates a new LRUCache. cap is the maximum size of the cache.\nfunc NewLRUCache(cap int) *LRUCache {\n\thead := &lruNode{}\n\ttail := &lruNode{}\n\thead.next = tail\n\ttail.prev = head\n\n\treturn &LRUCache{\n\t\tcap:        cap,\n\t\tm:          make(map[string]*lruNode, cap),\n\t\thead:       head,\n\t\ttail:       tail,\n\t\tinvalidSet: make(map[string]struct{}),\n\t}\n}\n\n// Get returns the statement description for sql. Returns nil if not found.\nfunc (c *LRUCache) Get(key string) *pgconn.StatementDescription {\n\tnode, ok := c.m[key]\n\tif !ok {\n\t\treturn nil\n\t}\n\tc.moveToFront(node)\n\treturn node.sd\n}\n\n// Put stores sd in the cache. Put panics if sd.SQL is \"\". Put does nothing if sd.SQL already exists in the cache or\n// sd.SQL has been invalidated and HandleInvalidated has not been called yet.\nfunc (c *LRUCache) Put(sd *pgconn.StatementDescription) {\n\tif sd.SQL == \"\" {\n\t\tpanic(\"cannot store statement description with empty SQL\")\n\t}\n\n\tif _, present := c.m[sd.SQL]; present {\n\t\treturn\n\t}\n\n\t// The statement may have been invalidated but not yet handled. Do not readd it to the cache.\n\tif _, invalidated := c.invalidSet[sd.SQL]; invalidated {\n\t\treturn\n\t}\n\n\tif c.len == c.cap {\n\t\tc.invalidateOldest()\n\t}\n\n\tnode := c.allocNode()\n\tnode.sd = sd\n\tc.insertAfter(c.head, node)\n\tc.m[sd.SQL] = node\n\tc.len++\n}\n\n// Invalidate invalidates statement description identified by sql. Does nothing if not found.\nfunc (c *LRUCache) Invalidate(sql string) {\n\tnode, ok := c.m[sql]\n\tif !ok {\n\t\treturn\n\t}\n\tdelete(c.m, sql)\n\tc.invalidStmts = append(c.invalidStmts, node.sd)\n\tc.invalidSet[sql] = struct{}{}\n\tc.unlink(node)\n\tc.len--\n\tc.freeNode(node)\n}\n\n// InvalidateAll invalidates all statement descriptions.\nfunc (c *LRUCache) InvalidateAll() {\n\tfor node := c.head.next; node != c.tail; {\n\t\tnext := node.next\n\t\tc.invalidStmts = append(c.invalidStmts, node.sd)\n\t\tc.invalidSet[node.sd.SQL] = struct{}{}\n\t\tc.freeNode(node)\n\t\tnode = next\n\t}\n\n\tclear(c.m)\n\tc.head.next = c.tail\n\tc.tail.prev = c.head\n\tc.len = 0\n}\n\n// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.\nfunc (c *LRUCache) GetInvalidated() []*pgconn.StatementDescription {\n\treturn c.invalidStmts\n}\n\n// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a\n// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were\n// never seen by the call to GetInvalidated.\nfunc (c *LRUCache) RemoveInvalidated() {\n\tc.invalidStmts = c.invalidStmts[:0]\n\tclear(c.invalidSet)\n}\n\n// Len returns the number of cached prepared statement descriptions.\nfunc (c *LRUCache) Len() int {\n\treturn c.len\n}\n\n// Cap returns the maximum number of cached prepared statement descriptions.\nfunc (c *LRUCache) Cap() int {\n\treturn c.cap\n}\n\nfunc (c *LRUCache) invalidateOldest() {\n\tnode := c.tail.prev\n\tif node == c.head {\n\t\treturn\n\t}\n\tc.invalidStmts = append(c.invalidStmts, node.sd)\n\tc.invalidSet[node.sd.SQL] = struct{}{}\n\tdelete(c.m, node.sd.SQL)\n\tc.unlink(node)\n\tc.len--\n\tc.freeNode(node)\n}\n\n// List operations - sentinel nodes eliminate nil checks\n\nfunc (c *LRUCache) insertAfter(at, node *lruNode) {\n\tnode.prev = at\n\tnode.next = at.next\n\tat.next.prev = node\n\tat.next = node\n}\n\nfunc (c *LRUCache) unlink(node *lruNode) {\n\tnode.prev.next = node.next\n\tnode.next.prev = node.prev\n}\n\nfunc (c *LRUCache) moveToFront(node *lruNode) {\n\tif node.prev == c.head {\n\t\treturn\n\t}\n\tc.unlink(node)\n\tc.insertAfter(c.head, node)\n}\n\n// Node pool operations - reuse evicted nodes to avoid allocations\n\nfunc (c *LRUCache) allocNode() *lruNode {\n\tif c.freelist != nil {\n\t\tnode := c.freelist\n\t\tc.freelist = node.next\n\t\tnode.next = nil\n\t\tnode.prev = nil\n\t\treturn node\n\t}\n\treturn &lruNode{}\n}\n\nfunc (c *LRUCache) freeNode(node *lruNode) {\n\tnode.sd = nil\n\tnode.prev = nil\n\tnode.next = c.freelist\n\tc.freelist = node\n}\n"
  },
  {
    "path": "internal/stmtcache/stmtcache.go",
    "content": "// Package stmtcache is a cache for statement descriptions.\npackage stmtcache\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// StatementName returns a statement name that will be stable for sql across multiple connections and program\n// executions.\nfunc StatementName(sql string) string {\n\tdigest := sha256.Sum256([]byte(sql))\n\treturn \"stmtcache_\" + hex.EncodeToString(digest[0:24])\n}\n\n// Cache caches statement descriptions.\ntype Cache interface {\n\t// Get returns the statement description for sql. Returns nil if not found.\n\tGet(sql string) *pgconn.StatementDescription\n\n\t// Put stores sd in the cache. Put panics if sd.SQL is \"\". Put does nothing if sd.SQL already exists in the cache.\n\tPut(sd *pgconn.StatementDescription)\n\n\t// Invalidate invalidates statement description identified by sql. Does nothing if not found.\n\tInvalidate(sql string)\n\n\t// InvalidateAll invalidates all statement descriptions.\n\tInvalidateAll()\n\n\t// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.\n\tGetInvalidated() []*pgconn.StatementDescription\n\n\t// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a\n\t// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were\n\t// never seen by the call to GetInvalidated.\n\tRemoveInvalidated()\n\n\t// Len returns the number of cached prepared statement descriptions.\n\tLen() int\n\n\t// Cap returns the maximum number of cached prepared statement descriptions.\n\tCap() int\n}\n"
  },
  {
    "path": "large_objects.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// The PostgreSQL wire protocol has a limit of 1 GB - 1 per message. See definition of\n// PQ_LARGE_MESSAGE_LIMIT in the PostgreSQL source code. To allow for the other data\n// in the message,maxLargeObjectMessageLength should be no larger than 1 GB - 1 KB.\nvar maxLargeObjectMessageLength = 1024*1024*1024 - 1024\n\n// LargeObjects is a structure used to access the large objects API. It is only valid within the transaction where it\n// was created.\n//\n// For more details see: http://www.postgresql.org/docs/current/static/largeobjects.html\ntype LargeObjects struct {\n\ttx Tx\n}\n\ntype LargeObjectMode int32\n\nconst (\n\tLargeObjectModeWrite LargeObjectMode = 0x20000\n\tLargeObjectModeRead  LargeObjectMode = 0x40000\n)\n\n// Create creates a new large object. If oid is zero, the server assigns an unused OID.\nfunc (o *LargeObjects) Create(ctx context.Context, oid uint32) (uint32, error) {\n\terr := o.tx.QueryRow(ctx, \"select lo_create($1)\", oid).Scan(&oid)\n\treturn oid, err\n}\n\n// Open opens an existing large object with the given mode. ctx will also be used for all operations on the opened large\n// object.\nfunc (o *LargeObjects) Open(ctx context.Context, oid uint32, mode LargeObjectMode) (*LargeObject, error) {\n\tvar fd int32\n\terr := o.tx.QueryRow(ctx, \"select lo_open($1, $2)\", oid, mode).Scan(&fd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &LargeObject{fd: fd, tx: o.tx, ctx: ctx}, nil\n}\n\n// Unlink removes a large object from the database.\nfunc (o *LargeObjects) Unlink(ctx context.Context, oid uint32) error {\n\tvar result int32\n\terr := o.tx.QueryRow(ctx, \"select lo_unlink($1)\", oid).Scan(&result)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif result != 1 {\n\t\treturn errors.New(\"failed to remove large object\")\n\t}\n\n\treturn nil\n}\n\n// A LargeObject is a large object stored on the server. It is only valid within the transaction that it was initialized\n// in. It uses the context it was initialized with for all operations. It implements these interfaces:\n//\n//\tio.Writer\n//\tio.Reader\n//\tio.Seeker\n//\tio.Closer\ntype LargeObject struct {\n\tctx context.Context\n\ttx  Tx\n\tfd  int32\n}\n\n// Write writes p to the large object and returns the number of bytes written and an error if not all of p was written.\nfunc (o *LargeObject) Write(p []byte) (int, error) {\n\tnTotal := 0\n\tfor {\n\t\texpected := len(p) - nTotal\n\t\tif expected == 0 {\n\t\t\tbreak\n\t\t} else if expected > maxLargeObjectMessageLength {\n\t\t\texpected = maxLargeObjectMessageLength\n\t\t}\n\n\t\tvar n int\n\t\terr := o.tx.QueryRow(o.ctx, \"select lowrite($1, $2)\", o.fd, p[nTotal:nTotal+expected]).Scan(&n)\n\t\tif err != nil {\n\t\t\treturn nTotal, err\n\t\t}\n\n\t\tif n < 0 {\n\t\t\treturn nTotal, errors.New(\"failed to write to large object\")\n\t\t}\n\n\t\tnTotal += n\n\n\t\tif n < expected {\n\t\t\treturn nTotal, errors.New(\"short write to large object\")\n\t\t} else if n > expected {\n\t\t\treturn nTotal, errors.New(\"invalid write to large object\")\n\t\t}\n\t}\n\n\treturn nTotal, nil\n}\n\n// Read reads up to len(p) bytes into p returning the number of bytes read.\nfunc (o *LargeObject) Read(p []byte) (int, error) {\n\tnTotal := 0\n\tfor {\n\t\texpected := len(p) - nTotal\n\t\tif expected == 0 {\n\t\t\tbreak\n\t\t} else if expected > maxLargeObjectMessageLength {\n\t\t\texpected = maxLargeObjectMessageLength\n\t\t}\n\n\t\tres := pgtype.PreallocBytes(p[nTotal:])\n\t\terr := o.tx.QueryRow(o.ctx, \"select loread($1, $2)\", o.fd, expected).Scan(&res)\n\t\t// We compute expected so that it always fits into p, so it should never happen\n\t\t// that PreallocBytes's ScanBytes had to allocate a new slice.\n\t\tnTotal += len(res)\n\t\tif err != nil {\n\t\t\treturn nTotal, err\n\t\t}\n\n\t\tif len(res) < expected {\n\t\t\treturn nTotal, io.EOF\n\t\t} else if len(res) > expected {\n\t\t\treturn nTotal, errors.New(\"invalid read of large object\")\n\t\t}\n\t}\n\n\treturn nTotal, nil\n}\n\n// Seek moves the current location pointer to the new location specified by offset.\nfunc (o *LargeObject) Seek(offset int64, whence int) (n int64, err error) {\n\terr = o.tx.QueryRow(o.ctx, \"select lo_lseek64($1, $2, $3)\", o.fd, offset, whence).Scan(&n)\n\treturn n, err\n}\n\n// Tell returns the current read or write location of the large object descriptor.\nfunc (o *LargeObject) Tell() (n int64, err error) {\n\terr = o.tx.QueryRow(o.ctx, \"select lo_tell64($1)\", o.fd).Scan(&n)\n\treturn n, err\n}\n\n// Truncate the large object to size.\nfunc (o *LargeObject) Truncate(size int64) (err error) {\n\t_, err = o.tx.Exec(o.ctx, \"select lo_truncate64($1, $2)\", o.fd, size)\n\treturn err\n}\n\n// Close the large object descriptor.\nfunc (o *LargeObject) Close() error {\n\t_, err := o.tx.Exec(o.ctx, \"select lo_close($1)\", o.fd)\n\treturn err\n}\n"
  },
  {
    "path": "large_objects_private_test.go",
    "content": "package pgx\n\nimport (\n\t\"testing\"\n)\n\n// SetMaxLargeObjectMessageLength sets internal maxLargeObjectMessageLength variable\n// to the given length for the duration of the test.\n//\n// Tests using this helper should not use t.Parallel().\nfunc SetMaxLargeObjectMessageLength(t *testing.T, length int) {\n\tt.Helper()\n\n\toriginal := maxLargeObjectMessageLength\n\tt.Cleanup(func() {\n\t\tmaxLargeObjectMessageLength = original\n\t})\n\n\tmaxLargeObjectMessageLength = length\n}\n"
  },
  {
    "path": "large_objects_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestLargeObjects(t *testing.T) {\n\t// We use a very short limit to test chunking logic.\n\tpgx.SetMaxLargeObjectMessageLength(t, 2)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does support large objects\")\n\n\ttx, err := conn.Begin(ctx)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLargeObjects(t, ctx, tx)\n}\n\nfunc TestLargeObjectsSimpleProtocol(t *testing.T) {\n\t// We use a very short limit to test chunking logic.\n\tpgx.SetMaxLargeObjectMessageLength(t, 2)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol\n\n\tconn, err := pgx.ConnectConfig(ctx, config)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does support large objects\")\n\n\ttx, err := conn.Begin(ctx)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLargeObjects(t, ctx, tx)\n}\n\nfunc testLargeObjects(t *testing.T, ctx context.Context, tx pgx.Tx) {\n\tlo := tx.LargeObjects()\n\n\tid, err := lo.Create(ctx, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tobj, err := lo.Open(ctx, id, pgx.LargeObjectModeRead|pgx.LargeObjectModeWrite)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tn, err := obj.Write([]byte(\"testing\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif n != 7 {\n\t\tt.Errorf(\"Expected n to be 7, got %d\", n)\n\t}\n\n\tpos, err := obj.Seek(1, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pos != 1 {\n\t\tt.Errorf(\"Expected pos to be 1, got %d\", pos)\n\t}\n\n\tres := make([]byte, 6)\n\tn, err = obj.Read(res)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif string(res) != \"esting\" {\n\t\tt.Errorf(`Expected res to be \"esting\", got %q`, res)\n\t}\n\tif n != 6 {\n\t\tt.Errorf(\"Expected n to be 6, got %d\", n)\n\t}\n\n\tn, err = obj.Read(res)\n\tif err != io.EOF {\n\t\tt.Error(\"Expected io.EOF, go nil\")\n\t}\n\tif n != 0 {\n\t\tt.Errorf(\"Expected n to be 0, got %d\", n)\n\t}\n\n\tpos, err = obj.Tell()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pos != 7 {\n\t\tt.Errorf(\"Expected pos to be 7, got %d\", pos)\n\t}\n\n\terr = obj.Truncate(1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpos, err = obj.Seek(-1, 2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pos != 0 {\n\t\tt.Errorf(\"Expected pos to be 0, got %d\", pos)\n\t}\n\n\tres = make([]byte, 2)\n\tn, err = obj.Read(res)\n\tif err != io.EOF {\n\t\tt.Errorf(\"Expected err to be io.EOF, got %v\", err)\n\t}\n\tif n != 1 {\n\t\tt.Errorf(\"Expected n to be 1, got %d\", n)\n\t}\n\tif res[0] != 't' {\n\t\tt.Errorf(\"Expected res[0] to be 't', got %v\", res[0])\n\t}\n\n\terr = obj.Close()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\terr = lo.Unlink(ctx, id)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = lo.Open(ctx, id, pgx.LargeObjectModeRead)\n\tif e, ok := err.(*pgconn.PgError); !ok || e.Code != \"42704\" {\n\t\tt.Errorf(\"Expected undefined_object error (42704), got %#v\", err)\n\t}\n}\n\nfunc TestLargeObjectsMultipleTransactions(t *testing.T) {\n\t// We use a very short limit to test chunking logic.\n\tpgx.SetMaxLargeObjectMessageLength(t, 2)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does support large objects\")\n\n\ttx, err := conn.Begin(ctx)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tlo := tx.LargeObjects()\n\n\tid, err := lo.Create(ctx, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tobj, err := lo.Open(ctx, id, pgx.LargeObjectModeWrite)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tn, err := obj.Write([]byte(\"testing\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif n != 7 {\n\t\tt.Errorf(\"Expected n to be 7, got %d\", n)\n\t}\n\n\t// Commit the first transaction\n\terr = tx.Commit(ctx)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// IMPORTANT: Use the same connection for another query\n\tquery := `select n from generate_series(1,10) n`\n\trows, err := conn.Query(ctx, query)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\trows.Close()\n\n\t// Start a new transaction\n\ttx2, err := conn.Begin(ctx)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tlo2 := tx2.LargeObjects()\n\n\t// Reopen the large object in the new transaction\n\tobj2, err := lo2.Open(ctx, id, pgx.LargeObjectModeRead|pgx.LargeObjectModeWrite)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpos, err := obj2.Seek(1, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pos != 1 {\n\t\tt.Errorf(\"Expected pos to be 1, got %d\", pos)\n\t}\n\n\tres := make([]byte, 6)\n\tn, err = obj2.Read(res)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif string(res) != \"esting\" {\n\t\tt.Errorf(`Expected res to be \"esting\", got %q`, res)\n\t}\n\tif n != 6 {\n\t\tt.Errorf(\"Expected n to be 6, got %d\", n)\n\t}\n\n\tn, err = obj2.Read(res)\n\tif err != io.EOF {\n\t\tt.Error(\"Expected io.EOF, go nil\")\n\t}\n\tif n != 0 {\n\t\tt.Errorf(\"Expected n to be 0, got %d\", n)\n\t}\n\n\tpos, err = obj2.Tell()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pos != 7 {\n\t\tt.Errorf(\"Expected pos to be 7, got %d\", pos)\n\t}\n\n\terr = obj2.Truncate(1)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpos, err = obj2.Seek(-1, 2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif pos != 0 {\n\t\tt.Errorf(\"Expected pos to be 0, got %d\", pos)\n\t}\n\n\tres = make([]byte, 2)\n\tn, err = obj2.Read(res)\n\tif err != io.EOF {\n\t\tt.Errorf(\"Expected err to be io.EOF, got %v\", err)\n\t}\n\tif n != 1 {\n\t\tt.Errorf(\"Expected n to be 1, got %d\", n)\n\t}\n\tif res[0] != 't' {\n\t\tt.Errorf(\"Expected res[0] to be 't', got %v\", res[0])\n\t}\n\n\terr = obj2.Close()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\terr = lo2.Unlink(ctx, id)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = lo2.Open(ctx, id, pgx.LargeObjectModeRead)\n\tif e, ok := err.(*pgconn.PgError); !ok || e.Code != \"42704\" {\n\t\tt.Errorf(\"Expected undefined_object error (42704), got %#v\", err)\n\t}\n}\n"
  },
  {
    "path": "log/testingadapter/adapter.go",
    "content": "// Package testingadapter provides a logger that writes to a test or benchmark\n// log.\npackage testingadapter\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/tracelog\"\n)\n\n// TestingLogger interface defines the subset of testing.TB methods used by this\n// adapter.\ntype TestingLogger interface {\n\tLog(args ...any)\n}\n\ntype Logger struct {\n\tl TestingLogger\n}\n\nfunc NewLogger(l TestingLogger) *Logger {\n\treturn &Logger{l: l}\n}\n\nfunc (l *Logger) Log(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) {\n\tlogArgs := make([]any, 0, 2+len(data))\n\tlogArgs = append(logArgs, level, msg)\n\tfor k, v := range data {\n\t\tlogArgs = append(logArgs, fmt.Sprintf(\"%s=%v\", k, v))\n\t}\n\tl.l.Log(logArgs...)\n}\n"
  },
  {
    "path": "multitracer/tracer.go",
    "content": "// Package multitracer provides a Tracer that can combine several tracers into one.\npackage multitracer\n\nimport (\n\t\"context\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n)\n\n// Tracer can combine several tracers into one.\n// You can use New to automatically split tracers by interface.\ntype Tracer struct {\n\tQueryTracers       []pgx.QueryTracer\n\tBatchTracers       []pgx.BatchTracer\n\tCopyFromTracers    []pgx.CopyFromTracer\n\tPrepareTracers     []pgx.PrepareTracer\n\tConnectTracers     []pgx.ConnectTracer\n\tPoolAcquireTracers []pgxpool.AcquireTracer\n\tPoolReleaseTracers []pgxpool.ReleaseTracer\n}\n\n// New returns new Tracer from tracers with automatically split tracers by interface.\nfunc New(tracers ...pgx.QueryTracer) *Tracer {\n\tvar t Tracer\n\n\tfor _, tracer := range tracers {\n\t\tt.QueryTracers = append(t.QueryTracers, tracer)\n\n\t\tif batchTracer, ok := tracer.(pgx.BatchTracer); ok {\n\t\t\tt.BatchTracers = append(t.BatchTracers, batchTracer)\n\t\t}\n\n\t\tif copyFromTracer, ok := tracer.(pgx.CopyFromTracer); ok {\n\t\t\tt.CopyFromTracers = append(t.CopyFromTracers, copyFromTracer)\n\t\t}\n\n\t\tif prepareTracer, ok := tracer.(pgx.PrepareTracer); ok {\n\t\t\tt.PrepareTracers = append(t.PrepareTracers, prepareTracer)\n\t\t}\n\n\t\tif connectTracer, ok := tracer.(pgx.ConnectTracer); ok {\n\t\t\tt.ConnectTracers = append(t.ConnectTracers, connectTracer)\n\t\t}\n\n\t\tif poolAcquireTracer, ok := tracer.(pgxpool.AcquireTracer); ok {\n\t\t\tt.PoolAcquireTracers = append(t.PoolAcquireTracers, poolAcquireTracer)\n\t\t}\n\n\t\tif poolReleaseTracer, ok := tracer.(pgxpool.ReleaseTracer); ok {\n\t\t\tt.PoolReleaseTracers = append(t.PoolReleaseTracers, poolReleaseTracer)\n\t\t}\n\t}\n\n\treturn &t\n}\n\nfunc (t *Tracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\tfor _, tracer := range t.QueryTracers {\n\t\tctx = tracer.TraceQueryStart(ctx, conn, data)\n\t}\n\n\treturn ctx\n}\n\nfunc (t *Tracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n\tfor _, tracer := range t.QueryTracers {\n\t\ttracer.TraceQueryEnd(ctx, conn, data)\n\t}\n}\n\nfunc (t *Tracer) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\tfor _, tracer := range t.BatchTracers {\n\t\tctx = tracer.TraceBatchStart(ctx, conn, data)\n\t}\n\n\treturn ctx\n}\n\nfunc (t *Tracer) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\tfor _, tracer := range t.BatchTracers {\n\t\ttracer.TraceBatchQuery(ctx, conn, data)\n\t}\n}\n\nfunc (t *Tracer) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\tfor _, tracer := range t.BatchTracers {\n\t\ttracer.TraceBatchEnd(ctx, conn, data)\n\t}\n}\n\nfunc (t *Tracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {\n\tfor _, tracer := range t.CopyFromTracers {\n\t\tctx = tracer.TraceCopyFromStart(ctx, conn, data)\n\t}\n\n\treturn ctx\n}\n\nfunc (t *Tracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {\n\tfor _, tracer := range t.CopyFromTracers {\n\t\ttracer.TraceCopyFromEnd(ctx, conn, data)\n\t}\n}\n\nfunc (t *Tracer) TracePrepareStart(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {\n\tfor _, tracer := range t.PrepareTracers {\n\t\tctx = tracer.TracePrepareStart(ctx, conn, data)\n\t}\n\n\treturn ctx\n}\n\nfunc (t *Tracer) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {\n\tfor _, tracer := range t.PrepareTracers {\n\t\ttracer.TracePrepareEnd(ctx, conn, data)\n\t}\n}\n\nfunc (t *Tracer) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {\n\tfor _, tracer := range t.ConnectTracers {\n\t\tctx = tracer.TraceConnectStart(ctx, data)\n\t}\n\n\treturn ctx\n}\n\nfunc (t *Tracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {\n\tfor _, tracer := range t.ConnectTracers {\n\t\ttracer.TraceConnectEnd(ctx, data)\n\t}\n}\n\nfunc (t *Tracer) TraceAcquireStart(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {\n\tfor _, tracer := range t.PoolAcquireTracers {\n\t\tctx = tracer.TraceAcquireStart(ctx, pool, data)\n\t}\n\n\treturn ctx\n}\n\nfunc (t *Tracer) TraceAcquireEnd(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {\n\tfor _, tracer := range t.PoolAcquireTracers {\n\t\ttracer.TraceAcquireEnd(ctx, pool, data)\n\t}\n}\n\nfunc (t *Tracer) TraceRelease(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {\n\tfor _, tracer := range t.PoolReleaseTracers {\n\t\ttracer.TraceRelease(pool, data)\n\t}\n}\n"
  },
  {
    "path": "multitracer/tracer_test.go",
    "content": "package multitracer_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/multitracer\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype testFullTracer struct{}\n\nfunc (tt *testFullTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testFullTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n}\n\nfunc (tt *testFullTracer) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testFullTracer) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n}\n\nfunc (tt *testFullTracer) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n}\n\nfunc (tt *testFullTracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testFullTracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {\n}\n\nfunc (tt *testFullTracer) TracePrepareStart(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testFullTracer) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {\n}\n\nfunc (tt *testFullTracer) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testFullTracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {\n}\n\nfunc (tt *testFullTracer) TraceAcquireStart(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testFullTracer) TraceAcquireEnd(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {\n}\n\nfunc (tt *testFullTracer) TraceRelease(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {\n}\n\ntype testCopyTracer struct{}\n\nfunc (tt *testCopyTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testCopyTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n}\n\nfunc (tt *testCopyTracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testCopyTracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {\n}\n\nfunc TestNew(t *testing.T) {\n\tt.Parallel()\n\n\tfullTracer := &testFullTracer{}\n\tcopyTracer := &testCopyTracer{}\n\n\tmt := multitracer.New(fullTracer, copyTracer)\n\trequire.Equal(\n\t\tt,\n\t\t&multitracer.Tracer{\n\t\t\tQueryTracers: []pgx.QueryTracer{\n\t\t\t\tfullTracer,\n\t\t\t\tcopyTracer,\n\t\t\t},\n\t\t\tBatchTracers: []pgx.BatchTracer{\n\t\t\t\tfullTracer,\n\t\t\t},\n\t\t\tCopyFromTracers: []pgx.CopyFromTracer{\n\t\t\t\tfullTracer,\n\t\t\t\tcopyTracer,\n\t\t\t},\n\t\t\tPrepareTracers: []pgx.PrepareTracer{\n\t\t\t\tfullTracer,\n\t\t\t},\n\t\t\tConnectTracers: []pgx.ConnectTracer{\n\t\t\t\tfullTracer,\n\t\t\t},\n\t\t\tPoolAcquireTracers: []pgxpool.AcquireTracer{\n\t\t\t\tfullTracer,\n\t\t\t},\n\t\t\tPoolReleaseTracers: []pgxpool.ReleaseTracer{\n\t\t\t\tfullTracer,\n\t\t\t},\n\t\t},\n\t\tmt,\n\t)\n}\n"
  },
  {
    "path": "named_args.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode/utf8\"\n)\n\n// NamedArgs can be used as the first argument to a query method. It will replace every '@' named placeholder with a '$'\n// ordinal placeholder and construct the appropriate arguments.\n//\n// For example, the following two queries are equivalent:\n//\n//\tconn.Query(ctx, \"select * from widgets where foo = @foo and bar = @bar\", pgx.NamedArgs{\"foo\": 1, \"bar\": 2})\n//\tconn.Query(ctx, \"select * from widgets where foo = $1 and bar = $2\", 1, 2)\n//\n// Named placeholders are case sensitive and must start with a letter or underscore. Subsequent characters can be\n// letters, numbers, or underscores.\ntype NamedArgs map[string]any\n\n// RewriteQuery implements the QueryRewriter interface.\nfunc (na NamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {\n\treturn rewriteQuery(na, sql, false)\n}\n\n// StrictNamedArgs can be used in the same way as NamedArgs, but provided arguments are also checked to include all\n// named arguments that the sql query uses, and no extra arguments.\ntype StrictNamedArgs map[string]any\n\n// RewriteQuery implements the QueryRewriter interface.\nfunc (sna StrictNamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {\n\treturn rewriteQuery(sna, sql, true)\n}\n\ntype namedArg string\n\ntype sqlLexer struct {\n\tsrc     string\n\tstart   int\n\tpos     int\n\tnested  int // multiline comment nesting level.\n\tstateFn stateFn\n\tparts   []any\n\n\tnameToOrdinal map[namedArg]int\n}\n\ntype stateFn func(*sqlLexer) stateFn\n\nfunc rewriteQuery(na map[string]any, sql string, isStrict bool) (newSQL string, newArgs []any, err error) {\n\tl := &sqlLexer{\n\t\tsrc:           sql,\n\t\tstateFn:       rawState,\n\t\tnameToOrdinal: make(map[namedArg]int, len(na)),\n\t}\n\n\tfor l.stateFn != nil {\n\t\tl.stateFn = l.stateFn(l)\n\t}\n\n\tsb := strings.Builder{}\n\tfor _, p := range l.parts {\n\t\tswitch p := p.(type) {\n\t\tcase string:\n\t\t\tsb.WriteString(p)\n\t\tcase namedArg:\n\t\t\tsb.WriteRune('$')\n\t\t\tsb.WriteString(strconv.Itoa(l.nameToOrdinal[p]))\n\t\t}\n\t}\n\n\tnewArgs = make([]any, len(l.nameToOrdinal))\n\tfor name, ordinal := range l.nameToOrdinal {\n\t\tvar found bool\n\t\tnewArgs[ordinal-1], found = na[string(name)]\n\t\tif isStrict && !found {\n\t\t\treturn \"\", nil, fmt.Errorf(\"argument %s found in sql query but not present in StrictNamedArgs\", name)\n\t\t}\n\t}\n\n\tif isStrict {\n\t\tfor name := range na {\n\t\t\tif _, found := l.nameToOrdinal[namedArg(name)]; !found {\n\t\t\t\treturn \"\", nil, fmt.Errorf(\"argument %s of StrictNamedArgs not found in sql query\", name)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sb.String(), newArgs, nil\n}\n\nfunc rawState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase 'e', 'E':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '\\'' {\n\t\t\t\tl.pos += width\n\t\t\t\treturn escapeStringState\n\t\t\t}\n\t\tcase '\\'':\n\t\t\treturn singleQuoteState\n\t\tcase '\"':\n\t\t\treturn doubleQuoteState\n\t\tcase '@':\n\t\t\tnextRune, _ := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif isLetter(nextRune) || nextRune == '_' {\n\t\t\t\tif l.pos-l.start > 0 {\n\t\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos-width])\n\t\t\t\t}\n\t\t\t\tl.start = l.pos\n\t\t\t\treturn namedArgState\n\t\t\t}\n\t\tcase '-':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '-' {\n\t\t\t\tl.pos += width\n\t\t\t\treturn oneLineCommentState\n\t\t\t}\n\t\tcase '/':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '*' {\n\t\t\t\tl.pos += width\n\t\t\t\treturn multilineCommentState\n\t\t\t}\n\t\tcase utf8.RuneError:\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc isLetter(r rune) bool {\n\treturn (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')\n}\n\nfunc namedArgState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tif r == utf8.RuneError {\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tna := namedArg(l.src[l.start:l.pos])\n\t\t\t\tif _, found := l.nameToOrdinal[na]; !found {\n\t\t\t\t\tl.nameToOrdinal[na] = len(l.nameToOrdinal) + 1\n\t\t\t\t}\n\t\t\t\tl.parts = append(l.parts, na)\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t} else if !(isLetter(r) || (r >= '0' && r <= '9') || r == '_') {\n\t\t\tl.pos -= width\n\t\t\tna := namedArg(l.src[l.start:l.pos])\n\t\t\tif _, found := l.nameToOrdinal[na]; !found {\n\t\t\t\tl.nameToOrdinal[na] = len(l.nameToOrdinal) + 1\n\t\t\t}\n\t\t\tl.parts = append(l.parts, namedArg(na))\n\t\t\tl.start = l.pos\n\t\t\treturn rawState\n\t\t}\n\t}\n}\n\nfunc singleQuoteState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\\'':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '\\'' {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.pos += width\n\t\tcase utf8.RuneError:\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc doubleQuoteState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\"':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '\"' {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.pos += width\n\t\tcase utf8.RuneError:\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc escapeStringState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\t_, width = utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tl.pos += width\n\t\tcase '\\'':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '\\'' {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.pos += width\n\t\tcase utf8.RuneError:\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc oneLineCommentState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\t_, width = utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tl.pos += width\n\t\tcase '\\n', '\\r':\n\t\t\treturn rawState\n\t\tcase utf8.RuneError:\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\nfunc multilineCommentState(l *sqlLexer) stateFn {\n\tfor {\n\t\tr, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\tl.pos += width\n\n\t\tswitch r {\n\t\tcase '/':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune == '*' {\n\t\t\t\tl.pos += width\n\t\t\t\tl.nested++\n\t\t\t}\n\t\tcase '*':\n\t\t\tnextRune, width := utf8.DecodeRuneInString(l.src[l.pos:])\n\t\t\tif nextRune != '/' {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tl.pos += width\n\t\t\tif l.nested == 0 {\n\t\t\t\treturn rawState\n\t\t\t}\n\t\t\tl.nested--\n\n\t\tcase utf8.RuneError:\n\t\t\tif l.pos-l.start > 0 {\n\t\t\t\tl.parts = append(l.parts, l.src[l.start:l.pos])\n\t\t\t\tl.start = l.pos\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "named_args_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNamedArgsRewriteQuery(t *testing.T) {\n\tt.Parallel()\n\n\tfor i, tt := range []struct {\n\t\tsql          string\n\t\targs         []any\n\t\tnamedArgs    pgx.NamedArgs\n\t\texpectedSQL  string\n\t\texpectedArgs []any\n\t}{\n\t\t{\n\t\t\tsql:          \"select * from users where id = @id\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"id\": int32(42)},\n\t\t\texpectedSQL:  \"select * from users where id = $1\",\n\t\t\texpectedArgs: []any{int32(42)},\n\t\t},\n\t\t{\n\t\t\tsql:          \"select * from t where foo < @abc and baz = @def and bar < @abc\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"abc\": int32(42), \"def\": int32(1)},\n\t\t\texpectedSQL:  \"select * from t where foo < $1 and baz = $2 and bar < $1\",\n\t\t\texpectedArgs: []any{int32(42), int32(1)},\n\t\t},\n\t\t{\n\t\t\tsql:          \"select @a::int, @b::text\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"a\": int32(42), \"b\": \"foo\"},\n\t\t\texpectedSQL:  \"select $1::int, $2::text\",\n\t\t\texpectedArgs: []any{int32(42), \"foo\"},\n\t\t},\n\t\t{\n\t\t\tsql:          \"select @Abc::int, @b_4::text, @_c::int\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"Abc\": int32(42), \"b_4\": \"foo\", \"_c\": int32(1)},\n\t\t\texpectedSQL:  \"select $1::int, $2::text, $3::int\",\n\t\t\texpectedArgs: []any{int32(42), \"foo\", int32(1)},\n\t\t},\n\t\t{\n\t\t\tsql:          \"at end @\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"a\": int32(42), \"b\": \"foo\"},\n\t\t\texpectedSQL:  \"at end @\",\n\t\t\texpectedArgs: []any{},\n\t\t},\n\t\t{\n\t\t\tsql:          \"ignores without valid character after @ foo bar\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"a\": int32(42), \"b\": \"foo\"},\n\t\t\texpectedSQL:  \"ignores without valid character after @ foo bar\",\n\t\t\texpectedArgs: []any{},\n\t\t},\n\t\t{\n\t\t\tsql:          \"name cannot start with number @1 foo bar\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"a\": int32(42), \"b\": \"foo\"},\n\t\t\texpectedSQL:  \"name cannot start with number @1 foo bar\",\n\t\t\texpectedArgs: []any{},\n\t\t},\n\t\t{\n\t\t\tsql:          `select *, '@foo' as \"@bar\" from users where id = @id`,\n\t\t\tnamedArgs:    pgx.NamedArgs{\"id\": int32(42)},\n\t\t\texpectedSQL:  `select *, '@foo' as \"@bar\" from users where id = $1`,\n\t\t\texpectedArgs: []any{int32(42)},\n\t\t},\n\t\t{\n\t\t\tsql: `select * -- @foo\n\t\t\tfrom users -- @single line comments\n\t\t\twhere id = @id;`,\n\t\t\tnamedArgs: pgx.NamedArgs{\"id\": int32(42)},\n\t\t\texpectedSQL: `select * -- @foo\n\t\t\tfrom users -- @single line comments\n\t\t\twhere id = $1;`,\n\t\t\texpectedArgs: []any{int32(42)},\n\t\t},\n\t\t{\n\t\t\tsql: `select * /* @multi line\n\t\t\t@comment\n\t\t\t*/\n\t\t\t/* /* with @nesting */ */\n\t\t\tfrom users\n\t\t\twhere id = @id;`,\n\t\t\tnamedArgs: pgx.NamedArgs{\"id\": int32(42)},\n\t\t\texpectedSQL: `select * /* @multi line\n\t\t\t@comment\n\t\t\t*/\n\t\t\t/* /* with @nesting */ */\n\t\t\tfrom users\n\t\t\twhere id = $1;`,\n\t\t\texpectedArgs: []any{int32(42)},\n\t\t},\n\t\t{\n\t\t\tsql:          \"extra provided argument\",\n\t\t\tnamedArgs:    pgx.NamedArgs{\"extra\": int32(1)},\n\t\t\texpectedSQL:  \"extra provided argument\",\n\t\t\texpectedArgs: []any{},\n\t\t},\n\t\t{\n\t\t\tsql:          \"@missing argument\",\n\t\t\tnamedArgs:    pgx.NamedArgs{},\n\t\t\texpectedSQL:  \"$1 argument\",\n\t\t\texpectedArgs: []any{nil},\n\t\t},\n\n\t\t// test comments and quotes\n\t} {\n\t\tsql, args, err := tt.namedArgs.RewriteQuery(context.Background(), nil, tt.sql, tt.args)\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, tt.expectedSQL, sql, \"%d\", i)\n\t\tassert.Equalf(t, tt.expectedArgs, args, \"%d\", i)\n\t}\n}\n\nfunc TestStrictNamedArgsRewriteQuery(t *testing.T) {\n\tt.Parallel()\n\n\tfor i, tt := range []struct {\n\t\tsql             string\n\t\tnamedArgs       pgx.StrictNamedArgs\n\t\texpectedSQL     string\n\t\texpectedArgs    []any\n\t\tisExpectedError bool\n\t}{\n\t\t{\n\t\t\tsql:             \"no arguments\",\n\t\t\tnamedArgs:       pgx.StrictNamedArgs{},\n\t\t\texpectedSQL:     \"no arguments\",\n\t\t\texpectedArgs:    []any{},\n\t\t\tisExpectedError: false,\n\t\t},\n\t\t{\n\t\t\tsql:             \"@all @matches\",\n\t\t\tnamedArgs:       pgx.StrictNamedArgs{\"all\": int32(1), \"matches\": int32(2)},\n\t\t\texpectedSQL:     \"$1 $2\",\n\t\t\texpectedArgs:    []any{int32(1), int32(2)},\n\t\t\tisExpectedError: false,\n\t\t},\n\t\t{\n\t\t\tsql:             \"extra provided argument\",\n\t\t\tnamedArgs:       pgx.StrictNamedArgs{\"extra\": int32(1)},\n\t\t\tisExpectedError: true,\n\t\t},\n\t\t{\n\t\t\tsql:             \"@missing argument\",\n\t\t\tnamedArgs:       pgx.StrictNamedArgs{},\n\t\t\tisExpectedError: true,\n\t\t},\n\t} {\n\t\tsql, args, err := tt.namedArgs.RewriteQuery(context.Background(), nil, tt.sql, nil)\n\t\tif tt.isExpectedError {\n\t\t\tassert.Errorf(t, err, \"%d\", i)\n\t\t} else {\n\t\t\trequire.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expectedSQL, sql, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expectedArgs, args, \"%d\", i)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgbouncer_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPgbouncerStatementCacheDescribe(t *testing.T) {\n\tconnString := os.Getenv(\"PGX_TEST_PGBOUNCER_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_PGBOUNCER_CONN_STRING\")\n\t}\n\n\tconfig := mustParseConfig(t, connString)\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheDescribe\n\tconfig.DescriptionCacheCapacity = 1024\n\n\ttestPgbouncer(t, config, 10, 100)\n}\n\nfunc TestPgbouncerSimpleProtocol(t *testing.T) {\n\tconnString := os.Getenv(\"PGX_TEST_PGBOUNCER_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_PGBOUNCER_CONN_STRING\")\n\t}\n\n\tconfig := mustParseConfig(t, connString)\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol\n\n\ttestPgbouncer(t, config, 10, 100)\n}\n\nfunc testPgbouncer(t *testing.T, config *pgx.ConnConfig, workers, iterations int) {\n\tdoneChan := make(chan struct{})\n\n\tfor range workers {\n\t\tgo func() {\n\t\t\tdefer func() { doneChan <- struct{}{} }()\n\t\t\tconn, err := pgx.ConnectConfig(context.Background(), config)\n\t\t\trequire.Nil(t, err)\n\t\t\tdefer closeConn(t, conn)\n\n\t\t\tfor range iterations {\n\t\t\t\tvar i32 int32\n\t\t\t\tvar i64 int64\n\t\t\t\tvar f32 float32\n\t\t\t\tvar s string\n\t\t\t\tvar s2 string\n\t\t\t\terr = conn.QueryRow(context.Background(), \"select 1::int4, 2::int8, 3::float4, 'hi'::text\").Scan(&i32, &i64, &f32, &s)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int32(1), i32)\n\t\t\t\tassert.Equal(t, int64(2), i64)\n\t\t\t\tassert.Equal(t, float32(3), f32)\n\t\t\t\tassert.Equal(t, \"hi\", s)\n\n\t\t\t\terr = conn.QueryRow(context.Background(), \"select 1::int8, 2::float4, 'bye'::text, 4::int4, 'whatever'::text\").Scan(&i64, &f32, &s, &i32, &s2)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, int64(1), i64)\n\t\t\t\tassert.Equal(t, float32(2), f32)\n\t\t\t\tassert.Equal(t, \"bye\", s)\n\t\t\t\tassert.Equal(t, int32(4), i32)\n\t\t\t\tassert.Equal(t, \"whatever\", s2)\n\t\t\t}\n\t\t}()\n\t}\n\n\tfor range workers {\n\t\t<-doneChan\n\t}\n}\n"
  },
  {
    "path": "pgconn/README.md",
    "content": "# pgconn\n\nPackage pgconn is a low-level PostgreSQL database driver. It operates at nearly the same level as the C library libpq.\nIt is primarily intended to serve as the foundation for higher level libraries such as https://github.com/jackc/pgx.\nApplications should handle normal queries with a higher level library and only use pgconn directly when required for\nlow-level access to PostgreSQL functionality.\n\n## Example Usage\n\n```go\npgConn, err := pgconn.Connect(context.Background(), os.Getenv(\"DATABASE_URL\"))\nif err != nil {\n\tlog.Fatalln(\"pgconn failed to connect:\", err)\n}\ndefer pgConn.Close(context.Background())\n\nresult := pgConn.ExecParams(context.Background(), \"SELECT email FROM users WHERE id=$1\", [][]byte{[]byte(\"123\")}, nil, nil, nil)\nfor result.NextRow() {\n\tfmt.Println(\"User 123 has email:\", string(result.Values()[0]))\n}\n_, err = result.Close()\nif err != nil {\n\tlog.Fatalln(\"failed reading result:\", err)\n}\n```\n\n## Testing\n\nSee CONTRIBUTING.md for setup instructions.\n"
  },
  {
    "path": "pgconn/auth_oauth.go",
    "content": "package pgconn\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n)\n\nfunc (c *PgConn) oauthAuth(ctx context.Context) error {\n\tif c.config.OAuthTokenProvider == nil {\n\t\treturn errors.New(\"OAuth authentication required but no token provider configured\")\n\t}\n\n\ttoken, err := c.config.OAuthTokenProvider(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to obtain OAuth token: %w\", err)\n\t}\n\n\t// https://www.rfc-editor.org/rfc/rfc7628.html#section-3.1\n\tinitialResponse := []byte(\"n,,\\x01auth=Bearer \" + token + \"\\x01\\x01\")\n\n\tsaslInitialResponse := &pgproto3.SASLInitialResponse{\n\t\tAuthMechanism: \"OAUTHBEARER\",\n\t\tData:          initialResponse,\n\t}\n\tc.frontend.Send(saslInitialResponse)\n\terr = c.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmsg, err := c.receiveMessage()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch m := msg.(type) {\n\tcase *pgproto3.AuthenticationOk:\n\t\treturn nil\n\tcase *pgproto3.AuthenticationSASLContinue:\n\t\t// Server sent error response in SASL continue\n\t\t// https://www.rfc-editor.org/rfc/rfc7628.html#section-3.2.2\n\t\t// https://www.rfc-editor.org/rfc/rfc7628.html#section-3.2.3\n\t\terrResponse := struct {\n\t\t\tStatus              string `json:\"status\"`\n\t\t\tScope               string `json:\"scope\"`\n\t\t\tOpenIDConfiguration string `json:\"openid-configuration\"`\n\t\t}{}\n\t\terr := json.Unmarshal(m.Data, &errResponse)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid OAuth error response from server: %w\", err)\n\t\t}\n\n\t\t// Per RFC 7628 section 3.2.3, we should send a SASLResponse which only contains \\x01.\n\t\t// However, since the connection will be closed anyway, we can skip this\n\t\treturn fmt.Errorf(\"OAuth authentication failed: %s\", errResponse.Status)\n\n\tcase *pgproto3.ErrorResponse:\n\t\treturn ErrorResponseToPgError(m)\n\n\tdefault:\n\t\treturn fmt.Errorf(\"unexpected message type during OAuth auth: %T\", msg)\n\t}\n}\n"
  },
  {
    "path": "pgconn/auth_scram.go",
    "content": "// SCRAM-SHA-256 and SCRAM-SHA-256-PLUS authentication\n//\n// Resources:\n//   https://tools.ietf.org/html/rfc5802\n//   https://tools.ietf.org/html/rfc5929\n//   https://tools.ietf.org/html/rfc8265\n//   https://www.postgresql.org/docs/current/sasl-authentication.html\n//\n// Inspiration drawn from other implementations:\n//   https://github.com/lib/pq/pull/608\n//   https://github.com/lib/pq/pull/788\n//   https://github.com/lib/pq/pull/833\n\npackage pgconn\n\nimport (\n\t\"bytes\"\n\t\"crypto/hmac\"\n\t\"crypto/pbkdf2\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash\"\n\t\"slices\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"golang.org/x/text/secure/precis\"\n)\n\nconst (\n\tclientNonceLen      = 18\n\tscramSHA256Name     = \"SCRAM-SHA-256\"\n\tscramSHA256PlusName = \"SCRAM-SHA-256-PLUS\"\n)\n\n// Perform SCRAM authentication.\nfunc (c *PgConn) scramAuth(serverAuthMechanisms []string) error {\n\tsc, err := newScramClient(serverAuthMechanisms, c.config.Password)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tserverHasPlus := slices.Contains(sc.serverAuthMechanisms, scramSHA256PlusName)\n\tif c.config.ChannelBinding == \"require\" && !serverHasPlus {\n\t\treturn errors.New(\"channel binding required but server does not support SCRAM-SHA-256-PLUS\")\n\t}\n\n\t// If we have a TLS connection and channel binding is not disabled, attempt to\n\t// extract the server certificate hash for tls-server-end-point channel binding.\n\tif tlsConn, ok := c.conn.(*tls.Conn); ok && c.config.ChannelBinding != \"disable\" {\n\t\tcertHash, err := getTLSCertificateHash(tlsConn)\n\t\tif err != nil && c.config.ChannelBinding == \"require\" {\n\t\t\treturn fmt.Errorf(\"channel binding required but failed to get server certificate hash: %w\", err)\n\t\t}\n\n\t\t// Upgrade to SCRAM-SHA-256-PLUS if we have binding data and the server supports it.\n\t\tif certHash != nil && serverHasPlus {\n\t\t\tsc.authMechanism = scramSHA256PlusName\n\t\t}\n\n\t\tsc.channelBindingData = certHash\n\t\tsc.hasTLS = true\n\t}\n\n\tif c.config.ChannelBinding == \"require\" && sc.channelBindingData == nil {\n\t\treturn errors.New(\"channel binding required but channel binding data is not available\")\n\t}\n\n\t// Send client-first-message in a SASLInitialResponse\n\tsaslInitialResponse := &pgproto3.SASLInitialResponse{\n\t\tAuthMechanism: sc.authMechanism,\n\t\tData:          sc.clientFirstMessage(),\n\t}\n\tc.frontend.Send(saslInitialResponse)\n\terr = c.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Receive server-first-message payload in an AuthenticationSASLContinue.\n\tsaslContinue, err := c.rxSASLContinue()\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = sc.recvServerFirstMessage(saslContinue.Data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Send client-final-message in a SASLResponse\n\tsaslResponse := &pgproto3.SASLResponse{\n\t\tData: []byte(sc.clientFinalMessage()),\n\t}\n\tc.frontend.Send(saslResponse)\n\terr = c.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Receive server-final-message payload in an AuthenticationSASLFinal.\n\tsaslFinal, err := c.rxSASLFinal()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn sc.recvServerFinalMessage(saslFinal.Data)\n}\n\nfunc (c *PgConn) rxSASLContinue() (*pgproto3.AuthenticationSASLContinue, error) {\n\tmsg, err := c.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tswitch m := msg.(type) {\n\tcase *pgproto3.AuthenticationSASLContinue:\n\t\treturn m, nil\n\tcase *pgproto3.ErrorResponse:\n\t\treturn nil, ErrorResponseToPgError(m)\n\t}\n\n\treturn nil, fmt.Errorf(\"expected AuthenticationSASLContinue message but received unexpected message %T\", msg)\n}\n\nfunc (c *PgConn) rxSASLFinal() (*pgproto3.AuthenticationSASLFinal, error) {\n\tmsg, err := c.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tswitch m := msg.(type) {\n\tcase *pgproto3.AuthenticationSASLFinal:\n\t\treturn m, nil\n\tcase *pgproto3.ErrorResponse:\n\t\treturn nil, ErrorResponseToPgError(m)\n\t}\n\n\treturn nil, fmt.Errorf(\"expected AuthenticationSASLFinal message but received unexpected message %T\", msg)\n}\n\ntype scramClient struct {\n\tserverAuthMechanisms []string\n\tpassword             string\n\tclientNonce          []byte\n\n\t// authMechanism is the selected SASL mechanism for the client. Must be\n\t// either SCRAM-SHA-256 (default) or SCRAM-SHA-256-PLUS.\n\t//\n\t// Upgraded to SCRAM-SHA-256-PLUS during authentication when channel binding\n\t// is not disabled, channel binding data is available (TLS connection with\n\t// an obtainable server certificate hash) and the server advertises\n\t// SCRAM-SHA-256-PLUS.\n\tauthMechanism string\n\n\t// hasTLS indicates whether the connection is using TLS. This is\n\t// needed because the GS2 header must distinguish between a client that\n\t// supports channel binding but the server does not (\"y,,\") versus one\n\t// that does not support it at all (\"n,,\").\n\thasTLS bool\n\n\t// channelBindingData is the hash of the server's TLS certificate, computed\n\t// per the tls-server-end-point channel binding type (RFC 5929). Used as\n\t// the binding input in SCRAM-SHA-256-PLUS. nil when not in use.\n\tchannelBindingData []byte\n\n\tclientFirstMessageBare []byte\n\tclientGS2Header        []byte\n\n\tserverFirstMessage   []byte\n\tclientAndServerNonce []byte\n\tsalt                 []byte\n\titerations           int\n\n\tsaltedPassword []byte\n\tauthMessage    []byte\n}\n\nfunc newScramClient(serverAuthMechanisms []string, password string) (*scramClient, error) {\n\tsc := &scramClient{\n\t\tserverAuthMechanisms: serverAuthMechanisms,\n\t\tauthMechanism:        scramSHA256Name,\n\t}\n\n\t// Ensure the server supports SCRAM-SHA-256. SCRAM-SHA-256-PLUS is the\n\t// channel binding variant and is only advertised when the server supports\n\t// SSL. PostgreSQL always advertises the base SCRAM-SHA-256 mechanism\n\t// regardless of SSL.\n\tif !slices.Contains(sc.serverAuthMechanisms, scramSHA256Name) {\n\t\treturn nil, errors.New(\"server does not support SCRAM-SHA-256\")\n\t}\n\n\t// precis.OpaqueString is equivalent to SASLprep for password.\n\tvar err error\n\tsc.password, err = precis.OpaqueString.String(password)\n\tif err != nil {\n\t\t// PostgreSQL allows passwords invalid according to SCRAM / SASLprep.\n\t\tsc.password = password\n\t}\n\n\tbuf := make([]byte, clientNonceLen)\n\t_, err = rand.Read(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsc.clientNonce = make([]byte, base64.RawStdEncoding.EncodedLen(len(buf)))\n\tbase64.RawStdEncoding.Encode(sc.clientNonce, buf)\n\n\treturn sc, nil\n}\n\nfunc (sc *scramClient) clientFirstMessage() []byte {\n\t// The client-first-message is the GS2 header concatenated with the bare\n\t// message (username + client nonce). The GS2 header communicates the\n\t// client's channel binding capability to the server:\n\t//\n\t//   \"n,,\"                      - client is not using TLS (channel binding not possible)\n\t//   \"y,,\"                      - client is using TLS but channel binding is not\n\t//                                in use (e.g., server did not advertise SCRAM-SHA-256-PLUS\n\t//                                or the server certificate hash was not obtainable)\n\t//   \"p=tls-server-end-point,,\" - channel binding is active via SCRAM-SHA-256-PLUS\n\t//\n\t// See:\n\t//   https://www.rfc-editor.org/rfc/rfc5802#section-6\n\t//   https://www.rfc-editor.org/rfc/rfc5929#section-4\n\t//   https://www.postgresql.org/docs/current/sasl-authentication.html#SASL-SCRAM-SHA-256\n\n\tsc.clientFirstMessageBare = fmt.Appendf(nil, \"n=,r=%s\", sc.clientNonce)\n\n\tif sc.authMechanism == scramSHA256PlusName {\n\t\tsc.clientGS2Header = []byte(\"p=tls-server-end-point,,\")\n\t} else if sc.hasTLS {\n\t\tsc.clientGS2Header = []byte(\"y,,\")\n\t} else {\n\t\tsc.clientGS2Header = []byte(\"n,,\")\n\t}\n\n\treturn append(sc.clientGS2Header, sc.clientFirstMessageBare...)\n}\n\nfunc (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error {\n\tsc.serverFirstMessage = serverFirstMessage\n\tbuf := serverFirstMessage\n\tif !bytes.HasPrefix(buf, []byte(\"r=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include r=\")\n\t}\n\tbuf = buf[2:]\n\n\tidx := bytes.IndexByte(buf, ',')\n\tif idx == -1 {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include s=\")\n\t}\n\tsc.clientAndServerNonce = buf[:idx]\n\tbuf = buf[idx+1:]\n\n\tif !bytes.HasPrefix(buf, []byte(\"s=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include s=\")\n\t}\n\tbuf = buf[2:]\n\n\tidx = bytes.IndexByte(buf, ',')\n\tif idx == -1 {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include i=\")\n\t}\n\tsaltStr := buf[:idx]\n\tbuf = buf[idx+1:]\n\n\tif !bytes.HasPrefix(buf, []byte(\"i=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include i=\")\n\t}\n\tbuf = buf[2:]\n\titerationsStr := buf\n\n\tvar err error\n\tsc.salt, err = base64.StdEncoding.DecodeString(string(saltStr))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid SCRAM salt received from server: %w\", err)\n\t}\n\n\tsc.iterations, err = strconv.Atoi(string(iterationsStr))\n\tif err != nil || sc.iterations <= 0 {\n\t\treturn fmt.Errorf(\"invalid SCRAM iteration count received from server: %w\", err)\n\t}\n\n\tif !bytes.HasPrefix(sc.clientAndServerNonce, sc.clientNonce) {\n\t\treturn errors.New(\"invalid SCRAM nonce: did not start with client nonce\")\n\t}\n\n\tif len(sc.clientAndServerNonce) <= len(sc.clientNonce) {\n\t\treturn errors.New(\"invalid SCRAM nonce: did not include server nonce\")\n\t}\n\n\treturn nil\n}\n\nfunc (sc *scramClient) clientFinalMessage() string {\n\t// The c= attribute carries the base64-encoded channel binding input.\n\t//\n\t// Without channel binding this is just the GS2 header alone (\"biws\" for\n\t// \"n,,\" or \"eSws\" for \"y,,\").\n\t//\n\t// With channel binding, this is the GS2 header with the channel binding data\n\t// (certificate hash) appended.\n\tchannelBindInput := sc.clientGS2Header\n\tif sc.authMechanism == scramSHA256PlusName {\n\t\tchannelBindInput = slices.Concat(sc.clientGS2Header, sc.channelBindingData)\n\t}\n\tchannelBindingEncoded := base64.StdEncoding.EncodeToString(channelBindInput)\n\tclientFinalMessageWithoutProof := fmt.Appendf(nil, \"c=%s,r=%s\", channelBindingEncoded, sc.clientAndServerNonce)\n\n\tvar err error\n\tsc.saltedPassword, err = pbkdf2.Key(sha256.New, sc.password, sc.salt, sc.iterations, 32)\n\tif err != nil {\n\t\tpanic(err) // This should never happen.\n\t}\n\tsc.authMessage = bytes.Join([][]byte{sc.clientFirstMessageBare, sc.serverFirstMessage, clientFinalMessageWithoutProof}, []byte(\",\"))\n\n\tclientProof := computeClientProof(sc.saltedPassword, sc.authMessage)\n\n\treturn fmt.Sprintf(\"%s,p=%s\", clientFinalMessageWithoutProof, clientProof)\n}\n\nfunc (sc *scramClient) recvServerFinalMessage(serverFinalMessage []byte) error {\n\tif !bytes.HasPrefix(serverFinalMessage, []byte(\"v=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-final-message received from server\")\n\t}\n\n\tserverSignature := serverFinalMessage[2:]\n\n\tif !hmac.Equal(serverSignature, computeServerSignature(sc.saltedPassword, sc.authMessage)) {\n\t\treturn errors.New(\"invalid SCRAM ServerSignature received from server\")\n\t}\n\n\treturn nil\n}\n\nfunc computeHMAC(key, msg []byte) []byte {\n\tmac := hmac.New(sha256.New, key)\n\tmac.Write(msg)\n\treturn mac.Sum(nil)\n}\n\nfunc computeClientProof(saltedPassword, authMessage []byte) []byte {\n\tclientKey := computeHMAC(saltedPassword, []byte(\"Client Key\"))\n\tstoredKey := sha256.Sum256(clientKey)\n\tclientSignature := computeHMAC(storedKey[:], authMessage)\n\n\tclientProof := make([]byte, len(clientSignature))\n\tfor i := range clientSignature {\n\t\tclientProof[i] = clientKey[i] ^ clientSignature[i]\n\t}\n\n\tbuf := make([]byte, base64.StdEncoding.EncodedLen(len(clientProof)))\n\tbase64.StdEncoding.Encode(buf, clientProof)\n\treturn buf\n}\n\nfunc computeServerSignature(saltedPassword, authMessage []byte) []byte {\n\tserverKey := computeHMAC(saltedPassword, []byte(\"Server Key\"))\n\tserverSignature := computeHMAC(serverKey, authMessage)\n\tbuf := make([]byte, base64.StdEncoding.EncodedLen(len(serverSignature)))\n\tbase64.StdEncoding.Encode(buf, serverSignature)\n\treturn buf\n}\n\n// Get the server certificate hash for SCRAM channel binding type\n// tls-server-end-point.\nfunc getTLSCertificateHash(conn *tls.Conn) ([]byte, error) {\n\tstate := conn.ConnectionState()\n\tif len(state.PeerCertificates) == 0 {\n\t\treturn nil, errors.New(\"no peer certificates for channel binding\")\n\t}\n\n\tcert := state.PeerCertificates[0]\n\n\t// Per RFC 5929 section 4.1: If the certificate's signatureAlgorithm uses\n\t// MD5 or SHA-1, use SHA-256. Otherwise use the hash from the signature\n\t// algorithm.\n\t//\n\t// See: https://www.rfc-editor.org/rfc/rfc5929.html#section-4.1\n\tvar h hash.Hash\n\tswitch cert.SignatureAlgorithm {\n\tcase x509.MD5WithRSA, x509.SHA1WithRSA, x509.ECDSAWithSHA1:\n\t\th = sha256.New()\n\tcase x509.SHA256WithRSA, x509.SHA256WithRSAPSS, x509.ECDSAWithSHA256:\n\t\th = sha256.New()\n\tcase x509.SHA384WithRSA, x509.SHA384WithRSAPSS, x509.ECDSAWithSHA384:\n\t\th = sha512.New384()\n\tcase x509.SHA512WithRSA, x509.SHA512WithRSAPSS, x509.ECDSAWithSHA512:\n\t\th = sha512.New()\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"tls-server-end-point channel binding is undefined for certificate signature algorithm %v\", cert.SignatureAlgorithm)\n\t}\n\n\th.Write(cert.Raw)\n\treturn h.Sum(nil), nil\n}\n"
  },
  {
    "path": "pgconn/auth_scram_test.go",
    "content": "package pgconn\n\nimport (\n\t\"bytes\"\n\t\"crypto/ecdsa\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc generateSelfSignedCert(t *testing.T, sigAlg x509.SignatureAlgorithm) tls.Certificate {\n\tt.Helper()\n\n\tvar curve elliptic.Curve\n\tswitch sigAlg {\n\tcase x509.ECDSAWithSHA1, x509.ECDSAWithSHA256:\n\t\tcurve = elliptic.P256()\n\tcase x509.ECDSAWithSHA384:\n\t\tcurve = elliptic.P384()\n\tcase x509.ECDSAWithSHA512:\n\t\tcurve = elliptic.P521()\n\tdefault:\n\t\tt.Fatalf(\"unsupported signature algorithm: %v\", sigAlg)\n\t}\n\n\tkey, err := ecdsa.GenerateKey(curve, rand.Reader)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttemplate := &x509.Certificate{\n\t\tSerialNumber:       big.NewInt(1),\n\t\tSubject:            pkix.Name{CommonName: \"test\"},\n\t\tNotBefore:          time.Now(),\n\t\tNotAfter:           time.Now().Add(time.Hour),\n\t\tSignatureAlgorithm: sigAlg,\n\t\tKeyUsage:           x509.KeyUsageDigitalSignature,\n\t\tExtKeyUsage:        []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t}\n\n\tcertDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcert, err := x509.ParseCertificate(certDER)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn tls.Certificate{\n\t\tCertificate: [][]byte{certDER},\n\t\tPrivateKey:  key,\n\t\tLeaf:        cert,\n\t}\n}\n\n// tlsConnWithCert performs a TLS handshake over a net.Pipe using the given\n// certificate and returns the client-side *tls.Conn with peer certificates\n// populated.\nfunc tlsConnWithCert(t *testing.T, cert tls.Certificate) *tls.Conn {\n\tt.Helper()\n\n\tclientConn, serverConn := net.Pipe()\n\n\tt.Cleanup(func() {\n\t\tclientConn.Close()\n\t\tserverConn.Close()\n\t})\n\n\ttlsServer := tls.Server(serverConn, &tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t})\n\n\ttlsClient := tls.Client(clientConn, &tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n\n\terrChan := make(chan error, 1)\n\tgo func() { errChan <- tlsServer.Handshake() }()\n\n\trequire.NoError(t, tlsClient.Handshake())\n\trequire.NoError(t, <-errChan)\n\n\treturn tlsClient\n}\n\nfunc TestGetTLSCertificateHash(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"SHA1\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Per RFC 5929 section 4.1: SHA-1 signed certs use SHA-256 for the hash.\n\t\tcert := generateSelfSignedCert(t, x509.ECDSAWithSHA1)\n\t\ttlsConn := tlsConnWithCert(t, cert)\n\n\t\thash, err := getTLSCertificateHash(tlsConn)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, hash, sha256.Size)\n\t})\n\n\tt.Run(\"SHA256\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcert := generateSelfSignedCert(t, x509.ECDSAWithSHA256)\n\t\ttlsConn := tlsConnWithCert(t, cert)\n\n\t\thash, err := getTLSCertificateHash(tlsConn)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, hash, sha256.Size)\n\t})\n\n\tt.Run(\"SHA384\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcert := generateSelfSignedCert(t, x509.ECDSAWithSHA384)\n\t\ttlsConn := tlsConnWithCert(t, cert)\n\n\t\thash, err := getTLSCertificateHash(tlsConn)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, hash, sha512.Size384)\n\t})\n\n\tt.Run(\"SHA512\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcert := generateSelfSignedCert(t, x509.ECDSAWithSHA512)\n\t\ttlsConn := tlsConnWithCert(t, cert)\n\n\t\thash, err := getTLSCertificateHash(tlsConn)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, hash, sha512.Size)\n\t})\n}\n\nfunc TestScramClientFirstMessage(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"ChannelBindingNotSupported\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tclient, err := newScramClient([]string{scramSHA256Name}, \"secret\")\n\t\trequire.NoError(t, err)\n\n\t\tfirstMessage := client.clientFirstMessage()\n\n\t\trequire.True(t, bytes.HasPrefix(firstMessage, []byte(\"n,,\")))\n\t\trequire.True(t, bytes.HasSuffix(firstMessage, client.clientNonce))\n\t})\n\n\tt.Run(\"ChannelBindingClientSupported\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tclient, err := newScramClient([]string{scramSHA256Name}, \"secret\")\n\t\trequire.NoError(t, err)\n\n\t\tclient.authMechanism = scramSHA256Name\n\t\tclient.hasTLS = true\n\t\tclient.channelBindingData = []byte{1, 2, 3}\n\n\t\tfirstMessage := client.clientFirstMessage()\n\t\trequire.True(t, bytes.HasPrefix(firstMessage, []byte(\"y,,\")))\n\t})\n\n\tt.Run(\"ChannelBindingTLSWithoutCertHash\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// When on TLS but cert hash is unavailable (e.g., unsupported signature\n\t\t// algorithm), the client should still send \"y,,\" to enable downgrade\n\t\t// detection per RFC 5802.\n\t\tclient, err := newScramClient([]string{scramSHA256Name}, \"secret\")\n\t\trequire.NoError(t, err)\n\n\t\tclient.authMechanism = scramSHA256Name\n\t\tclient.hasTLS = true\n\t\tclient.channelBindingData = nil\n\n\t\tfirstMessage := client.clientFirstMessage()\n\t\trequire.True(t, bytes.HasPrefix(firstMessage, []byte(\"y,,\")))\n\t})\n\n\tt.Run(\"ChannelBindingActive\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tclient, err := newScramClient([]string{scramSHA256Name, scramSHA256PlusName}, \"secret\")\n\t\trequire.NoError(t, err)\n\n\t\tclient.authMechanism = scramSHA256PlusName\n\t\tclient.channelBindingData = []byte{1, 2, 3}\n\n\t\tfirstMessage := client.clientFirstMessage()\n\t\trequire.True(t, bytes.HasPrefix(firstMessage, []byte(\"p=tls-server-end-point,,\")))\n\t})\n}\n\nfunc TestScramClientFinalMessage(t *testing.T) {\n\tt.Parallel()\n\n\tsetup := func(t *testing.T) *scramClient {\n\t\tt.Helper()\n\n\t\treturn &scramClient{\n\t\t\tclientNonce:   []byte(\"testnonce\"),\n\t\t\tpassword:      \"secret\",\n\t\t\tauthMechanism: scramSHA256Name,\n\t\t}\n\t}\n\n\t// withServerChallenge advances the scramClient through the client-first\n\t// and server-first (challenge) messages, leaving it ready to produce the\n\t// client-final-message.\n\twithServerChallenge := func(t *testing.T, sc *scramClient) {\n\t\tt.Helper()\n\n\t\tsc.clientFirstMessage()\n\n\t\tserverNonce := string(sc.clientNonce) + \"servernonce\"\n\t\tsalt := base64.StdEncoding.EncodeToString([]byte(\"testsalt\"))\n\t\tserverFirstMsg := fmt.Sprintf(\"r=%s,s=%s,i=4096\", serverNonce, salt)\n\t\trequire.NoError(t, sc.recvServerFirstMessage([]byte(serverFirstMsg)))\n\t}\n\n\tt.Run(\"ChannelBindingNone\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsc := setup(t)\n\t\twithServerChallenge(t, sc)\n\n\t\tmsg := sc.clientFinalMessage()\n\n\t\texpected := base64.StdEncoding.EncodeToString([]byte(\"n,,\"))\n\t\trequire.Contains(t, msg, \"c=\"+expected)\n\t})\n\n\tt.Run(\"ChannelBindingClientSupports\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsc := setup(t)\n\t\tsc.hasTLS = true\n\t\tsc.channelBindingData = []byte{1, 2, 3}\n\n\t\twithServerChallenge(t, sc)\n\n\t\tmsg := sc.clientFinalMessage()\n\n\t\texpected := base64.StdEncoding.EncodeToString([]byte(\"y,,\"))\n\t\trequire.Contains(t, msg, \"c=\"+expected)\n\t})\n\n\tt.Run(\"ChannelBindingActive\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsc := setup(t)\n\t\tsc.authMechanism = scramSHA256PlusName\n\t\tsc.hasTLS = true\n\t\tsc.channelBindingData = []byte{1, 2, 3}\n\n\t\twithServerChallenge(t, sc)\n\n\t\tmsg := sc.clientFinalMessage()\n\n\t\texpected := base64.StdEncoding.EncodeToString(append([]byte(\"p=tls-server-end-point,,\"), 0x01, 0x02, 0x03))\n\t\trequire.Contains(t, msg, \"c=\"+expected)\n\t})\n}\n\nfunc TestScramClientMechanismValidation(t *testing.T) {\n\tt.Parallel()\n\n\t// Server does not support SSL.\n\t_, err := newScramClient([]string{scramSHA256Name}, \"password\")\n\trequire.NoError(t, err)\n\n\t// Server supports SSL.\n\t_, err = newScramClient([]string{scramSHA256PlusName, scramSHA256Name}, \"password\")\n\trequire.NoError(t, err)\n\n\t// Invalid.\n\t_, err = newScramClient([]string{\"MD5\"}, \"password\")\n\trequire.Error(t, err)\n}\n\nfunc TestScramClientRecvServerFirstMessage(t *testing.T) {\n\tt.Parallel()\n\n\tclientNonce := \"testnonce\"\n\tserverNonce := clientNonce + \"servernonce\"\n\tsalt := \"testsalt\"\n\tsaltEncoded := base64.StdEncoding.EncodeToString([]byte(salt))\n\n\tt.Run(\"Valid\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// SCRAM server-first-message has the form: r=<client+server nonce>,s=<base64 salt>,i=<iterations>\n\t\tvalidMsg := fmt.Sprintf(\"r=%s,s=%s,i=%d\", serverNonce, saltEncoded, 4096)\n\n\t\tsc := &scramClient{clientNonce: []byte(clientNonce)}\n\t\terr := sc.recvServerFirstMessage([]byte(validMsg))\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, []byte(serverNonce), sc.clientAndServerNonce)\n\t\trequire.Equal(t, []byte(salt), sc.salt)\n\t\trequire.Equal(t, 4096, sc.iterations)\n\t})\n\n\tt.Run(\"Invalid\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\tsc := &scramClient{clientNonce: []byte(clientNonce)}\n\n\t\t// Missing nonce.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"s=\" + saltEncoded + \",i=4096\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"did not include r=\")\n\t\t}\n\n\t\t// Missing salt.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=\" + serverNonce + \",i=4096\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"did not include s=\")\n\t\t}\n\n\t\t// Missing iterations.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=\" + serverNonce + \",s=\" + saltEncoded))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"did not include i=\")\n\t\t}\n\n\t\t// Invalid salt encoding.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=\" + serverNonce + \",s=%%%invalid,i=4096\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"invalid SCRAM salt\")\n\t\t}\n\n\t\t// Non-numeric iteration count.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=\" + serverNonce + \",s=\" + saltEncoded + \",i=notanumber\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"invalid SCRAM iteration count\")\n\t\t}\n\n\t\t// Zero iteration count.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=\" + serverNonce + \",s=\" + saltEncoded + \",i=0\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"invalid SCRAM iteration count\")\n\t\t}\n\n\t\t// Nonce missing client prefix.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=wrongnonce,s=\" + saltEncoded + \",i=4096\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"did not start with client nonce\")\n\t\t}\n\n\t\t// Nonce without server contribution.\n\t\t{\n\t\t\terr := sc.recvServerFirstMessage([]byte(\"r=\" + clientNonce + \",s=\" + saltEncoded + \",i=4096\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"did not include server nonce\")\n\t\t}\n\t})\n}\n\nfunc TestScramClientRecvServerFinalMessage(t *testing.T) {\n\tt.Parallel()\n\n\tsetup := func(t *testing.T) *scramClient {\n\t\tt.Helper()\n\n\t\t// Build a scramClient that has completed the full message exchange up\n\t\t// through clientFinalMessage, ready to receive server-final-message.\n\t\tsc := &scramClient{\n\t\t\tclientNonce:   []byte(\"testnonce\"),\n\t\t\tauthMechanism: scramSHA256Name,\n\t\t\tpassword:      \"secret\",\n\t\t}\n\t\tsc.clientFirstMessage()\n\n\t\tserverNonce := string(sc.clientNonce) + \"servernonce\"\n\t\tsalt := base64.StdEncoding.EncodeToString([]byte(\"testsalt\"))\n\t\tserverFirstMsg := fmt.Sprintf(\"r=%s,s=%s,i=4096\", serverNonce, salt)\n\t\trequire.NoError(t, sc.recvServerFirstMessage([]byte(serverFirstMsg)))\n\n\t\tsc.clientFinalMessage()\n\n\t\treturn sc\n\t}\n\n\tt.Run(\"Valid\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsc := setup(t)\n\n\t\tvalidSignature := computeServerSignature(sc.saltedPassword, sc.authMessage)\n\t\terr := sc.recvServerFinalMessage(append([]byte(\"v=\"), validSignature...))\n\t\trequire.NoError(t, err)\n\t})\n\n\tt.Run(\"Invalid\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsc := setup(t)\n\n\t\t// Missing server signature attribute.\n\t\t{\n\t\t\terr := sc.recvServerFinalMessage([]byte(\"e=some-error\"))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"invalid SCRAM server-final-message\")\n\t\t}\n\n\t\t// Invalid server signature.\n\t\t{\n\t\t\twrongSig := base64.StdEncoding.EncodeToString([]byte(\"wrong\"))\n\t\t\terr := sc.recvServerFinalMessage([]byte(\"v=\" + wrongSig))\n\t\t\trequire.Error(t, err)\n\t\t\trequire.Contains(t, err.Error(), \"invalid SCRAM ServerSignature\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgconn/benchmark_private_test.go",
    "content": "package pgconn\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc BenchmarkCommandTagRowsAffected(b *testing.B) {\n\tbenchmarks := []struct {\n\t\tcommandTag   string\n\t\trowsAffected int64\n\t}{\n\t\t{\"UPDATE 1\", 1},\n\t\t{\"UPDATE 123456789\", 123456789},\n\t\t{\"INSERT 0 1\", 1},\n\t\t{\"INSERT 0 123456789\", 123456789},\n\t}\n\n\tfor _, bm := range benchmarks {\n\t\tct := CommandTag{s: bm.commandTag}\n\t\tb.Run(bm.commandTag, func(b *testing.B) {\n\t\t\tvar n int64\n\t\t\tfor b.Loop() {\n\t\t\t\tn = ct.RowsAffected()\n\t\t\t}\n\t\t\tif n != bm.rowsAffected {\n\t\t\t\tb.Errorf(\"expected %d got %d\", bm.rowsAffected, n)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkCommandTagTypeFromString(b *testing.B) {\n\tct := CommandTag{s: \"UPDATE 1\"}\n\n\tvar update bool\n\tfor b.Loop() {\n\t\tupdate = strings.HasPrefix(ct.String(), \"UPDATE\")\n\t}\n\tif !update {\n\t\tb.Error(\"expected update\")\n\t}\n}\n\nfunc BenchmarkCommandTagInsert(b *testing.B) {\n\tbenchmarks := []struct {\n\t\tcommandTag string\n\t\tis         bool\n\t}{\n\t\t{\"INSERT 1\", true},\n\t\t{\"INSERT 1234567890\", true},\n\t\t{\"UPDATE 1\", false},\n\t\t{\"UPDATE 1234567890\", false},\n\t\t{\"DELETE 1\", false},\n\t\t{\"DELETE 1234567890\", false},\n\t\t{\"SELECT 1\", false},\n\t\t{\"SELECT 1234567890\", false},\n\t\t{\"UNKNOWN 1234567890\", false},\n\t}\n\n\tfor _, bm := range benchmarks {\n\t\tct := CommandTag{s: bm.commandTag}\n\t\tb.Run(bm.commandTag, func(b *testing.B) {\n\t\t\tvar is bool\n\t\t\tfor b.Loop() {\n\t\t\t\tis = ct.Insert()\n\t\t\t}\n\t\t\tif is != bm.is {\n\t\t\t\tb.Errorf(\"expected %v got %v\", bm.is, is)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgconn/benchmark_test.go",
    "content": "package pgconn_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc BenchmarkConnect(b *testing.B) {\n\tbenchmarks := []struct {\n\t\tname string\n\t\tenv  string\n\t}{\n\t\t{\"Unix socket\", \"PGX_TEST_UNIX_SOCKET_CONN_STRING\"},\n\t\t{\"TCP\", \"PGX_TEST_TCP_CONN_STRING\"},\n\t}\n\n\tfor _, bm := range benchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tconnString := os.Getenv(bm.env)\n\t\t\tif connString == \"\" {\n\t\t\t\tb.Skipf(\"Skipping due to missing environment variable %v\", bm.env)\n\t\t\t}\n\n\t\t\tfor b.Loop() {\n\t\t\t\tconn, err := pgconn.Connect(context.Background(), connString)\n\t\t\t\trequire.Nil(b, err)\n\n\t\t\t\terr = conn.Close(context.Background())\n\t\t\t\trequire.Nil(b, err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkExec(b *testing.B) {\n\texpectedValues := [][]byte{[]byte(\"hello\"), []byte(\"42\"), []byte(\"2019-01-01\")}\n\tbenchmarks := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t}{\n\t\t// Using an empty context other than context.Background() to compare\n\t\t// performance\n\t\t{\"background context\", context.Background()},\n\t\t{\"empty context\", context.TODO()},\n\t}\n\n\tfor _, bm := range benchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tconn, err := pgconn.Connect(bm.ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\trequire.Nil(b, err)\n\t\t\tdefer closeConn(b, conn)\n\n\t\t\tb.ResetTimer()\n\n\t\t\tfor b.Loop() {\n\t\t\t\tmrr := conn.Exec(bm.ctx, \"select 'hello'::text as a, 42::int4 as b, '2019-01-01'::date\")\n\n\t\t\t\tfor mrr.NextResult() {\n\t\t\t\t\trr := mrr.ResultReader()\n\n\t\t\t\t\trowCount := 0\n\t\t\t\t\tfor rr.NextRow() {\n\t\t\t\t\t\trowCount++\n\t\t\t\t\t\tif len(rr.Values()) != len(expectedValues) {\n\t\t\t\t\t\t\tb.Fatalf(\"unexpected number of values: %d\", len(rr.Values()))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor i := range rr.Values() {\n\t\t\t\t\t\t\tif !bytes.Equal(rr.Values()[i], expectedValues[i]) {\n\t\t\t\t\t\t\t\tb.Fatalf(\"unexpected values: %s %s\", rr.Values()[i], expectedValues[i])\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t_, err = rr.Close()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\tif rowCount != 1 {\n\t\t\t\t\t\tb.Fatalf(\"unexpected rowCount: %d\", rowCount)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\terr := mrr.Close()\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkExecPossibleToCancel(b *testing.B) {\n\tconn, err := pgconn.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.Nil(b, err)\n\tdefer closeConn(b, conn)\n\n\texpectedValues := [][]byte{[]byte(\"hello\"), []byte(\"42\"), []byte(\"2019-01-01\")}\n\n\tctx := b.Context()\n\n\tfor b.Loop() {\n\t\tmrr := conn.Exec(ctx, \"select 'hello'::text as a, 42::int4 as b, '2019-01-01'::date\")\n\n\t\tfor mrr.NextResult() {\n\t\t\trr := mrr.ResultReader()\n\n\t\t\trowCount := 0\n\t\t\tfor rr.NextRow() {\n\t\t\t\trowCount++\n\t\t\t\tif len(rr.Values()) != len(expectedValues) {\n\t\t\t\t\tb.Fatalf(\"unexpected number of values: %d\", len(rr.Values()))\n\t\t\t\t}\n\t\t\t\tfor i := range rr.Values() {\n\t\t\t\t\tif !bytes.Equal(rr.Values()[i], expectedValues[i]) {\n\t\t\t\t\t\tb.Fatalf(\"unexpected values: %s %s\", rr.Values()[i], expectedValues[i])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t_, err = rr.Close()\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t\tif rowCount != 1 {\n\t\t\t\tb.Fatalf(\"unexpected rowCount: %d\", rowCount)\n\t\t\t}\n\t\t}\n\n\t\terr := mrr.Close()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkExecPrepared(b *testing.B) {\n\texpectedValues := [][]byte{[]byte(\"hello\"), []byte(\"42\"), []byte(\"2019-01-01\")}\n\n\tbenchmarks := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t}{\n\t\t// Using an empty context other than context.Background() to compare\n\t\t// performance\n\t\t{\"background context\", context.Background()},\n\t\t{\"empty context\", context.TODO()},\n\t}\n\n\tfor _, bm := range benchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tconn, err := pgconn.Connect(bm.ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\trequire.Nil(b, err)\n\t\t\tdefer closeConn(b, conn)\n\n\t\t\t_, err = conn.Prepare(bm.ctx, \"ps1\", \"select 'hello'::text as a, 42::int4 as b, '2019-01-01'::date\", nil)\n\t\t\trequire.Nil(b, err)\n\n\t\t\tb.ResetTimer()\n\n\t\t\tfor b.Loop() {\n\t\t\t\trr := conn.ExecPrepared(bm.ctx, \"ps1\", nil, nil, nil)\n\n\t\t\t\trowCount := 0\n\t\t\t\tfor rr.NextRow() {\n\t\t\t\t\trowCount++\n\t\t\t\t\tif len(rr.Values()) != len(expectedValues) {\n\t\t\t\t\t\tb.Fatalf(\"unexpected number of values: %d\", len(rr.Values()))\n\t\t\t\t\t}\n\t\t\t\t\tfor i := range rr.Values() {\n\t\t\t\t\t\tif !bytes.Equal(rr.Values()[i], expectedValues[i]) {\n\t\t\t\t\t\t\tb.Fatalf(\"unexpected values: %s %s\", rr.Values()[i], expectedValues[i])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t_, err = rr.Close()\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif rowCount != 1 {\n\t\t\t\t\tb.Fatalf(\"unexpected rowCount: %d\", rowCount)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkExecPreparedPossibleToCancel(b *testing.B) {\n\tconn, err := pgconn.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.Nil(b, err)\n\tdefer closeConn(b, conn)\n\n\tctx := b.Context()\n\n\t_, err = conn.Prepare(ctx, \"ps1\", \"select 'hello'::text as a, 42::int4 as b, '2019-01-01'::date\", nil)\n\trequire.Nil(b, err)\n\n\texpectedValues := [][]byte{[]byte(\"hello\"), []byte(\"42\"), []byte(\"2019-01-01\")}\n\n\tfor b.Loop() {\n\t\trr := conn.ExecPrepared(ctx, \"ps1\", nil, nil, nil)\n\n\t\trowCount := 0\n\t\tfor rr.NextRow() {\n\t\t\trowCount += 1\n\t\t\tif len(rr.Values()) != len(expectedValues) {\n\t\t\t\tb.Fatalf(\"unexpected number of values: %d\", len(rr.Values()))\n\t\t\t}\n\t\t\tfor i := range rr.Values() {\n\t\t\t\tif !bytes.Equal(rr.Values()[i], expectedValues[i]) {\n\t\t\t\t\tb.Fatalf(\"unexpected values: %s %s\", rr.Values()[i], expectedValues[i])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t_, err = rr.Close()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif rowCount != 1 {\n\t\t\tb.Fatalf(\"unexpected rowCount: %d\", rowCount)\n\t\t}\n\t}\n}\n\n// func BenchmarkChanToSetDeadlinePossibleToCancel(b *testing.B) {\n// \tconn, err := pgconn.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n// \trequire.Nil(b, err)\n// \tdefer closeConn(b, conn)\n\n// \tctx, cancel := context.WithCancel(context.Background())\n// \tdefer cancel()\n\n// \tb.ResetTimer()\n\n// \tfor i := 0; i < b.N; i++ {\n// \t\tconn.ChanToSetDeadline().Watch(ctx)\n// \t\tconn.ChanToSetDeadline().Ignore()\n// \t}\n// }\n"
  },
  {
    "path": "pgconn/config.go",
    "content": "package pgconn\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"math\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jackc/pgpassfile\"\n\t\"github.com/jackc/pgservicefile\"\n\t\"github.com/jackc/pgx/v5/pgconn/ctxwatch\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n)\n\ntype (\n\tAfterConnectFunc    func(ctx context.Context, pgconn *PgConn) error\n\tValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error\n\tGetSSLPasswordFunc  func(ctx context.Context) string\n)\n\n// Config is the settings used to establish a connection to a PostgreSQL server. It must be created by [ParseConfig]. A\n// manually initialized Config will cause ConnectConfig to panic.\ntype Config struct {\n\tHost           string // host (e.g. localhost) or absolute path to unix domain socket directory (e.g. /private/tmp)\n\tPort           uint16\n\tDatabase       string\n\tUser           string\n\tPassword       string\n\tTLSConfig      *tls.Config // nil disables TLS\n\tConnectTimeout time.Duration\n\tDialFunc       DialFunc   // e.g. net.Dialer.DialContext\n\tLookupFunc     LookupFunc // e.g. net.Resolver.LookupHost\n\tBuildFrontend  BuildFrontendFunc\n\n\t// BuildContextWatcherHandler is called to create a ContextWatcherHandler for a connection. The handler is called\n\t// when a context passed to a PgConn method is canceled.\n\tBuildContextWatcherHandler func(*PgConn) ctxwatch.Handler\n\n\tRuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)\n\n\tKerberosSrvName string\n\tKerberosSpn     string\n\tFallbacks       []*FallbackConfig\n\n\tSSLNegotiation string // sslnegotiation=postgres or sslnegotiation=direct\n\n\t// AfterNetConnect is called after the network connection, including TLS if applicable, is established but before any\n\t// PostgreSQL protocol communication. It takes the established net.Conn and returns a net.Conn that will be used in\n\t// its place. It can be used to wrap the net.Conn (e.g. for logging, diagnostics, or testing). Its functionality has\n\t// some overlap with DialFunc. However, DialFunc takes place before TLS is established and cannot be used to control\n\t// the final net.Conn used for PostgreSQL protocol communication while AfterNetConnect can.\n\tAfterNetConnect func(ctx context.Context, config *Config, conn net.Conn) (net.Conn, error)\n\n\t// ValidateConnect is called during a connection attempt after a successful authentication with the PostgreSQL server.\n\t// It can be used to validate that the server is acceptable. If this returns an error the connection is closed and the next\n\t// fallback config is tried. This allows implementing high availability behavior such as libpq does with target_session_attrs.\n\tValidateConnect ValidateConnectFunc\n\n\t// AfterConnect is called after ValidateConnect. It can be used to set up the connection (e.g. Set session variables\n\t// or prepare statements). If this returns an error the connection attempt fails.\n\tAfterConnect AfterConnectFunc\n\n\t// OnNotice is a callback function called when a notice response is received.\n\tOnNotice NoticeHandler\n\n\t// OnNotification is a callback function called when a notification from the LISTEN/NOTIFY system is received.\n\tOnNotification NotificationHandler\n\n\t// OnPgError is a callback function called when a Postgres error is received by the server. The default handler will close\n\t// the connection on any FATAL errors. If you override this handler you should call the previously set handler or ensure\n\t// that you close on FATAL errors by returning false.\n\tOnPgError PgErrorHandler\n\n\t// OAuthTokenProvider is a function that returns an OAuth token for authentication. If set, it will be used for\n\t// OAUTHBEARER SASL authentication when the server requests it.\n\tOAuthTokenProvider func(context.Context) (string, error)\n\n\t// MinProtocolVersion is the minimum acceptable PostgreSQL protocol version.\n\t// If the server does not support at least this version, the connection will fail.\n\t// Valid values: \"3.0\", \"3.2\", \"latest\". Defaults to \"3.0\".\n\tMinProtocolVersion string\n\n\t// MaxProtocolVersion is the maximum PostgreSQL protocol version to request from the server.\n\t// Valid values: \"3.0\", \"3.2\", \"latest\". Defaults to \"3.0\" for compatibility.\n\tMaxProtocolVersion string\n\n\t// ChannelBinding is the channel_binding parameter for SCRAM-SHA-256-PLUS authentication.\n\t// Valid values: \"disable\", \"prefer\", \"require\". Defaults to \"prefer\".\n\tChannelBinding string\n\n\tcreatedByParseConfig bool // Used to enforce created by ParseConfig rule.\n}\n\n// ParseConfigOptions contains options that control how a config is built such as GetSSLPassword.\ntype ParseConfigOptions struct {\n\t// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the libpq function\n\t// PQsetSSLKeyPassHook_OpenSSL.\n\tGetSSLPassword GetSSLPasswordFunc\n}\n\n// Copy returns a deep copy of the config that is safe to use and modify.\n// The only exception is the TLSConfig field:\n// according to the tls.Config docs it must not be modified after creation.\nfunc (c *Config) Copy() *Config {\n\tnewConf := new(Config)\n\t*newConf = *c\n\tif newConf.TLSConfig != nil {\n\t\tnewConf.TLSConfig = c.TLSConfig.Clone()\n\t}\n\tif newConf.RuntimeParams != nil {\n\t\tnewConf.RuntimeParams = make(map[string]string, len(c.RuntimeParams))\n\t\tmaps.Copy(newConf.RuntimeParams, c.RuntimeParams)\n\t}\n\tif newConf.Fallbacks != nil {\n\t\tnewConf.Fallbacks = make([]*FallbackConfig, len(c.Fallbacks))\n\t\tfor i, fallback := range c.Fallbacks {\n\t\t\tnewFallback := new(FallbackConfig)\n\t\t\t*newFallback = *fallback\n\t\t\tif newFallback.TLSConfig != nil {\n\t\t\t\tnewFallback.TLSConfig = fallback.TLSConfig.Clone()\n\t\t\t}\n\t\t\tnewConf.Fallbacks[i] = newFallback\n\t\t}\n\t}\n\treturn newConf\n}\n\n// FallbackConfig is additional settings to attempt a connection with when the primary Config fails to establish a\n// network connection. It is used for TLS fallback such as sslmode=prefer and high availability (HA) connections.\ntype FallbackConfig struct {\n\tHost      string // host (e.g. localhost) or path to unix domain socket directory (e.g. /private/tmp)\n\tPort      uint16\n\tTLSConfig *tls.Config // nil disables TLS\n}\n\n// connectOneConfig is the configuration for a single attempt to connect to a single host.\ntype connectOneConfig struct {\n\tnetwork          string\n\taddress          string\n\toriginalHostname string      // original hostname before resolving\n\ttlsConfig        *tls.Config // nil disables TLS\n}\n\n// isAbsolutePath checks if the provided value is an absolute path either\n// beginning with a forward slash (as on Linux-based systems) or with a capital\n// letter A-Z followed by a colon and a backslash, e.g., \"C:\\\", (as on Windows).\nfunc isAbsolutePath(path string) bool {\n\tisWindowsPath := func(p string) bool {\n\t\tif len(p) < 3 {\n\t\t\treturn false\n\t\t}\n\t\tdrive := p[0]\n\t\tcolon := p[1]\n\t\tbackslash := p[2]\n\t\tif drive >= 'A' && drive <= 'Z' && colon == ':' && backslash == '\\\\' {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\treturn strings.HasPrefix(path, \"/\") || isWindowsPath(path)\n}\n\n// NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with\n// net.Dial.\nfunc NetworkAddress(host string, port uint16) (network, address string) {\n\tif isAbsolutePath(host) {\n\t\tnetwork = \"unix\"\n\t\taddress = filepath.Join(host, \".s.PGSQL.\") + strconv.FormatInt(int64(port), 10)\n\t} else {\n\t\tnetwork = \"tcp\"\n\t\taddress = net.JoinHostPort(host, strconv.Itoa(int(port)))\n\t}\n\treturn network, address\n}\n\n// ParseConfig builds a *Config from connString with similar behavior to the PostgreSQL standard C library libpq. It\n// uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely\n// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format. See\n// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be empty\n// to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.\n//\n//\t# Example Keyword/Value\n//\tuser=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca\n//\n//\t# Example URL\n//\tpostgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca\n//\n// The returned *Config may be modified. However, it is strongly recommended that any configuration that can be done\n// through the connection string be done there. In particular the fields Host, Port, TLSConfig, and Fallbacks can be\n// interdependent (e.g. TLSConfig needs knowledge of the host to validate the server certificate). These fields should\n// not be modified individually. They should all be modified or all left unchanged.\n//\n// ParseConfig supports specifying multiple hosts in similar manner to libpq. Host and port may include comma separated\n// values that will be tried in order. This can be used as part of a high availability system. See\n// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS for more information.\n//\n//\t# Example URL\n//\tpostgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb\n//\n// ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed\n// via database URL or keyword/value:\n//\n//\tPGHOST\n//\tPGPORT\n//\tPGDATABASE\n//\tPGUSER\n//\tPGPASSWORD\n//\tPGPASSFILE\n//\tPGSERVICE\n//\tPGSERVICEFILE\n//\tPGSSLMODE\n//\tPGSSLCERT\n//\tPGSSLKEY\n//\tPGSSLROOTCERT\n//\tPGSSLPASSWORD\n//\tPGOPTIONS\n//\tPGAPPNAME\n//\tPGCONNECT_TIMEOUT\n//\tPGTARGETSESSIONATTRS\n//\tPGTZ\n//\tPGMINPROTOCOLVERSION\n//\tPGMAXPROTOCOLVERSION\n//\n// See http://www.postgresql.org/docs/current/static/libpq-envars.html for details on the meaning of environment variables.\n//\n// See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS for parameter key word names. They are\n// usually but not always the environment variable name downcased and without the \"PG\" prefix.\n//\n// Important Security Notes:\n//\n// ParseConfig tries to match libpq behavior with regard to PGSSLMODE. This includes defaulting to \"prefer\" behavior if\n// not set.\n//\n// See http://www.postgresql.org/docs/current/static/libpq-ssl.html#LIBPQ-SSL-PROTECTION for details on what level of\n// security each sslmode provides.\n//\n// The sslmode \"prefer\" (the default), sslmode \"allow\", and multiple hosts are implemented via the Fallbacks field of\n// the Config struct. If TLSConfig is manually changed it will not affect the fallbacks. For example, in the case of\n// sslmode \"prefer\" this means it will first try the main Config settings which use TLS, then it will try the fallback\n// which does not use TLS. This can lead to an unexpected unencrypted connection if the main TLS config is manually\n// changed later but the unencrypted fallback is present. Ensure there are no stale fallbacks when manually setting\n// TLSConfig.\n//\n// Other known differences with libpq:\n//\n// When multiple hosts are specified, libpq allows them to have different passwords set via the .pgpass file. pgconn\n// does not.\n//\n// In addition, ParseConfig accepts the following options:\n//\n//   - servicefile.\n//     libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a\n//     part of the connection string.\nfunc ParseConfig(connString string) (*Config, error) {\n\tvar parseConfigOptions ParseConfigOptions\n\treturn ParseConfigWithOptions(connString, parseConfigOptions)\n}\n\n// ParseConfigWithOptions builds a *Config from connString and options with similar behavior to the PostgreSQL standard\n// C library libpq. options contains settings that cannot be specified in a connString such as providing a function to\n// get the SSL password.\nfunc ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Config, error) {\n\tdefaultSettings := defaultSettings()\n\tenvSettings := parseEnvSettings()\n\n\tconnStringSettings := make(map[string]string)\n\tif connString != \"\" {\n\t\tvar err error\n\t\t// connString may be a database URL or in PostgreSQL keyword/value format\n\t\tif strings.HasPrefix(connString, \"postgres://\") || strings.HasPrefix(connString, \"postgresql://\") {\n\t\t\tconnStringSettings, err = parseURLSettings(connString)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"failed to parse as URL\", err: err}\n\t\t\t}\n\t\t} else {\n\t\t\tconnStringSettings, err = parseKeywordValueSettings(connString)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"failed to parse as keyword/value\", err: err}\n\t\t\t}\n\t\t}\n\t}\n\n\tsettings := mergeSettings(defaultSettings, envSettings, connStringSettings)\n\tif service, present := settings[\"service\"]; present {\n\t\tserviceSettings, err := parseServiceSettings(settings[\"servicefile\"], service)\n\t\tif err != nil {\n\t\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"failed to read service\", err: err}\n\t\t}\n\n\t\tsettings = mergeSettings(defaultSettings, envSettings, serviceSettings, connStringSettings)\n\t}\n\n\tconfig := &Config{\n\t\tcreatedByParseConfig: true,\n\t\tDatabase:             settings[\"database\"],\n\t\tUser:                 settings[\"user\"],\n\t\tPassword:             settings[\"password\"],\n\t\tRuntimeParams:        make(map[string]string),\n\t\tBuildFrontend: func(r io.Reader, w io.Writer) *pgproto3.Frontend {\n\t\t\treturn pgproto3.NewFrontend(r, w)\n\t\t},\n\t\tBuildContextWatcherHandler: func(pgConn *PgConn) ctxwatch.Handler {\n\t\t\treturn &DeadlineContextWatcherHandler{Conn: pgConn.conn}\n\t\t},\n\t\tOnPgError: func(_ *PgConn, pgErr *PgError) bool {\n\t\t\t// we want to automatically close any fatal errors\n\t\t\tif strings.EqualFold(pgErr.Severity, \"FATAL\") {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t},\n\t}\n\n\tif connectTimeoutSetting, present := settings[\"connect_timeout\"]; present {\n\t\tconnectTimeout, err := parseConnectTimeoutSetting(connectTimeoutSetting)\n\t\tif err != nil {\n\t\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"invalid connect_timeout\", err: err}\n\t\t}\n\t\tconfig.ConnectTimeout = connectTimeout\n\t\tconfig.DialFunc = makeConnectTimeoutDialFunc(connectTimeout)\n\t} else {\n\t\tdefaultDialer := makeDefaultDialer()\n\t\tconfig.DialFunc = defaultDialer.DialContext\n\t}\n\n\tconfig.LookupFunc = makeDefaultResolver().LookupHost\n\n\tnotRuntimeParams := map[string]struct{}{\n\t\t\"host\":                 {},\n\t\t\"port\":                 {},\n\t\t\"database\":             {},\n\t\t\"user\":                 {},\n\t\t\"password\":             {},\n\t\t\"passfile\":             {},\n\t\t\"connect_timeout\":      {},\n\t\t\"sslmode\":              {},\n\t\t\"sslkey\":               {},\n\t\t\"sslcert\":              {},\n\t\t\"sslrootcert\":          {},\n\t\t\"sslnegotiation\":       {},\n\t\t\"sslpassword\":          {},\n\t\t\"sslsni\":               {},\n\t\t\"krbspn\":               {},\n\t\t\"krbsrvname\":           {},\n\t\t\"target_session_attrs\": {},\n\t\t\"service\":              {},\n\t\t\"servicefile\":          {},\n\t\t\"min_protocol_version\": {},\n\t\t\"max_protocol_version\": {},\n\t\t\"channel_binding\":      {},\n\t}\n\n\t// Adding kerberos configuration\n\tif _, present := settings[\"krbsrvname\"]; present {\n\t\tconfig.KerberosSrvName = settings[\"krbsrvname\"]\n\t}\n\tif _, present := settings[\"krbspn\"]; present {\n\t\tconfig.KerberosSpn = settings[\"krbspn\"]\n\t}\n\n\tfor k, v := range settings {\n\t\tif _, present := notRuntimeParams[k]; present {\n\t\t\tcontinue\n\t\t}\n\t\tconfig.RuntimeParams[k] = v\n\t}\n\n\tfallbacks := []*FallbackConfig{}\n\n\thosts := strings.Split(settings[\"host\"], \",\")\n\tports := strings.Split(settings[\"port\"], \",\")\n\n\tfor i, host := range hosts {\n\t\tvar portStr string\n\t\tif i < len(ports) {\n\t\t\tportStr = ports[i]\n\t\t} else {\n\t\t\tportStr = ports[0]\n\t\t}\n\n\t\tport, err := parsePort(portStr)\n\t\tif err != nil {\n\t\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"invalid port\", err: err}\n\t\t}\n\n\t\tvar tlsConfigs []*tls.Config\n\n\t\t// Ignore TLS settings if Unix domain socket like libpq\n\t\tif network, _ := NetworkAddress(host, port); network == \"unix\" {\n\t\t\ttlsConfigs = append(tlsConfigs, nil)\n\t\t} else {\n\t\t\tvar err error\n\t\t\ttlsConfigs, err = configTLS(settings, host, options)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"failed to configure TLS\", err: err}\n\t\t\t}\n\t\t}\n\n\t\tfor _, tlsConfig := range tlsConfigs {\n\t\t\tfallbacks = append(fallbacks, &FallbackConfig{\n\t\t\t\tHost:      host,\n\t\t\t\tPort:      port,\n\t\t\t\tTLSConfig: tlsConfig,\n\t\t\t})\n\t\t}\n\t}\n\n\tconfig.Host = fallbacks[0].Host\n\tconfig.Port = fallbacks[0].Port\n\tconfig.TLSConfig = fallbacks[0].TLSConfig\n\tconfig.Fallbacks = fallbacks[1:]\n\tconfig.SSLNegotiation = settings[\"sslnegotiation\"]\n\n\tpassfile, err := pgpassfile.ReadPassfile(settings[\"passfile\"])\n\tif err == nil {\n\t\tif config.Password == \"\" {\n\t\t\thost := config.Host\n\t\t\tif network, _ := NetworkAddress(config.Host, config.Port); network == \"unix\" {\n\t\t\t\thost = \"localhost\"\n\t\t\t}\n\n\t\t\tconfig.Password = passfile.FindPassword(host, strconv.Itoa(int(config.Port)), config.Database, config.User)\n\t\t}\n\t}\n\n\tswitch tsa := settings[\"target_session_attrs\"]; tsa {\n\tcase \"read-write\":\n\t\tconfig.ValidateConnect = ValidateConnectTargetSessionAttrsReadWrite\n\tcase \"read-only\":\n\t\tconfig.ValidateConnect = ValidateConnectTargetSessionAttrsReadOnly\n\tcase \"primary\":\n\t\tconfig.ValidateConnect = ValidateConnectTargetSessionAttrsPrimary\n\tcase \"standby\":\n\t\tconfig.ValidateConnect = ValidateConnectTargetSessionAttrsStandby\n\tcase \"prefer-standby\":\n\t\tconfig.ValidateConnect = ValidateConnectTargetSessionAttrsPreferStandby\n\tcase \"any\":\n\t\t// do nothing\n\tdefault:\n\t\treturn nil, &ParseConfigError{ConnString: connString, msg: fmt.Sprintf(\"unknown target_session_attrs value: %v\", tsa)}\n\t}\n\n\tminProto, err := parseProtocolVersion(settings[\"min_protocol_version\"])\n\tif err != nil {\n\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"invalid min_protocol_version\", err: err}\n\t}\n\tmaxProto, err := parseProtocolVersion(settings[\"max_protocol_version\"])\n\tif err != nil {\n\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"invalid max_protocol_version\", err: err}\n\t}\n\tif minProto > maxProto {\n\t\treturn nil, &ParseConfigError{ConnString: connString, msg: \"min_protocol_version cannot be greater than max_protocol_version\"}\n\t}\n\n\tconfig.MinProtocolVersion = settings[\"min_protocol_version\"]\n\tconfig.MaxProtocolVersion = settings[\"max_protocol_version\"]\n\tif config.MinProtocolVersion == \"\" {\n\t\tconfig.MinProtocolVersion = \"3.0\"\n\t}\n\tif config.MaxProtocolVersion == \"\" {\n\t\tconfig.MaxProtocolVersion = \"3.0\"\n\t}\n\n\tswitch channelBinding := settings[\"channel_binding\"]; channelBinding {\n\tcase \"\", \"prefer\":\n\t\tconfig.ChannelBinding = \"prefer\"\n\tcase \"disable\":\n\t\tconfig.ChannelBinding = \"disable\"\n\tcase \"require\":\n\t\tconfig.ChannelBinding = \"require\"\n\tdefault:\n\t\treturn nil, &ParseConfigError{ConnString: connString, msg: fmt.Sprintf(\"unknown channel_binding value: %v\", channelBinding)}\n\t}\n\n\treturn config, nil\n}\n\nfunc mergeSettings(settingSets ...map[string]string) map[string]string {\n\tsettings := make(map[string]string)\n\n\tfor _, s2 := range settingSets {\n\t\tmaps.Copy(settings, s2)\n\t}\n\n\treturn settings\n}\n\nfunc parseEnvSettings() map[string]string {\n\tsettings := make(map[string]string)\n\n\tnameMap := map[string]string{\n\t\t\"PGHOST\":               \"host\",\n\t\t\"PGPORT\":               \"port\",\n\t\t\"PGDATABASE\":           \"database\",\n\t\t\"PGUSER\":               \"user\",\n\t\t\"PGPASSWORD\":           \"password\",\n\t\t\"PGPASSFILE\":           \"passfile\",\n\t\t\"PGAPPNAME\":            \"application_name\",\n\t\t\"PGCONNECT_TIMEOUT\":    \"connect_timeout\",\n\t\t\"PGSSLMODE\":            \"sslmode\",\n\t\t\"PGSSLKEY\":             \"sslkey\",\n\t\t\"PGSSLCERT\":            \"sslcert\",\n\t\t\"PGSSLSNI\":             \"sslsni\",\n\t\t\"PGSSLROOTCERT\":        \"sslrootcert\",\n\t\t\"PGSSLPASSWORD\":        \"sslpassword\",\n\t\t\"PGSSLNEGOTIATION\":     \"sslnegotiation\",\n\t\t\"PGTARGETSESSIONATTRS\": \"target_session_attrs\",\n\t\t\"PGSERVICE\":            \"service\",\n\t\t\"PGSERVICEFILE\":        \"servicefile\",\n\t\t\"PGTZ\":                 \"timezone\",\n\t\t\"PGOPTIONS\":            \"options\",\n\t\t\"PGMINPROTOCOLVERSION\": \"min_protocol_version\",\n\t\t\"PGMAXPROTOCOLVERSION\": \"max_protocol_version\",\n\t}\n\n\tfor envname, realname := range nameMap {\n\t\tvalue := os.Getenv(envname)\n\t\tif value != \"\" {\n\t\t\tsettings[realname] = value\n\t\t}\n\t}\n\n\treturn settings\n}\n\nfunc parseURLSettings(connString string) (map[string]string, error) {\n\tsettings := make(map[string]string)\n\n\tparsedURL, err := url.Parse(connString)\n\tif err != nil {\n\t\tif urlErr := new(url.Error); errors.As(err, &urlErr) {\n\t\t\treturn nil, urlErr.Err\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tif parsedURL.User != nil {\n\t\tif u := parsedURL.User.Username(); u != \"\" {\n\t\t\tsettings[\"user\"] = u\n\t\t}\n\t\tif password, present := parsedURL.User.Password(); present {\n\t\t\tsettings[\"password\"] = password\n\t\t}\n\t}\n\n\t// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.\n\tvar hosts []string\n\tvar ports []string\n\tfor host := range strings.SplitSeq(parsedURL.Host, \",\") {\n\t\tif host == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif isIPOnly(host) {\n\t\t\thosts = append(hosts, strings.Trim(host, \"[]\"))\n\t\t\tcontinue\n\t\t}\n\t\th, p, err := net.SplitHostPort(host)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to split host:port in '%s', err: %w\", host, err)\n\t\t}\n\t\tif h != \"\" {\n\t\t\thosts = append(hosts, h)\n\t\t}\n\t\tif p != \"\" {\n\t\t\tports = append(ports, p)\n\t\t}\n\t}\n\tif len(hosts) > 0 {\n\t\tsettings[\"host\"] = strings.Join(hosts, \",\")\n\t}\n\tif len(ports) > 0 {\n\t\tsettings[\"port\"] = strings.Join(ports, \",\")\n\t}\n\n\tdatabase := strings.TrimLeft(parsedURL.Path, \"/\")\n\tif database != \"\" {\n\t\tsettings[\"database\"] = database\n\t}\n\n\tnameMap := map[string]string{\n\t\t\"dbname\": \"database\",\n\t}\n\n\tfor k, v := range parsedURL.Query() {\n\t\tif k2, present := nameMap[k]; present {\n\t\t\tk = k2\n\t\t}\n\n\t\tsettings[k] = v[0]\n\t}\n\n\treturn settings, nil\n}\n\nfunc isIPOnly(host string) bool {\n\treturn net.ParseIP(strings.Trim(host, \"[]\")) != nil || !strings.Contains(host, \":\")\n}\n\nvar asciiSpace = [256]uint8{'\\t': 1, '\\n': 1, '\\v': 1, '\\f': 1, '\\r': 1, ' ': 1}\n\nfunc parseKeywordValueSettings(s string) (map[string]string, error) {\n\tsettings := make(map[string]string)\n\n\tnameMap := map[string]string{\n\t\t\"dbname\": \"database\",\n\t}\n\n\tfor len(s) > 0 {\n\t\tvar key, val string\n\t\teqIdx := strings.IndexRune(s, '=')\n\t\tif eqIdx < 0 {\n\t\t\treturn nil, errors.New(\"invalid keyword/value\")\n\t\t}\n\n\t\tkey = strings.Trim(s[:eqIdx], \" \\t\\n\\r\\v\\f\")\n\t\ts = strings.TrimLeft(s[eqIdx+1:], \" \\t\\n\\r\\v\\f\")\n\t\tif len(s) == 0 {\n\t\t} else if s[0] != '\\'' {\n\t\t\tend := 0\n\t\t\tfor ; end < len(s); end++ {\n\t\t\t\tif asciiSpace[s[end]] == 1 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif s[end] == '\\\\' {\n\t\t\t\t\tend++\n\t\t\t\t\tif end == len(s) {\n\t\t\t\t\t\treturn nil, errors.New(\"invalid backslash\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tval = strings.Replace(strings.Replace(s[:end], \"\\\\\\\\\", \"\\\\\", -1), \"\\\\'\", \"'\", -1)\n\t\t\tif end == len(s) {\n\t\t\t\ts = \"\"\n\t\t\t} else {\n\t\t\t\ts = s[end+1:]\n\t\t\t}\n\t\t} else { // quoted string\n\t\t\ts = s[1:]\n\t\t\tend := 0\n\t\t\tfor ; end < len(s); end++ {\n\t\t\t\tif s[end] == '\\'' {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif s[end] == '\\\\' {\n\t\t\t\t\tend++\n\t\t\t\t}\n\t\t\t}\n\t\t\tif end == len(s) {\n\t\t\t\treturn nil, errors.New(\"unterminated quoted string in connection info string\")\n\t\t\t}\n\t\t\tval = strings.Replace(strings.Replace(s[:end], \"\\\\\\\\\", \"\\\\\", -1), \"\\\\'\", \"'\", -1)\n\t\t\tif end == len(s) {\n\t\t\t\ts = \"\"\n\t\t\t} else {\n\t\t\t\ts = s[end+1:]\n\t\t\t}\n\t\t}\n\n\t\tif k, ok := nameMap[key]; ok {\n\t\t\tkey = k\n\t\t}\n\n\t\tif key == \"\" {\n\t\t\treturn nil, errors.New(\"invalid keyword/value\")\n\t\t}\n\n\t\tif key == \"user\" && val == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tsettings[key] = val\n\t}\n\n\treturn settings, nil\n}\n\nfunc parseServiceSettings(servicefilePath, serviceName string) (map[string]string, error) {\n\tservicefile, err := pgservicefile.ReadServicefile(servicefilePath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read service file: %v\", servicefilePath)\n\t}\n\n\tservice, err := servicefile.GetService(serviceName)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to find service: %v\", serviceName)\n\t}\n\n\tnameMap := map[string]string{\n\t\t\"dbname\": \"database\",\n\t}\n\n\tsettings := make(map[string]string, len(service.Settings))\n\tfor k, v := range service.Settings {\n\t\tif k2, present := nameMap[k]; present {\n\t\t\tk = k2\n\t\t}\n\t\tsettings[k] = v\n\t}\n\n\treturn settings, nil\n}\n\n// configTLS uses libpq's TLS parameters to construct  []*tls.Config. It is\n// necessary to allow returning multiple TLS configs as sslmode \"allow\" and\n// \"prefer\" allow fallback.\nfunc configTLS(settings map[string]string, thisHost string, parseConfigOptions ParseConfigOptions) ([]*tls.Config, error) {\n\thost := thisHost\n\tsslmode := settings[\"sslmode\"]\n\tsslrootcert := settings[\"sslrootcert\"]\n\tsslcert := settings[\"sslcert\"]\n\tsslkey := settings[\"sslkey\"]\n\tsslpassword := settings[\"sslpassword\"]\n\tsslsni := settings[\"sslsni\"]\n\tsslnegotiation := settings[\"sslnegotiation\"]\n\n\t// Match libpq default behavior\n\tif sslmode == \"\" {\n\t\tsslmode = \"prefer\"\n\t}\n\tif sslsni == \"\" {\n\t\tsslsni = \"1\"\n\t}\n\n\ttlsConfig := &tls.Config{}\n\n\tif sslnegotiation == \"direct\" {\n\t\ttlsConfig.NextProtos = []string{\"postgresql\"}\n\t\tif sslmode == \"prefer\" {\n\t\t\tsslmode = \"require\"\n\t\t}\n\t}\n\n\tif sslrootcert != \"\" {\n\t\tvar caCertPool *x509.CertPool\n\n\t\tif sslrootcert == \"system\" {\n\t\t\tvar err error\n\n\t\t\tcaCertPool, err = x509.SystemCertPool()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to load system certificate pool: %w\", err)\n\t\t\t}\n\n\t\t\tsslmode = \"verify-full\"\n\t\t} else {\n\t\t\tcaCertPool = x509.NewCertPool()\n\n\t\t\tcaPath := sslrootcert\n\t\t\tcaCert, err := os.ReadFile(caPath)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to read CA file: %w\", err)\n\t\t\t}\n\n\t\t\tif !caCertPool.AppendCertsFromPEM(caCert) {\n\t\t\t\treturn nil, errors.New(\"unable to add CA to cert pool\")\n\t\t\t}\n\t\t}\n\n\t\ttlsConfig.RootCAs = caCertPool\n\t\ttlsConfig.ClientCAs = caCertPool\n\t}\n\n\tswitch sslmode {\n\tcase \"disable\":\n\t\treturn []*tls.Config{nil}, nil\n\tcase \"allow\", \"prefer\":\n\t\ttlsConfig.InsecureSkipVerify = true\n\tcase \"require\":\n\t\t// According to PostgreSQL documentation, if a root CA file exists,\n\t\t// the behavior of sslmode=require should be the same as that of verify-ca\n\t\t//\n\t\t// See https://www.postgresql.org/docs/current/libpq-ssl.html\n\t\tif sslrootcert != \"\" {\n\t\t\tgoto nextCase\n\t\t}\n\t\ttlsConfig.InsecureSkipVerify = true\n\t\tbreak\n\tnextCase:\n\t\tfallthrough\n\tcase \"verify-ca\":\n\t\t// Don't perform the default certificate verification because it\n\t\t// will verify the hostname. Instead, verify the server's\n\t\t// certificate chain ourselves in VerifyPeerCertificate and\n\t\t// ignore the server name. This emulates libpq's verify-ca\n\t\t// behavior.\n\t\t//\n\t\t// See https://github.com/golang/go/issues/21971#issuecomment-332693931\n\t\t// and https://pkg.go.dev/crypto/tls?tab=doc#example-Config-VerifyPeerCertificate\n\t\t// for more info.\n\t\ttlsConfig.InsecureSkipVerify = true\n\t\ttlsConfig.VerifyPeerCertificate = func(certificates [][]byte, _ [][]*x509.Certificate) error {\n\t\t\tcerts := make([]*x509.Certificate, len(certificates))\n\t\t\tfor i, asn1Data := range certificates {\n\t\t\t\tcert, err := x509.ParseCertificate(asn1Data)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.New(\"failed to parse certificate from server: \" + err.Error())\n\t\t\t\t}\n\t\t\t\tcerts[i] = cert\n\t\t\t}\n\n\t\t\t// Leave DNSName empty to skip hostname verification.\n\t\t\topts := x509.VerifyOptions{\n\t\t\t\tRoots:         tlsConfig.RootCAs,\n\t\t\t\tIntermediates: x509.NewCertPool(),\n\t\t\t}\n\t\t\t// Skip the first cert because it's the leaf. All others\n\t\t\t// are intermediates.\n\t\t\tfor _, cert := range certs[1:] {\n\t\t\t\topts.Intermediates.AddCert(cert)\n\t\t\t}\n\t\t\t_, err := certs[0].Verify(opts)\n\t\t\treturn err\n\t\t}\n\tcase \"verify-full\":\n\t\ttlsConfig.ServerName = host\n\tdefault:\n\t\treturn nil, errors.New(\"sslmode is invalid\")\n\t}\n\n\tif (sslcert != \"\" && sslkey == \"\") || (sslcert == \"\" && sslkey != \"\") {\n\t\treturn nil, errors.New(`both \"sslcert\" and \"sslkey\" are required`)\n\t}\n\n\tif sslcert != \"\" && sslkey != \"\" {\n\t\tbuf, err := os.ReadFile(sslkey)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to read sslkey: %w\", err)\n\t\t}\n\t\tblock, _ := pem.Decode(buf)\n\t\tif block == nil {\n\t\t\treturn nil, errors.New(\"failed to decode sslkey\")\n\t\t}\n\t\tvar pemKey []byte\n\t\tvar decryptedKey []byte\n\t\tvar decryptedError error\n\t\t// If PEM is encrypted, attempt to decrypt using pass phrase\n\t\tif x509.IsEncryptedPEMBlock(block) {\n\t\t\t// Attempt decryption with pass phrase\n\t\t\t// NOTE: only supports RSA (PKCS#1)\n\t\t\tif sslpassword != \"\" {\n\t\t\t\tdecryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword)) //nolint:ineffassign\n\t\t\t}\n\t\t\t// if sslpassword not provided or has decryption error when use it\n\t\t\t// try to find sslpassword with callback function\n\t\t\tif sslpassword == \"\" || decryptedError != nil {\n\t\t\t\tif parseConfigOptions.GetSSLPassword != nil {\n\t\t\t\t\tsslpassword = parseConfigOptions.GetSSLPassword(context.Background())\n\t\t\t\t}\n\t\t\t\tif sslpassword == \"\" {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unable to find sslpassword\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tdecryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword))\n\t\t\t// Should we also provide warning for PKCS#1 needed?\n\t\t\tif decryptedError != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to decrypt key: %w\", decryptedError)\n\t\t\t}\n\n\t\t\tpemBytes := pem.Block{\n\t\t\t\tType:  \"RSA PRIVATE KEY\",\n\t\t\t\tBytes: decryptedKey,\n\t\t\t}\n\t\t\tpemKey = pem.EncodeToMemory(&pemBytes)\n\t\t} else {\n\t\t\tpemKey = pem.EncodeToMemory(block)\n\t\t}\n\t\tcertfile, err := os.ReadFile(sslcert)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to read cert: %w\", err)\n\t\t}\n\t\tcert, err := tls.X509KeyPair(certfile, pemKey)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to load cert: %w\", err)\n\t\t}\n\t\ttlsConfig.Certificates = []tls.Certificate{cert}\n\t}\n\n\t// Set Server Name Indication (SNI), if enabled by connection parameters.\n\t// Per RFC 6066, do not set it if the host is a literal IP address (IPv4\n\t// or IPv6).\n\tif sslsni == \"1\" && net.ParseIP(host) == nil {\n\t\ttlsConfig.ServerName = host\n\t}\n\n\tswitch sslmode {\n\tcase \"allow\":\n\t\treturn []*tls.Config{nil, tlsConfig}, nil\n\tcase \"prefer\":\n\t\treturn []*tls.Config{tlsConfig, nil}, nil\n\tcase \"require\", \"verify-ca\", \"verify-full\":\n\t\treturn []*tls.Config{tlsConfig}, nil\n\tdefault:\n\t\tpanic(\"BUG: bad sslmode should already have been caught\")\n\t}\n}\n\nfunc parsePort(s string) (uint16, error) {\n\tport, err := strconv.ParseUint(s, 10, 16)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif port < 1 || port > math.MaxUint16 {\n\t\treturn 0, errors.New(\"outside range\")\n\t}\n\treturn uint16(port), nil\n}\n\nfunc makeDefaultDialer() *net.Dialer {\n\t// rely on GOLANG KeepAlive settings\n\treturn &net.Dialer{}\n}\n\nfunc makeDefaultResolver() *net.Resolver {\n\treturn net.DefaultResolver\n}\n\nfunc parseConnectTimeoutSetting(s string) (time.Duration, error) {\n\ttimeout, err := strconv.ParseInt(s, 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif timeout < 0 {\n\t\treturn 0, errors.New(\"negative timeout\")\n\t}\n\treturn time.Duration(timeout) * time.Second, nil\n}\n\nfunc makeConnectTimeoutDialFunc(timeout time.Duration) DialFunc {\n\td := makeDefaultDialer()\n\td.Timeout = timeout\n\treturn d.DialContext\n}\n\n// ValidateConnectTargetSessionAttrsReadWrite is a ValidateConnectFunc that implements libpq compatible\n// target_session_attrs=read-write.\nfunc ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgConn) error {\n\tresult, err := pgConn.Exec(ctx, \"show transaction_read_only\").ReadAll()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(result[0].Rows[0][0]) == \"on\" {\n\t\treturn errors.New(\"read only connection\")\n\t}\n\n\treturn nil\n}\n\n// ValidateConnectTargetSessionAttrsReadOnly is a ValidateConnectFunc that implements libpq compatible\n// target_session_attrs=read-only.\nfunc ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgConn) error {\n\tresult, err := pgConn.Exec(ctx, \"show transaction_read_only\").ReadAll()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(result[0].Rows[0][0]) != \"on\" {\n\t\treturn errors.New(\"connection is not read only\")\n\t}\n\n\treturn nil\n}\n\n// ValidateConnectTargetSessionAttrsStandby is a ValidateConnectFunc that implements libpq compatible\n// target_session_attrs=standby.\nfunc ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgConn) error {\n\tresult, err := pgConn.Exec(ctx, \"select pg_is_in_recovery()\").ReadAll()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(result[0].Rows[0][0]) != \"t\" {\n\t\treturn errors.New(\"server is not in hot standby mode\")\n\t}\n\n\treturn nil\n}\n\n// ValidateConnectTargetSessionAttrsPrimary is a ValidateConnectFunc that implements libpq compatible\n// target_session_attrs=primary.\nfunc ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgConn) error {\n\tresult, err := pgConn.Exec(ctx, \"select pg_is_in_recovery()\").ReadAll()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(result[0].Rows[0][0]) == \"t\" {\n\t\treturn errors.New(\"server is in standby mode\")\n\t}\n\n\treturn nil\n}\n\n// ValidateConnectTargetSessionAttrsPreferStandby is a ValidateConnectFunc that implements libpq compatible\n// target_session_attrs=prefer-standby.\nfunc ValidateConnectTargetSessionAttrsPreferStandby(ctx context.Context, pgConn *PgConn) error {\n\tresult, err := pgConn.Exec(ctx, \"select pg_is_in_recovery()\").ReadAll()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif string(result[0].Rows[0][0]) != \"t\" {\n\t\treturn &NotPreferredError{err: errors.New(\"server is not in hot standby mode\")}\n\t}\n\n\treturn nil\n}\n\nfunc parseProtocolVersion(s string) (uint32, error) {\n\tswitch s {\n\tcase \"\", \"3.0\":\n\t\treturn pgproto3.ProtocolVersion30, nil\n\tcase \"3.2\", \"latest\":\n\t\treturn pgproto3.ProtocolVersion32, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"invalid protocol version: %q\", s)\n\t}\n}\n"
  },
  {
    "path": "pgconn/config_test.go",
    "content": "package pgconn_test\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc skipOnWindows(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"FIXME: skipping on Windows, investigate why this test fails in CI environment\")\n\t}\n}\n\nfunc getDefaultPort(t *testing.T) uint16 {\n\tif envPGPORT := os.Getenv(\"PGPORT\"); envPGPORT != \"\" {\n\t\tp, err := strconv.ParseUint(envPGPORT, 10, 16)\n\t\trequire.NoError(t, err)\n\t\treturn uint16(p)\n\t}\n\treturn 5432\n}\n\nfunc getDefaultUser(t *testing.T) string {\n\tif pguser := os.Getenv(\"PGUSER\"); pguser != \"\" {\n\t\treturn pguser\n\t}\n\n\tvar osUserName string\n\tosUser, err := user.Current()\n\tif err == nil {\n\t\t// Windows gives us the username here as `DOMAIN\\user` or `LOCALPCNAME\\user`,\n\t\t// but the libpq default is just the `user` portion, so we strip off the first part.\n\t\tif runtime.GOOS == \"windows\" && strings.Contains(osUser.Username, \"\\\\\") {\n\t\t\tosUserName = osUser.Username[strings.LastIndex(osUser.Username, \"\\\\\")+1:]\n\t\t} else {\n\t\t\tosUserName = osUser.Username\n\t\t}\n\t}\n\n\treturn osUserName\n}\n\nvar pgEnvvars = []string{\"PGHOST\", \"PGPORT\", \"PGDATABASE\", \"PGUSER\", \"PGPASSWORD\", \"PGAPPNAME\", \"PGSSLMODE\", \"PGCONNECT_TIMEOUT\", \"PGSSLSNI\", \"PGTZ\", \"PGOPTIONS\"}\n\nfunc clearPgEnvvars(t *testing.T) {\n\tfor _, env := range pgEnvvars {\n\t\tt.Setenv(env, \"\")\n\t}\n}\n\nfunc TestParseConfig(t *testing.T) {\n\tskipOnWindows(t)\n\tclearPgEnvvars(t)\n\n\tconfig, err := pgconn.ParseConfig(\"\")\n\trequire.NoError(t, err)\n\tdefaultHost := config.Host\n\n\tdefaultUser := getDefaultUser(t)\n\tdefaultPort := getDefaultPort(t)\n\n\ttests := []struct {\n\t\tname       string\n\t\tconnString string\n\t\tconfig     *pgconn.Config\n\t}{\n\t\t// Test all sslmodes\n\t\t{\n\t\t\tname:       \"sslmode not set (prefer)\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"localhost\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"localhost\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"localhost\",\n\t\t\t\t\t\tPort:      5432,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"sslmode disable\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"sslmode allow\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=allow\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost: \"localhost\",\n\t\t\t\t\t\tPort: 5432,\n\t\t\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\t\t\tServerName:         \"localhost\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"sslmode prefer\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=prefer\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"localhost\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"localhost\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"localhost\",\n\t\t\t\t\t\tPort:      5432,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"sslmode require\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=require\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"localhost\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"localhost\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"sslmode verify-ca\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=verify-ca\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"localhost\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"localhost\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"sslmode verify-full\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=verify-full\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     &tls.Config{ServerName: \"localhost\"},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url everything\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&application_name=pgxtest&search_path=myschema&connect_timeout=5\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:           \"jack\",\n\t\t\t\tPassword:       \"secret\",\n\t\t\t\tHost:           \"localhost\",\n\t\t\t\tPort:           5432,\n\t\t\t\tDatabase:       \"mydb\",\n\t\t\t\tTLSConfig:      nil,\n\t\t\t\tConnectTimeout: 5 * time.Second,\n\t\t\t\tRuntimeParams: map[string]string{\n\t\t\t\t\t\"application_name\": \"pgxtest\",\n\t\t\t\t\t\"search_path\":      \"myschema\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url missing password\",\n\t\t\tconnString: \"postgres://jack@localhost:5432/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url missing user and password\",\n\t\t\tconnString: \"postgres://localhost:5432/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          defaultUser,\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url missing port\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url unix domain socket host\",\n\t\t\tconnString: \"postgres:///foo?host=/tmp\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          defaultUser,\n\t\t\t\tHost:          \"/tmp\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"foo\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url unix domain socket host on windows\",\n\t\t\tconnString: \"postgres:///foo?host=C:\\\\tmp\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          defaultUser,\n\t\t\t\tHost:          \"C:\\\\tmp\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"foo\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url dbname\",\n\t\t\tconnString: \"postgres://localhost/?dbname=foo&sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          defaultUser,\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"foo\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url postgresql protocol\",\n\t\t\tconnString: \"postgresql://jack@localhost:5432/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url IPv4 with port\",\n\t\t\tconnString: \"postgresql://jack@127.0.0.1:5433/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"127.0.0.1\",\n\t\t\t\tPort:          5433,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url IPv6 with port\",\n\t\t\tconnString: \"postgresql://jack@[2001:db8::1]:5433/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"2001:db8::1\",\n\t\t\t\tPort:          5433,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"database url IPv6 no port\",\n\t\t\tconnString: \"postgresql://jack@[2001:db8::1]/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"2001:db8::1\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value everything\",\n\t\t\tconnString: \"user=jack password=secret host=localhost port=5432 dbname=mydb sslmode=disable application_name=pgxtest search_path=myschema connect_timeout=5\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:           \"jack\",\n\t\t\t\tPassword:       \"secret\",\n\t\t\t\tHost:           \"localhost\",\n\t\t\t\tPort:           5432,\n\t\t\t\tDatabase:       \"mydb\",\n\t\t\t\tTLSConfig:      nil,\n\t\t\t\tConnectTimeout: 5 * time.Second,\n\t\t\t\tRuntimeParams: map[string]string{\n\t\t\t\t\t\"application_name\": \"pgxtest\",\n\t\t\t\t\t\"search_path\":      \"myschema\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value with escaped single quote\",\n\t\t\tconnString: \"user=jack\\\\'s password=secret host=localhost port=5432 dbname=mydb sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack's\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value with escaped backslash\",\n\t\t\tconnString: \"user=jack password=sooper\\\\\\\\secret host=localhost port=5432 dbname=mydb sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"sooper\\\\secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value with single quoted values\",\n\t\t\tconnString: \"user='jack' host='localhost' dbname='mydb' sslmode='disable'\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value with single quoted value with escaped single quote\",\n\t\t\tconnString: \"user='jack\\\\'s' host='localhost' dbname='mydb' sslmode='disable'\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack's\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value with empty single quoted value\",\n\t\t\tconnString: \"user='jack' password='' host='localhost' dbname='mydb' sslmode='disable'\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value with space between key and value\",\n\t\t\tconnString: \"user = 'jack' password = '' host = 'localhost' dbname = 'mydb' sslmode='disable'\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"URL multiple hosts\",\n\t\t\tconnString: \"postgres://jack:secret@foo,bar,baz/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"foo\",\n\t\t\t\tPort:          defaultPort,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"bar\",\n\t\t\t\t\t\tPort:      defaultPort,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"baz\",\n\t\t\t\t\t\tPort:      defaultPort,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"URL multiple hosts and ports\",\n\t\t\tconnString: \"postgres://jack:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"foo\",\n\t\t\t\tPort:          1,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"bar\",\n\t\t\t\t\t\tPort:      2,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"baz\",\n\t\t\t\t\t\tPort:      3,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t// https://github.com/jackc/pgconn/issues/72\n\t\t{\n\t\t\tname:       \"URL without host but with port still uses default host\",\n\t\t\tconnString: \"postgres://jack:secret@:1/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          defaultHost,\n\t\t\t\tPort:          1,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value multiple hosts one port\",\n\t\t\tconnString: \"user=jack password=secret host=foo,bar,baz port=5432 dbname=mydb sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"foo\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"bar\",\n\t\t\t\t\t\tPort:      5432,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"baz\",\n\t\t\t\t\t\tPort:      5432,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"Key/value multiple hosts multiple ports\",\n\t\t\tconnString: \"user=jack password=secret host=foo,bar,baz port=1,2,3 dbname=mydb sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"foo\",\n\t\t\t\tPort:          1,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"bar\",\n\t\t\t\t\t\tPort:      2,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"baz\",\n\t\t\t\t\t\tPort:      3,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"multiple hosts and fallback tls\",\n\t\t\tconnString: \"user=jack password=secret host=foo,bar,baz dbname=mydb sslmode=prefer\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"foo\",\n\t\t\t\tPort:     defaultPort,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"foo\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"foo\",\n\t\t\t\t\t\tPort:      defaultPort,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost: \"bar\",\n\t\t\t\t\t\tPort: defaultPort,\n\t\t\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\t\t\tServerName:         \"bar\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"bar\",\n\t\t\t\t\t\tPort:      defaultPort,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost: \"baz\",\n\t\t\t\t\t\tPort: defaultPort,\n\t\t\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\t\t\tServerName:         \"baz\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"baz\",\n\t\t\t\t\t\tPort:      defaultPort,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs read-write\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=read-write\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:            \"jack\",\n\t\t\t\tPassword:        \"secret\",\n\t\t\t\tHost:            \"localhost\",\n\t\t\t\tPort:            5432,\n\t\t\t\tDatabase:        \"mydb\",\n\t\t\t\tTLSConfig:       nil,\n\t\t\t\tRuntimeParams:   map[string]string{},\n\t\t\t\tValidateConnect: pgconn.ValidateConnectTargetSessionAttrsReadWrite,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs read-only\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=read-only\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:            \"jack\",\n\t\t\t\tPassword:        \"secret\",\n\t\t\t\tHost:            \"localhost\",\n\t\t\t\tPort:            5432,\n\t\t\t\tDatabase:        \"mydb\",\n\t\t\t\tTLSConfig:       nil,\n\t\t\t\tRuntimeParams:   map[string]string{},\n\t\t\t\tValidateConnect: pgconn.ValidateConnectTargetSessionAttrsReadOnly,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs primary\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=primary\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:            \"jack\",\n\t\t\t\tPassword:        \"secret\",\n\t\t\t\tHost:            \"localhost\",\n\t\t\t\tPort:            5432,\n\t\t\t\tDatabase:        \"mydb\",\n\t\t\t\tTLSConfig:       nil,\n\t\t\t\tRuntimeParams:   map[string]string{},\n\t\t\t\tValidateConnect: pgconn.ValidateConnectTargetSessionAttrsPrimary,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs standby\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=standby\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:            \"jack\",\n\t\t\t\tPassword:        \"secret\",\n\t\t\t\tHost:            \"localhost\",\n\t\t\t\tPort:            5432,\n\t\t\t\tDatabase:        \"mydb\",\n\t\t\t\tTLSConfig:       nil,\n\t\t\t\tRuntimeParams:   map[string]string{},\n\t\t\t\tValidateConnect: pgconn.ValidateConnectTargetSessionAttrsStandby,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs prefer-standby\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=prefer-standby\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:            \"jack\",\n\t\t\t\tPassword:        \"secret\",\n\t\t\t\tHost:            \"localhost\",\n\t\t\t\tPort:            5432,\n\t\t\t\tDatabase:        \"mydb\",\n\t\t\t\tTLSConfig:       nil,\n\t\t\t\tRuntimeParams:   map[string]string{},\n\t\t\t\tValidateConnect: pgconn.ValidateConnectTargetSessionAttrsPreferStandby,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs any\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable&target_session_attrs=any\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"target_session_attrs not set (any)\",\n\t\t\tconnString: \"postgres://jack:secret@localhost:5432/mydb?sslmode=disable\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:          \"jack\",\n\t\t\t\tPassword:      \"secret\",\n\t\t\t\tHost:          \"localhost\",\n\t\t\t\tPort:          5432,\n\t\t\t\tDatabase:      \"mydb\",\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"SNI is set by default\",\n\t\t\tconnString: \"postgres://jack:secret@sni.test:5432/mydb?sslmode=require\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"sni.test\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"sni.test\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"SNI is not set for IPv4\",\n\t\t\tconnString: \"postgres://jack:secret@1.1.1.1:5432/mydb?sslmode=require\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"1.1.1.1\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"SNI is not set for IPv6\",\n\t\t\tconnString: \"postgres://jack:secret@[::1]:5432/mydb?sslmode=require\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"::1\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"SNI is not set when disabled (URL-style)\",\n\t\t\tconnString: \"postgres://jack:secret@sni.test:5432/mydb?sslmode=require&sslsni=0\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"sni.test\",\n\t\t\t\tPort:     5432,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"SNI is not set when disabled (key/value style)\",\n\t\t\tconnString: \"user=jack password=secret host=sni.test dbname=mydb sslmode=require sslsni=0\",\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser:     \"jack\",\n\t\t\t\tPassword: \"secret\",\n\t\t\t\tHost:     \"sni.test\",\n\t\t\t\tPort:     defaultPort,\n\t\t\t\tDatabase: \"mydb\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tconfig, err := pgconn.ParseConfig(tt.connString)\n\t\tif !assert.Nilf(t, err, \"Test %d (%s)\", i, tt.name) {\n\t\t\tcontinue\n\t\t}\n\n\t\tassertConfigsEqual(t, tt.config, config, fmt.Sprintf(\"Test %d (%s)\", i, tt.name))\n\t}\n}\n\n// https://github.com/jackc/pgconn/issues/47\nfunc TestParseConfigKVWithTrailingEmptyEqualDoesNotPanic(t *testing.T) {\n\t_, err := pgconn.ParseConfig(\"host= user= password= port= database=\")\n\trequire.NoError(t, err)\n}\n\nfunc TestParseConfigKVLeadingEqual(t *testing.T) {\n\t_, err := pgconn.ParseConfig(\"= user=jack\")\n\trequire.Error(t, err)\n}\n\n// https://github.com/jackc/pgconn/issues/49\nfunc TestParseConfigKVTrailingBackslash(t *testing.T) {\n\t_, err := pgconn.ParseConfig(`x=x\\`)\n\trequire.Error(t, err)\n\tassert.Contains(t, err.Error(), \"invalid backslash\")\n}\n\nfunc TestConfigCopyReturnsEqualConfig(t *testing.T) {\n\tconnString := \"postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5\"\n\toriginal, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\tassertConfigsEqual(t, original, copied, \"Test Config.Copy() returns equal config\")\n}\n\nfunc TestConfigCopyOriginalConfigDidNotChange(t *testing.T) {\n\tconnString := \"postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5&sslmode=prefer\"\n\toriginal, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\tassertConfigsEqual(t, original, copied, \"Test Config.Copy() returns equal config\")\n\n\tcopied.Port = uint16(5433)\n\tcopied.RuntimeParams[\"foo\"] = \"bar\"\n\tcopied.Fallbacks[0].Port = uint16(5433)\n\n\tassert.Equal(t, uint16(5432), original.Port)\n\tassert.Equal(t, \"\", original.RuntimeParams[\"foo\"])\n\tassert.Equal(t, uint16(5432), original.Fallbacks[0].Port)\n}\n\nfunc TestConfigCopyCanBeUsedToConnect(t *testing.T) {\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\toriginal, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\tassert.NotPanics(t, func() {\n\t\t_, err = pgconn.ConnectConfig(context.Background(), copied)\n\t})\n\tassert.NoError(t, err)\n}\n\nfunc TestNetworkAddress(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\thost    string\n\t\twantNet string\n\t}{\n\t\t{\n\t\t\tname:    \"Default Unix socket address\",\n\t\t\thost:    \"/var/run/postgresql\",\n\t\t\twantNet: \"unix\",\n\t\t},\n\t\t{\n\t\t\tname:    \"Windows Unix socket address (standard drive name)\",\n\t\t\thost:    \"C:\\\\tmp\",\n\t\t\twantNet: \"unix\",\n\t\t},\n\t\t{\n\t\t\tname:    \"Windows Unix socket address (first drive name)\",\n\t\t\thost:    \"A:\\\\tmp\",\n\t\t\twantNet: \"unix\",\n\t\t},\n\t\t{\n\t\t\tname:    \"Windows Unix socket address (last drive name)\",\n\t\t\thost:    \"Z:\\\\tmp\",\n\t\t\twantNet: \"unix\",\n\t\t},\n\t\t{\n\t\t\tname:    \"Assume TCP for unknown formats\",\n\t\t\thost:    \"a/tmp\",\n\t\t\twantNet: \"tcp\",\n\t\t},\n\t\t{\n\t\t\tname:    \"loopback interface\",\n\t\t\thost:    \"localhost\",\n\t\t\twantNet: \"tcp\",\n\t\t},\n\t\t{\n\t\t\tname:    \"IP address\",\n\t\t\thost:    \"127.0.0.1\",\n\t\t\twantNet: \"tcp\",\n\t\t},\n\t}\n\tfor i, tt := range tests {\n\t\tgotNet, _ := pgconn.NetworkAddress(tt.host, 5432)\n\n\t\tassert.Equalf(t, tt.wantNet, gotNet, \"Test %d (%s)\", i, tt.name)\n\t}\n}\n\nfunc assertConfigsEqual(t *testing.T, expected, actual *pgconn.Config, testName string) {\n\tif !assert.NotNil(t, expected) {\n\t\treturn\n\t}\n\tif !assert.NotNil(t, actual) {\n\t\treturn\n\t}\n\n\tassert.Equalf(t, expected.Host, actual.Host, \"%s - Host\", testName)\n\tassert.Equalf(t, expected.Database, actual.Database, \"%s - Database\", testName)\n\tassert.Equalf(t, expected.Port, actual.Port, \"%s - Port\", testName)\n\tassert.Equalf(t, expected.User, actual.User, \"%s - User\", testName)\n\tassert.Equalf(t, expected.Password, actual.Password, \"%s - Password\", testName)\n\tassert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, \"%s - ConnectTimeout\", testName)\n\tassert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, \"%s - RuntimeParams\", testName)\n\n\t// Can't test function equality, so just test that they are set or not.\n\tassert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, \"%s - ValidateConnect\", testName)\n\tassert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, \"%s - AfterConnect\", testName)\n\n\tif assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, \"%s - TLSConfig\", testName) {\n\t\tif expected.TLSConfig != nil {\n\t\t\tassert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, \"%s - TLSConfig InsecureSkipVerify\", testName)\n\t\t\tassert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, \"%s - TLSConfig ServerName\", testName)\n\t\t}\n\t}\n\n\tif assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), \"%s - Fallbacks\", testName) {\n\t\tfor i := range expected.Fallbacks {\n\t\t\tassert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, \"%s - Fallback %d - Host\", testName, i)\n\t\t\tassert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, \"%s - Fallback %d - Port\", testName, i)\n\n\t\t\tif assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, \"%s - Fallback %d - TLSConfig\", testName, i) {\n\t\t\t\tif expected.Fallbacks[i].TLSConfig != nil {\n\t\t\t\t\tassert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, \"%s - Fallback %d - TLSConfig InsecureSkipVerify\", testName)\n\t\t\t\t\tassert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, \"%s - Fallback %d - TLSConfig ServerName\", testName)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestParseConfigEnvLibpq(t *testing.T) {\n\tvar osUserName string\n\tosUser, err := user.Current()\n\tif err == nil {\n\t\t// Windows gives us the username here as `DOMAIN\\user` or `LOCALPCNAME\\user`,\n\t\t// but the libpq default is just the `user` portion, so we strip off the first part.\n\t\tif runtime.GOOS == \"windows\" && strings.Contains(osUser.Username, \"\\\\\") {\n\t\t\tosUserName = osUser.Username[strings.LastIndex(osUser.Username, \"\\\\\")+1:]\n\t\t} else {\n\t\t\tosUserName = osUser.Username\n\t\t}\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\tenvvars map[string]string\n\t\tconfig  *pgconn.Config\n\t}{\n\t\t{\n\t\t\t// not testing no environment at all as that would use default host and that can vary.\n\t\t\tname:    \"PGHOST only\",\n\t\t\tenvvars: map[string]string{\"PGHOST\": \"123.123.123.123\"},\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser: osUserName,\n\t\t\t\tHost: \"123.123.123.123\",\n\t\t\t\tPort: 5432,\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"123.123.123.123\",\n\t\t\t\t\t\tPort:      5432,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"All non-TLS environment\",\n\t\t\tenvvars: map[string]string{\n\t\t\t\t\"PGHOST\":            \"123.123.123.123\",\n\t\t\t\t\"PGPORT\":            \"7777\",\n\t\t\t\t\"PGDATABASE\":        \"foo\",\n\t\t\t\t\"PGUSER\":            \"bar\",\n\t\t\t\t\"PGPASSWORD\":        \"baz\",\n\t\t\t\t\"PGCONNECT_TIMEOUT\": \"10\",\n\t\t\t\t\"PGSSLMODE\":         \"disable\",\n\t\t\t\t\"PGAPPNAME\":         \"pgxtest\",\n\t\t\t\t\"PGTZ\":              \"America/New_York\",\n\t\t\t\t\"PGOPTIONS\":         \"-c search_path=myschema\",\n\t\t\t},\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tHost:           \"123.123.123.123\",\n\t\t\t\tPort:           7777,\n\t\t\t\tDatabase:       \"foo\",\n\t\t\t\tUser:           \"bar\",\n\t\t\t\tPassword:       \"baz\",\n\t\t\t\tConnectTimeout: 10 * time.Second,\n\t\t\t\tTLSConfig:      nil,\n\t\t\t\tRuntimeParams:  map[string]string{\"application_name\": \"pgxtest\", \"timezone\": \"America/New_York\", \"options\": \"-c search_path=myschema\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"SNI can be disabled via environment variable\",\n\t\t\tenvvars: map[string]string{\n\t\t\t\t\"PGHOST\":    \"test.foo\",\n\t\t\t\t\"PGSSLMODE\": \"require\",\n\t\t\t\t\"PGSSLSNI\":  \"0\",\n\t\t\t},\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tUser: osUserName,\n\t\t\t\tHost: \"test.foo\",\n\t\t\t\tPort: 5432,\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tfor _, env := range pgEnvvars {\n\t\t\tt.Setenv(env, tt.envvars[env])\n\t\t}\n\n\t\tconfig, err := pgconn.ParseConfig(\"\")\n\t\tif !assert.Nilf(t, err, \"Test %d (%s)\", i, tt.name) {\n\t\t\tcontinue\n\t\t}\n\n\t\tassertConfigsEqual(t, tt.config, config, fmt.Sprintf(\"Test %d (%s)\", i, tt.name))\n\t}\n}\n\nfunc TestParseConfigReadsPgPassfile(t *testing.T) {\n\tskipOnWindows(t)\n\tclearPgEnvvars(t)\n\n\ttfName := filepath.Join(t.TempDir(), \"config\")\n\terr := os.WriteFile(tfName, []byte(\"test1:5432:curlydb:curly:nyuknyuknyuk\"), 0o600)\n\trequire.NoError(t, err)\n\n\tconnString := fmt.Sprintf(\"postgres://curly@test1:5432/curlydb?sslmode=disable&passfile=%s\", tfName)\n\texpected := &pgconn.Config{\n\t\tUser:          \"curly\",\n\t\tPassword:      \"nyuknyuknyuk\",\n\t\tHost:          \"test1\",\n\t\tPort:          5432,\n\t\tDatabase:      \"curlydb\",\n\t\tTLSConfig:     nil,\n\t\tRuntimeParams: map[string]string{},\n\t}\n\n\tactual, err := pgconn.ParseConfig(connString)\n\tassert.NoError(t, err)\n\n\tassertConfigsEqual(t, expected, actual, \"passfile\")\n}\n\nfunc TestParseConfigReadsPgServiceFile(t *testing.T) {\n\tskipOnWindows(t)\n\tclearPgEnvvars(t)\n\n\ttfName := filepath.Join(t.TempDir(), \"config\")\n\n\terr := os.WriteFile(tfName, []byte(`\n[abc]\nhost=abc.example.com\nport=9999\ndbname=abcdb\nuser=abcuser\n\n[def]\nhost = def.example.com\ndbname = defdb\nuser = defuser\napplication_name = spaced string\n`), 0o600)\n\trequire.NoError(t, err)\n\n\tdefaultPort := getDefaultPort(t)\n\n\ttests := []struct {\n\t\tname       string\n\t\tconnString string\n\t\tconfig     *pgconn.Config\n\t}{\n\t\t{\n\t\t\tname:       \"abc\",\n\t\t\tconnString: fmt.Sprintf(\"postgres:///?servicefile=%s&service=%s\", tfName, \"abc\"),\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tHost:     \"abc.example.com\",\n\t\t\t\tDatabase: \"abcdb\",\n\t\t\t\tUser:     \"abcuser\",\n\t\t\t\tPort:     9999,\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"abc.example.com\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"abc.example.com\",\n\t\t\t\t\t\tPort:      9999,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"def\",\n\t\t\tconnString: fmt.Sprintf(\"postgres:///?servicefile=%s&service=%s\", tfName, \"def\"),\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tHost:     \"def.example.com\",\n\t\t\t\tPort:     defaultPort,\n\t\t\t\tDatabase: \"defdb\",\n\t\t\t\tUser:     \"defuser\",\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\tServerName:         \"def.example.com\",\n\t\t\t\t},\n\t\t\t\tRuntimeParams: map[string]string{\"application_name\": \"spaced string\"},\n\t\t\t\tFallbacks: []*pgconn.FallbackConfig{\n\t\t\t\t\t{\n\t\t\t\t\t\tHost:      \"def.example.com\",\n\t\t\t\t\t\tPort:      defaultPort,\n\t\t\t\t\t\tTLSConfig: nil,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"conn string has precedence\",\n\t\t\tconnString: fmt.Sprintf(\"postgres://other.example.com:7777/?servicefile=%s&service=%s&sslmode=disable\", tfName, \"abc\"),\n\t\t\tconfig: &pgconn.Config{\n\t\t\t\tHost:          \"other.example.com\",\n\t\t\t\tDatabase:      \"abcdb\",\n\t\t\t\tUser:          \"abcuser\",\n\t\t\t\tPort:          7777,\n\t\t\t\tTLSConfig:     nil,\n\t\t\t\tRuntimeParams: map[string]string{},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tconfig, err := pgconn.ParseConfig(tt.connString)\n\t\tif !assert.NoErrorf(t, err, \"Test %d (%s)\", i, tt.name) {\n\t\t\tcontinue\n\t\t}\n\n\t\tassertConfigsEqual(t, tt.config, config, fmt.Sprintf(\"Test %d (%s)\", i, tt.name))\n\t}\n}\n\nfunc TestParseConfigExplicitEmptyUserDefaultsToOSUser(t *testing.T) {\n\tskipOnWindows(t)\n\tclearPgEnvvars(t)\n\n\tcurrentUser, err := user.Current()\n\tif err != nil {\n\t\tt.Skip(\"cannot determine current OS user\")\n\t}\n\n\ttests := []struct {\n\t\tname       string\n\t\tconnString string\n\t\texpected   string\n\t}{\n\t\t{\n\t\t\tname:       \"keyword value explicit empty user\",\n\t\t\tconnString: \"host=localhost dbname=test user=\",\n\t\t\texpected:   currentUser.Username,\n\t\t},\n\t\t{\n\t\t\tname:       \"keyword value quoted empty user\",\n\t\t\tconnString: \"host=localhost dbname=test user=''\",\n\t\t\texpected:   currentUser.Username,\n\t\t},\n\t\t{\n\t\t\tname:       \"url explicit empty user without password\",\n\t\t\tconnString: \"postgres://@localhost/test\",\n\t\t\texpected:   currentUser.Username,\n\t\t},\n\t\t{\n\t\t\tname:       \"url explicit empty user with password\",\n\t\t\tconnString: \"postgres://:secret@localhost/test\",\n\t\t\texpected:   currentUser.Username,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tconfig, err := pgconn.ParseConfig(tt.connString)\n\t\tif !assert.NoErrorf(t, err, \"Test %d (%s)\", i, tt.name) {\n\t\t\tcontinue\n\t\t}\n\n\t\tassert.Equalf(\n\t\t\tt,\n\t\t\ttt.expected,\n\t\t\tconfig.User,\n\t\t\t\"Test %d (%s): unexpected user\",\n\t\t\ti,\n\t\t\ttt.name,\n\t\t)\n\t}\n}\n\nfunc TestParseConfigProtocolVersion(t *testing.T) {\n\ttests := []struct {\n\t\tname               string\n\t\tconnString         string\n\t\tenvMin             string\n\t\tenvMax             string\n\t\texpectedMin        string\n\t\texpectedMax        string\n\t\texpectError        bool\n\t\texpectedErrContain string\n\t}{\n\t\t{\n\t\t\tname:        \"defaults to 3.0\",\n\t\t\tconnString:  \"postgres://localhost/test\",\n\t\t\texpectedMin: \"3.0\",\n\t\t\texpectedMax: \"3.0\",\n\t\t},\n\t\t{\n\t\t\tname:        \"max_protocol_version=3.2\",\n\t\t\tconnString:  \"postgres://localhost/test?max_protocol_version=3.2\",\n\t\t\texpectedMin: \"3.0\",\n\t\t\texpectedMax: \"3.2\",\n\t\t},\n\t\t{\n\t\t\tname:        \"min_protocol_version=3.2 and max_protocol_version=3.2\",\n\t\t\tconnString:  \"postgres://localhost/test?min_protocol_version=3.2&max_protocol_version=3.2\",\n\t\t\texpectedMin: \"3.2\",\n\t\t\texpectedMax: \"3.2\",\n\t\t},\n\t\t{\n\t\t\tname:        \"max_protocol_version=latest\",\n\t\t\tconnString:  \"postgres://localhost/test?max_protocol_version=latest\",\n\t\t\texpectedMin: \"3.0\",\n\t\t\texpectedMax: \"latest\",\n\t\t},\n\t\t{\n\t\t\tname:        \"min and max = latest\",\n\t\t\tconnString:  \"postgres://localhost/test?min_protocol_version=latest&max_protocol_version=latest\",\n\t\t\texpectedMin: \"latest\",\n\t\t\texpectedMax: \"latest\",\n\t\t},\n\t\t{\n\t\t\tname:               \"invalid min_protocol_version\",\n\t\t\tconnString:         \"postgres://localhost/test?min_protocol_version=2.0\",\n\t\t\texpectError:        true,\n\t\t\texpectedErrContain: \"invalid min_protocol_version\",\n\t\t},\n\t\t{\n\t\t\tname:               \"invalid max_protocol_version\",\n\t\t\tconnString:         \"postgres://localhost/test?max_protocol_version=4.0\",\n\t\t\texpectError:        true,\n\t\t\texpectedErrContain: \"invalid max_protocol_version\",\n\t\t},\n\t\t{\n\t\t\tname:               \"min > max\",\n\t\t\tconnString:         \"postgres://localhost/test?min_protocol_version=3.2&max_protocol_version=3.0\",\n\t\t\texpectError:        true,\n\t\t\texpectedErrContain: \"min_protocol_version cannot be greater than max_protocol_version\",\n\t\t},\n\t\t{\n\t\t\tname:               \"environment variable PGMINPROTOCOLVERSION without matching max fails\",\n\t\t\tconnString:         \"postgres://localhost/test\",\n\t\t\tenvMin:             \"3.2\",\n\t\t\texpectError:        true,\n\t\t\texpectedErrContain: \"min_protocol_version cannot be greater than max_protocol_version\",\n\t\t},\n\t\t{\n\t\t\tname:        \"environment variables PGMINPROTOCOLVERSION and PGMAXPROTOCOLVERSION together\",\n\t\t\tconnString:  \"postgres://localhost/test\",\n\t\t\tenvMin:      \"3.2\",\n\t\t\tenvMax:      \"3.2\",\n\t\t\texpectedMin: \"3.2\",\n\t\t\texpectedMax: \"3.2\",\n\t\t},\n\t\t{\n\t\t\tname:        \"environment variable PGMAXPROTOCOLVERSION\",\n\t\t\tconnString:  \"postgres://localhost/test\",\n\t\t\tenvMax:      \"3.2\",\n\t\t\texpectedMin: \"3.0\",\n\t\t\texpectedMax: \"3.2\",\n\t\t},\n\t\t{\n\t\t\tname:        \"conn string overrides environment variable\",\n\t\t\tconnString:  \"postgres://localhost/test?max_protocol_version=3.0\",\n\t\t\tenvMax:      \"3.2\",\n\t\t\texpectedMin: \"3.0\",\n\t\t\texpectedMax: \"3.0\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t// Clear protocol version env vars\n\t\t\tt.Setenv(\"PGMINPROTOCOLVERSION\", \"\")\n\t\t\tt.Setenv(\"PGMAXPROTOCOLVERSION\", \"\")\n\n\t\t\tif tt.envMin != \"\" {\n\t\t\t\tt.Setenv(\"PGMINPROTOCOLVERSION\", tt.envMin)\n\t\t\t}\n\t\t\tif tt.envMax != \"\" {\n\t\t\t\tt.Setenv(\"PGMAXPROTOCOLVERSION\", tt.envMax)\n\t\t\t}\n\n\t\t\tconfig, err := pgconn.ParseConfig(tt.connString)\n\t\t\tif tt.expectError {\n\t\t\t\trequire.ErrorContains(t, err, tt.expectedErrContain)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.expectedMin, config.MinProtocolVersion, \"MinProtocolVersion\")\n\t\t\tassert.Equal(t, tt.expectedMax, config.MaxProtocolVersion, \"MaxProtocolVersion\")\n\t\t})\n\t}\n}\n\nfunc TestParseConfigChannelBinding(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname            string\n\t\tconnString      string\n\t\texpected        string\n\t\texpectError     bool\n\t\texpectedErrText string\n\t}{\n\t\t{\n\t\t\tname:       \"defaults to prefer\",\n\t\t\tconnString: \"postgres://localhost/test\",\n\t\t\texpected:   \"prefer\",\n\t\t},\n\t\t{\n\t\t\tname:       \"explicit prefer\",\n\t\t\tconnString: \"postgres://localhost/test?channel_binding=prefer\",\n\t\t\texpected:   \"prefer\",\n\t\t},\n\t\t{\n\t\t\tname:       \"disable\",\n\t\t\tconnString: \"postgres://localhost/test?channel_binding=disable\",\n\t\t\texpected:   \"disable\",\n\t\t},\n\t\t{\n\t\t\tname:       \"require\",\n\t\t\tconnString: \"postgres://localhost/test?channel_binding=require\",\n\t\t\texpected:   \"require\",\n\t\t},\n\t\t{\n\t\t\tname:            \"invalid value\",\n\t\t\tconnString:      \"postgres://localhost/test?channel_binding=invalid\",\n\t\t\texpectError:     true,\n\t\t\texpectedErrText: \"unknown channel_binding value\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tconfig, err := pgconn.ParseConfig(tt.connString)\n\t\t\tif tt.expectError {\n\t\t\t\trequire.ErrorContains(t, err, tt.expectedErrText)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.expected, config.ChannelBinding)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgconn/ctxwatch/context_watcher.go",
    "content": "package ctxwatch\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\n// ContextWatcher watches a context and performs an action when the context is canceled. It can watch one context at a\n// time.\ntype ContextWatcher struct {\n\thandler Handler\n\n\t// Lock protects the members below.\n\tlock sync.Mutex\n\t// Stop is the handle for an \"after func\". See [context.AfterFunc].\n\tstop func() bool\n\tdone chan struct{}\n}\n\n// NewContextWatcher returns a ContextWatcher. onCancel will be called when a watched context is canceled.\n// OnUnwatchAfterCancel will be called when Unwatch is called and the watched context had already been canceled and\n// onCancel called.\nfunc NewContextWatcher(handler Handler) *ContextWatcher {\n\tcw := &ContextWatcher{\n\t\thandler: handler,\n\t}\n\n\treturn cw\n}\n\n// Watch starts watching ctx. If ctx is canceled then the onCancel function passed to NewContextWatcher will be called.\nfunc (cw *ContextWatcher) Watch(ctx context.Context) {\n\tcw.lock.Lock()\n\tdefer cw.lock.Unlock()\n\n\tif cw.stop != nil {\n\t\tpanic(\"watch already in progress\")\n\t}\n\n\tif ctx.Done() != nil {\n\t\tcw.done = make(chan struct{})\n\t\tcw.stop = context.AfterFunc(ctx, func() {\n\t\t\tcw.handler.HandleCancel(ctx)\n\t\t\tclose(cw.done)\n\t\t})\n\t}\n}\n\n// Unwatch stops watching the previously watched context. If the onCancel function passed to NewContextWatcher was\n// called then onUnwatchAfterCancel will also be called.\nfunc (cw *ContextWatcher) Unwatch() {\n\tcw.lock.Lock()\n\tdefer cw.lock.Unlock()\n\n\tif cw.stop != nil {\n\t\tif !cw.stop() {\n\t\t\t<-cw.done\n\t\t\tcw.handler.HandleUnwatchAfterCancel()\n\t\t}\n\t\tcw.stop = nil\n\t\tcw.done = nil\n\t}\n}\n\ntype Handler interface {\n\t// HandleCancel is called when the context that a ContextWatcher is currently watching is canceled. canceledCtx is the\n\t// context that was canceled.\n\tHandleCancel(canceledCtx context.Context)\n\n\t// HandleUnwatchAfterCancel is called when a ContextWatcher that called HandleCancel on this Handler is unwatched.\n\tHandleUnwatchAfterCancel()\n}\n"
  },
  {
    "path": "pgconn/ctxwatch/context_watcher_test.go",
    "content": "package ctxwatch_test\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn/ctxwatch\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype testHandler struct {\n\thandleCancel             func(context.Context)\n\thandleUnwatchAfterCancel func()\n}\n\nfunc (h *testHandler) HandleCancel(ctx context.Context) {\n\th.handleCancel(ctx)\n}\n\nfunc (h *testHandler) HandleUnwatchAfterCancel() {\n\th.handleUnwatchAfterCancel()\n}\n\nfunc TestContextWatcherContextCancelled(t *testing.T) {\n\tcanceledChan := make(chan struct{})\n\tcleanupCalled := false\n\tcw := ctxwatch.NewContextWatcher(&testHandler{\n\t\thandleCancel: func(context.Context) {\n\t\t\tcanceledChan <- struct{}{}\n\t\t}, handleUnwatchAfterCancel: func() {\n\t\t\tcleanupCalled = true\n\t\t},\n\t})\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tcw.Watch(ctx)\n\tcancel()\n\n\tselect {\n\tcase <-canceledChan:\n\tcase <-time.NewTimer(time.Second).C:\n\t\tt.Fatal(\"Timed out waiting for cancel func to be called\")\n\t}\n\n\tcw.Unwatch()\n\n\trequire.True(t, cleanupCalled, \"Cleanup func was not called\")\n}\n\nfunc TestContextWatcherUnwatchedBeforeContextCancelled(t *testing.T) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{\n\t\thandleCancel: func(context.Context) {\n\t\t\tt.Error(\"cancel func should not have been called\")\n\t\t}, handleUnwatchAfterCancel: func() {\n\t\t\tt.Error(\"cleanup func should not have been called\")\n\t\t},\n\t})\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tcw.Watch(ctx)\n\tcw.Unwatch()\n\tcancel()\n}\n\nfunc TestContextWatcherMultipleWatchPanics(t *testing.T) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}})\n\n\tctx, cancel := context.WithCancel(t.Context())\n\tdefer cancel()\n\tcw.Watch(ctx)\n\tdefer cw.Unwatch()\n\n\tctx2, cancel2 := context.WithCancel(t.Context())\n\tdefer cancel2()\n\trequire.Panics(t, func() { cw.Watch(ctx2) }, \"Expected panic when Watch called multiple times\")\n}\n\nfunc TestContextWatcherUnwatchWhenNotWatchingIsSafe(t *testing.T) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}})\n\tcw.Unwatch() // unwatch when not / never watching\n\n\tctx := t.Context()\n\tcw.Watch(ctx)\n\tcw.Unwatch()\n\tcw.Unwatch() // double unwatch\n}\n\nfunc TestContextWatcherUnwatchIsConcurrencySafe(t *testing.T) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}})\n\n\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\tdefer cancel()\n\tcw.Watch(ctx)\n\n\tgo cw.Unwatch()\n\tgo cw.Unwatch()\n\n\t<-ctx.Done()\n}\n\nfunc TestContextWatcherStress(t *testing.T) {\n\tvar cancelFuncCalls int64\n\tvar cleanupFuncCalls int64\n\n\tcw := ctxwatch.NewContextWatcher(&testHandler{\n\t\thandleCancel: func(context.Context) {\n\t\t\tatomic.AddInt64(&cancelFuncCalls, 1)\n\t\t}, handleUnwatchAfterCancel: func() {\n\t\t\tatomic.AddInt64(&cleanupFuncCalls, 1)\n\t\t},\n\t})\n\n\tcycleCount := 100_000\n\n\tfor i := range cycleCount {\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tdefer cancel() // satisfy linter; cancel is idempotent\n\t\tcw.Watch(ctx)\n\t\tif i%2 == 0 {\n\t\t\tcancel()\n\t\t}\n\n\t\t// Without time.Sleep, cw.Unwatch will almost always run before the cancel func which means cancel will never happen. This gives us a better mix.\n\t\tif i%333 == 0 {\n\t\t\t// on Windows Sleep takes more time than expected so we try to get here less frequently to avoid\n\t\t\t// the CI takes a long time\n\t\t\ttime.Sleep(time.Nanosecond)\n\t\t}\n\n\t\tcw.Unwatch()\n\t\tif i%2 == 1 {\n\t\t\tcancel()\n\t\t}\n\t}\n\n\tactualCancelFuncCalls := atomic.LoadInt64(&cancelFuncCalls)\n\tactualCleanupFuncCalls := atomic.LoadInt64(&cleanupFuncCalls)\n\n\tif actualCancelFuncCalls == 0 {\n\t\tt.Fatal(\"actualCancelFuncCalls == 0\")\n\t}\n\n\tmaxCancelFuncCalls := int64(cycleCount) / 2\n\tif actualCancelFuncCalls > maxCancelFuncCalls {\n\t\tt.Errorf(\"cancel func calls should be no more than %d but was %d\", actualCancelFuncCalls, maxCancelFuncCalls)\n\t}\n\n\tif actualCancelFuncCalls != actualCleanupFuncCalls {\n\t\tt.Errorf(\"cancel func calls (%d) should be equal to cleanup func calls (%d) but was not\", actualCancelFuncCalls, actualCleanupFuncCalls)\n\t}\n}\n\nfunc BenchmarkContextWatcherUncancellable(b *testing.B) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}})\n\n\tfor b.Loop() {\n\t\tcw.Watch(context.Background())\n\t\tcw.Unwatch()\n\t}\n}\n\nfunc BenchmarkContextWatcherCancelled(b *testing.B) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}})\n\n\tfor b.Loop() {\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tcw.Watch(ctx)\n\t\tcancel()\n\t\tcw.Unwatch()\n\t}\n}\n\nfunc BenchmarkContextWatcherCancellable(b *testing.B) {\n\tcw := ctxwatch.NewContextWatcher(&testHandler{handleCancel: func(context.Context) {}, handleUnwatchAfterCancel: func() {}})\n\n\tctx := b.Context()\n\n\tfor b.Loop() {\n\t\tcw.Watch(ctx)\n\t\tcw.Unwatch()\n\t}\n}\n"
  },
  {
    "path": "pgconn/ctxwatch/synctest_test.go",
    "content": "//go:build go1.26\n\npackage ctxwatch_test\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"testing/synctest\"\n\n\t\"github.com/jackc/pgx/v5/pgconn/ctxwatch\"\n)\n\nfunc TestContextWatchGoroutineBuildup(t *testing.T) {\n\tsynctest.Test(t, func(t *testing.T) {\n\t\tvar cancelFuncCalls int64\n\t\tvar cleanupFuncCalls int64\n\t\th := &testHandler{\n\t\t\thandleCancel: func(context.Context) {\n\t\t\t\tatomic.AddInt64(&cancelFuncCalls, 1)\n\t\t\t},\n\t\t\thandleUnwatchAfterCancel: func() {\n\t\t\t\tatomic.AddInt64(&cleanupFuncCalls, 1)\n\t\t\t},\n\t\t}\n\t\tctx, done := context.WithCancel(t.Context())\n\t\tdefer done()\n\t\tfloor := runtime.NumGoroutine()\n\n\t\tfor range 10 {\n\t\t\tcw := ctxwatch.NewContextWatcher(h)\n\t\t\tcw.Watch(ctx)\n\t\t\tdefer cw.Unwatch()\n\t\t}\n\t\tsynctest.Wait()\n\t\tdone()\n\t\tfor range 10 {\n\t\t\tcw := ctxwatch.NewContextWatcher(h)\n\t\t\tcw.Watch(ctx)\n\t\t\tcw.Unwatch()\n\t\t}\n\n\t\tsynctest.Wait()\n\t\toutstanding := runtime.NumGoroutine() - floor\n\t\tt.Log(\"outstanding goroutines:\", outstanding)\n\t\tif outstanding != 0 {\n\t\t\tt.Fail()\n\t\t}\n\n\t\tactualCancelFuncCalls := atomic.LoadInt64(&cancelFuncCalls)\n\t\tt.Log(\"cancel:\", actualCancelFuncCalls)\n\t\tif actualCancelFuncCalls != 20 {\n\t\t\tt.Fail()\n\t\t}\n\t\tactualCleanupFuncCalls := atomic.LoadInt64(&cleanupFuncCalls)\n\t\tt.Log(\"cleanup:\", actualCleanupFuncCalls)\n\t\tif actualCleanupFuncCalls != 10 {\n\t\t\tt.Fail()\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgconn/defaults.go",
    "content": "//go:build !windows\n// +build !windows\n\npackage pgconn\n\nimport (\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n)\n\nfunc defaultSettings() map[string]string {\n\tsettings := make(map[string]string)\n\n\tsettings[\"host\"] = defaultHost()\n\tsettings[\"port\"] = \"5432\"\n\n\t// Default to the OS user name. Purposely ignoring err getting user name from\n\t// OS. The client application will simply have to specify the user in that\n\t// case (which they typically will be doing anyway).\n\tuser, err := user.Current()\n\tif err == nil {\n\t\tsettings[\"user\"] = user.Username\n\t\tsettings[\"passfile\"] = filepath.Join(user.HomeDir, \".pgpass\")\n\t\tsettings[\"servicefile\"] = filepath.Join(user.HomeDir, \".pg_service.conf\")\n\t\tsslcert := filepath.Join(user.HomeDir, \".postgresql\", \"postgresql.crt\")\n\t\tsslkey := filepath.Join(user.HomeDir, \".postgresql\", \"postgresql.key\")\n\t\tif _, err := os.Stat(sslcert); err == nil {\n\t\t\tif _, err := os.Stat(sslkey); err == nil {\n\t\t\t\t// Both the cert and key must be present to use them, or do not use either\n\t\t\t\tsettings[\"sslcert\"] = sslcert\n\t\t\t\tsettings[\"sslkey\"] = sslkey\n\t\t\t}\n\t\t}\n\t\tsslrootcert := filepath.Join(user.HomeDir, \".postgresql\", \"root.crt\")\n\t\tif _, err := os.Stat(sslrootcert); err == nil {\n\t\t\tsettings[\"sslrootcert\"] = sslrootcert\n\t\t}\n\t}\n\n\tsettings[\"target_session_attrs\"] = \"any\"\n\n\treturn settings\n}\n\n// defaultHost attempts to mimic libpq's default host. libpq uses the default unix socket location on *nix and localhost\n// on Windows. The default socket location is compiled into libpq. Since pgx does not have access to that default it\n// checks the existence of common locations.\nfunc defaultHost() string {\n\tcandidatePaths := []string{\n\t\t\"/var/run/postgresql\", // Debian\n\t\t\"/private/tmp\",        // OSX - homebrew\n\t\t\"/tmp\",                // standard PostgreSQL\n\t}\n\n\tfor _, path := range candidatePaths {\n\t\tif _, err := os.Stat(path); err == nil {\n\t\t\treturn path\n\t\t}\n\t}\n\n\treturn \"localhost\"\n}\n"
  },
  {
    "path": "pgconn/defaults_windows.go",
    "content": "package pgconn\n\nimport (\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc defaultSettings() map[string]string {\n\tsettings := make(map[string]string)\n\n\tsettings[\"host\"] = defaultHost()\n\tsettings[\"port\"] = \"5432\"\n\n\t// Default to the OS user name. Purposely ignoring err getting user name from\n\t// OS. The client application will simply have to specify the user in that\n\t// case (which they typically will be doing anyway).\n\tuser, err := user.Current()\n\tappData := os.Getenv(\"APPDATA\")\n\tif err == nil {\n\t\t// Windows gives us the username here as `DOMAIN\\user` or `LOCALPCNAME\\user`,\n\t\t// but the libpq default is just the `user` portion, so we strip off the first part.\n\t\tusername := user.Username\n\t\tif strings.Contains(username, \"\\\\\") {\n\t\t\tusername = username[strings.LastIndex(username, \"\\\\\")+1:]\n\t\t}\n\n\t\tsettings[\"user\"] = username\n\t\tsettings[\"passfile\"] = filepath.Join(appData, \"postgresql\", \"pgpass.conf\")\n\t\tsettings[\"servicefile\"] = filepath.Join(user.HomeDir, \".pg_service.conf\")\n\t\tsslcert := filepath.Join(appData, \"postgresql\", \"postgresql.crt\")\n\t\tsslkey := filepath.Join(appData, \"postgresql\", \"postgresql.key\")\n\t\tif _, err := os.Stat(sslcert); err == nil {\n\t\t\tif _, err := os.Stat(sslkey); err == nil {\n\t\t\t\t// Both the cert and key must be present to use them, or do not use either\n\t\t\t\tsettings[\"sslcert\"] = sslcert\n\t\t\t\tsettings[\"sslkey\"] = sslkey\n\t\t\t}\n\t\t}\n\t\tsslrootcert := filepath.Join(appData, \"postgresql\", \"root.crt\")\n\t\tif _, err := os.Stat(sslrootcert); err == nil {\n\t\t\tsettings[\"sslrootcert\"] = sslrootcert\n\t\t}\n\t}\n\n\tsettings[\"target_session_attrs\"] = \"any\"\n\n\treturn settings\n}\n\n// defaultHost attempts to mimic libpq's default host. libpq uses the default unix socket location on *nix and localhost\n// on Windows. The default socket location is compiled into libpq. Since pgx does not have access to that default it\n// checks the existence of common locations.\nfunc defaultHost() string {\n\treturn \"localhost\"\n}\n"
  },
  {
    "path": "pgconn/doc.go",
    "content": "// Package pgconn is a low-level PostgreSQL database driver.\n/*\npgconn provides lower level access to a PostgreSQL connection than a database/sql or pgx connection. It operates at\nnearly the same level is the C library libpq.\n\nEstablishing a Connection\n\nUse Connect to establish a connection. It accepts a connection string in URL or keyword/value format and will read the\nenvironment for libpq style environment variables.\n\nExecuting a Query\n\nExecParams and ExecPrepared execute a single query. They return readers that iterate over each row. The Read method\nreads all rows into memory.\n\nExecuting Multiple Queries in a Single Round Trip\n\nExec and ExecBatch can execute multiple queries in a single round trip. They return readers that iterate over each query\nresult. The ReadAll method reads all query results into memory.\n\nPipeline Mode\n\nPipeline mode allows sending queries without having read the results of previously sent queries. It allows control of\nexactly how many and when network round trips occur.\n\nContext Support\n\nAll potentially blocking operations take a context.Context. The default behavior when a context is canceled is for the\nmethod to immediately return. In most circumstances, this will also close the underlying connection. This behavior can\nbe customized by using BuildContextWatcherHandler on the Config to create a ctxwatch.Handler with different behavior.\nThis can be especially useful when queries that are frequently canceled and the overhead of creating new connections is\na problem. DeadlineContextWatcherHandler and CancelRequestContextWatcherHandler can be used to introduce a delay before\ninterrupting the query in such a way as to close the connection.\n\nThe CancelRequest method may be used to request the PostgreSQL server cancel an in-progress query without forcing the\nclient to abort.\n*/\npackage pgconn\n"
  },
  {
    "path": "pgconn/errors.go",
    "content": "package pgconn\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n)\n\n// SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.\nfunc SafeToRetry(err error) bool {\n\tvar retryableErr interface{ SafeToRetry() bool }\n\tif errors.As(err, &retryableErr) {\n\t\treturn retryableErr.SafeToRetry()\n\t}\n\treturn false\n}\n\n// Timeout checks if err was caused by a timeout. To be specific, it is true if err was caused within pgconn by a\n// context.DeadlineExceeded or an implementer of net.Error where Timeout() is true.\nfunc Timeout(err error) bool {\n\tvar timeoutErr *errTimeout\n\treturn errors.As(err, &timeoutErr)\n}\n\n// PgError represents an error reported by the PostgreSQL server. See\n// http://www.postgresql.org/docs/current/static/protocol-error-fields.html for\n// detailed field description.\ntype PgError struct {\n\tSeverity            string\n\tSeverityUnlocalized string\n\tCode                string\n\tMessage             string\n\tDetail              string\n\tHint                string\n\tPosition            int32\n\tInternalPosition    int32\n\tInternalQuery       string\n\tWhere               string\n\tSchemaName          string\n\tTableName           string\n\tColumnName          string\n\tDataTypeName        string\n\tConstraintName      string\n\tFile                string\n\tLine                int32\n\tRoutine             string\n}\n\nfunc (pe *PgError) Error() string {\n\treturn pe.Severity + \": \" + pe.Message + \" (SQLSTATE \" + pe.Code + \")\"\n}\n\n// SQLState returns the SQLState of the error.\nfunc (pe *PgError) SQLState() string {\n\treturn pe.Code\n}\n\n// ConnectError is the error returned when a connection attempt fails.\ntype ConnectError struct {\n\tConfig *Config // The configuration that was used in the connection attempt.\n\terr    error\n}\n\nfunc (e *ConnectError) Error() string {\n\tprefix := fmt.Sprintf(\"failed to connect to `user=%s database=%s`:\", e.Config.User, e.Config.Database)\n\tdetails := e.err.Error()\n\tif strings.Contains(details, \"\\n\") {\n\t\treturn prefix + \"\\n\\t\" + strings.ReplaceAll(details, \"\\n\", \"\\n\\t\")\n\t} else {\n\t\treturn prefix + \" \" + details\n\t}\n}\n\nfunc (e *ConnectError) Unwrap() error {\n\treturn e.err\n}\n\ntype perDialConnectError struct {\n\taddress          string\n\toriginalHostname string\n\terr              error\n}\n\nfunc (e *perDialConnectError) Error() string {\n\treturn fmt.Sprintf(\"%s (%s): %s\", e.address, e.originalHostname, e.err.Error())\n}\n\nfunc (e *perDialConnectError) Unwrap() error {\n\treturn e.err\n}\n\ntype connLockError struct {\n\tstatus string\n}\n\nfunc (e *connLockError) SafeToRetry() bool {\n\treturn true // a lock failure by definition happens before the connection is used.\n}\n\nfunc (e *connLockError) Error() string {\n\treturn e.status\n}\n\n// ParseConfigError is the error returned when a connection string cannot be parsed.\ntype ParseConfigError struct {\n\tConnString string // The connection string that could not be parsed.\n\tmsg        string\n\terr        error\n}\n\nfunc NewParseConfigError(conn, msg string, err error) error {\n\treturn &ParseConfigError{\n\t\tConnString: conn,\n\t\tmsg:        msg,\n\t\terr:        err,\n\t}\n}\n\nfunc (e *ParseConfigError) Error() string {\n\t// Now that ParseConfigError is public and ConnString is available to the developer, perhaps it would be better only\n\t// return a static string. That would ensure that the error message cannot leak a password. The ConnString field would\n\t// allow access to the original string if desired and Unwrap would allow access to the underlying error.\n\tconnString := redactPW(e.ConnString)\n\tif e.err == nil {\n\t\treturn fmt.Sprintf(\"cannot parse `%s`: %s\", connString, e.msg)\n\t}\n\treturn fmt.Sprintf(\"cannot parse `%s`: %s (%s)\", connString, e.msg, e.err.Error())\n}\n\nfunc (e *ParseConfigError) Unwrap() error {\n\treturn e.err\n}\n\nfunc normalizeTimeoutError(ctx context.Context, err error) error {\n\tvar netErr net.Error\n\tif errors.As(err, &netErr) && netErr.Timeout() {\n\t\tif ctx.Err() == context.Canceled {\n\t\t\t// Since the timeout was caused by a context cancellation, the actual error is context.Canceled not the timeout error.\n\t\t\treturn context.Canceled\n\t\t} else if ctx.Err() == context.DeadlineExceeded {\n\t\t\treturn &errTimeout{err: ctx.Err()}\n\t\t} else {\n\t\t\treturn &errTimeout{err: netErr}\n\t\t}\n\t}\n\treturn err\n}\n\ntype pgconnError struct {\n\tmsg         string\n\terr         error\n\tsafeToRetry bool\n}\n\nfunc (e *pgconnError) Error() string {\n\tif e.msg == \"\" {\n\t\treturn e.err.Error()\n\t}\n\tif e.err == nil {\n\t\treturn e.msg\n\t}\n\treturn fmt.Sprintf(\"%s: %s\", e.msg, e.err.Error())\n}\n\nfunc (e *pgconnError) SafeToRetry() bool {\n\treturn e.safeToRetry\n}\n\nfunc (e *pgconnError) Unwrap() error {\n\treturn e.err\n}\n\n// errTimeout occurs when an error was caused by a timeout. Specifically, it wraps an error which is\n// context.Canceled, context.DeadlineExceeded, or an implementer of net.Error where Timeout() is true.\ntype errTimeout struct {\n\terr error\n}\n\nfunc (e *errTimeout) Error() string {\n\treturn fmt.Sprintf(\"timeout: %s\", e.err.Error())\n}\n\nfunc (e *errTimeout) SafeToRetry() bool {\n\treturn SafeToRetry(e.err)\n}\n\nfunc (e *errTimeout) Unwrap() error {\n\treturn e.err\n}\n\ntype contextAlreadyDoneError struct {\n\terr error\n}\n\nfunc (e *contextAlreadyDoneError) Error() string {\n\treturn fmt.Sprintf(\"context already done: %s\", e.err.Error())\n}\n\nfunc (e *contextAlreadyDoneError) SafeToRetry() bool {\n\treturn true\n}\n\nfunc (e *contextAlreadyDoneError) Unwrap() error {\n\treturn e.err\n}\n\n// newContextAlreadyDoneError double-wraps a context error in `contextAlreadyDoneError` and `errTimeout`.\nfunc newContextAlreadyDoneError(ctx context.Context) (err error) {\n\treturn &errTimeout{&contextAlreadyDoneError{err: ctx.Err()}}\n}\n\nfunc redactPW(connString string) string {\n\tif strings.HasPrefix(connString, \"postgres://\") || strings.HasPrefix(connString, \"postgresql://\") {\n\t\tif u, err := url.Parse(connString); err == nil {\n\t\t\treturn redactURL(u)\n\t\t}\n\t}\n\tquotedKV := regexp.MustCompile(`password='[^']*'`)\n\tconnString = quotedKV.ReplaceAllLiteralString(connString, \"password=xxxxx\")\n\tplainKV := regexp.MustCompile(`password=[^ ]*`)\n\tconnString = plainKV.ReplaceAllLiteralString(connString, \"password=xxxxx\")\n\tbrokenURL := regexp.MustCompile(`:[^:@]+?@`)\n\tconnString = brokenURL.ReplaceAllLiteralString(connString, \":xxxxxx@\")\n\treturn connString\n}\n\nfunc redactURL(u *url.URL) string {\n\tif u == nil {\n\t\treturn \"\"\n\t}\n\tif _, pwSet := u.User.Password(); pwSet {\n\t\tu.User = url.UserPassword(u.User.Username(), \"xxxxx\")\n\t}\n\treturn u.String()\n}\n\ntype NotPreferredError struct {\n\terr         error\n\tsafeToRetry bool\n}\n\nfunc (e *NotPreferredError) Error() string {\n\treturn fmt.Sprintf(\"standby server not found: %s\", e.err.Error())\n}\n\nfunc (e *NotPreferredError) SafeToRetry() bool {\n\treturn e.safeToRetry\n}\n\nfunc (e *NotPreferredError) Unwrap() error {\n\treturn e.err\n}\n\ntype PrepareError struct {\n\terr error\n\n\tParseComplete bool // Indicates whether the error occurred after a ParseComplete message was received.\n}\n\nfunc (e *PrepareError) Error() string {\n\tif e.ParseComplete {\n\t\treturn fmt.Sprintf(\"prepare failed after ParseComplete: %s\", e.err.Error())\n\t}\n\treturn e.err.Error()\n}\n\nfunc (e *PrepareError) Unwrap() error {\n\treturn e.err\n}\n"
  },
  {
    "path": "pgconn/errors_test.go",
    "content": "package pgconn_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestConfigError(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\terr         error\n\t\texpectedMsg string\n\t}{\n\t\t{\n\t\t\tname:        \"url with password\",\n\t\t\terr:         pgconn.NewParseConfigError(\"postgresql://foo:password@host\", \"msg\", nil),\n\t\t\texpectedMsg: \"cannot parse `postgresql://foo:xxxxx@host`: msg\",\n\t\t},\n\t\t{\n\t\t\tname:        \"keyword/value with password unquoted\",\n\t\t\terr:         pgconn.NewParseConfigError(\"host=host password=password user=user\", \"msg\", nil),\n\t\t\texpectedMsg: \"cannot parse `host=host password=xxxxx user=user`: msg\",\n\t\t},\n\t\t{\n\t\t\tname:        \"keyword/value with password quoted\",\n\t\t\terr:         pgconn.NewParseConfigError(\"host=host password='pass word' user=user\", \"msg\", nil),\n\t\t\texpectedMsg: \"cannot parse `host=host password=xxxxx user=user`: msg\",\n\t\t},\n\t\t{\n\t\t\tname:        \"weird url\",\n\t\t\terr:         pgconn.NewParseConfigError(\"postgresql://foo::password@host:1:\", \"msg\", nil),\n\t\t\texpectedMsg: \"cannot parse `postgresql://foo:xxxxx@host:1:`: msg\",\n\t\t},\n\t\t{\n\t\t\tname:        \"weird url with slash in password\",\n\t\t\terr:         pgconn.NewParseConfigError(\"postgres://user:pass/word@host:5432/db_name\", \"msg\", nil),\n\t\t\texpectedMsg: \"cannot parse `postgres://user:xxxxxx@host:5432/db_name`: msg\",\n\t\t},\n\t\t{\n\t\t\tname:        \"url without password\",\n\t\t\terr:         pgconn.NewParseConfigError(\"postgresql://other@host/db\", \"msg\", nil),\n\t\t\texpectedMsg: \"cannot parse `postgresql://other@host/db`: msg\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tassert.EqualError(t, tt.err, tt.expectedMsg)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgconn/export_test.go",
    "content": "// File export_test exports some methods for better testing.\n\npackage pgconn\n"
  },
  {
    "path": "pgconn/helper_test.go",
    "content": "package pgconn_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc closeConn(t testing.TB, conn *pgconn.PgConn) {\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\trequire.NoError(t, conn.Close(ctx))\n\tselect {\n\tcase <-conn.CleanupDone():\n\tcase <-time.After(30 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\n// Do a simple query to ensure the connection is still usable\nfunc ensureConnValid(t *testing.T, pgConn *pgconn.PgConn) {\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tresult := pgConn.ExecParams(ctx, \"select generate_series(1,$1)\", [][]byte{[]byte(\"3\")}, nil, nil, nil).Read()\n\tcancel()\n\n\trequire.Nil(t, result.Err)\n\tassert.Equal(t, 3, len(result.Rows))\n\tassert.Equal(t, \"1\", string(result.Rows[0][0]))\n\tassert.Equal(t, \"2\", string(result.Rows[1][0]))\n\tassert.Equal(t, \"3\", string(result.Rows[2][0]))\n}\n"
  },
  {
    "path": "pgconn/internal/bgreader/bgreader.go",
    "content": "// Package bgreader provides a io.Reader that can optionally buffer reads in the background.\npackage bgreader\n\nimport (\n\t\"io\"\n\t\"sync\"\n\n\t\"github.com/jackc/pgx/v5/internal/iobufpool\"\n)\n\nconst (\n\tStatusStopped = iota\n\tStatusRunning\n\tStatusStopping\n)\n\n// BGReader is an io.Reader that can optionally buffer reads in the background. It is safe for concurrent use.\ntype BGReader struct {\n\tr io.Reader\n\n\tcond        *sync.Cond\n\tstatus      int32\n\treadResults []readResult\n}\n\ntype readResult struct {\n\tbuf *[]byte\n\terr error\n}\n\n// Start starts the backgrounder reader. If the background reader is already running this is a no-op. The background\n// reader will stop automatically when the underlying reader returns an error.\nfunc (r *BGReader) Start() {\n\tr.cond.L.Lock()\n\tdefer r.cond.L.Unlock()\n\n\tswitch r.status {\n\tcase StatusStopped:\n\t\tr.status = StatusRunning\n\t\tgo r.bgRead()\n\tcase StatusRunning:\n\t\t// no-op\n\tcase StatusStopping:\n\t\tr.status = StatusRunning\n\t}\n}\n\n// Stop tells the background reader to stop after the in progress Read returns. It is safe to call Stop when the\n// background reader is not running.\nfunc (r *BGReader) Stop() {\n\tr.cond.L.Lock()\n\tdefer r.cond.L.Unlock()\n\n\tswitch r.status {\n\tcase StatusStopped:\n\t\t// no-op\n\tcase StatusRunning:\n\t\tr.status = StatusStopping\n\tcase StatusStopping:\n\t\t// no-op\n\t}\n}\n\n// Status returns the current status of the background reader.\nfunc (r *BGReader) Status() int32 {\n\tr.cond.L.Lock()\n\tdefer r.cond.L.Unlock()\n\treturn r.status\n}\n\nfunc (r *BGReader) bgRead() {\n\tkeepReading := true\n\tfor keepReading {\n\t\tbuf := iobufpool.Get(8192)\n\t\tn, err := r.r.Read(*buf)\n\t\t*buf = (*buf)[:n]\n\n\t\tr.cond.L.Lock()\n\t\tr.readResults = append(r.readResults, readResult{buf: buf, err: err})\n\t\tif r.status == StatusStopping || err != nil {\n\t\t\tr.status = StatusStopped\n\t\t\tkeepReading = false\n\t\t}\n\t\tr.cond.L.Unlock()\n\t\tr.cond.Broadcast()\n\t}\n}\n\n// Read implements the io.Reader interface.\nfunc (r *BGReader) Read(p []byte) (int, error) {\n\tr.cond.L.Lock()\n\tdefer r.cond.L.Unlock()\n\n\tif len(r.readResults) > 0 {\n\t\treturn r.readFromReadResults(p)\n\t}\n\n\t// There are no unread background read results and the background reader is stopped.\n\tif r.status == StatusStopped {\n\t\treturn r.r.Read(p)\n\t}\n\n\t// Wait for results from the background reader\n\tfor len(r.readResults) == 0 {\n\t\tr.cond.Wait()\n\t}\n\treturn r.readFromReadResults(p)\n}\n\n// readBackgroundResults reads a result previously read by the background reader. r.cond.L must be held.\nfunc (r *BGReader) readFromReadResults(p []byte) (int, error) {\n\tbuf := r.readResults[0].buf\n\tvar err error\n\n\tn := copy(p, *buf)\n\tif n == len(*buf) {\n\t\terr = r.readResults[0].err\n\t\tiobufpool.Put(buf)\n\t\tif len(r.readResults) == 1 {\n\t\t\tr.readResults = nil\n\t\t} else {\n\t\t\tr.readResults = r.readResults[1:]\n\t\t}\n\t} else {\n\t\t*buf = (*buf)[n:]\n\t\tr.readResults[0].buf = buf\n\t}\n\n\treturn n, err\n}\n\nfunc New(r io.Reader) *BGReader {\n\treturn &BGReader{\n\t\tr: r,\n\t\tcond: &sync.Cond{\n\t\t\tL: &sync.Mutex{},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "pgconn/internal/bgreader/bgreader_test.go",
    "content": "package bgreader_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"math/rand/v2\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn/internal/bgreader\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBGReaderReadWhenStopped(t *testing.T) {\n\tr := bytes.NewReader([]byte(\"foo bar baz\"))\n\tbgr := bgreader.New(r)\n\tbuf, err := io.ReadAll(bgr)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(\"foo bar baz\"), buf)\n}\n\nfunc TestBGReaderReadWhenStarted(t *testing.T) {\n\tr := bytes.NewReader([]byte(\"foo bar baz\"))\n\tbgr := bgreader.New(r)\n\tbgr.Start()\n\tbuf, err := io.ReadAll(bgr)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(\"foo bar baz\"), buf)\n}\n\ntype mockReadFunc func(p []byte) (int, error)\n\ntype mockReader struct {\n\treadFuncs []mockReadFunc\n}\n\nfunc (r *mockReader) Read(p []byte) (int, error) {\n\tif len(r.readFuncs) == 0 {\n\t\treturn 0, io.EOF\n\t}\n\n\tfn := r.readFuncs[0]\n\tr.readFuncs = r.readFuncs[1:]\n\n\treturn fn(p)\n}\n\nfunc TestBGReaderReadWaitsForBackgroundRead(t *testing.T) {\n\trr := &mockReader{\n\t\treadFuncs: []mockReadFunc{\n\t\t\tfunc(p []byte) (int, error) { time.Sleep(1 * time.Second); return copy(p, []byte(\"foo\")), nil },\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"bar\")), nil },\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"baz\")), nil },\n\t\t},\n\t}\n\tbgr := bgreader.New(rr)\n\tbgr.Start()\n\tbuf := make([]byte, 3)\n\tn, err := bgr.Read(buf)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 3, n)\n\trequire.Equal(t, []byte(\"foo\"), buf)\n}\n\nfunc TestBGReaderErrorWhenStarted(t *testing.T) {\n\trr := &mockReader{\n\t\treadFuncs: []mockReadFunc{\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"foo\")), nil },\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"bar\")), nil },\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"baz\")), errors.New(\"oops\") },\n\t\t},\n\t}\n\n\tbgr := bgreader.New(rr)\n\tbgr.Start()\n\tbuf, err := io.ReadAll(bgr)\n\trequire.Equal(t, []byte(\"foobarbaz\"), buf)\n\trequire.EqualError(t, err, \"oops\")\n}\n\nfunc TestBGReaderErrorWhenStopped(t *testing.T) {\n\trr := &mockReader{\n\t\treadFuncs: []mockReadFunc{\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"foo\")), nil },\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"bar\")), nil },\n\t\t\tfunc(p []byte) (int, error) { return copy(p, []byte(\"baz\")), errors.New(\"oops\") },\n\t\t},\n\t}\n\n\tbgr := bgreader.New(rr)\n\tbuf, err := io.ReadAll(bgr)\n\trequire.Equal(t, []byte(\"foobarbaz\"), buf)\n\trequire.EqualError(t, err, \"oops\")\n}\n\ntype numberReader struct {\n\tv   uint8\n\trng *rand.Rand\n}\n\nfunc (nr *numberReader) Read(p []byte) (int, error) {\n\tn := nr.rng.IntN(len(p))\n\tfor i := range n {\n\t\tp[i] = nr.v\n\t\tnr.v++\n\t}\n\n\treturn n, nil\n}\n\n// TestBGReaderStress stress tests BGReader by reading a lot of bytes in random sizes while randomly starting and\n// stopping the background worker from other goroutines.\nfunc TestBGReaderStress(t *testing.T) {\n\tnr := &numberReader{rng: rand.New(rand.NewPCG(0, 0))}\n\tbgr := bgreader.New(nr)\n\n\tbytesRead := 0\n\tvar expected uint8\n\tbuf := make([]byte, 10_000)\n\trng := rand.New(rand.NewPCG(0, 0))\n\n\tfor bytesRead < 1_000_000 {\n\t\trandomNumber := rng.IntN(100)\n\t\tswitch {\n\t\tcase randomNumber < 10:\n\t\t\tgo bgr.Start()\n\t\tcase randomNumber < 20:\n\t\t\tgo bgr.Stop()\n\t\tdefault:\n\t\t\tn, err := bgr.Read(buf)\n\t\t\trequire.NoError(t, err)\n\t\t\tfor i := range n {\n\t\t\t\trequire.Equal(t, expected, buf[i])\n\t\t\t\texpected++\n\t\t\t}\n\t\t\tbytesRead += n\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgconn/krb5.go",
    "content": "package pgconn\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n)\n\n// NewGSSFunc creates a GSS authentication provider, for use with\n// RegisterGSSProvider.\ntype NewGSSFunc func() (GSS, error)\n\nvar newGSS NewGSSFunc\n\n// RegisterGSSProvider registers a GSS authentication provider. For example, if\n// you need to use Kerberos to authenticate with your server, add this to your\n// main package:\n//\n//\timport \"github.com/otan/gopgkrb5\"\n//\n//\tfunc init() {\n//\t\tpgconn.RegisterGSSProvider(func() (pgconn.GSS, error) { return gopgkrb5.NewGSS() })\n//\t}\nfunc RegisterGSSProvider(newGSSArg NewGSSFunc) {\n\tnewGSS = newGSSArg\n}\n\n// GSS provides GSSAPI authentication (e.g., Kerberos).\ntype GSS interface {\n\tGetInitToken(host, service string) ([]byte, error)\n\tGetInitTokenFromSPN(spn string) ([]byte, error)\n\tContinue(inToken []byte) (done bool, outToken []byte, err error)\n}\n\nfunc (c *PgConn) gssAuth() error {\n\tif newGSS == nil {\n\t\treturn errors.New(\"kerberos error: no GSSAPI provider registered, see https://github.com/otan/gopgkrb5\")\n\t}\n\tcli, err := newGSS()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar nextData []byte\n\tif c.config.KerberosSpn != \"\" {\n\t\t// Use the supplied SPN if provided.\n\t\tnextData, err = cli.GetInitTokenFromSPN(c.config.KerberosSpn)\n\t} else {\n\t\t// Allow the kerberos service name to be overridden\n\t\tservice := \"postgres\"\n\t\tif c.config.KerberosSrvName != \"\" {\n\t\t\tservice = c.config.KerberosSrvName\n\t\t}\n\t\tnextData, err = cli.GetInitToken(c.config.Host, service)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tgssResponse := &pgproto3.GSSResponse{\n\t\t\tData: nextData,\n\t\t}\n\t\tc.frontend.Send(gssResponse)\n\t\terr = c.flushWithPotentialWriteReadDeadlock()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresp, err := c.rxGSSContinue()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar done bool\n\t\tdone, nextData, err = cli.Continue(resp.Data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif done {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *PgConn) rxGSSContinue() (*pgproto3.AuthenticationGSSContinue, error) {\n\tmsg, err := c.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch m := msg.(type) {\n\tcase *pgproto3.AuthenticationGSSContinue:\n\t\treturn m, nil\n\tcase *pgproto3.ErrorResponse:\n\t\treturn nil, ErrorResponseToPgError(m)\n\t}\n\n\treturn nil, fmt.Errorf(\"expected AuthenticationGSSContinue message but received unexpected message %T\", msg)\n}\n"
  },
  {
    "path": "pgconn/pgconn.go",
    "content": "package pgconn\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"crypto/md5\"\n\t\"crypto/tls\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/iobufpool\"\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n\t\"github.com/jackc/pgx/v5/pgconn/ctxwatch\"\n\t\"github.com/jackc/pgx/v5/pgconn/internal/bgreader\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\nconst (\n\tconnStatusUninitialized = iota\n\tconnStatusConnecting\n\tconnStatusClosed\n\tconnStatusIdle\n\tconnStatusBusy\n)\n\n// Notice represents a notice response message reported by the PostgreSQL server. Be aware that this is distinct from\n// LISTEN/NOTIFY notification.\ntype Notice PgError\n\n// Notification is a message received from the PostgreSQL LISTEN/NOTIFY system\ntype Notification struct {\n\tPID     uint32 // backend pid that sent the notification\n\tChannel string // channel from which notification was received\n\tPayload string\n}\n\n// DialFunc is a function that can be used to connect to a PostgreSQL server.\ntype DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)\n\n// LookupFunc is a function that can be used to lookup IPs addrs from host. Optionally an ip:port combination can be\n// returned in order to override the connection string's port.\ntype LookupFunc func(ctx context.Context, host string) (addrs []string, err error)\n\n// BuildFrontendFunc is a function that can be used to create Frontend implementation for connection.\ntype BuildFrontendFunc func(r io.Reader, w io.Writer) *pgproto3.Frontend\n\n// PgErrorHandler is a function that handles errors returned from Postgres. This function must return true to keep\n// the connection open. Returning false will cause the connection to be closed immediately. You should return\n// false on any FATAL-severity errors. This will not receive network errors. The *PgConn is provided so the handler is\n// aware of the origin of the error, but it must not invoke any query method.\ntype PgErrorHandler func(*PgConn, *PgError) bool\n\n// NoticeHandler is a function that can handle notices received from the PostgreSQL server. Notices can be received at\n// any time, usually during handling of a query response. The *PgConn is provided so the handler is aware of the origin\n// of the notice, but it must not invoke any query method. Be aware that this is distinct from LISTEN/NOTIFY\n// notification.\ntype NoticeHandler func(*PgConn, *Notice)\n\n// NotificationHandler is a function that can handle notifications received from the PostgreSQL server. Notifications\n// can be received at any time, usually during handling of a query response. The *PgConn is provided so the handler is\n// aware of the origin of the notice, but it must not invoke any query method. Be aware that this is distinct from a\n// notice event.\ntype NotificationHandler func(*PgConn, *Notification)\n\n// PgConn is a low-level PostgreSQL connection handle. It is not safe for concurrent usage.\ntype PgConn struct {\n\tconn              net.Conn\n\tpid               uint32            // backend pid\n\tsecretKey         []byte            // key to use to send a cancel query message to the server\n\tparameterStatuses map[string]string // parameters that have been reported by the server\n\ttxStatus          byte\n\tfrontend          *pgproto3.Frontend\n\tbgReader          *bgreader.BGReader\n\tslowWriteTimer    *time.Timer\n\tbgReaderStarted   chan struct{}\n\n\tcustomData map[string]any\n\n\tconfig *Config\n\n\tstatus byte // One of connStatus* constants\n\n\tbufferingReceive    bool\n\tbufferingReceiveMux sync.Mutex\n\tbufferingReceiveMsg pgproto3.BackendMessage\n\tbufferingReceiveErr error\n\n\tpeekedMsg pgproto3.BackendMessage\n\n\t// Reusable / preallocated resources\n\tresultReader      ResultReader\n\tmultiResultReader MultiResultReader\n\tpipeline          Pipeline\n\tcontextWatcher    *ctxwatch.ContextWatcher\n\tfieldDescriptions [16]FieldDescription\n\n\tcleanupDone chan struct{}\n}\n\n// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or keyword/value\n// format) to provide configuration. See documentation for [ParseConfig] for details. ctx can be used to cancel a\n// connect attempt.\nfunc Connect(ctx context.Context, connString string) (*PgConn, error) {\n\tconfig, err := ParseConfig(connString)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ConnectConfig(ctx, config)\n}\n\n// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or keyword/value\n// format) and ParseConfigOptions to provide additional configuration. See documentation for [ParseConfig] for details.\n// ctx can be used to cancel a connect attempt.\nfunc ConnectWithOptions(ctx context.Context, connString string, parseConfigOptions ParseConfigOptions) (*PgConn, error) {\n\tconfig, err := ParseConfigWithOptions(connString, parseConfigOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ConnectConfig(ctx, config)\n}\n\n// Connect establishes a connection to a PostgreSQL server using config. config must have been constructed with\n// [ParseConfig]. ctx can be used to cancel a connect attempt.\n//\n// If config.Fallbacks are present they will sequentially be tried in case of error establishing network connection. An\n// authentication error will terminate the chain of attempts (like libpq:\n// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error.\nfunc ConnectConfig(ctx context.Context, config *Config) (*PgConn, error) {\n\t// Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from\n\t// zero values.\n\tif !config.createdByParseConfig {\n\t\tpanic(\"config must be created by ParseConfig\")\n\t}\n\n\tvar allErrors []error\n\n\tconnectConfigs, errs := buildConnectOneConfigs(ctx, config)\n\tif len(errs) > 0 {\n\t\tallErrors = append(allErrors, errs...)\n\t}\n\n\tif len(connectConfigs) == 0 {\n\t\treturn nil, &ConnectError{Config: config, err: fmt.Errorf(\"hostname resolving error: %w\", errors.Join(allErrors...))}\n\t}\n\n\tpgConn, errs := connectPreferred(ctx, config, connectConfigs)\n\tif len(errs) > 0 {\n\t\tallErrors = append(allErrors, errs...)\n\t\treturn nil, &ConnectError{Config: config, err: errors.Join(allErrors...)}\n\t}\n\n\tif config.AfterConnect != nil {\n\t\terr := config.AfterConnect(ctx, pgConn)\n\t\tif err != nil {\n\t\t\tpgConn.conn.Close()\n\t\t\treturn nil, &ConnectError{Config: config, err: fmt.Errorf(\"AfterConnect error: %w\", err)}\n\t\t}\n\t}\n\n\treturn pgConn, nil\n}\n\n// buildConnectOneConfigs resolves hostnames and builds a list of connectOneConfigs to try connecting to. It returns a\n// slice of successfully resolved connectOneConfigs and a slice of errors. It is possible for both slices to contain\n// values if some hosts were successfully resolved and others were not.\nfunc buildConnectOneConfigs(ctx context.Context, config *Config) ([]*connectOneConfig, []error) {\n\t// Simplify usage by treating primary config and fallbacks the same.\n\tfallbackConfigs := []*FallbackConfig{\n\t\t{\n\t\t\tHost:      config.Host,\n\t\t\tPort:      config.Port,\n\t\t\tTLSConfig: config.TLSConfig,\n\t\t},\n\t}\n\tfallbackConfigs = append(fallbackConfigs, config.Fallbacks...)\n\n\tvar configs []*connectOneConfig\n\n\tvar allErrors []error\n\n\tfor _, fb := range fallbackConfigs {\n\t\t// skip resolve for unix sockets\n\t\tif isAbsolutePath(fb.Host) {\n\t\t\tnetwork, address := NetworkAddress(fb.Host, fb.Port)\n\t\t\tconfigs = append(configs, &connectOneConfig{\n\t\t\t\tnetwork:          network,\n\t\t\t\taddress:          address,\n\t\t\t\toriginalHostname: fb.Host,\n\t\t\t\ttlsConfig:        fb.TLSConfig,\n\t\t\t})\n\n\t\t\tcontinue\n\t\t}\n\n\t\tips, err := config.LookupFunc(ctx, fb.Host)\n\t\tif err != nil {\n\t\t\tallErrors = append(allErrors, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, ip := range ips {\n\t\t\tsplitIP, splitPort, err := net.SplitHostPort(ip)\n\t\t\tif err == nil {\n\t\t\t\tport, err := strconv.ParseUint(splitPort, 10, 16)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, []error{fmt.Errorf(\"error parsing port (%s) from lookup: %w\", splitPort, err)}\n\t\t\t\t}\n\t\t\t\tnetwork, address := NetworkAddress(splitIP, uint16(port))\n\t\t\t\tconfigs = append(configs, &connectOneConfig{\n\t\t\t\t\tnetwork:          network,\n\t\t\t\t\taddress:          address,\n\t\t\t\t\toriginalHostname: fb.Host,\n\t\t\t\t\ttlsConfig:        fb.TLSConfig,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tnetwork, address := NetworkAddress(ip, fb.Port)\n\t\t\t\tconfigs = append(configs, &connectOneConfig{\n\t\t\t\t\tnetwork:          network,\n\t\t\t\t\taddress:          address,\n\t\t\t\t\toriginalHostname: fb.Host,\n\t\t\t\t\ttlsConfig:        fb.TLSConfig,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn configs, allErrors\n}\n\n// connectPreferred attempts to connect to the preferred host from connectOneConfigs. The connections are attempted in\n// order. If a connection is successful it is returned. If no connection is successful then all errors are returned. If\n// a connection attempt returns a [NotPreferredError], then that host will be used if no other hosts are successful.\nfunc connectPreferred(ctx context.Context, config *Config, connectOneConfigs []*connectOneConfig) (*PgConn, []error) {\n\toctx := ctx\n\tvar allErrors []error\n\n\tvar fallbackConnectOneConfig *connectOneConfig\n\tfor i, c := range connectOneConfigs {\n\t\t// ConnectTimeout restricts the whole connection process.\n\t\tif config.ConnectTimeout != 0 {\n\t\t\t// create new context first time or when previous host was different\n\t\t\tif i == 0 || (connectOneConfigs[i].address != connectOneConfigs[i-1].address) {\n\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\tctx, cancel = context.WithTimeout(octx, config.ConnectTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\t\t} else {\n\t\t\tctx = octx\n\t\t}\n\n\t\tpgConn, err := connectOne(ctx, config, c, false)\n\t\tif pgConn != nil {\n\t\t\treturn pgConn, nil\n\t\t}\n\n\t\tallErrors = append(allErrors, err)\n\n\t\tvar pgErr *PgError\n\t\tif errors.As(err, &pgErr) {\n\t\t\t// pgx will try next host even if libpq does not in certain cases (see #2246)\n\t\t\t// consider change for the next major version\n\n\t\t\tconst ERRCODE_INVALID_PASSWORD = \"28P01\"\n\t\t\tconst ERRCODE_INVALID_CATALOG_NAME = \"3D000\"   // db does not exist\n\t\t\tconst ERRCODE_INSUFFICIENT_PRIVILEGE = \"42501\" // missing connect privilege\n\n\t\t\t// auth failed due to invalid password, db does not exist or user has no permission\n\t\t\tif pgErr.Code == ERRCODE_INVALID_PASSWORD ||\n\t\t\t\tpgErr.Code == ERRCODE_INVALID_CATALOG_NAME ||\n\t\t\t\tpgErr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE {\n\t\t\t\treturn nil, allErrors\n\t\t\t}\n\t\t}\n\n\t\tvar npErr *NotPreferredError\n\t\tif errors.As(err, &npErr) {\n\t\t\tfallbackConnectOneConfig = c\n\t\t}\n\t}\n\n\tif fallbackConnectOneConfig != nil {\n\t\tpgConn, err := connectOne(ctx, config, fallbackConnectOneConfig, true)\n\t\tif err == nil {\n\t\t\treturn pgConn, nil\n\t\t}\n\t\tallErrors = append(allErrors, err)\n\t}\n\n\treturn nil, allErrors\n}\n\n// connectOne makes one connection attempt to a single host.\nfunc connectOne(ctx context.Context, config *Config, connectConfig *connectOneConfig,\n\tignoreNotPreferredErr bool,\n) (*PgConn, error) {\n\tpgConn := new(PgConn)\n\tpgConn.config = config\n\tpgConn.cleanupDone = make(chan struct{})\n\tpgConn.customData = make(map[string]any)\n\n\tvar err error\n\n\tnewPerDialConnectError := func(msg string, err error) *perDialConnectError {\n\t\terr = normalizeTimeoutError(ctx, err)\n\t\te := &perDialConnectError{address: connectConfig.address, originalHostname: connectConfig.originalHostname, err: fmt.Errorf(\"%s: %w\", msg, err)}\n\t\treturn e\n\t}\n\n\tmaxProtocolVersion, err := parseProtocolVersion(config.MaxProtocolVersion)\n\tif err != nil {\n\t\treturn nil, newPerDialConnectError(\"invalid max_protocol_version\", err)\n\t}\n\tminProtocolVersion, err := parseProtocolVersion(config.MinProtocolVersion)\n\tif err != nil {\n\t\treturn nil, newPerDialConnectError(\"invalid min_protocol_version\", err)\n\t}\n\n\tpgConn.conn, err = config.DialFunc(ctx, connectConfig.network, connectConfig.address)\n\tif err != nil {\n\t\treturn nil, newPerDialConnectError(\"dial error\", err)\n\t}\n\n\tif connectConfig.tlsConfig != nil {\n\t\tpgConn.contextWatcher = ctxwatch.NewContextWatcher(&DeadlineContextWatcherHandler{Conn: pgConn.conn})\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tvar (\n\t\t\ttlsConn net.Conn\n\t\t\terr     error\n\t\t)\n\t\tif config.SSLNegotiation == \"direct\" {\n\t\t\ttlsConn = tls.Client(pgConn.conn, connectConfig.tlsConfig)\n\t\t} else {\n\t\t\ttlsConn, err = startTLS(pgConn.conn, connectConfig.tlsConfig)\n\t\t}\n\t\tpgConn.contextWatcher.Unwatch() // Always unwatch `netConn` after TLS.\n\t\tif err != nil {\n\t\t\tpgConn.conn.Close()\n\t\t\treturn nil, newPerDialConnectError(\"tls error\", err)\n\t\t}\n\n\t\tpgConn.conn = tlsConn\n\t}\n\n\tif config.AfterNetConnect != nil {\n\t\tpgConn.conn, err = config.AfterNetConnect(ctx, config, pgConn.conn)\n\t\tif err != nil {\n\t\t\tpgConn.conn.Close()\n\t\t\treturn nil, newPerDialConnectError(\"AfterNetConnect failed\", err)\n\t\t}\n\t}\n\n\tpgConn.contextWatcher = ctxwatch.NewContextWatcher(config.BuildContextWatcherHandler(pgConn))\n\tpgConn.contextWatcher.Watch(ctx)\n\tdefer pgConn.contextWatcher.Unwatch()\n\n\tpgConn.parameterStatuses = make(map[string]string)\n\tpgConn.status = connStatusConnecting\n\tpgConn.bgReader = bgreader.New(pgConn.conn)\n\tpgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64),\n\t\tfunc() {\n\t\t\tpgConn.bgReader.Start()\n\t\t\tpgConn.bgReaderStarted <- struct{}{}\n\t\t},\n\t)\n\tpgConn.slowWriteTimer.Stop()\n\tpgConn.bgReaderStarted = make(chan struct{})\n\tpgConn.frontend = config.BuildFrontend(pgConn.bgReader, pgConn.conn)\n\n\tstartupMsg := pgproto3.StartupMessage{\n\t\tProtocolVersion: maxProtocolVersion,\n\t\tParameters:      make(map[string]string),\n\t}\n\n\t// Copy default run-time params\n\tmaps.Copy(startupMsg.Parameters, config.RuntimeParams)\n\n\tstartupMsg.Parameters[\"user\"] = config.User\n\tif config.Database != \"\" {\n\t\tstartupMsg.Parameters[\"database\"] = config.Database\n\t}\n\n\tpgConn.frontend.Send(&startupMsg)\n\tif err := pgConn.flushWithPotentialWriteReadDeadlock(); err != nil {\n\t\tpgConn.conn.Close()\n\t\treturn nil, newPerDialConnectError(\"failed to write startup message\", err)\n\t}\n\n\tfor {\n\t\tmsg, err := pgConn.receiveMessage()\n\t\tif err != nil {\n\t\t\tpgConn.conn.Close()\n\t\t\tif err, ok := err.(*PgError); ok {\n\t\t\t\treturn nil, newPerDialConnectError(\"server error\", err)\n\t\t\t}\n\t\t\treturn nil, newPerDialConnectError(\"failed to receive message\", err)\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.BackendKeyData:\n\t\t\tpgConn.pid = msg.ProcessID\n\t\t\tpgConn.secretKey = msg.SecretKey\n\n\t\tcase *pgproto3.AuthenticationOk:\n\t\tcase *pgproto3.AuthenticationCleartextPassword:\n\t\t\terr = pgConn.txPasswordMessage(pgConn.config.Password)\n\t\t\tif err != nil {\n\t\t\t\tpgConn.conn.Close()\n\t\t\t\treturn nil, newPerDialConnectError(\"failed to write password message\", err)\n\t\t\t}\n\t\tcase *pgproto3.AuthenticationMD5Password:\n\t\t\tdigestedPassword := \"md5\" + hexMD5(hexMD5(pgConn.config.Password+pgConn.config.User)+string(msg.Salt[:]))\n\t\t\terr = pgConn.txPasswordMessage(digestedPassword)\n\t\t\tif err != nil {\n\t\t\t\tpgConn.conn.Close()\n\t\t\t\treturn nil, newPerDialConnectError(\"failed to write password message\", err)\n\t\t\t}\n\t\tcase *pgproto3.AuthenticationSASL:\n\t\t\t// Check if OAUTHBEARER is supported\n\t\t\tserverSupportsOAuthBearer := false\n\t\t\tfor _, mech := range msg.AuthMechanisms {\n\t\t\t\tif mech == \"OAUTHBEARER\" {\n\t\t\t\t\tserverSupportsOAuthBearer = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif serverSupportsOAuthBearer && pgConn.config.OAuthTokenProvider != nil {\n\t\t\t\terr = pgConn.oauthAuth(ctx)\n\t\t\t} else {\n\t\t\t\terr = pgConn.scramAuth(msg.AuthMechanisms)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tpgConn.conn.Close()\n\t\t\t\treturn nil, newPerDialConnectError(\"failed SASL auth\", err)\n\t\t\t}\n\t\tcase *pgproto3.AuthenticationGSS:\n\t\t\terr = pgConn.gssAuth()\n\t\t\tif err != nil {\n\t\t\t\tpgConn.conn.Close()\n\t\t\t\treturn nil, newPerDialConnectError(\"failed GSS auth\", err)\n\t\t\t}\n\t\tcase *pgproto3.ReadyForQuery:\n\t\t\tpgConn.status = connStatusIdle\n\t\t\tif config.ValidateConnect != nil {\n\t\t\t\t// ValidateConnect may execute commands that cause the context to be watched again. Unwatch first to avoid\n\t\t\t\t// the watch already in progress panic. This is that last thing done by this method so there is no need to\n\t\t\t\t// restart the watch after ValidateConnect returns.\n\t\t\t\t//\n\t\t\t\t// See https://github.com/jackc/pgconn/issues/40.\n\t\t\t\tpgConn.contextWatcher.Unwatch()\n\n\t\t\t\terr := config.ValidateConnect(ctx, pgConn)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif _, ok := err.(*NotPreferredError); ignoreNotPreferredErr && ok {\n\t\t\t\t\t\treturn pgConn, nil\n\t\t\t\t\t}\n\t\t\t\t\tpgConn.conn.Close()\n\t\t\t\t\treturn nil, newPerDialConnectError(\"ValidateConnect failed\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn pgConn, nil\n\t\tcase *pgproto3.ParameterStatus, *pgproto3.NoticeResponse:\n\t\t\t// handled by ReceiveMessage\n\t\tcase *pgproto3.NegotiateProtocolVersion:\n\t\t\tserverVersion := pgproto3.ProtocolVersion30&0xFFFF0000 | uint32(msg.NewestMinorProtocol)\n\t\t\tif serverVersion < minProtocolVersion {\n\t\t\t\tpgConn.conn.Close()\n\t\t\t\treturn nil, newPerDialConnectError(\"server protocol version too low\", nil)\n\t\t\t}\n\t\tcase *pgproto3.ErrorResponse:\n\t\t\tpgConn.conn.Close()\n\t\t\treturn nil, newPerDialConnectError(\"server error\", ErrorResponseToPgError(msg))\n\t\tdefault:\n\t\t\tpgConn.conn.Close()\n\t\t\treturn nil, newPerDialConnectError(\"received unexpected message\", err)\n\t\t}\n\t}\n}\n\nfunc startTLS(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {\n\terr := binary.Write(conn, binary.BigEndian, []int32{8, 80877103})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresponse := make([]byte, 1)\n\tif _, err = io.ReadFull(conn, response); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif response[0] != 'S' {\n\t\treturn nil, errors.New(\"server refused TLS connection\")\n\t}\n\n\treturn tls.Client(conn, tlsConfig), nil\n}\n\nfunc (pgConn *PgConn) txPasswordMessage(password string) (err error) {\n\tpgConn.frontend.Send(&pgproto3.PasswordMessage{Password: password})\n\treturn pgConn.flushWithPotentialWriteReadDeadlock()\n}\n\nfunc hexMD5(s string) string {\n\thash := md5.New()\n\tio.WriteString(hash, s)\n\treturn hex.EncodeToString(hash.Sum(nil))\n}\n\nfunc (pgConn *PgConn) signalMessage() chan struct{} {\n\tif pgConn.bufferingReceive {\n\t\tpanic(\"BUG: signalMessage when already in progress\")\n\t}\n\n\tpgConn.bufferingReceive = true\n\tpgConn.bufferingReceiveMux.Lock()\n\n\tch := make(chan struct{})\n\tgo func() {\n\t\tpgConn.bufferingReceiveMsg, pgConn.bufferingReceiveErr = pgConn.frontend.Receive()\n\t\tpgConn.bufferingReceiveMux.Unlock()\n\t\tclose(ch)\n\t}()\n\n\treturn ch\n}\n\n// ReceiveMessage receives one wire protocol message from the PostgreSQL server. It must only be used when the\n// connection is not busy. e.g. It is an error to call ReceiveMessage while reading the result of a query. The messages\n// are still handled by the core pgconn message handling system so receiving a NotificationResponse will still trigger\n// the OnNotification callback.\n//\n// This is a very low level method that requires deep understanding of the PostgreSQL wire protocol to use correctly.\n// See https://www.postgresql.org/docs/current/protocol.html.\nfunc (pgConn *PgConn) ReceiveMessage(ctx context.Context) (pgproto3.BackendMessage, error) {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn nil, err\n\t}\n\tdefer pgConn.unlock()\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil, newContextAlreadyDoneError(ctx)\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\tmsg, err := pgConn.receiveMessage()\n\tif err != nil {\n\t\terr = &pgconnError{\n\t\t\tmsg:         \"receive message failed\",\n\t\t\terr:         normalizeTimeoutError(ctx, err),\n\t\t\tsafeToRetry: true,\n\t\t}\n\t}\n\treturn msg, err\n}\n\n// peekMessage peeks at the next message without setting up context cancellation.\nfunc (pgConn *PgConn) peekMessage() (pgproto3.BackendMessage, error) {\n\tif pgConn.peekedMsg != nil {\n\t\treturn pgConn.peekedMsg, nil\n\t}\n\n\tvar msg pgproto3.BackendMessage\n\tvar err error\n\tif pgConn.bufferingReceive {\n\t\tpgConn.bufferingReceiveMux.Lock()\n\t\tmsg = pgConn.bufferingReceiveMsg\n\t\terr = pgConn.bufferingReceiveErr\n\t\tpgConn.bufferingReceiveMux.Unlock()\n\t\tpgConn.bufferingReceive = false\n\n\t\t// If a timeout error happened in the background try the read again.\n\t\tvar netErr net.Error\n\t\tif errors.As(err, &netErr) && netErr.Timeout() {\n\t\t\tmsg, err = pgConn.frontend.Receive()\n\t\t}\n\t} else {\n\t\tmsg, err = pgConn.frontend.Receive()\n\t}\n\n\tif err != nil {\n\t\t// Close on anything other than timeout error - everything else is fatal\n\t\tvar netErr net.Error\n\t\tisNetErr := errors.As(err, &netErr)\n\t\tif !(isNetErr && netErr.Timeout()) {\n\t\t\tpgConn.asyncClose()\n\t\t}\n\n\t\treturn nil, err\n\t}\n\n\tpgConn.peekedMsg = msg\n\treturn msg, nil\n}\n\n// receiveMessage receives a message without setting up context cancellation\nfunc (pgConn *PgConn) receiveMessage() (pgproto3.BackendMessage, error) {\n\tif pgConn.status == connStatusClosed {\n\t\treturn nil, &connLockError{status: \"conn closed\"}\n\t}\n\n\tmsg, err := pgConn.peekMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpgConn.peekedMsg = nil\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.ReadyForQuery:\n\t\tpgConn.txStatus = msg.TxStatus\n\tcase *pgproto3.ParameterStatus:\n\t\tpgConn.parameterStatuses[msg.Name] = msg.Value\n\tcase *pgproto3.ErrorResponse:\n\t\terr := ErrorResponseToPgError(msg)\n\t\tif pgConn.config.OnPgError != nil && !pgConn.config.OnPgError(pgConn, err) {\n\t\t\tpgConn.status = connStatusClosed\n\t\t\tpgConn.conn.Close() // Ignore error as the connection is already broken and there is already an error to return.\n\t\t\tclose(pgConn.cleanupDone)\n\t\t\treturn nil, err\n\t\t}\n\tcase *pgproto3.NoticeResponse:\n\t\tif pgConn.config.OnNotice != nil {\n\t\t\tpgConn.config.OnNotice(pgConn, noticeResponseToNotice(msg))\n\t\t}\n\tcase *pgproto3.NotificationResponse:\n\t\tif pgConn.config.OnNotification != nil {\n\t\t\tpgConn.config.OnNotification(pgConn, &Notification{PID: msg.PID, Channel: msg.Channel, Payload: msg.Payload})\n\t\t}\n\t}\n\n\treturn msg, nil\n}\n\n// Conn returns the underlying net.Conn. This rarely necessary. If the connection will be directly used for reading or\n// writing then SyncConn should usually be called before Conn.\nfunc (pgConn *PgConn) Conn() net.Conn {\n\treturn pgConn.conn\n}\n\n// PID returns the backend PID.\nfunc (pgConn *PgConn) PID() uint32 {\n\treturn pgConn.pid\n}\n\n// TxStatus returns the current TxStatus as reported by the server in the ReadyForQuery message.\n//\n// Possible return values:\n//\n//\t'I' - idle / not in transaction\n//\t'T' - in a transaction\n//\t'E' - in a failed transaction\n//\n// See https://www.postgresql.org/docs/current/protocol-message-formats.html.\nfunc (pgConn *PgConn) TxStatus() byte {\n\treturn pgConn.txStatus\n}\n\n// SecretKey returns the backend secret key used to send a cancel query message to the server.\nfunc (pgConn *PgConn) SecretKey() []byte {\n\treturn pgConn.secretKey\n}\n\n// Frontend returns the underlying *pgproto3.Frontend. This rarely necessary.\nfunc (pgConn *PgConn) Frontend() *pgproto3.Frontend {\n\treturn pgConn.frontend\n}\n\n// Close closes a connection. It is safe to call Close on an already closed connection. Close attempts a clean close by\n// sending the exit message to PostgreSQL. However, this could block so ctx is available to limit the time to wait. The\n// underlying net.Conn.Close() will always be called regardless of any other errors.\nfunc (pgConn *PgConn) Close(ctx context.Context) error {\n\tif pgConn.status == connStatusClosed {\n\t\treturn nil\n\t}\n\tpgConn.status = connStatusClosed\n\n\tdefer close(pgConn.cleanupDone)\n\tdefer pgConn.conn.Close()\n\n\tif ctx != context.Background() {\n\t\t// Close may be called while a cancellable query is in progress. This will most often be triggered by panic when\n\t\t// a defer closes the connection (possibly indirectly via a transaction or a connection pool). Unwatch to end any\n\t\t// previous watch. It is safe to Unwatch regardless of whether a watch is already is progress.\n\t\t//\n\t\t// See https://github.com/jackc/pgconn/issues/29\n\t\tpgConn.contextWatcher.Unwatch()\n\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\t// Ignore any errors sending Terminate message and waiting for server to close connection.\n\t// This mimics the behavior of libpq PQfinish. It calls closePGconn which calls sendTerminateConn which purposefully\n\t// ignores errors.\n\t//\n\t// See https://github.com/jackc/pgx/issues/637\n\tpgConn.frontend.Send(&pgproto3.Terminate{})\n\tpgConn.flushWithPotentialWriteReadDeadlock()\n\n\treturn pgConn.conn.Close()\n}\n\n// asyncClose marks the connection as closed and asynchronously sends a cancel query message and closes the underlying\n// connection.\nfunc (pgConn *PgConn) asyncClose() {\n\tif pgConn.status == connStatusClosed {\n\t\treturn\n\t}\n\tpgConn.status = connStatusClosed\n\n\tgo func() {\n\t\tdefer close(pgConn.cleanupDone)\n\t\tdefer pgConn.conn.Close()\n\n\t\tdeadline := time.Now().Add(time.Second * 15)\n\n\t\tctx, cancel := context.WithDeadline(context.Background(), deadline)\n\t\tdefer cancel()\n\n\t\tpgConn.CancelRequest(ctx)\n\n\t\tpgConn.conn.SetDeadline(deadline)\n\n\t\tpgConn.frontend.Send(&pgproto3.Terminate{})\n\t\tpgConn.flushWithPotentialWriteReadDeadlock()\n\t}()\n}\n\n// CleanupDone returns a channel that will be closed after all underlying resources have been cleaned up. A closed\n// connection is no longer usable, but underlying resources, in particular the net.Conn, may not have finished closing\n// yet. This is because certain errors such as a context cancellation require that the interrupted function call return\n// immediately, but the error may also cause the connection to be closed. In these cases the underlying resources are\n// closed asynchronously.\n//\n// This is only likely to be useful to connection pools. It gives them a way avoid establishing a new connection while\n// an old connection is still being cleaned up and thereby exceeding the maximum pool size.\nfunc (pgConn *PgConn) CleanupDone() chan (struct{}) {\n\treturn pgConn.cleanupDone\n}\n\n// IsClosed reports if the connection has been closed.\n//\n// CleanupDone() can be used to determine if all cleanup has been completed.\nfunc (pgConn *PgConn) IsClosed() bool {\n\treturn pgConn.status < connStatusIdle\n}\n\n// IsBusy reports if the connection is busy.\nfunc (pgConn *PgConn) IsBusy() bool {\n\treturn pgConn.status == connStatusBusy\n}\n\n// lock locks the connection.\nfunc (pgConn *PgConn) lock() error {\n\tswitch pgConn.status {\n\tcase connStatusBusy:\n\t\treturn &connLockError{status: \"conn busy\"} // This only should be possible in case of an application bug.\n\tcase connStatusClosed:\n\t\treturn &connLockError{status: \"conn closed\"}\n\tcase connStatusUninitialized:\n\t\treturn &connLockError{status: \"conn uninitialized\"}\n\t}\n\tpgConn.status = connStatusBusy\n\treturn nil\n}\n\nfunc (pgConn *PgConn) unlock() {\n\tswitch pgConn.status {\n\tcase connStatusBusy:\n\t\tpgConn.status = connStatusIdle\n\tcase connStatusClosed:\n\tdefault:\n\t\tpanic(\"BUG: cannot unlock unlocked connection\") // This should only be possible if there is a bug in this package.\n\t}\n}\n\n// ParameterStatus returns the value of a parameter reported by the server (e.g.\n// server_version). Returns an empty string for unknown parameters.\nfunc (pgConn *PgConn) ParameterStatus(key string) string {\n\treturn pgConn.parameterStatuses[key]\n}\n\n// CommandTag is the status text returned by PostgreSQL for a query.\ntype CommandTag struct {\n\ts string\n}\n\n// NewCommandTag makes a CommandTag from s.\nfunc NewCommandTag(s string) CommandTag {\n\treturn CommandTag{s: s}\n}\n\n// RowsAffected returns the number of rows affected. If the CommandTag was not\n// for a row affecting command (e.g. \"CREATE TABLE\") then it returns 0.\nfunc (ct CommandTag) RowsAffected() int64 {\n\t// Parse the number from the end in a single pass.\n\tvar n int64\n\tvar mult int64 = 1\n\n\tfor i := len(ct.s) - 1; i >= 0; i-- {\n\t\tc := ct.s[i]\n\t\tif c >= '0' && c <= '9' {\n\t\t\tn += int64(c-'0') * mult\n\t\t\tmult *= 10\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn n\n}\n\nfunc (ct CommandTag) String() string {\n\treturn ct.s\n}\n\n// Insert is true if the command tag starts with \"INSERT\".\nfunc (ct CommandTag) Insert() bool {\n\treturn strings.HasPrefix(ct.s, \"INSERT\")\n}\n\n// Update is true if the command tag starts with \"UPDATE\".\nfunc (ct CommandTag) Update() bool {\n\treturn strings.HasPrefix(ct.s, \"UPDATE\")\n}\n\n// Delete is true if the command tag starts with \"DELETE\".\nfunc (ct CommandTag) Delete() bool {\n\treturn strings.HasPrefix(ct.s, \"DELETE\")\n}\n\n// Select is true if the command tag starts with \"SELECT\".\nfunc (ct CommandTag) Select() bool {\n\treturn strings.HasPrefix(ct.s, \"SELECT\")\n}\n\ntype FieldDescription struct {\n\tName                 string\n\tTableOID             uint32\n\tTableAttributeNumber uint16\n\tDataTypeOID          uint32\n\tDataTypeSize         int16\n\tTypeModifier         int32\n\tFormat               int16\n}\n\nfunc (pgConn *PgConn) getFieldDescriptionSlice(n int) []FieldDescription {\n\tif cap(pgConn.fieldDescriptions) >= n {\n\t\treturn pgConn.fieldDescriptions[:n:n]\n\t} else {\n\t\treturn make([]FieldDescription, n)\n\t}\n}\n\nfunc convertRowDescription(dst []FieldDescription, rd *pgproto3.RowDescription) {\n\tfor i := range rd.Fields {\n\t\tdst[i].Name = string(rd.Fields[i].Name)\n\t\tdst[i].TableOID = rd.Fields[i].TableOID\n\t\tdst[i].TableAttributeNumber = rd.Fields[i].TableAttributeNumber\n\t\tdst[i].DataTypeOID = rd.Fields[i].DataTypeOID\n\t\tdst[i].DataTypeSize = rd.Fields[i].DataTypeSize\n\t\tdst[i].TypeModifier = rd.Fields[i].TypeModifier\n\t\tdst[i].Format = rd.Fields[i].Format\n\t}\n}\n\ntype StatementDescription struct {\n\tName      string\n\tSQL       string\n\tParamOIDs []uint32\n\tFields    []FieldDescription\n}\n\n// Prepare creates a prepared statement. If the name is empty, the anonymous prepared statement will be used. This\n// allows Prepare to also to describe statements without creating a server-side prepared statement.\n//\n// Prepare does not send a PREPARE statement to the server. It uses the PostgreSQL Parse and Describe protocol messages\n// directly.\n//\n// In extremely rare cases, Prepare may fail after the Parse is successful, but before the Describe is complete. In this\n// case, the returned error will be an error where errors.As with a *PrepareError succeeds and the *PrepareError has\n// ParseComplete set to true.\nfunc (pgConn *PgConn) Prepare(ctx context.Context, name, sql string, paramOIDs []uint32) (*StatementDescription, error) {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn nil, err\n\t}\n\tdefer pgConn.unlock()\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil, newContextAlreadyDoneError(ctx)\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\tpgConn.frontend.SendParse(&pgproto3.Parse{Name: name, Query: sql, ParameterOIDs: paramOIDs})\n\tpgConn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'S', Name: name})\n\tpgConn.frontend.SendSync(&pgproto3.Sync{})\n\terr := pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\treturn nil, err\n\t}\n\n\tpsd := &StatementDescription{Name: name, SQL: sql}\n\n\tvar ParseComplete bool\n\tvar pgErr *PgError\n\nreadloop:\n\tfor {\n\t\tmsg, err := pgConn.receiveMessage()\n\t\tif err != nil {\n\t\t\tpgConn.asyncClose()\n\t\t\treturn nil, normalizeTimeoutError(ctx, err)\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.ParseComplete:\n\t\t\tParseComplete = true\n\t\tcase *pgproto3.ParameterDescription:\n\t\t\tpsd.ParamOIDs = make([]uint32, len(msg.ParameterOIDs))\n\t\t\tcopy(psd.ParamOIDs, msg.ParameterOIDs)\n\t\tcase *pgproto3.RowDescription:\n\t\t\tpsd.Fields = make([]FieldDescription, len(msg.Fields))\n\t\t\tconvertRowDescription(psd.Fields, msg)\n\t\tcase *pgproto3.ErrorResponse:\n\t\t\tpgErr = ErrorResponseToPgError(msg)\n\t\tcase *pgproto3.ReadyForQuery:\n\t\t\tbreak readloop\n\t\t}\n\t}\n\n\tif pgErr != nil {\n\t\treturn nil, &PrepareError{err: pgErr, ParseComplete: ParseComplete}\n\t}\n\treturn psd, nil\n}\n\n// Deallocate deallocates a prepared statement.\n//\n// Deallocate does not send a DEALLOCATE statement to the server. It uses the PostgreSQL Close protocol message\n// directly. This has slightly different behavior than executing DEALLOCATE statement.\n//   - Deallocate can succeed in an aborted transaction.\n//   - Deallocating a non-existent prepared statement is not an error.\nfunc (pgConn *PgConn) Deallocate(ctx context.Context, name string) error {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn err\n\t}\n\tdefer pgConn.unlock()\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn newContextAlreadyDoneError(ctx)\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\tpgConn.frontend.SendClose(&pgproto3.Close{ObjectType: 'S', Name: name})\n\tpgConn.frontend.SendSync(&pgproto3.Sync{})\n\terr := pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\treturn err\n\t}\n\n\tfor {\n\t\tmsg, err := pgConn.receiveMessage()\n\t\tif err != nil {\n\t\t\tpgConn.asyncClose()\n\t\t\treturn normalizeTimeoutError(ctx, err)\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.ErrorResponse:\n\t\t\treturn ErrorResponseToPgError(msg)\n\t\tcase *pgproto3.ReadyForQuery:\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\n// ErrorResponseToPgError converts a wire protocol error message to a *PgError.\nfunc ErrorResponseToPgError(msg *pgproto3.ErrorResponse) *PgError {\n\treturn &PgError{\n\t\tSeverity:            msg.Severity,\n\t\tSeverityUnlocalized: msg.SeverityUnlocalized,\n\t\tCode:                string(msg.Code),\n\t\tMessage:             string(msg.Message),\n\t\tDetail:              string(msg.Detail),\n\t\tHint:                msg.Hint,\n\t\tPosition:            msg.Position,\n\t\tInternalPosition:    msg.InternalPosition,\n\t\tInternalQuery:       string(msg.InternalQuery),\n\t\tWhere:               string(msg.Where),\n\t\tSchemaName:          string(msg.SchemaName),\n\t\tTableName:           string(msg.TableName),\n\t\tColumnName:          string(msg.ColumnName),\n\t\tDataTypeName:        string(msg.DataTypeName),\n\t\tConstraintName:      msg.ConstraintName,\n\t\tFile:                string(msg.File),\n\t\tLine:                msg.Line,\n\t\tRoutine:             string(msg.Routine),\n\t}\n}\n\nfunc noticeResponseToNotice(msg *pgproto3.NoticeResponse) *Notice {\n\tpgerr := ErrorResponseToPgError((*pgproto3.ErrorResponse)(msg))\n\treturn (*Notice)(pgerr)\n}\n\n// CancelRequest sends a cancel request to the PostgreSQL server. It returns an error if unable to deliver the cancel\n// request, but lack of an error does not ensure that the query was canceled. As specified in the documentation, there\n// is no way to be sure a query was canceled.\n// See https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-CANCELING-REQUESTS\nfunc (pgConn *PgConn) CancelRequest(ctx context.Context) error {\n\t// Open a cancellation request to the same server. The address is taken from the net.Conn directly instead of reusing\n\t// the connection config. This is important in high availability configurations where fallback connections may be\n\t// specified or DNS may be used to load balance.\n\tserverAddr := pgConn.conn.RemoteAddr()\n\tvar serverNetwork string\n\tvar serverAddress string\n\tif serverAddr.Network() == \"unix\" {\n\t\t// for unix sockets, RemoteAddr() calls getpeername() which returns the name the\n\t\t// server passed to bind(). For Postgres, this is always a relative path \"./.s.PGSQL.5432\"\n\t\t// so connecting to it will fail. Fall back to the config's value\n\t\tserverNetwork, serverAddress = NetworkAddress(pgConn.config.Host, pgConn.config.Port)\n\t} else {\n\t\tserverNetwork, serverAddress = serverAddr.Network(), serverAddr.String()\n\t}\n\tcancelConn, err := pgConn.config.DialFunc(ctx, serverNetwork, serverAddress)\n\tif err != nil {\n\t\t// In case of unix sockets, RemoteAddr() returns only the file part of the path. If the\n\t\t// first connect failed, try the config.\n\t\tif serverAddr.Network() != \"unix\" {\n\t\t\treturn err\n\t\t}\n\t\tserverNetwork, serverAddr := NetworkAddress(pgConn.config.Host, pgConn.config.Port)\n\t\tcancelConn, err = pgConn.config.DialFunc(ctx, serverNetwork, serverAddr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tdefer cancelConn.Close()\n\n\tif ctx != context.Background() {\n\t\tcontextWatcher := ctxwatch.NewContextWatcher(&DeadlineContextWatcherHandler{Conn: cancelConn})\n\t\tcontextWatcher.Watch(ctx)\n\t\tdefer contextWatcher.Unwatch()\n\t}\n\n\tbuf := make([]byte, 12+len(pgConn.secretKey))\n\tbinary.BigEndian.PutUint32(buf[0:4], uint32(len(buf)))\n\tbinary.BigEndian.PutUint32(buf[4:8], 80877102)\n\tbinary.BigEndian.PutUint32(buf[8:12], pgConn.pid)\n\tcopy(buf[12:], pgConn.secretKey)\n\n\tif _, err := cancelConn.Write(buf); err != nil {\n\t\treturn fmt.Errorf(\"write to connection for cancellation: %w\", err)\n\t}\n\n\t// Wait for the cancel request to be acknowledged by the server.\n\t// It copies the behavior of the libpq: https://github.com/postgres/postgres/blob/REL_16_0/src/interfaces/libpq/fe-connect.c#L4946-L4960\n\t_, _ = cancelConn.Read(buf)\n\n\treturn nil\n}\n\n// WaitForNotification waits for a LISTEN/NOTIFY message to be received. It returns an error if a notification was not\n// received.\nfunc (pgConn *PgConn) WaitForNotification(ctx context.Context) error {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn err\n\t}\n\tdefer pgConn.unlock()\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn newContextAlreadyDoneError(ctx)\n\t\tdefault:\n\t\t}\n\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\tfor {\n\t\tmsg, err := pgConn.receiveMessage()\n\t\tif err != nil {\n\t\t\treturn normalizeTimeoutError(ctx, err)\n\t\t}\n\n\t\tswitch msg.(type) {\n\t\tcase *pgproto3.NotificationResponse:\n\t\t\treturn nil\n\t\t}\n\t}\n}\n\n// Exec executes SQL via the PostgreSQL simple query protocol. SQL may contain multiple queries. Execution is\n// implicitly wrapped in a transaction unless a transaction is already in progress or SQL contains transaction control\n// statements.\n//\n// Prefer ExecParams unless executing arbitrary SQL that may contain multiple queries.\nfunc (pgConn *PgConn) Exec(ctx context.Context, sql string) *MultiResultReader {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn &MultiResultReader{\n\t\t\tclosed: true,\n\t\t\terr:    err,\n\t\t}\n\t}\n\n\tpgConn.multiResultReader = MultiResultReader{\n\t\tpgConn: pgConn,\n\t\tctx:    ctx,\n\t}\n\tmultiResult := &pgConn.multiResultReader\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tmultiResult.closed = true\n\t\t\tmultiResult.err = newContextAlreadyDoneError(ctx)\n\t\t\tpgConn.unlock()\n\t\t\treturn multiResult\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t}\n\n\tpgConn.frontend.SendQuery(&pgproto3.Query{String: sql})\n\terr := pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\tpgConn.contextWatcher.Unwatch()\n\t\tmultiResult.closed = true\n\t\tmultiResult.err = err\n\t\tpgConn.unlock()\n\t\treturn multiResult\n\t}\n\n\treturn multiResult\n}\n\n// ExecParams executes a command via the PostgreSQL extended query protocol.\n//\n// sql is a SQL command string. It may only contain one query. Parameter substitution is positional using $1, $2, $3,\n// etc.\n//\n// paramValues are the parameter values. It must be encoded in the format given by paramFormats.\n//\n// paramOIDs is a slice of data type OIDs for paramValues. If paramOIDs is nil, the server will infer the data type for\n// all parameters. Any paramOID element that is 0 that will cause the server to infer the data type for that parameter.\n// ExecParams will panic if len(paramOIDs) is not 0, 1, or len(paramValues).\n//\n// paramFormats is a slice of format codes determining for each paramValue column whether it is encoded in text or\n// binary format. If paramFormats is nil all params are text format. ExecParams will panic if\n// len(paramFormats) is not 0, 1, or len(paramValues).\n//\n// resultFormats is a slice of format codes determining for each result column whether it is encoded in text or\n// binary format. If resultFormats is nil all results will be in text format.\n//\n// ResultReader must be closed before PgConn can be used again.\nfunc (pgConn *PgConn) ExecParams(ctx context.Context, sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats, resultFormats []int16) *ResultReader {\n\tresult := pgConn.execExtendedPrefix(ctx, paramValues)\n\tif result.closed {\n\t\treturn result\n\t}\n\n\tpgConn.frontend.SendParse(&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs})\n\tpgConn.frontend.SendBind(&pgproto3.Bind{ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})\n\n\tpgConn.execExtendedSuffix(result, nil, nil)\n\n\treturn result\n}\n\n// ExecPrepared enqueues the execution of a prepared statement via the PostgreSQL extended query protocol.\n//\n// paramValues are the parameter values. It must be encoded in the format given by paramFormats.\n//\n// paramFormats is a slice of format codes determining for each paramValue column whether it is encoded in text or\n// binary format. If paramFormats is nil all params are text format. ExecPrepared will panic if\n// len(paramFormats) is not 0, 1, or len(paramValues).\n//\n// resultFormats is a slice of format codes determining for each result column whether it is encoded in text or\n// binary format. If resultFormats is nil all results will be in text format.\n//\n// ResultReader must be closed before PgConn can be used again.\nfunc (pgConn *PgConn) ExecPrepared(ctx context.Context, stmtName string, paramValues [][]byte, paramFormats, resultFormats []int16) *ResultReader {\n\tresult := pgConn.execExtendedPrefix(ctx, paramValues)\n\tif result.closed {\n\t\treturn result\n\t}\n\n\tpgConn.frontend.SendBind(&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})\n\n\tpgConn.execExtendedSuffix(result, nil, nil)\n\n\treturn result\n}\n\n// ExecStatement enqueues the execution of a prepared statement via the PostgreSQL extended query protocol.\n//\n// This differs from ExecPrepared in that it takes a *StatementDescription instead of the prepared statement name.\n// Because it has the *StatementDescription it can avoid the Describe Portal message that ExecPrepared must send to get\n// the result column descriptions.\n//\n// paramValues are the parameter values. It must be encoded in the format given by paramFormats.\n//\n// paramFormats is a slice of format codes determining for each paramValue column whether it is encoded in text or\n// binary format. If paramFormats is nil all params are text format. ExecPrepared will panic if len(paramFormats) is not\n// 0, 1, or len(paramValues).\n//\n// resultFormats is a slice of format codes determining for each result column whether it is encoded in text or binary\n// format. If resultFormats is nil all results will be in text format.\n//\n// ResultReader must be closed before PgConn can be used again.\nfunc (pgConn *PgConn) ExecStatement(ctx context.Context, statementDescription *StatementDescription, paramValues [][]byte, paramFormats, resultFormats []int16) *ResultReader {\n\tresult := pgConn.execExtendedPrefix(ctx, paramValues)\n\tif result.closed {\n\t\treturn result\n\t}\n\n\tpgConn.frontend.SendBind(&pgproto3.Bind{PreparedStatement: statementDescription.Name, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})\n\n\tpgConn.execExtendedSuffix(result, statementDescription, resultFormats)\n\n\treturn result\n}\n\nfunc (pgConn *PgConn) execExtendedPrefix(ctx context.Context, paramValues [][]byte) *ResultReader {\n\tpgConn.resultReader = ResultReader{\n\t\tpgConn: pgConn,\n\t\tctx:    ctx,\n\t}\n\tresult := &pgConn.resultReader\n\n\tif err := pgConn.lock(); err != nil {\n\t\tresult.concludeCommand(CommandTag{}, err)\n\t\tresult.closed = true\n\t\treturn result\n\t}\n\n\tif len(paramValues) > math.MaxUint16 {\n\t\tresult.concludeCommand(CommandTag{}, fmt.Errorf(\"extended protocol limited to %v parameters\", math.MaxUint16))\n\t\tresult.closed = true\n\t\tpgConn.unlock()\n\t\treturn result\n\t}\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tresult.concludeCommand(CommandTag{}, newContextAlreadyDoneError(ctx))\n\t\t\tresult.closed = true\n\t\t\tpgConn.unlock()\n\t\t\treturn result\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t}\n\n\treturn result\n}\n\nfunc (pgConn *PgConn) execExtendedSuffix(result *ResultReader, statementDescription *StatementDescription, resultFormats []int16) {\n\tif statementDescription == nil {\n\t\tpgConn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'P'})\n\t}\n\tpgConn.frontend.SendExecute(&pgproto3.Execute{})\n\tpgConn.frontend.SendSync(&pgproto3.Sync{})\n\n\terr := pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\tresult.concludeCommand(CommandTag{}, err)\n\t\tpgConn.contextWatcher.Unwatch()\n\t\tresult.closed = true\n\t\tpgConn.unlock()\n\t\treturn\n\t}\n\n\tresult.readUntilRowDescription(statementDescription, resultFormats)\n}\n\n// CopyTo executes the copy command sql and copies the results to w.\nfunc (pgConn *PgConn) CopyTo(ctx context.Context, w io.Writer, sql string) (CommandTag, error) {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn CommandTag{}, err\n\t}\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tpgConn.unlock()\n\t\t\treturn CommandTag{}, newContextAlreadyDoneError(ctx)\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\t// Send copy to command\n\tpgConn.frontend.SendQuery(&pgproto3.Query{String: sql})\n\n\terr := pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\tpgConn.unlock()\n\t\treturn CommandTag{}, err\n\t}\n\n\t// Read results\n\tvar commandTag CommandTag\n\tvar pgErr error\n\tfor {\n\t\tmsg, err := pgConn.receiveMessage()\n\t\tif err != nil {\n\t\t\tpgConn.asyncClose()\n\t\t\treturn CommandTag{}, normalizeTimeoutError(ctx, err)\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.CopyDone:\n\t\tcase *pgproto3.CopyData:\n\t\t\t_, err := w.Write(msg.Data)\n\t\t\tif err != nil {\n\t\t\t\tpgConn.asyncClose()\n\t\t\t\treturn CommandTag{}, err\n\t\t\t}\n\t\tcase *pgproto3.ReadyForQuery:\n\t\t\tpgConn.unlock()\n\t\t\treturn commandTag, pgErr\n\t\tcase *pgproto3.CommandComplete:\n\t\t\tcommandTag = pgConn.makeCommandTag(msg.CommandTag)\n\t\tcase *pgproto3.ErrorResponse:\n\t\t\tpgErr = ErrorResponseToPgError(msg)\n\t\t}\n\t}\n}\n\n// CopyFrom executes the copy command sql and copies all of r to the PostgreSQL server.\n//\n// Note: context cancellation will only interrupt operations on the underlying PostgreSQL network connection. Reads on r\n// could still block.\nfunc (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (CommandTag, error) {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn CommandTag{}, err\n\t}\n\tdefer pgConn.unlock()\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn CommandTag{}, newContextAlreadyDoneError(ctx)\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t\tdefer pgConn.contextWatcher.Unwatch()\n\t}\n\n\t// Send copy from query\n\tpgConn.frontend.SendQuery(&pgproto3.Query{String: sql})\n\terr := pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\treturn CommandTag{}, err\n\t}\n\n\t// Send copy data\n\tabortCopyChan := make(chan struct{})\n\tcopyErrChan := make(chan error, 1)\n\tsignalMessageChan := pgConn.signalMessage()\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tbuf := iobufpool.Get(65536)\n\t\tdefer iobufpool.Put(buf)\n\t\t(*buf)[0] = 'd'\n\n\t\tfor {\n\t\t\tn, readErr := r.Read((*buf)[5:cap(*buf)])\n\t\t\tif n > 0 {\n\t\t\t\t*buf = (*buf)[0 : n+5]\n\t\t\t\tpgio.SetInt32((*buf)[1:], int32(n+4))\n\n\t\t\t\twriteErr := pgConn.frontend.SendUnbufferedEncodedCopyData(*buf)\n\t\t\t\tif writeErr != nil {\n\t\t\t\t\t// Write errors are always fatal, but we can't use asyncClose because we are in a different goroutine. Not\n\t\t\t\t\t// setting pgConn.status or closing pgConn.cleanupDone for the same reason.\n\t\t\t\t\tpgConn.conn.Close()\n\n\t\t\t\t\tcopyErrChan <- writeErr\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif readErr != nil {\n\t\t\t\tcopyErrChan <- readErr\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-abortCopyChan:\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t}()\n\n\tvar pgErr error\n\tvar copyErr error\n\tfor copyErr == nil && pgErr == nil {\n\t\tselect {\n\t\tcase copyErr = <-copyErrChan:\n\t\tcase <-signalMessageChan:\n\t\t\t// If pgConn.receiveMessage encounters an error it will call pgConn.asyncClose. But that is a race condition with\n\t\t\t// the goroutine. So instead check pgConn.bufferingReceiveErr which will have been set by the signalMessage. If an\n\t\t\t// error is found then forcibly close the connection without sending the Terminate message.\n\t\t\tif err := pgConn.bufferingReceiveErr; err != nil {\n\t\t\t\tpgConn.status = connStatusClosed\n\t\t\t\tpgConn.conn.Close()\n\t\t\t\tclose(pgConn.cleanupDone)\n\t\t\t\treturn CommandTag{}, normalizeTimeoutError(ctx, err)\n\t\t\t}\n\t\t\t// peekMessage never returns err in the bufferingReceive mode - it only forwards the bufferingReceive variables.\n\t\t\t// Therefore, the only case for receiveMessage to return err is during handling of the ErrorResponse message type\n\t\t\t// and using pgOnError handler to determine the connection is no longer valid (and thus closing the conn).\n\t\t\tmsg, serverError := pgConn.receiveMessage()\n\t\t\tif serverError != nil {\n\t\t\t\tclose(abortCopyChan)\n\t\t\t\treturn CommandTag{}, serverError\n\t\t\t}\n\n\t\t\tswitch msg := msg.(type) {\n\t\t\tcase *pgproto3.ErrorResponse:\n\t\t\t\tpgErr = ErrorResponseToPgError(msg)\n\t\t\tdefault:\n\t\t\t\tsignalMessageChan = pgConn.signalMessage()\n\t\t\t}\n\t\t}\n\t}\n\tclose(abortCopyChan)\n\t// Make sure io goroutine finishes before writing.\n\twg.Wait()\n\n\tif copyErr == io.EOF || pgErr != nil {\n\t\tpgConn.frontend.Send(&pgproto3.CopyDone{})\n\t} else {\n\t\tpgConn.frontend.Send(&pgproto3.CopyFail{Message: copyErr.Error()})\n\t}\n\terr = pgConn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\tpgConn.asyncClose()\n\t\treturn CommandTag{}, err\n\t}\n\n\t// Read results\n\tvar commandTag CommandTag\n\tfor {\n\t\tmsg, err := pgConn.receiveMessage()\n\t\tif err != nil {\n\t\t\tpgConn.asyncClose()\n\t\t\treturn CommandTag{}, normalizeTimeoutError(ctx, err)\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.ReadyForQuery:\n\t\t\treturn commandTag, pgErr\n\t\tcase *pgproto3.CommandComplete:\n\t\t\tcommandTag = pgConn.makeCommandTag(msg.CommandTag)\n\t\tcase *pgproto3.ErrorResponse:\n\t\t\tpgErr = ErrorResponseToPgError(msg)\n\t\t}\n\t}\n}\n\n// MultiResultReader is a reader for a command that could return multiple results such as Exec or ExecBatch.\ntype MultiResultReader struct {\n\tpgConn *PgConn\n\tctx    context.Context\n\n\trr *ResultReader\n\n\t// Data from when the batch was queued.\n\tstatementDescriptions []*StatementDescription\n\tresultFormats         [][]int16\n\n\tclosed bool\n\terr    error\n}\n\n// ReadAll reads all available results. Calling ReadAll is mutually exclusive with all other MultiResultReader methods.\nfunc (mrr *MultiResultReader) ReadAll() ([]*Result, error) {\n\tvar results []*Result\n\n\tfor mrr.NextResult() {\n\t\tresults = append(results, mrr.ResultReader().Read())\n\t}\n\terr := mrr.Close()\n\n\treturn results, err\n}\n\nfunc (mrr *MultiResultReader) receiveMessage() (pgproto3.BackendMessage, error) {\n\tmsg, err := mrr.pgConn.receiveMessage()\n\tif err != nil {\n\t\tmrr.pgConn.contextWatcher.Unwatch()\n\t\tmrr.err = normalizeTimeoutError(mrr.ctx, err)\n\t\tmrr.closed = true\n\t\tmrr.pgConn.asyncClose()\n\t\treturn nil, mrr.err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.ReadyForQuery:\n\t\tmrr.closed = true\n\t\tmrr.pgConn.contextWatcher.Unwatch()\n\t\tmrr.pgConn.unlock()\n\tcase *pgproto3.ErrorResponse:\n\t\tmrr.err = ErrorResponseToPgError(msg)\n\t}\n\n\treturn msg, nil\n}\n\n// NextResult returns advances the MultiResultReader to the next result and returns true if a result is available.\nfunc (mrr *MultiResultReader) NextResult() bool {\n\tfor !mrr.closed && mrr.err == nil {\n\t\tmsg, _ := mrr.pgConn.peekMessage()\n\t\tif _, ok := msg.(*pgproto3.DataRow); ok {\n\t\t\tif len(mrr.statementDescriptions) > 0 {\n\t\t\t\trr := ResultReader{\n\t\t\t\t\tpgConn:            mrr.pgConn,\n\t\t\t\t\tmultiResultReader: mrr,\n\t\t\t\t\tctx:               mrr.ctx,\n\t\t\t\t}\n\n\t\t\t\t// This result corresponds to a prepared statement description that was provided when queuing the batch.\n\t\t\t\tsd := mrr.statementDescriptions[0]\n\t\t\t\tmrr.statementDescriptions = mrr.statementDescriptions[1:]\n\n\t\t\t\tresultFormats := mrr.resultFormats[0]\n\t\t\t\tmrr.resultFormats = mrr.resultFormats[1:]\n\n\t\t\t\tsdFields := sd.Fields\n\t\t\t\trr.fieldDescriptions = rr.pgConn.getFieldDescriptionSlice(len(sdFields))\n\n\t\t\t\terr := combineFieldDescriptionsAndResultFormats(rr.fieldDescriptions, sdFields, resultFormats)\n\t\t\t\tif err != nil {\n\t\t\t\t\trr.concludeCommand(CommandTag{}, err)\n\t\t\t\t}\n\n\t\t\t\tmrr.pgConn.resultReader = rr\n\t\t\t\tmrr.rr = &mrr.pgConn.resultReader\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\tmrr.err = fmt.Errorf(\"unexpected DataRow message without preceding RowDescription\")\n\t\t\treturn false\n\t\t}\n\n\t\tmsg, err := mrr.receiveMessage()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.RowDescription:\n\t\t\tmrr.pgConn.resultReader = ResultReader{\n\t\t\t\tpgConn:            mrr.pgConn,\n\t\t\t\tmultiResultReader: mrr,\n\t\t\t\tctx:               mrr.ctx,\n\t\t\t\tfieldDescriptions: mrr.pgConn.getFieldDescriptionSlice(len(msg.Fields)),\n\t\t\t}\n\t\t\tconvertRowDescription(mrr.pgConn.resultReader.fieldDescriptions, msg)\n\n\t\t\tmrr.rr = &mrr.pgConn.resultReader\n\t\t\treturn true\n\t\tcase *pgproto3.CommandComplete:\n\t\t\tmrr.pgConn.resultReader = ResultReader{\n\t\t\t\tcommandTag:       mrr.pgConn.makeCommandTag(msg.CommandTag),\n\t\t\t\tcommandConcluded: true,\n\t\t\t\tclosed:           true,\n\t\t\t}\n\t\t\tmrr.rr = &mrr.pgConn.resultReader\n\t\t\treturn true\n\t\tcase *pgproto3.EmptyQueryResponse:\n\t\t\tmrr.pgConn.resultReader = ResultReader{\n\t\t\t\tcommandConcluded: true,\n\t\t\t\tclosed:           true,\n\t\t\t}\n\t\t\tmrr.rr = &mrr.pgConn.resultReader\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// ResultReader returns the current ResultReader.\nfunc (mrr *MultiResultReader) ResultReader() *ResultReader {\n\treturn mrr.rr\n}\n\n// Close closes the MultiResultReader and returns the first error that occurred during the MultiResultReader's use.\nfunc (mrr *MultiResultReader) Close() error {\n\tfor !mrr.closed {\n\t\t_, err := mrr.receiveMessage()\n\t\tif err != nil {\n\t\t\treturn mrr.err\n\t\t}\n\t}\n\n\treturn mrr.err\n}\n\n// ResultReader is a reader for the result of a single query.\ntype ResultReader struct {\n\tpgConn            *PgConn\n\tmultiResultReader *MultiResultReader\n\tpipeline          *Pipeline\n\tctx               context.Context\n\n\tfieldDescriptions []FieldDescription\n\trowValues         [][]byte\n\tcommandTag        CommandTag\n\tpreloaded         bool\n\tcommandConcluded  bool\n\tclosed            bool\n\terr               error\n}\n\n// Result is the saved query response that is returned by calling Read on a ResultReader.\ntype Result struct {\n\tFieldDescriptions []FieldDescription\n\tRows              [][][]byte\n\tCommandTag        CommandTag\n\tErr               error\n}\n\n// Read saves the query response to a Result.\nfunc (rr *ResultReader) Read() *Result {\n\tbr := &Result{}\n\n\tfor rr.NextRow() {\n\t\tif br.FieldDescriptions == nil {\n\t\t\tbr.FieldDescriptions = make([]FieldDescription, len(rr.FieldDescriptions()))\n\t\t\tcopy(br.FieldDescriptions, rr.FieldDescriptions())\n\t\t}\n\n\t\tvalues := rr.Values()\n\t\trow := make([][]byte, len(values))\n\t\tfor i := range row {\n\t\t\tif values[i] != nil {\n\t\t\t\trow[i] = make([]byte, len(values[i]))\n\t\t\t\tcopy(row[i], values[i])\n\t\t\t}\n\t\t}\n\t\tbr.Rows = append(br.Rows, row)\n\t}\n\n\tbr.CommandTag, br.Err = rr.Close()\n\n\treturn br\n}\n\n// NextRow advances the ResultReader to the next row and returns true if a row is available.\nfunc (rr *ResultReader) NextRow() bool {\n\tif rr.preloaded {\n\t\trr.preloaded = false\n\t\treturn true\n\t}\n\n\tfor !rr.commandConcluded {\n\t\tmsg, err := rr.receiveMessage()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.DataRow:\n\t\t\trr.rowValues = msg.Values\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (rr *ResultReader) preloadRowValues(values [][]byte) {\n\trr.rowValues = values\n\trr.preloaded = true\n}\n\n// FieldDescriptions returns the field descriptions for the current result set. The returned slice is only valid until\n// the ResultReader is closed. It may return nil (for example, if the query did not return a result set or an error was\n// encountered.)\nfunc (rr *ResultReader) FieldDescriptions() []FieldDescription {\n\treturn rr.fieldDescriptions\n}\n\n// Values returns the current row data. NextRow must have been previously been called. The returned [][]byte is only\n// valid until the next NextRow call or the ResultReader is closed.\nfunc (rr *ResultReader) Values() [][]byte {\n\treturn rr.rowValues\n}\n\n// Close consumes any remaining result data and returns the command tag or\n// error.\nfunc (rr *ResultReader) Close() (CommandTag, error) {\n\tif rr.closed {\n\t\treturn rr.commandTag, rr.err\n\t}\n\trr.closed = true\n\n\tfor !rr.commandConcluded {\n\t\t_, err := rr.receiveMessage()\n\t\tif err != nil {\n\t\t\treturn CommandTag{}, rr.err\n\t\t}\n\t}\n\n\tif rr.multiResultReader == nil && rr.pipeline == nil {\n\t\tfor {\n\t\t\tmsg, err := rr.receiveMessage()\n\t\t\tif err != nil {\n\t\t\t\treturn CommandTag{}, rr.err\n\t\t\t}\n\n\t\t\tswitch msg := msg.(type) {\n\t\t\t// Detect a deferred constraint violation where the ErrorResponse is sent after CommandComplete.\n\t\t\tcase *pgproto3.ErrorResponse:\n\t\t\t\trr.err = ErrorResponseToPgError(msg)\n\t\t\tcase *pgproto3.ReadyForQuery:\n\t\t\t\trr.pgConn.contextWatcher.Unwatch()\n\t\t\t\trr.pgConn.unlock()\n\t\t\t\treturn rr.commandTag, rr.err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn rr.commandTag, rr.err\n}\n\n// readUntilRowDescription ensures the ResultReader's fieldDescriptions are loaded. It does not return an error as any\n// error will be stored in the ResultReader.\nfunc (rr *ResultReader) readUntilRowDescription(statementDescription *StatementDescription, resultFormats []int16) {\n\tfor !rr.commandConcluded {\n\t\tmsg, _ := rr.receiveMessage()\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.RowDescription:\n\t\t\treturn\n\t\tcase *pgproto3.DataRow:\n\t\t\trr.preloadRowValues(msg.Values)\n\t\t\tif statementDescription != nil {\n\t\t\t\tsdFields := statementDescription.Fields\n\t\t\t\trr.fieldDescriptions = rr.pgConn.getFieldDescriptionSlice(len(sdFields))\n\n\t\t\t\terr := combineFieldDescriptionsAndResultFormats(rr.fieldDescriptions, sdFields, resultFormats)\n\t\t\t\tif err != nil {\n\t\t\t\t\trr.concludeCommand(CommandTag{}, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\tcase *pgproto3.CommandComplete:\n\t\t\tif statementDescription != nil {\n\t\t\t\tsdFields := statementDescription.Fields\n\t\t\t\trr.fieldDescriptions = rr.pgConn.getFieldDescriptionSlice(len(sdFields))\n\n\t\t\t\terr := combineFieldDescriptionsAndResultFormats(rr.fieldDescriptions, sdFields, resultFormats)\n\t\t\t\tif err != nil {\n\t\t\t\t\trr.concludeCommand(CommandTag{}, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (rr *ResultReader) receiveMessage() (msg pgproto3.BackendMessage, err error) {\n\tif rr.multiResultReader == nil {\n\t\tmsg, err = rr.pgConn.receiveMessage()\n\t} else {\n\t\tmsg, err = rr.multiResultReader.receiveMessage()\n\t}\n\n\tif err != nil {\n\t\terr = normalizeTimeoutError(rr.ctx, err)\n\t\trr.concludeCommand(CommandTag{}, err)\n\t\trr.pgConn.contextWatcher.Unwatch()\n\t\trr.closed = true\n\t\tif rr.multiResultReader == nil {\n\t\t\trr.pgConn.asyncClose()\n\t\t}\n\n\t\treturn nil, rr.err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.RowDescription:\n\t\trr.fieldDescriptions = rr.pgConn.getFieldDescriptionSlice(len(msg.Fields))\n\t\tconvertRowDescription(rr.fieldDescriptions, msg)\n\tcase *pgproto3.CommandComplete:\n\t\trr.concludeCommand(rr.pgConn.makeCommandTag(msg.CommandTag), nil)\n\tcase *pgproto3.EmptyQueryResponse:\n\t\trr.concludeCommand(CommandTag{}, nil)\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tif rr.pipeline != nil {\n\t\t\trr.pipeline.state.HandleError(pgErr)\n\t\t}\n\t\trr.concludeCommand(CommandTag{}, pgErr)\n\t}\n\n\treturn msg, nil\n}\n\nfunc (rr *ResultReader) concludeCommand(commandTag CommandTag, err error) {\n\t// Keep the first error that is recorded. Store the error before checking if the command is already concluded to\n\t// allow for receiving an error after CommandComplete but before ReadyForQuery.\n\tif err != nil && rr.err == nil {\n\t\trr.err = err\n\t}\n\n\tif rr.commandConcluded {\n\t\treturn\n\t}\n\n\trr.commandTag = commandTag\n\trr.rowValues = nil\n\trr.commandConcluded = true\n}\n\n// Batch is a collection of queries that can be sent to the PostgreSQL server in a single round-trip.\ntype Batch struct {\n\tbuf                   []byte\n\tstatementDescriptions []*StatementDescription\n\tresultFormats         [][]int16\n\terr                   error\n}\n\n// ExecParams appends an ExecParams command to the batch. See PgConn.ExecParams for parameter descriptions.\nfunc (batch *Batch) ExecParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats, resultFormats []int16) {\n\tif batch.err != nil {\n\t\treturn\n\t}\n\n\tbatch.buf, batch.err = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\treturn\n\t}\n\tbatch.ExecPrepared(\"\", paramValues, paramFormats, resultFormats)\n}\n\n// ExecPrepared appends an ExecPrepared e command to the batch. See PgConn.ExecPrepared for parameter descriptions.\nfunc (batch *Batch) ExecPrepared(stmtName string, paramValues [][]byte, paramFormats, resultFormats []int16) {\n\tif batch.err != nil {\n\t\treturn\n\t}\n\n\tbatch.buf, batch.err = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\treturn\n\t}\n\n\tbatch.buf, batch.err = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\treturn\n\t}\n\n\tbatch.buf, batch.err = (&pgproto3.Execute{}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\treturn\n\t}\n}\n\n// ExecStatement appends an ExecStatement command to the batch. See PgConn.ExecPrepared for parameter descriptions.\n//\n// This differs from ExecPrepared in that it takes a *StatementDescription instead of just the prepared statement name.\n// Because it has the *StatementDescription it can avoid the Describe Portal message that ExecPrepared must send to get\n// the result column descriptions.\nfunc (batch *Batch) ExecStatement(statementDescription *StatementDescription, paramValues [][]byte, paramFormats, resultFormats []int16) {\n\tif batch.err != nil {\n\t\treturn\n\t}\n\n\tbatch.buf, batch.err = (&pgproto3.Bind{PreparedStatement: statementDescription.Name, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\treturn\n\t}\n\n\tbatch.statementDescriptions = append(batch.statementDescriptions, statementDescription)\n\tbatch.resultFormats = append(batch.resultFormats, resultFormats)\n\n\tbatch.buf, batch.err = (&pgproto3.Execute{}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\treturn\n\t}\n}\n\n// ExecBatch executes all the queries in batch in a single round-trip. Execution is implicitly transactional unless a\n// transaction is already in progress or SQL contains transaction control statements. This is a simpler way of executing\n// multiple queries in a single round trip than using pipeline mode.\nfunc (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultReader {\n\tif batch.err != nil {\n\t\treturn &MultiResultReader{\n\t\t\tclosed: true,\n\t\t\terr:    batch.err,\n\t\t}\n\t}\n\n\tif err := pgConn.lock(); err != nil {\n\t\treturn &MultiResultReader{\n\t\t\tclosed: true,\n\t\t\terr:    err,\n\t\t}\n\t}\n\n\tpgConn.multiResultReader = MultiResultReader{\n\t\tpgConn:                pgConn,\n\t\tctx:                   ctx,\n\t\tstatementDescriptions: batch.statementDescriptions,\n\t\tresultFormats:         batch.resultFormats,\n\t}\n\tmultiResult := &pgConn.multiResultReader\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tmultiResult.closed = true\n\t\t\tmultiResult.err = newContextAlreadyDoneError(ctx)\n\t\t\tpgConn.unlock()\n\t\t\treturn multiResult\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t}\n\n\tbatch.buf, batch.err = (&pgproto3.Sync{}).Encode(batch.buf)\n\tif batch.err != nil {\n\t\tpgConn.contextWatcher.Unwatch()\n\t\tmultiResult.err = normalizeTimeoutError(multiResult.ctx, batch.err)\n\t\tmultiResult.closed = true\n\t\tpgConn.asyncClose()\n\t\treturn multiResult\n\t}\n\n\t_, err := func(buf []byte) (int, error) {\n\t\tpgConn.enterPotentialWriteReadDeadlock()\n\t\tdefer pgConn.exitPotentialWriteReadDeadlock()\n\t\treturn pgConn.conn.Write(buf)\n\t}(batch.buf)\n\tif err != nil {\n\t\tpgConn.contextWatcher.Unwatch()\n\t\tmultiResult.err = normalizeTimeoutError(multiResult.ctx, err)\n\t\tmultiResult.closed = true\n\t\tpgConn.asyncClose()\n\t\treturn multiResult\n\t}\n\n\treturn multiResult\n}\n\n// EscapeString escapes a string such that it can safely be interpolated into a SQL command string. It does not include\n// the surrounding single quotes.\n//\n// The current implementation requires that standard_conforming_strings=on and client_encoding=\"UTF8\". If these\n// conditions are not met an error will be returned. It is possible these restrictions will be lifted in the future.\nfunc (pgConn *PgConn) EscapeString(s string) (string, error) {\n\tif pgConn.ParameterStatus(\"standard_conforming_strings\") != \"on\" {\n\t\treturn \"\", errors.New(\"EscapeString must be run with standard_conforming_strings=on\")\n\t}\n\n\tif pgConn.ParameterStatus(\"client_encoding\") != \"UTF8\" {\n\t\treturn \"\", errors.New(\"EscapeString must be run with client_encoding=UTF8\")\n\t}\n\n\treturn strings.Replace(s, \"'\", \"''\", -1), nil\n}\n\n// CheckConn checks the underlying connection without writing any bytes. This is currently implemented by doing a read\n// with a very short deadline. This can be useful because a TCP connection can be broken such that a write will appear\n// to succeed even though it will never actually reach the server. Reading immediately before a write will detect this\n// condition. If this is done immediately before sending a query it reduces the chances a query will be sent that fails\n// without the client knowing whether the server received it or not.\n//\n// Deprecated: CheckConn is deprecated in favor of Ping. CheckConn cannot detect all types of broken connections where\n// the write would still appear to succeed. Prefer Ping unless on a high latency connection.\nfunc (pgConn *PgConn) CheckConn() error {\n\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)\n\tdefer cancel()\n\n\t_, err := pgConn.ReceiveMessage(ctx)\n\tif err != nil {\n\t\tif !Timeout(err) {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Ping pings the server. This can be useful because a TCP connection can be broken such that a write will appear to\n// succeed even though it will never actually reach the server. Pinging immediately before sending a query reduces the\n// chances a query will be sent that fails without the client knowing whether the server received it or not.\nfunc (pgConn *PgConn) Ping(ctx context.Context) error {\n\treturn pgConn.Exec(ctx, \"-- ping\").Close()\n}\n\n// makeCommandTag makes a CommandTag. It does not retain a reference to buf or buf's underlying memory.\nfunc (pgConn *PgConn) makeCommandTag(buf []byte) CommandTag {\n\treturn CommandTag{s: string(buf)}\n}\n\n// enterPotentialWriteReadDeadlock must be called before a write that could deadlock if the server is simultaneously\n// blocked writing to us.\nfunc (pgConn *PgConn) enterPotentialWriteReadDeadlock() {\n\t// The time to wait is somewhat arbitrary. A Write should only take as long as the syscall and memcpy to the OS\n\t// outbound network buffer unless the buffer is full (which potentially is a block). It needs to be long enough for\n\t// the normal case, but short enough not to kill performance if a block occurs.\n\t//\n\t// In addition, on Windows the default timer resolution is 15.6ms. So setting the timer to less than that is\n\t// ineffective.\n\tif pgConn.slowWriteTimer.Reset(15 * time.Millisecond) {\n\t\tpanic(\"BUG: slow write timer already active\")\n\t}\n}\n\n// exitPotentialWriteReadDeadlock must be called after a call to enterPotentialWriteReadDeadlock.\nfunc (pgConn *PgConn) exitPotentialWriteReadDeadlock() {\n\tif !pgConn.slowWriteTimer.Stop() {\n\t\t// The timer starts its function in a separate goroutine. It is necessary to ensure the background reader has\n\t\t// started before calling Stop. Otherwise, the background reader may not be stopped. That on its own is not a\n\t\t// serious problem. But what is a serious problem is that the background reader may start at an inopportune time in\n\t\t// a subsequent query. For example, if a subsequent query was canceled then a deadline may be set on the net.Conn to\n\t\t// interrupt an in-progress read. After the read is interrupted, but before the deadline is cleared, the background\n\t\t// reader could start and read a deadline error. Then the next query would receive the an unexpected deadline error.\n\t\t<-pgConn.bgReaderStarted\n\t\tpgConn.bgReader.Stop()\n\t}\n}\n\nfunc (pgConn *PgConn) flushWithPotentialWriteReadDeadlock() error {\n\tpgConn.enterPotentialWriteReadDeadlock()\n\tdefer pgConn.exitPotentialWriteReadDeadlock()\n\terr := pgConn.frontend.Flush()\n\treturn err\n}\n\n// SyncConn prepares the underlying net.Conn for direct use. PgConn may internally buffer reads or use goroutines for\n// background IO. This means that any direct use of the underlying net.Conn may be corrupted if a read is already\n// buffered or a read is in progress. SyncConn drains read buffers and stops background IO. In some cases this may\n// require sending a ping to the server. ctx can be used to cancel this operation. This should be called before any\n// operation that will use the underlying net.Conn directly. e.g. Before Conn() or Hijack().\n//\n// This should not be confused with the PostgreSQL protocol Sync message.\nfunc (pgConn *PgConn) SyncConn(ctx context.Context) error {\n\tfor range 10 {\n\t\tif pgConn.bgReader.Status() == bgreader.StatusStopped && pgConn.frontend.ReadBufferLen() == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\terr := pgConn.Ping(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"SyncConn: Ping failed while syncing conn: %w\", err)\n\t\t}\n\t}\n\n\t// This should never happen. Only way I can imagine this occurring is if the server is constantly sending data such as\n\t// LISTEN/NOTIFY or log notifications such that we never can get an empty buffer.\n\treturn errors.New(\"SyncConn: conn never synchronized\")\n}\n\n// CustomData returns a map that can be used to associate custom data with the connection.\nfunc (pgConn *PgConn) CustomData() map[string]any {\n\treturn pgConn.customData\n}\n\n// HijackedConn is the result of hijacking a connection.\n//\n// Due to the necessary exposure of internal implementation details, it is not covered by the semantic versioning\n// compatibility.\ntype HijackedConn struct {\n\tConn              net.Conn\n\tPID               uint32            // backend pid\n\tSecretKey         []byte            // key to use to send a cancel query message to the server\n\tParameterStatuses map[string]string // parameters that have been reported by the server\n\tTxStatus          byte\n\tFrontend          *pgproto3.Frontend\n\tConfig            *Config\n\tCustomData        map[string]any\n}\n\n// Hijack extracts the internal connection data. pgConn must be in an idle state. SyncConn should be called immediately\n// before Hijack. pgConn is unusable after hijacking. Hijacking is typically only useful when using pgconn to establish\n// a connection, but taking complete control of the raw connection after that (e.g. a load balancer or proxy).\n//\n// Due to the necessary exposure of internal implementation details, it is not covered by the semantic versioning\n// compatibility.\nfunc (pgConn *PgConn) Hijack() (*HijackedConn, error) {\n\tif err := pgConn.lock(); err != nil {\n\t\treturn nil, err\n\t}\n\tpgConn.status = connStatusClosed\n\n\treturn &HijackedConn{\n\t\tConn:              pgConn.conn,\n\t\tPID:               pgConn.pid,\n\t\tSecretKey:         pgConn.secretKey,\n\t\tParameterStatuses: pgConn.parameterStatuses,\n\t\tTxStatus:          pgConn.txStatus,\n\t\tFrontend:          pgConn.frontend,\n\t\tConfig:            pgConn.config,\n\t\tCustomData:        pgConn.customData,\n\t}, nil\n}\n\n// Construct created a PgConn from an already established connection to a PostgreSQL server. This is the inverse of\n// PgConn.Hijack. The connection must be in an idle state.\n//\n// hc.Frontend is replaced by a new pgproto3.Frontend built by hc.Config.BuildFrontend.\n//\n// Due to the necessary exposure of internal implementation details, it is not covered by the semantic versioning\n// compatibility.\nfunc Construct(hc *HijackedConn) (*PgConn, error) {\n\tpgConn := &PgConn{\n\t\tconn:              hc.Conn,\n\t\tpid:               hc.PID,\n\t\tsecretKey:         hc.SecretKey,\n\t\tparameterStatuses: hc.ParameterStatuses,\n\t\ttxStatus:          hc.TxStatus,\n\t\tfrontend:          hc.Frontend,\n\t\tconfig:            hc.Config,\n\t\tcustomData:        hc.CustomData,\n\n\t\tstatus: connStatusIdle,\n\n\t\tcleanupDone: make(chan struct{}),\n\t}\n\n\tpgConn.contextWatcher = ctxwatch.NewContextWatcher(hc.Config.BuildContextWatcherHandler(pgConn))\n\tpgConn.bgReader = bgreader.New(pgConn.conn)\n\tpgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64),\n\t\tfunc() {\n\t\t\tpgConn.bgReader.Start()\n\t\t\tpgConn.bgReaderStarted <- struct{}{}\n\t\t},\n\t)\n\tpgConn.slowWriteTimer.Stop()\n\tpgConn.bgReaderStarted = make(chan struct{})\n\tpgConn.frontend = hc.Config.BuildFrontend(pgConn.bgReader, pgConn.conn)\n\n\treturn pgConn, nil\n}\n\n// Pipeline represents a connection in pipeline mode.\n//\n// SendPrepare, SendQueryParams, SendQueryPrepared, and SendQueryStatement queue requests to the server. These requests\n// are not written until pipeline is flushed by Flush or Sync. Sync must be called after the last request is queued.\n// Requests between synchronization points are implicitly transactional unless explicit transaction control statements\n// have been issued.\n//\n// The context the pipeline was started with is in effect for the entire life of the Pipeline.\n//\n// For a deeper understanding of pipeline mode see the PostgreSQL documentation for the extended query protocol\n// (https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY) and the libpq pipeline mode\n// (https://www.postgresql.org/docs/current/libpq-pipeline-mode.html).\ntype Pipeline struct {\n\tconn *PgConn\n\tctx  context.Context\n\n\tstate  pipelineState\n\terr    error\n\tclosed bool\n}\n\n// PipelineSync is returned by GetResults when a ReadyForQuery message is received.\ntype PipelineSync struct{}\n\n// CloseComplete is returned by GetResults when a CloseComplete message is received.\ntype CloseComplete struct{}\n\ntype pipelineRequestType int\n\nconst (\n\tpipelineNil pipelineRequestType = iota\n\tpipelinePrepare\n\tpipelineQueryParams\n\tpipelineQueryPrepared\n\tpipelineQueryStatement\n\tpipelineDeallocate\n\tpipelineSyncRequest\n\tpipelineFlushRequest\n)\n\ntype pipelineRequestEvent struct {\n\tRequestType       pipelineRequestType\n\tWasSentToServer   bool\n\tBeforeFlushOrSync bool\n}\n\ntype pipelineState struct {\n\trequestEventQueue          list.List\n\tstatementDescriptionsQueue list.List\n\tresultFormatsQueue         list.List\n\tlastRequestType            pipelineRequestType\n\tpgErr                      *PgError\n\texpectedReadyForQueryCount int\n}\n\nfunc (s *pipelineState) Init() {\n\ts.requestEventQueue.Init()\n\ts.statementDescriptionsQueue.Init()\n\ts.resultFormatsQueue.Init()\n\ts.lastRequestType = pipelineNil\n}\n\nfunc (s *pipelineState) RegisterSendingToServer() {\n\tfor elem := s.requestEventQueue.Back(); elem != nil; elem = elem.Prev() {\n\t\tval := elem.Value.(pipelineRequestEvent)\n\t\tif val.WasSentToServer {\n\t\t\treturn\n\t\t}\n\t\tval.WasSentToServer = true\n\t\telem.Value = val\n\t}\n}\n\nfunc (s *pipelineState) registerFlushingBufferOnServer() {\n\tfor elem := s.requestEventQueue.Back(); elem != nil; elem = elem.Prev() {\n\t\tval := elem.Value.(pipelineRequestEvent)\n\t\tif val.BeforeFlushOrSync {\n\t\t\treturn\n\t\t}\n\t\tval.BeforeFlushOrSync = true\n\t\telem.Value = val\n\t}\n}\n\nfunc (s *pipelineState) PushBackRequestType(req pipelineRequestType) {\n\tif req == pipelineNil {\n\t\treturn\n\t}\n\n\tif req != pipelineFlushRequest {\n\t\ts.requestEventQueue.PushBack(pipelineRequestEvent{RequestType: req})\n\t}\n\tif req == pipelineFlushRequest || req == pipelineSyncRequest {\n\t\ts.registerFlushingBufferOnServer()\n\t}\n\ts.lastRequestType = req\n\n\tif req == pipelineSyncRequest {\n\t\ts.expectedReadyForQueryCount++\n\t}\n}\n\nfunc (s *pipelineState) ExtractFrontRequestType() pipelineRequestType {\n\tfor {\n\t\telem := s.requestEventQueue.Front()\n\t\tif elem == nil {\n\t\t\treturn pipelineNil\n\t\t}\n\t\tval := elem.Value.(pipelineRequestEvent)\n\t\tif !(val.WasSentToServer && val.BeforeFlushOrSync) {\n\t\t\treturn pipelineNil\n\t\t}\n\n\t\ts.requestEventQueue.Remove(elem)\n\t\tif val.RequestType == pipelineSyncRequest {\n\t\t\ts.pgErr = nil\n\t\t}\n\t\tif s.pgErr == nil {\n\t\t\treturn val.RequestType\n\t\t}\n\t}\n}\n\nfunc (s *pipelineState) PushBackStatementData(sd *StatementDescription, resultFormats []int16) {\n\ts.statementDescriptionsQueue.PushBack(sd)\n\ts.resultFormatsQueue.PushBack(resultFormats)\n}\n\nfunc (s *pipelineState) ExtractFrontStatementData() (*StatementDescription, []int16) {\n\tsdElem := s.statementDescriptionsQueue.Front()\n\tvar sd *StatementDescription\n\tif sdElem != nil {\n\t\ts.statementDescriptionsQueue.Remove(sdElem)\n\t\tsd = sdElem.Value.(*StatementDescription)\n\t}\n\n\trfElem := s.resultFormatsQueue.Front()\n\tvar resultFormats []int16\n\tif rfElem != nil {\n\t\ts.resultFormatsQueue.Remove(rfElem)\n\t\tresultFormats = rfElem.Value.([]int16)\n\t}\n\n\treturn sd, resultFormats\n}\n\nfunc (s *pipelineState) HandleError(err *PgError) {\n\ts.pgErr = err\n}\n\nfunc (s *pipelineState) HandleReadyForQuery() {\n\ts.expectedReadyForQueryCount--\n}\n\nfunc (s *pipelineState) PendingSync() bool {\n\tvar notPendingSync bool\n\n\tif elem := s.requestEventQueue.Back(); elem != nil {\n\t\tval := elem.Value.(pipelineRequestEvent)\n\t\tnotPendingSync = (val.RequestType == pipelineSyncRequest) && val.WasSentToServer\n\t} else {\n\t\tnotPendingSync = (s.lastRequestType == pipelineSyncRequest) || (s.lastRequestType == pipelineNil)\n\t}\n\n\treturn !notPendingSync\n}\n\nfunc (s *pipelineState) ExpectedReadyForQuery() int {\n\treturn s.expectedReadyForQueryCount\n}\n\n// StartPipeline switches the connection to pipeline mode and returns a *Pipeline. In pipeline mode requests can be sent\n// to the server without waiting for a response. Close must be called on the returned *Pipeline to return the connection\n// to normal mode. While in pipeline mode, no methods that communicate with the server may be called except\n// CancelRequest and Close. ctx is in effect for entire life of the *Pipeline.\n//\n// Prefer ExecBatch when only sending one group of queries at once.\nfunc (pgConn *PgConn) StartPipeline(ctx context.Context) *Pipeline {\n\tif err := pgConn.lock(); err != nil {\n\t\tpipeline := &Pipeline{\n\t\t\tclosed: true,\n\t\t\terr:    err,\n\t\t}\n\t\tpipeline.state.Init()\n\n\t\treturn pipeline\n\t}\n\n\tpgConn.resultReader = ResultReader{closed: true}\n\n\tpgConn.pipeline = Pipeline{\n\t\tconn: pgConn,\n\t\tctx:  ctx,\n\t}\n\tpgConn.pipeline.state.Init()\n\n\tpipeline := &pgConn.pipeline\n\n\tif ctx != context.Background() {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tpipeline.closed = true\n\t\t\tpipeline.err = newContextAlreadyDoneError(ctx)\n\t\t\tpgConn.unlock()\n\t\t\treturn pipeline\n\t\tdefault:\n\t\t}\n\t\tpgConn.contextWatcher.Watch(ctx)\n\t}\n\n\treturn pipeline\n}\n\n// SendPrepare is the pipeline version of *PgConn.Prepare.\nfunc (p *Pipeline) SendPrepare(name, sql string, paramOIDs []uint32) {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.SendParse(&pgproto3.Parse{Name: name, Query: sql, ParameterOIDs: paramOIDs})\n\tp.conn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'S', Name: name})\n\tp.state.PushBackRequestType(pipelinePrepare)\n}\n\n// SendDeallocate deallocates a prepared statement.\nfunc (p *Pipeline) SendDeallocate(name string) {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.SendClose(&pgproto3.Close{ObjectType: 'S', Name: name})\n\tp.state.PushBackRequestType(pipelineDeallocate)\n}\n\n// SendQueryParams is the pipeline version of *PgConn.ExecParams.\nfunc (p *Pipeline) SendQueryParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats, resultFormats []int16) {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.SendParse(&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs})\n\tp.conn.frontend.SendBind(&pgproto3.Bind{ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})\n\tp.conn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'P'})\n\tp.conn.frontend.SendExecute(&pgproto3.Execute{})\n\tp.state.PushBackRequestType(pipelineQueryParams)\n}\n\n// SendQueryPrepared is the pipeline version of *PgConn.ExecPrepared.\nfunc (p *Pipeline) SendQueryPrepared(stmtName string, paramValues [][]byte, paramFormats, resultFormats []int16) {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.SendBind(&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})\n\tp.conn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'P'})\n\tp.conn.frontend.SendExecute(&pgproto3.Execute{})\n\tp.state.PushBackRequestType(pipelineQueryPrepared)\n}\n\n// SendQueryStatement is the pipeline version of *PgConn.ExecStatement.\nfunc (p *Pipeline) SendQueryStatement(statementDescription *StatementDescription, paramValues [][]byte, paramFormats, resultFormats []int16) {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.SendBind(&pgproto3.Bind{PreparedStatement: statementDescription.Name, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})\n\tp.conn.frontend.SendExecute(&pgproto3.Execute{})\n\tp.state.PushBackRequestType(pipelineQueryStatement)\n\tp.state.PushBackStatementData(statementDescription, resultFormats)\n}\n\n// SendFlushRequest sends a request for the server to flush its output buffer.\n//\n// The server flushes its output buffer automatically as a result of Sync being called,\n// or on any request when not in pipeline mode; this function is useful to cause the server\n// to flush its output buffer in pipeline mode without establishing a synchronization point.\n// Note that the request is not itself flushed to the server automatically; use Flush if\n// necessary. This copies the behavior of libpq PQsendFlushRequest.\nfunc (p *Pipeline) SendFlushRequest() {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.Send(&pgproto3.Flush{})\n\tp.state.PushBackRequestType(pipelineFlushRequest)\n}\n\n// SendPipelineSync marks a synchronization point in a pipeline by sending a sync message\n// without flushing the send buffer. This serves as the delimiter of an implicit\n// transaction and an error recovery point.\n//\n// Note that the request is not itself flushed to the server automatically; use Flush if\n// necessary. This copies the behavior of libpq PQsendPipelineSync.\nfunc (p *Pipeline) SendPipelineSync() {\n\tif p.closed {\n\t\treturn\n\t}\n\n\tp.conn.frontend.SendSync(&pgproto3.Sync{})\n\tp.state.PushBackRequestType(pipelineSyncRequest)\n}\n\n// Flush flushes the queued requests without establishing a synchronization point.\nfunc (p *Pipeline) Flush() error {\n\tif p.closed {\n\t\tif p.err != nil {\n\t\t\treturn p.err\n\t\t}\n\t\treturn errors.New(\"pipeline closed\")\n\t}\n\n\terr := p.conn.flushWithPotentialWriteReadDeadlock()\n\tif err != nil {\n\t\terr = normalizeTimeoutError(p.ctx, err)\n\n\t\tp.conn.asyncClose()\n\n\t\tp.conn.contextWatcher.Unwatch()\n\t\tp.conn.unlock()\n\t\tp.closed = true\n\t\tp.err = err\n\t\treturn err\n\t}\n\n\tp.state.RegisterSendingToServer()\n\treturn nil\n}\n\n// Sync establishes a synchronization point and flushes the queued requests.\nfunc (p *Pipeline) Sync() error {\n\tp.SendPipelineSync()\n\treturn p.Flush()\n}\n\n// GetResults gets the next results. If results are present, results may be a *ResultReader, *StatementDescription, or\n// *PipelineSync. If an ErrorResponse is received from the server, results will be nil and err will be a *PgError. If no\n// results are available, results and err will both be nil.\nfunc (p *Pipeline) GetResults() (results any, err error) {\n\tif p.closed {\n\t\tif p.err != nil {\n\t\t\treturn nil, p.err\n\t\t}\n\t\treturn nil, errors.New(\"pipeline closed\")\n\t}\n\n\treturn p.getResults()\n}\n\nfunc (p *Pipeline) getResults() (results any, err error) {\n\tif !p.conn.resultReader.closed {\n\t\t_, err := p.conn.resultReader.Close()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tcurrentRequestType := p.state.ExtractFrontRequestType()\n\tswitch currentRequestType {\n\tcase pipelineNil:\n\t\treturn nil, nil\n\tcase pipelinePrepare:\n\t\treturn p.getResultsPrepare()\n\tcase pipelineQueryParams:\n\t\treturn p.getResultsQueryParams()\n\tcase pipelineQueryPrepared:\n\t\treturn p.getResultsQueryPrepared()\n\tcase pipelineQueryStatement:\n\t\treturn p.getResultsQueryStatement()\n\tcase pipelineDeallocate:\n\t\treturn p.getResultsDeallocate()\n\tcase pipelineSyncRequest:\n\t\treturn p.getResultsSync()\n\tcase pipelineFlushRequest:\n\t\treturn nil, errors.New(\"BUG: pipelineFlushRequest should not be in request queue\")\n\tdefault:\n\t\treturn nil, errors.New(\"BUG: unknown pipeline request type\")\n\t}\n}\n\nfunc (p *Pipeline) getResultsPrepare() (*StatementDescription, error) {\n\terr := p.receiveParseComplete(\"Prepare\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpsd := &StatementDescription{}\n\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.ParameterDescription:\n\t\tpsd.ParamOIDs = make([]uint32, len(msg.ParameterOIDs))\n\t\tcopy(psd.ParamOIDs, msg.ParameterOIDs)\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(\"Prepare ParameterDescription\", msg)\n\t}\n\n\tmsg, err = p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.RowDescription:\n\t\tpsd.Fields = make([]FieldDescription, len(msg.Fields))\n\t\tconvertRowDescription(psd.Fields, msg)\n\t\treturn psd, nil\n\n\t// NoData is returned instead of RowDescription when there is no expected result. e.g. An INSERT without a RETURNING\n\t// clause.\n\tcase *pgproto3.NoData:\n\t\treturn psd, nil\n\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(\"Prepare RowDescription\", msg)\n\t}\n}\n\nfunc (p *Pipeline) getResultsQueryParams() (*ResultReader, error) {\n\terr := p.receiveParseComplete(\"QueryParams\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = p.receiveBindComplete(\"QueryParams\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.receiveDescribedResultReader(\"QueryParams\")\n}\n\nfunc (p *Pipeline) getResultsQueryPrepared() (*ResultReader, error) {\n\terr := p.receiveBindComplete(\"QueryPrepared\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn p.receiveDescribedResultReader(\"QueryPrepared\")\n}\n\nfunc (p *Pipeline) getResultsQueryStatement() (*ResultReader, error) {\n\terr := p.receiveBindComplete(\"QueryStatement\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsd, resultFormats := p.state.ExtractFrontStatementData()\n\tif sd == nil {\n\t\treturn nil, errors.New(\"BUG: missing statement description or result formats for QueryStatement\")\n\t}\n\tsdFields := sd.Fields\n\tfieldDescriptions := p.conn.getFieldDescriptionSlice(len(sdFields))\n\terr = combineFieldDescriptionsAndResultFormats(fieldDescriptions, sdFields, resultFormats)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.DataRow:\n\t\trr := ResultReader{\n\t\t\tpgConn:            p.conn,\n\t\t\tpipeline:          p,\n\t\t\tctx:               p.ctx,\n\t\t\tfieldDescriptions: fieldDescriptions,\n\t\t}\n\t\trr.preloadRowValues(msg.Values)\n\t\tp.conn.resultReader = rr\n\t\treturn &p.conn.resultReader, nil\n\tcase *pgproto3.CommandComplete:\n\t\tp.conn.resultReader = ResultReader{\n\t\t\tcommandTag:        p.conn.makeCommandTag(msg.CommandTag),\n\t\t\tcommandConcluded:  true,\n\t\t\tclosed:            true,\n\t\t\tfieldDescriptions: fieldDescriptions,\n\t\t}\n\t\treturn &p.conn.resultReader, nil\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\tp.conn.resultReader.closed = true\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(\"QueryStatement\", msg)\n\t}\n}\n\nfunc (p *Pipeline) getResultsDeallocate() (*CloseComplete, error) {\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.CloseComplete:\n\t\treturn &CloseComplete{}, nil\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\tp.conn.resultReader.closed = true\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(\"Deallocate\", msg)\n\t}\n}\n\nfunc (p *Pipeline) getResultsSync() (*PipelineSync, error) {\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.ReadyForQuery:\n\t\tp.state.HandleReadyForQuery()\n\t\treturn &PipelineSync{}, nil\n\tcase *pgproto3.ErrorResponse:\n\t\t// Error message that is received while expecting a Sync message still consumes the expected Sync. Put it back.\n\t\tp.state.requestEventQueue.PushFront(pipelineRequestEvent{RequestType: pipelineSyncRequest, WasSentToServer: true, BeforeFlushOrSync: true})\n\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\tp.conn.resultReader.closed = true\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(\"Sync\", msg)\n\t}\n}\n\nfunc (p *Pipeline) receiveParseComplete(errStr string) error {\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.ParseComplete:\n\t\treturn nil\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\treturn pgErr\n\tdefault:\n\t\treturn p.handleUnexpectedMessage(fmt.Sprintf(\"%s Parse\", errStr), msg)\n\t}\n}\n\nfunc (p *Pipeline) receiveBindComplete(errStr string) error {\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.BindComplete:\n\t\treturn nil\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\treturn pgErr\n\tdefault:\n\t\treturn p.handleUnexpectedMessage(fmt.Sprintf(\"%s Bind\", errStr), msg)\n\t}\n}\n\nfunc (p *Pipeline) receiveDescribedResultReader(errStr string) (*ResultReader, error) {\n\tmsg, err := p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.RowDescription:\n\t\tp.conn.resultReader = ResultReader{\n\t\t\tpgConn:            p.conn,\n\t\t\tpipeline:          p,\n\t\t\tctx:               p.ctx,\n\t\t\tfieldDescriptions: p.conn.getFieldDescriptionSlice(len(msg.Fields)),\n\t\t}\n\t\tconvertRowDescription(p.conn.resultReader.fieldDescriptions, msg)\n\t\treturn &p.conn.resultReader, nil\n\tcase *pgproto3.NoData:\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\tp.conn.resultReader.closed = true\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(fmt.Sprintf(\"%s RowDescription or NoData\", errStr), msg)\n\t}\n\n\tmsg, err = p.receiveMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch msg := msg.(type) {\n\tcase *pgproto3.CommandComplete:\n\t\tp.conn.resultReader = ResultReader{\n\t\t\tcommandTag:       p.conn.makeCommandTag(msg.CommandTag),\n\t\t\tcommandConcluded: true,\n\t\t\tclosed:           true,\n\t\t}\n\t\treturn &p.conn.resultReader, nil\n\tcase *pgproto3.ErrorResponse:\n\t\tpgErr := ErrorResponseToPgError(msg)\n\t\tp.state.HandleError(pgErr)\n\t\tp.conn.resultReader.closed = true\n\t\treturn nil, pgErr\n\tdefault:\n\t\treturn nil, p.handleUnexpectedMessage(fmt.Sprintf(\"%s CommandComplete\", errStr), msg)\n\t}\n}\n\nfunc (p *Pipeline) receiveMessage() (pgproto3.BackendMessage, error) {\n\tfor {\n\t\tmsg, err := p.conn.receiveMessage()\n\t\tif err != nil {\n\t\t\tp.err = err\n\t\t\tp.conn.asyncClose()\n\t\t\treturn nil, normalizeTimeoutError(p.ctx, err)\n\t\t}\n\n\t\tswitch msg := msg.(type) {\n\t\tcase *pgproto3.ParameterStatus, *pgproto3.NoticeResponse, *pgproto3.NotificationResponse:\n\t\t\t// Filter these message types out in pipeline mode. The normal processing is handled by PgConn.receiveMessage.\n\t\tdefault:\n\t\t\treturn msg, nil\n\t\t}\n\t}\n}\n\nfunc (p *Pipeline) handleUnexpectedMessage(errStr string, msg pgproto3.BackendMessage) error {\n\tp.err = fmt.Errorf(\"pipeline: %s: received unexpected message type %T\", errStr, msg)\n\tp.conn.asyncClose()\n\treturn p.err\n}\n\n// Close closes the pipeline and returns the connection to normal mode.\nfunc (p *Pipeline) Close() error {\n\tif p.closed {\n\t\treturn p.err\n\t}\n\n\tp.closed = true\n\n\tif p.state.PendingSync() {\n\t\tp.conn.asyncClose()\n\t\tp.err = errors.New(\"pipeline has unsynced requests\")\n\t\tp.conn.contextWatcher.Unwatch()\n\t\tp.conn.unlock()\n\n\t\treturn p.err\n\t}\n\n\tfor p.state.ExpectedReadyForQuery() > 0 {\n\t\tresults, err := p.getResults()\n\t\tif err != nil {\n\t\t\tp.err = err\n\t\t\tvar pgErr *PgError\n\t\t\tif !errors.As(err, &pgErr) {\n\t\t\t\tp.conn.asyncClose()\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else if results == nil {\n\t\t\t// getResults returns (nil, nil) when the request queue is exhausted but\n\t\t\t// ExpectedReadyForQuery is still > 0. This can happen when FATAL errors consume\n\t\t\t// queued request slots without the server ever sending ReadyForQuery.\n\t\t\tp.conn.asyncClose()\n\t\t\tif p.err == nil {\n\t\t\t\tp.err = errors.New(\"pipeline: no more results but expected ReadyForQuery\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tp.conn.contextWatcher.Unwatch()\n\tp.conn.unlock()\n\n\treturn p.err\n}\n\n// DeadlineContextWatcherHandler handles canceled contexts by setting a deadline on a net.Conn.\ntype DeadlineContextWatcherHandler struct {\n\tConn net.Conn\n\n\t// DeadlineDelay is the delay to set on the deadline set on net.Conn when the context is canceled.\n\tDeadlineDelay time.Duration\n}\n\nfunc (h *DeadlineContextWatcherHandler) HandleCancel(ctx context.Context) {\n\th.Conn.SetDeadline(time.Now().Add(h.DeadlineDelay))\n}\n\nfunc (h *DeadlineContextWatcherHandler) HandleUnwatchAfterCancel() {\n\th.Conn.SetDeadline(time.Time{})\n}\n\n// CancelRequestContextWatcherHandler handles canceled contexts by sending a cancel request to the server. It also sets\n// a deadline on a net.Conn as a fallback.\ntype CancelRequestContextWatcherHandler struct {\n\tConn *PgConn\n\n\t// CancelRequestDelay is the delay before sending the cancel request to the server.\n\tCancelRequestDelay time.Duration\n\n\t// DeadlineDelay is the delay to set on the deadline set on net.Conn when the context is canceled.\n\tDeadlineDelay time.Duration\n\n\tcancelFinishedChan             chan struct{}\n\thandleUnwatchAfterCancelCalled func()\n}\n\nfunc (h *CancelRequestContextWatcherHandler) HandleCancel(context.Context) {\n\th.cancelFinishedChan = make(chan struct{})\n\tvar handleUnwatchedAfterCancelCalledCtx context.Context\n\thandleUnwatchedAfterCancelCalledCtx, h.handleUnwatchAfterCancelCalled = context.WithCancel(context.Background())\n\n\tdeadline := time.Now().Add(h.DeadlineDelay)\n\th.Conn.conn.SetDeadline(deadline)\n\n\tgo func() {\n\t\tdefer close(h.cancelFinishedChan)\n\n\t\tselect {\n\t\tcase <-handleUnwatchedAfterCancelCalledCtx.Done():\n\t\t\treturn\n\t\tcase <-time.After(h.CancelRequestDelay):\n\t\t}\n\n\t\tcancelRequestCtx, cancel := context.WithDeadline(handleUnwatchedAfterCancelCalledCtx, deadline)\n\t\tdefer cancel()\n\t\th.Conn.CancelRequest(cancelRequestCtx)\n\n\t\t// CancelRequest is inherently racy. Even though the cancel request has been received by the server at this point,\n\t\t// it hasn't necessarily been delivered to the other connection. If we immediately return and the connection is\n\t\t// immediately used then it is possible the CancelRequest will actually cancel our next query. The\n\t\t// TestCancelRequestContextWatcherHandler Stress test can produce this error without the sleep below. The sleep time\n\t\t// is arbitrary, but should be sufficient to prevent this error case.\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}()\n}\n\nfunc (h *CancelRequestContextWatcherHandler) HandleUnwatchAfterCancel() {\n\th.handleUnwatchAfterCancelCalled()\n\t<-h.cancelFinishedChan\n\n\th.Conn.conn.SetDeadline(time.Time{})\n}\n\nfunc combineFieldDescriptionsAndResultFormats(outputFields, inputFields []FieldDescription, resultFormats []int16) error {\n\tswitch {\n\tcase len(resultFormats) == 0:\n\t\t// No format codes provided means text format for all columns.\n\t\tfor i := range inputFields {\n\t\t\toutputFields[i] = inputFields[i]\n\t\t\toutputFields[i].Format = pgtype.TextFormatCode\n\t\t}\n\tcase len(resultFormats) == 1:\n\t\t// Single format code applies to all columns.\n\t\tformat := resultFormats[0]\n\t\tfor i := range inputFields {\n\t\t\toutputFields[i] = inputFields[i]\n\t\t\toutputFields[i].Format = format\n\t\t}\n\tcase len(resultFormats) == len(inputFields):\n\t\t// One format code per column.\n\t\tfor i := range inputFields {\n\t\t\toutputFields[i] = inputFields[i]\n\t\t\toutputFields[i].Format = resultFormats[i]\n\t\t}\n\tdefault:\n\t\t// This should not occur if Bind validation is correct, but handle gracefully\n\t\treturn fmt.Errorf(\"result format codes length %d does not match field count %d\", len(resultFormats), len(inputFields))\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pgconn/pgconn_private_test.go",
    "content": "package pgconn\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCommandTag(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tcommandTag   CommandTag\n\t\trowsAffected int64\n\t\tisInsert     bool\n\t\tisUpdate     bool\n\t\tisDelete     bool\n\t\tisSelect     bool\n\t}{\n\t\t{commandTag: CommandTag{s: \"INSERT 0 5\"}, rowsAffected: 5, isInsert: true},\n\t\t{commandTag: CommandTag{s: \"UPDATE 0\"}, rowsAffected: 0, isUpdate: true},\n\t\t{commandTag: CommandTag{s: \"UPDATE 1\"}, rowsAffected: 1, isUpdate: true},\n\t\t{commandTag: CommandTag{s: \"DELETE 0\"}, rowsAffected: 0, isDelete: true},\n\t\t{commandTag: CommandTag{s: \"DELETE 1\"}, rowsAffected: 1, isDelete: true},\n\t\t{commandTag: CommandTag{s: \"DELETE 1234567890\"}, rowsAffected: 1234567890, isDelete: true},\n\t\t{commandTag: CommandTag{s: \"SELECT 1\"}, rowsAffected: 1, isSelect: true},\n\t\t{commandTag: CommandTag{s: \"SELECT 99999999999\"}, rowsAffected: 99999999999, isSelect: true},\n\t\t{commandTag: CommandTag{s: \"CREATE TABLE\"}, rowsAffected: 0},\n\t\t{commandTag: CommandTag{s: \"ALTER TABLE\"}, rowsAffected: 0},\n\t\t{commandTag: CommandTag{s: \"DROP TABLE\"}, rowsAffected: 0},\n\t}\n\n\tfor i, tt := range tests {\n\t\tct := tt.commandTag\n\t\tassert.Equalf(t, tt.rowsAffected, ct.RowsAffected(), \"%d. %v\", i, tt.commandTag)\n\t\tassert.Equalf(t, tt.isInsert, ct.Insert(), \"%d. %v\", i, tt.commandTag)\n\t\tassert.Equalf(t, tt.isUpdate, ct.Update(), \"%d. %v\", i, tt.commandTag)\n\t\tassert.Equalf(t, tt.isDelete, ct.Delete(), \"%d. %v\", i, tt.commandTag)\n\t\tassert.Equalf(t, tt.isSelect, ct.Select(), \"%d. %v\", i, tt.commandTag)\n\t}\n}\n"
  },
  {
    "path": "pgconn/pgconn_stress_test.go",
    "content": "package pgconn_test\n\nimport (\n\t\"context\"\n\t\"math/rand/v2\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnStress(t *testing.T) {\n\tpgConn, err := pgconn.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tactionCount := 10_000\n\tif s := os.Getenv(\"PGX_TEST_STRESS_FACTOR\"); s != \"\" {\n\t\tstressFactor, err := strconv.ParseInt(s, 10, 64)\n\t\trequire.Nil(t, err, \"Failed to parse PGX_TEST_STRESS_FACTOR\")\n\t\tactionCount *= int(stressFactor)\n\t}\n\n\tsetupStressDB(t, pgConn)\n\n\tactions := []struct {\n\t\tname string\n\t\tfn   func(*pgconn.PgConn) error\n\t}{\n\t\t{\"Exec Select\", stressExecSelect},\n\t\t{\"ExecParams Select\", stressExecParamsSelect},\n\t\t{\"Batch\", stressBatch},\n\t}\n\n\tfor i := 0; i < actionCount; i++ {\n\t\taction := actions[rand.IntN(len(actions))]\n\t\terr := action.fn(pgConn)\n\t\trequire.Nilf(t, err, \"%d: %s\", i, action.name)\n\t}\n\n\t// Each call with a context starts a goroutine. Ensure they are cleaned up when context is not canceled.\n\tnumGoroutine := runtime.NumGoroutine()\n\trequire.Truef(t, numGoroutine < 1000, \"goroutines appear to be orphaned: %d in process\", numGoroutine)\n}\n\nfunc setupStressDB(t *testing.T, pgConn *pgconn.PgConn) {\n\t_, err := pgConn.Exec(context.Background(), `\n\t\tcreate temporary table widgets(\n\t\t\tid serial primary key,\n\t\t\tname varchar not null,\n\t\t\tdescription text,\n\t\t\tcreation_time timestamptz default now()\n\t\t);\n\n\t\tinsert into widgets(name, description) values\n\t\t\t('Foo', 'bar'),\n\t\t\t('baz', 'Something really long Something really long Something really long Something really long Something really long'),\n\t\t\t('a', 'b')`).ReadAll()\n\trequire.NoError(t, err)\n}\n\nfunc stressExecSelect(pgConn *pgconn.PgConn) error {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\t_, err := pgConn.Exec(ctx, \"select * from widgets\").ReadAll()\n\treturn err\n}\n\nfunc stressExecParamsSelect(pgConn *pgconn.PgConn) error {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\tresult := pgConn.ExecParams(ctx, \"select * from widgets where id < $1\", [][]byte{[]byte(\"10\")}, nil, nil, nil).Read()\n\treturn result.Err\n}\n\nfunc stressBatch(pgConn *pgconn.PgConn) error {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tbatch := &pgconn.Batch{}\n\n\tbatch.ExecParams(\"select * from widgets\", nil, nil, nil, nil)\n\tbatch.ExecParams(\"select * from widgets where id < $1\", [][]byte{[]byte(\"10\")}, nil, nil, nil)\n\t_, err := pgConn.ExecBatch(ctx, batch).ReadAll()\n\treturn err\n}\n"
  },
  {
    "path": "pgconn/pgconn_test.go",
    "content": "package pgconn_test\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"math\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n\t\"github.com/jackc/pgx/v5/internal/pgmock\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgconn/ctxwatch\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\nconst (\n\tpgbouncerConnStringEnvVar = \"PGX_TEST_PGBOUNCER_CONN_STRING\"\n\t// runOAuthTestEnvVar has to be set to \"true\" to run OAuth tests\n\trunOAuthTestEnvVar = \"PGX_TEST_OAUTH\"\n)\n\nfunc TestConnect(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tenv  string\n\t}{\n\t\t{\"Unix socket\", \"PGX_TEST_UNIX_SOCKET_CONN_STRING\"},\n\t\t{\"TCP\", \"PGX_TEST_TCP_CONN_STRING\"},\n\t\t{\"Plain password\", \"PGX_TEST_PLAIN_PASSWORD_CONN_STRING\"},\n\t\t{\"MD5 password\", \"PGX_TEST_MD5_PASSWORD_CONN_STRING\"},\n\t\t{\"SCRAM password\", \"PGX_TEST_SCRAM_PASSWORD_CONN_STRING\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tconnString := os.Getenv(tt.env)\n\t\t\tif connString == \"\" {\n\t\t\t\tt.Skipf(\"Skipping due to missing environment variable %v\", tt.env)\n\t\t\t}\n\n\t\t\tconn, err := pgconn.Connect(ctx, connString)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tcloseConn(t, conn)\n\t\t})\n\t}\n}\n\nfunc TestConnectWithOptions(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tenv  string\n\t}{\n\t\t{\"Unix socket\", \"PGX_TEST_UNIX_SOCKET_CONN_STRING\"},\n\t\t{\"TCP\", \"PGX_TEST_TCP_CONN_STRING\"},\n\t\t{\"Plain password\", \"PGX_TEST_PLAIN_PASSWORD_CONN_STRING\"},\n\t\t{\"MD5 password\", \"PGX_TEST_MD5_PASSWORD_CONN_STRING\"},\n\t\t{\"SCRAM password\", \"PGX_TEST_SCRAM_PASSWORD_CONN_STRING\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tconnString := os.Getenv(tt.env)\n\t\t\tif connString == \"\" {\n\t\t\t\tt.Skipf(\"Skipping due to missing environment variable %v\", tt.env)\n\t\t\t}\n\t\t\tvar sslOptions pgconn.ParseConfigOptions\n\t\t\tsslOptions.GetSSLPassword = GetSSLPassword\n\t\t\tconn, err := pgconn.ConnectWithOptions(ctx, connString, sslOptions)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tcloseConn(t, conn)\n\t\t})\n\t}\n}\n\n// TestConnectTLS is separate from other connect tests because it has an additional test to ensure it really is a secure\n// connection.\nfunc TestConnectTLS(t *testing.T) {\n\tt.Parallel()\n\n\tsetup := func(t *testing.T, connString string) (*pgconn.PgConn, context.Context) {\n\t\tt.Helper()\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\tt.Cleanup(cancel)\n\n\t\tconn, err := pgconn.Connect(ctx, connString)\n\t\trequire.NoError(t, err)\n\t\tt.Cleanup(func() { closeConn(t, conn) })\n\n\t\treturn conn, ctx\n\t}\n\n\tt.Run(\"WithChannelBinding\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconnString := os.Getenv(\"PGX_TEST_SCRAM_PLUS_CONN_STRING\")\n\t\tif connString == \"\" {\n\t\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_SCRAM_PLUS_CONN_STRING\")\n\t\t}\n\n\t\tconn, ctx := setup(t, connString)\n\n\t\tresult := conn.ExecParams(ctx, `select ssl from pg_stat_ssl where pg_backend_pid() = pid;`, nil, nil, nil, nil).Read()\n\t\trequire.NoError(t, result.Err)\n\t\trequire.Len(t, result.Rows, 1)\n\t\trequire.Len(t, result.Rows[0], 1)\n\t\trequire.Equalf(t, \"t\", string(result.Rows[0][0]), \"not a TLS connection\")\n\t})\n\n\tt.Run(\"WithoutChannelBinding\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconnString := os.Getenv(\"PGX_TEST_TLS_CONN_STRING\")\n\t\tif connString == \"\" {\n\t\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TLS_CONN_STRING\")\n\t\t}\n\n\t\tconn, ctx := setup(t, connString)\n\n\t\tresult := conn.ExecParams(ctx, `select ssl from pg_stat_ssl where pg_backend_pid() = pid;`, nil, nil, nil, nil).Read()\n\t\trequire.NoError(t, result.Err)\n\t\trequire.Len(t, result.Rows, 1)\n\t\trequire.Len(t, result.Rows[0], 1)\n\t\trequire.Equalf(t, \"t\", string(result.Rows[0][0]), \"not a TLS connection\")\n\t})\n}\n\nfunc TestConnectChannelBinding(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"RequireFailsWithoutTLS\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tscript := &pgmock.Script{\n\t\t\tSteps: []pgmock.Step{\n\t\t\t\tpgmock.ExpectAnyMessage(&pgproto3.StartupMessage{ProtocolVersion: pgproto3.ProtocolVersion30, Parameters: map[string]string{}}),\n\t\t\t\tpgmock.SendMessage(&pgproto3.AuthenticationSASL{AuthMechanisms: []string{\"SCRAM-SHA-256\", \"SCRAM-SHA-256-PLUS\"}}),\n\t\t\t\tpgmock.WaitForClose(),\n\t\t\t},\n\t\t}\n\n\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\t\trequire.NoError(t, err)\n\t\tdefer ln.Close()\n\n\t\tserverErrChan := make(chan error, 1)\n\t\tgo func() {\n\t\t\tdefer close(serverErrChan)\n\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer conn.Close()\n\n\t\t\terr = conn.SetDeadline(time.Now().Add(time.Second * 5))\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terr = script.Run(pgproto3.NewBackend(conn, conn))\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}()\n\n\t\thost, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\t\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s channel_binding=require\", host, port)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\t\tdefer cancel()\n\n\t\t_, err = pgconn.Connect(ctx, connStr)\n\t\trequire.ErrorContains(t, err, \"channel binding required but channel binding data is not available\")\n\t})\n\n\tt.Run(\"RequireFailsWithoutServerPlus\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tscript := &pgmock.Script{\n\t\t\tSteps: []pgmock.Step{\n\t\t\t\tpgmock.ExpectAnyMessage(&pgproto3.StartupMessage{ProtocolVersion: pgproto3.ProtocolVersion30, Parameters: map[string]string{}}),\n\t\t\t\tpgmock.SendMessage(&pgproto3.AuthenticationSASL{AuthMechanisms: []string{\"SCRAM-SHA-256\"}}),\n\t\t\t\tpgmock.WaitForClose(),\n\t\t\t},\n\t\t}\n\n\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\t\trequire.NoError(t, err)\n\t\tdefer ln.Close()\n\n\t\tserverErrChan := make(chan error, 1)\n\t\tgo func() {\n\t\t\tdefer close(serverErrChan)\n\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer conn.Close()\n\n\t\t\terr = conn.SetDeadline(time.Now().Add(time.Second * 5))\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terr = script.Run(pgproto3.NewBackend(conn, conn))\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}()\n\n\t\thost, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\t\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s channel_binding=require\", host, port)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\t\tdefer cancel()\n\n\t\t_, err = pgconn.Connect(ctx, connStr)\n\t\trequire.ErrorContains(t, err, \"channel binding required but server does not support SCRAM-SHA-256-PLUS\")\n\t})\n}\n\n// TestConnectOAuth is separate from other connect tests because it specifically\n// needs a configured OAuthTokenProvider. Further it's only available in Postgres\n// 18+ and requires the dummy OAuth validator module installed.\nfunc TestConnectOAuth(t *testing.T) {\n\tif os.Getenv(runOAuthTestEnvVar) != \"true\" {\n\t\tt.Skipf(\"Skipping as '%s=true' is not set\", runOAuthTestEnvVar)\n\t}\n\n\tconfig, err := pgconn.ParseConfig(\"host=127.0.0.1 user=pgx_oauth dbname=pgx_test\")\n\trequire.NoError(t, err)\n\n\t// Configure OAuthTokenProvider for dummy validator.\n\t// The dummy validator accepts any token and maps it to the user equal to the\n\t// token string.\n\tconfig.OAuthTokenProvider = func(ctx context.Context) (string, error) {\n\t\treturn \"pgx_oauth\", nil\n\t}\n\n\tconn, err := pgconn.ConnectConfig(context.Background(), config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, conn)\n\n\tresult := conn.ExecParams(context.Background(), \"SELECT CURRENT_USER\", nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Len(t, result.Rows, 1)\n\trequire.Len(t, result.Rows[0], 1)\n\trequire.Equalf(t, \"pgx_oauth\", string(result.Rows[0][0]), \"not logged in as expected user.\")\n}\n\nfunc TestConnectOAuthError(t *testing.T) {\n\tif os.Getenv(runOAuthTestEnvVar) != \"true\" {\n\t\tt.Skipf(\"Skipping as '%s=true' is not set\", runOAuthTestEnvVar)\n\t}\n\n\tconfig, err := pgconn.ParseConfig(\"host=127.0.0.1 user=pgx_oauth dbname=pgx_test\")\n\trequire.NoError(t, err)\n\n\t// Configure OAuthTokenProvider for dummy validator.\n\t// The dummy validator accepts any token and maps it to the user equal to the\n\t// token string. In this case that token will be accepted but as there is no\n\t// user 'INVALID_TOKEN' the connection should fail.\n\tconfig.OAuthTokenProvider = func(ctx context.Context) (string, error) {\n\t\treturn \"INVALID_TOKEN\", nil\n\t}\n\n\t_, err = pgconn.ConnectConfig(context.Background(), config)\n\trequire.Error(t, err, \"connect should return error for invalid token\")\n}\n\nfunc TestConnectTLSPasswordProtectedClientCertWithSSLPassword(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconnString := os.Getenv(\"PGX_TEST_TLS_CLIENT_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TLS_CLIENT_CONN_STRING\")\n\t}\n\tif os.Getenv(\"PGX_SSL_PASSWORD\") == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_SSL_PASSWORD\")\n\t}\n\n\tconnString += \" sslpassword=\" + os.Getenv(\"PGX_SSL_PASSWORD\")\n\n\tconn, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\n\tresult := conn.ExecParams(ctx, `select ssl from pg_stat_ssl where pg_backend_pid() = pid;`, nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Len(t, result.Rows, 1)\n\trequire.Len(t, result.Rows[0], 1)\n\trequire.Equalf(t, \"t\", string(result.Rows[0][0]), \"not a TLS connection\")\n\n\tcloseConn(t, conn)\n}\n\nfunc TestConnectTLSPasswordProtectedClientCertWithGetSSLPasswordConfigOption(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconnString := os.Getenv(\"PGX_TEST_TLS_CLIENT_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TLS_CLIENT_CONN_STRING\")\n\t}\n\tif os.Getenv(\"PGX_SSL_PASSWORD\") == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_SSL_PASSWORD\")\n\t}\n\n\tvar sslOptions pgconn.ParseConfigOptions\n\tsslOptions.GetSSLPassword = GetSSLPassword\n\tconfig, err := pgconn.ParseConfigWithOptions(connString, sslOptions)\n\trequire.Nil(t, err)\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\n\tresult := conn.ExecParams(ctx, `select ssl from pg_stat_ssl where pg_backend_pid() = pid;`, nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Len(t, result.Rows, 1)\n\trequire.Len(t, result.Rows[0], 1)\n\trequire.Equalf(t, \"t\", string(result.Rows[0][0]), \"not a TLS connection\")\n\n\tcloseConn(t, conn)\n}\n\ntype pgmockWaitStep time.Duration\n\nfunc (s pgmockWaitStep) Step(*pgproto3.Backend) error {\n\ttime.Sleep(time.Duration(s))\n\treturn nil\n}\n\nfunc TestConnectTimeout(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname    string\n\t\tconnect func(connStr string) error\n\t}{\n\t\t{\n\t\t\tname: \"via context that times out\",\n\t\t\tconnect: func(connStr string) error {\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)\n\t\t\t\tdefer cancel()\n\t\t\t\t_, err := pgconn.Connect(ctx, connStr)\n\t\t\t\treturn err\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"via config ConnectTimeout\",\n\t\t\tconnect: func(connStr string) error {\n\t\t\t\tconf, err := pgconn.ParseConfig(connStr)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tconf.ConnectTimeout = time.Microsecond * 50\n\t\t\t\t_, err = pgconn.ConnectConfig(context.Background(), conf)\n\t\t\t\treturn err\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tscript := &pgmock.Script{\n\t\t\t\tSteps: []pgmock.Step{\n\t\t\t\t\tpgmock.ExpectAnyMessage(&pgproto3.StartupMessage{ProtocolVersion: pgproto3.ProtocolVersion30, Parameters: map[string]string{}}),\n\t\t\t\t\tpgmock.SendMessage(&pgproto3.AuthenticationOk{}),\n\t\t\t\t\tpgmockWaitStep(time.Millisecond * 500),\n\t\t\t\t\tpgmock.SendMessage(&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: []byte{0, 0, 0, 0}}),\n\t\t\t\t\tpgmock.SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}),\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer ln.Close()\n\n\t\t\tserverErrChan := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\tdefer close(serverErrChan)\n\n\t\t\t\tconn, err := ln.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer conn.Close()\n\n\t\t\t\terr = conn.SetDeadline(time.Now().Add(time.Millisecond * 450))\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\terr = script.Run(pgproto3.NewBackend(conn, conn))\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\thost, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\t\t\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s\", host, port)\n\t\t\ttooLate := time.Now().Add(time.Millisecond * 500)\n\n\t\t\terr = tt.connect(connStr)\n\t\t\trequire.True(t, pgconn.Timeout(err), err)\n\t\t\trequire.True(t, time.Now().Before(tooLate))\n\t\t})\n\t}\n}\n\nfunc TestConnectTimeoutStuckOnTLSHandshake(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname    string\n\t\tconnect func(connStr string) error\n\t}{\n\t\t{\n\t\t\tname: \"via context that times out\",\n\t\t\tconnect: func(connStr string) error {\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*10)\n\t\t\t\tdefer cancel()\n\t\t\t\t_, err := pgconn.Connect(ctx, connStr)\n\t\t\t\treturn err\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"via config ConnectTimeout\",\n\t\t\tconnect: func(connStr string) error {\n\t\t\t\tconf, err := pgconn.ParseConfig(connStr)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tconf.ConnectTimeout = time.Millisecond * 10\n\t\t\t\t_, err = pgconn.ConnectConfig(context.Background(), conf)\n\t\t\t\treturn err\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer ln.Close()\n\n\t\t\tserverErrChan := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\tconn, err := ln.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer conn.Close()\n\n\t\t\t\tvar buf []byte\n\t\t\t\t_, err = conn.Read(buf)\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Sleeping to hang the TLS handshake.\n\t\t\t\ttime.Sleep(time.Minute)\n\t\t\t}()\n\n\t\t\thost, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\t\t\tconnStr := fmt.Sprintf(\"host=%s port=%s\", host, port)\n\n\t\t\terrChan := make(chan error)\n\t\t\tgo func() {\n\t\t\t\terr := tt.connect(connStr)\n\t\t\t\terrChan <- err\n\t\t\t}()\n\n\t\t\tselect {\n\t\t\tcase err = <-errChan:\n\t\t\t\trequire.True(t, pgconn.Timeout(err), err)\n\t\t\tcase err = <-serverErrChan:\n\t\t\t\tt.Fatalf(\"server failed with error: %s\", err)\n\t\t\tcase <-time.After(time.Millisecond * 500):\n\t\t\t\tt.Fatal(\"exceeded connection timeout without erroring out\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectInvalidUser(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconnString := os.Getenv(\"PGX_TEST_TCP_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TCP_CONN_STRING\")\n\t}\n\n\tconfig, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tconfig.User = \"pgxinvalidusertest\"\n\n\t_, err = pgconn.ConnectConfig(ctx, config)\n\trequire.Error(t, err)\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, err, &pgErr)\n\tif pgErr.Code != \"28000\" && pgErr.Code != \"28P01\" {\n\t\tt.Fatalf(\"Expected to receive a PgError with code 28000 or 28P01, instead received: %v\", pgErr)\n\t}\n}\n\nfunc TestConnectWithConnectionRefused(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\t// Presumably nothing is listening on 127.0.0.1:1\n\tconn, err := pgconn.Connect(ctx, \"host=127.0.0.1 port=1\")\n\tif err == nil {\n\t\tconn.Close(ctx)\n\t\tt.Fatal(\"Expected error establishing connection to bad port\")\n\t}\n}\n\nfunc TestConnectCustomDialer(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tdialed := false\n\tconfig.DialFunc = func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\tdialed = true\n\t\treturn net.Dial(network, address)\n\t}\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\trequire.True(t, dialed)\n\tcloseConn(t, conn)\n}\n\nfunc TestConnectCustomLookup(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconnString := os.Getenv(\"PGX_TEST_TCP_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TCP_CONN_STRING\")\n\t}\n\n\tconfig, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tlooked := false\n\tconfig.LookupFunc = func(ctx context.Context, host string) (addrs []string, err error) {\n\t\tlooked = true\n\t\treturn net.LookupHost(host)\n\t}\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\trequire.True(t, looked)\n\tcloseConn(t, conn)\n}\n\nfunc TestConnectCustomLookupWithPort(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconnString := os.Getenv(\"PGX_TEST_TCP_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TCP_CONN_STRING\")\n\t}\n\n\tconfig, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\torigPort := config.Port\n\t// Change the config an invalid port so it will fail if used\n\tconfig.Port = 0\n\n\tlooked := false\n\tconfig.LookupFunc = func(ctx context.Context, host string) ([]string, error) {\n\t\tlooked = true\n\t\taddrs, err := net.LookupHost(host)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor i := range addrs {\n\t\t\taddrs[i] = net.JoinHostPort(addrs[i], strconv.FormatUint(uint64(origPort), 10))\n\t\t}\n\t\treturn addrs, nil\n\t}\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\trequire.True(t, looked)\n\tcloseConn(t, conn)\n}\n\nfunc TestConnectWithRuntimeParams(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.RuntimeParams = map[string]string{\n\t\t\"application_name\": \"pgxtest\",\n\t\t\"search_path\":      \"myschema\",\n\t}\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, conn)\n\n\tresult := conn.ExecParams(ctx, \"show application_name\", nil, nil, nil, nil).Read()\n\trequire.Nil(t, result.Err)\n\tassert.Equal(t, 1, len(result.Rows))\n\tassert.Equal(t, \"pgxtest\", string(result.Rows[0][0]))\n\n\tresult = conn.ExecParams(ctx, \"show search_path\", nil, nil, nil, nil).Read()\n\trequire.Nil(t, result.Err)\n\tassert.Equal(t, 1, len(result.Rows))\n\tassert.Equal(t, \"myschema\", string(result.Rows[0][0]))\n}\n\nfunc TestConnectWithFallback(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\t// Prepend current primary config to fallbacks\n\tconfig.Fallbacks = append([]*pgconn.FallbackConfig{\n\t\t{\n\t\t\tHost:      config.Host,\n\t\t\tPort:      config.Port,\n\t\t\tTLSConfig: config.TLSConfig,\n\t\t},\n\t}, config.Fallbacks...)\n\n\t// Make primary config bad\n\tconfig.Host = \"localhost\"\n\tconfig.Port = 1 // presumably nothing listening here\n\n\t// Prepend bad first fallback\n\tconfig.Fallbacks = append([]*pgconn.FallbackConfig{\n\t\t{\n\t\t\tHost:      \"localhost\",\n\t\t\tPort:      1,\n\t\t\tTLSConfig: config.TLSConfig,\n\t\t},\n\t}, config.Fallbacks...)\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tcloseConn(t, conn)\n}\n\nfunc TestConnectFailsWithResolveFailureAndFailedConnectionAttempts(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgconn.Connect(ctx, \"host=localhost,127.0.0.1,foo.invalid port=1,2,3 sslmode=disable\")\n\trequire.Error(t, err)\n\trequire.Nil(t, conn)\n\n\trequire.ErrorContains(t, err, \"lookup foo.invalid\")\n\t// Not testing the entire string as depending on IPv4 or IPv6 support localhost may resolve to 127.0.0.1 or ::1.\n\trequire.ErrorContains(t, err, \":1 (localhost): dial error:\")\n\trequire.ErrorContains(t, err, \":2 (127.0.0.1): dial error:\")\n}\n\ntype testConnWrapper struct {\n\tconn net.Conn\n}\n\nfunc (w *testConnWrapper) Read(b []byte) (n int, err error) {\n\treturn w.conn.Read(b)\n}\n\nfunc (w *testConnWrapper) Write(b []byte) (n int, err error) {\n\treturn w.conn.Write(b)\n}\n\nfunc (w *testConnWrapper) Close() error {\n\treturn w.conn.Close()\n}\n\nfunc (w *testConnWrapper) LocalAddr() net.Addr {\n\treturn w.conn.LocalAddr()\n}\n\nfunc (w *testConnWrapper) RemoteAddr() net.Addr {\n\treturn w.conn.RemoteAddr()\n}\n\nfunc (w *testConnWrapper) SetDeadline(t time.Time) error {\n\treturn w.conn.SetDeadline(t)\n}\n\nfunc (w *testConnWrapper) SetReadDeadline(t time.Time) error {\n\treturn w.conn.SetReadDeadline(t)\n}\n\nfunc (w *testConnWrapper) SetWriteDeadline(t time.Time) error {\n\treturn w.conn.SetWriteDeadline(t)\n}\n\nfunc TestConnectWithAfterNetConnect(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar wrappedConn net.Conn\n\tconfig.AfterNetConnect = func(ctx context.Context, config *pgconn.Config, conn net.Conn) (net.Conn, error) {\n\t\twrappedConn = &testConnWrapper{conn: conn}\n\t\treturn wrappedConn, nil\n\t}\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, wrappedConn, conn.Conn())\n\tcloseConn(t, conn)\n}\n\nfunc TestConnectWithValidateConnect(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tdialCount := 0\n\tconfig.DialFunc = func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\tdialCount++\n\t\treturn net.Dial(network, address)\n\t}\n\n\tacceptConnCount := 0\n\tconfig.ValidateConnect = func(ctx context.Context, conn *pgconn.PgConn) error {\n\t\tacceptConnCount++\n\t\tif acceptConnCount < 2 {\n\t\t\treturn errors.New(\"reject first conn\")\n\t\t}\n\t\treturn nil\n\t}\n\n\t// Append current primary config to fallbacks\n\tconfig.Fallbacks = append(config.Fallbacks, &pgconn.FallbackConfig{\n\t\tHost:      config.Host,\n\t\tPort:      config.Port,\n\t\tTLSConfig: config.TLSConfig,\n\t})\n\n\t// Repeat fallbacks\n\tconfig.Fallbacks = append(config.Fallbacks, config.Fallbacks...)\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tcloseConn(t, conn)\n\n\tassert.True(t, dialCount > 1)\n\tassert.True(t, acceptConnCount > 1)\n}\n\nfunc TestConnectWithValidateConnectTargetSessionAttrsReadWrite(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.ValidateConnect = pgconn.ValidateConnectTargetSessionAttrsReadWrite\n\tconfig.RuntimeParams[\"default_transaction_read_only\"] = \"on\"\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\tif !assert.NotNil(t, err) {\n\t\tconn.Close(ctx)\n\t}\n}\n\nfunc TestConnectWithAfterConnect(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.AfterConnect = func(ctx context.Context, conn *pgconn.PgConn) error {\n\t\t_, err := conn.Exec(ctx, \"set search_path to foobar;\").ReadAll()\n\t\treturn err\n\t}\n\n\tconn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\n\tresults, err := conn.Exec(ctx, \"show search_path;\").ReadAll()\n\trequire.NoError(t, err)\n\tdefer closeConn(t, conn)\n\n\tassert.Equal(t, []byte(\"foobar\"), results[0].Rows[0][0])\n}\n\nfunc TestConnectConfigRequiresConfigFromParseConfig(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig := &pgconn.Config{}\n\n\trequire.PanicsWithValue(t, \"config must be created by ParseConfig\", func() { pgconn.ConnectConfig(ctx, config) })\n}\n\nfunc TestConnPrepareSyntaxError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", \"SYNTAX ERROR\", nil)\n\trequire.Nil(t, psd)\n\trequire.NotNil(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnPrepareContextPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tcancel()\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", \"select 1\", nil)\n\tassert.Nil(t, psd)\n\tassert.Error(t, err)\n\tassert.True(t, errors.Is(err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(err))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnDeallocate(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"select 1\", nil)\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil).Close()\n\trequire.NoError(t, err)\n\n\terr = pgConn.Deallocate(ctx, \"ps1\")\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil).Close()\n\trequire.Error(t, err)\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, err, &pgErr)\n\trequire.Equal(t, \"26000\", pgErr.Code)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnDeallocateSucceedsInAbortedTransaction(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\terr = pgConn.Exec(ctx, \"begin\").Close()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"select 1\", nil)\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil).Close()\n\trequire.NoError(t, err)\n\n\terr = pgConn.Exec(ctx, \"select 1/0\").Close() // break transaction with divide by 0 error\n\trequire.Error(t, err)\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, err, &pgErr)\n\trequire.Equal(t, \"22012\", pgErr.Code)\n\n\terr = pgConn.Deallocate(ctx, \"ps1\")\n\trequire.NoError(t, err)\n\n\terr = pgConn.Exec(ctx, \"rollback\").Close()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil).Close()\n\trequire.Error(t, err)\n\trequire.ErrorAs(t, err, &pgErr)\n\trequire.Equal(t, \"26000\", pgErr.Code)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnDeallocateNonExistentStatementSucceeds(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\terr = pgConn.Deallocate(ctx, \"ps1\")\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExec(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresults, err := pgConn.Exec(ctx, \"select 'Hello, world'\").ReadAll()\n\tassert.NoError(t, err)\n\n\tassert.Len(t, results, 1)\n\tassert.Nil(t, results[0].Err)\n\tassert.Equal(t, \"SELECT 1\", results[0].CommandTag.String())\n\tassert.Len(t, results[0].Rows, 1)\n\tassert.Equal(t, \"Hello, world\", string(results[0].Rows[0][0]))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecEmpty(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresults, err := pgConn.Exec(ctx, \";\").ReadAll()\n\trequire.NoError(t, err)\n\trequire.Len(t, results, 1)\n\trequire.Nil(t, results[0].Err)\n\trequire.Equal(t, \"\", results[0].CommandTag.String())\n\trequire.Nil(t, results[0].FieldDescriptions)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecMultipleQueries(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresults, err := pgConn.Exec(ctx, \"select 'Hello, world'; select 1\").ReadAll()\n\tassert.NoError(t, err)\n\n\tassert.Len(t, results, 2)\n\n\tassert.Nil(t, results[0].Err)\n\tassert.Equal(t, \"SELECT 1\", results[0].CommandTag.String())\n\tassert.Len(t, results[0].Rows, 1)\n\tassert.Equal(t, \"Hello, world\", string(results[0].Rows[0][0]))\n\n\tassert.Nil(t, results[1].Err)\n\tassert.Equal(t, \"SELECT 1\", results[1].CommandTag.String())\n\tassert.Len(t, results[1].Rows, 1)\n\tassert.Equal(t, \"1\", string(results[1].Rows[0][0]))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecMultipleQueriesEagerFieldDescriptions(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tmrr := pgConn.Exec(ctx, \"select 'Hello, world' as msg; select 1 as num\")\n\n\trequire.True(t, mrr.NextResult())\n\trequire.Len(t, mrr.ResultReader().FieldDescriptions(), 1)\n\tassert.Equal(t, \"msg\", mrr.ResultReader().FieldDescriptions()[0].Name)\n\t_, err = mrr.ResultReader().Close()\n\trequire.NoError(t, err)\n\n\trequire.True(t, mrr.NextResult())\n\trequire.Len(t, mrr.ResultReader().FieldDescriptions(), 1)\n\tassert.Equal(t, \"num\", mrr.ResultReader().FieldDescriptions()[0].Name)\n\t_, err = mrr.ResultReader().Close()\n\trequire.NoError(t, err)\n\n\trequire.False(t, mrr.NextResult())\n\n\trequire.NoError(t, mrr.Close())\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecMultipleQueriesError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresults, err := pgConn.Exec(ctx, \"select 1; select 1/0; select 1\").ReadAll()\n\trequire.NotNil(t, err)\n\tif pgErr, ok := err.(*pgconn.PgError); ok {\n\t\tassert.Equal(t, \"22012\", pgErr.Code)\n\t} else {\n\t\tt.Errorf(\"unexpected error: %v\", err)\n\t}\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// CockroachDB starts the second query result set and then sends the divide by zero error.\n\t\trequire.Len(t, results, 2)\n\t\tassert.Len(t, results[0].Rows, 1)\n\t\tassert.Equal(t, \"1\", string(results[0].Rows[0][0]))\n\t\tassert.Len(t, results[1].Rows, 0)\n\t} else {\n\t\t// PostgreSQL sends the divide by zero and never sends the second query result set.\n\t\trequire.Len(t, results, 1)\n\t\tassert.Len(t, results[0].Rows, 1)\n\t\tassert.Equal(t, \"1\", string(results[0].Rows[0][0]))\n\t}\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecDeferredError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\t}\n\n\tsetupSQL := `create temporary table t (\n\t\tid text primary key,\n\t\tn int not null,\n\t\tunique (n) deferrable initially deferred\n\t);\n\n\tinsert into t (id, n) values ('a', 1), ('b', 2), ('c', 3);`\n\n\t_, err = pgConn.Exec(ctx, setupSQL).ReadAll()\n\tassert.NoError(t, err)\n\n\t_, err = pgConn.Exec(ctx, `update t set n=n+1 where id='b' returning *`).ReadAll()\n\trequire.NotNil(t, err)\n\n\tvar pgErr *pgconn.PgError\n\trequire.True(t, errors.As(err, &pgErr))\n\trequire.Equal(t, \"23505\", pgErr.Code)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecContextCanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\tcancel()\n\n\tctx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)\n\tdefer cancel()\n\tmultiResult := pgConn.Exec(ctx, \"select 'Hello, world', pg_sleep(1)\")\n\n\tfor multiResult.NextResult() {\n\t}\n\terr = multiResult.Close()\n\tassert.True(t, pgconn.Timeout(err))\n\tassert.ErrorIs(t, err, context.DeadlineExceeded)\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\nfunc TestConnExecContextPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tcancel()\n\t_, err = pgConn.Exec(ctx, \"select 'Hello, world'\").ReadAll()\n\tassert.Error(t, err)\n\tassert.True(t, errors.Is(err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(err))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecParams(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresult := pgConn.ExecParams(ctx, \"select $1::text as msg\", [][]byte{[]byte(\"Hello, world\")}, nil, nil, nil)\n\trequire.Len(t, result.FieldDescriptions(), 1)\n\tassert.Equal(t, \"msg\", result.FieldDescriptions()[0].Name)\n\n\trowCount := 0\n\tfor result.NextRow() {\n\t\trowCount += 1\n\t\tassert.Equal(t, \"Hello, world\", string(result.Values()[0]))\n\t}\n\tassert.Equal(t, 1, rowCount)\n\tcommandTag, err := result.Close()\n\tassert.Equal(t, \"SELECT 1\", commandTag.String())\n\tassert.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecParamsDeferredError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\t}\n\n\tsetupSQL := `create temporary table t (\n\t\tid text primary key,\n\t\tn int not null,\n\t\tunique (n) deferrable initially deferred\n\t);\n\n\tinsert into t (id, n) values ('a', 1), ('b', 2), ('c', 3);`\n\n\t_, err = pgConn.Exec(ctx, setupSQL).ReadAll()\n\tassert.NoError(t, err)\n\n\tresult := pgConn.ExecParams(ctx, `update t set n=n+1 where id='b' returning *`, nil, nil, nil, nil).Read()\n\trequire.NotNil(t, result.Err)\n\tvar pgErr *pgconn.PgError\n\trequire.True(t, errors.As(result.Err, &pgErr))\n\trequire.Equal(t, \"23505\", pgErr.Code)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecParamsMaxNumberOfParams(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tparamCount := math.MaxUint16\n\tparams := make([]string, 0, paramCount)\n\targs := make([][]byte, 0, paramCount)\n\tfor i := range paramCount {\n\t\tparams = append(params, fmt.Sprintf(\"($%d::text)\", i+1))\n\t\targs = append(args, []byte(strconv.Itoa(i)))\n\t}\n\tsql := \"values\" + strings.Join(params, \", \")\n\n\tresult := pgConn.ExecParams(ctx, sql, args, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Len(t, result.Rows, paramCount)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecParamsTooManyParams(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tparamCount := math.MaxUint16 + 1\n\tparams := make([]string, 0, paramCount)\n\targs := make([][]byte, 0, paramCount)\n\tfor i := range paramCount {\n\t\tparams = append(params, fmt.Sprintf(\"($%d::text)\", i+1))\n\t\targs = append(args, []byte(strconv.Itoa(i)))\n\t}\n\tsql := \"values\" + strings.Join(params, \", \")\n\n\tresult := pgConn.ExecParams(ctx, sql, args, nil, nil, nil).Read()\n\trequire.Error(t, result.Err)\n\trequire.Equal(t, \"extended protocol limited to 65535 parameters\", result.Err.Error())\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecParamsCanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond)\n\tdefer cancel()\n\tresult := pgConn.ExecParams(ctx, \"select current_database(), pg_sleep(1)\", nil, nil, nil, nil)\n\trowCount := 0\n\tfor result.NextRow() {\n\t\trowCount += 1\n\t}\n\tassert.Equal(t, 0, rowCount)\n\tcommandTag, err := result.Close()\n\tassert.Equal(t, pgconn.CommandTag{}, commandTag)\n\tassert.True(t, pgconn.Timeout(err))\n\tassert.ErrorIs(t, err, context.DeadlineExceeded)\n\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\nfunc TestConnExecParamsPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tcancel()\n\tresult := pgConn.ExecParams(ctx, \"select $1::text\", [][]byte{[]byte(\"Hello, world\")}, nil, nil, nil).Read()\n\trequire.Error(t, result.Err)\n\tassert.True(t, errors.Is(result.Err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(result.Err))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecParamsEmptySQL(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresult := pgConn.ExecParams(ctx, \"\", nil, nil, nil, nil).Read()\n\tassert.Equal(t, pgconn.CommandTag{}, result.CommandTag)\n\tassert.Len(t, result.Rows, 0)\n\tassert.NoError(t, result.Err)\n\n\tensureConnValid(t, pgConn)\n}\n\n// https://github.com/jackc/pgx/issues/859\nfunc TestResultReaderValuesHaveSameCapacityAsLength(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresult := pgConn.ExecParams(ctx, \"select $1::text as msg\", [][]byte{[]byte(\"Hello, world\")}, nil, nil, nil)\n\trequire.Len(t, result.FieldDescriptions(), 1)\n\tassert.Equal(t, \"msg\", result.FieldDescriptions()[0].Name)\n\n\trowCount := 0\n\tfor result.NextRow() {\n\t\trowCount += 1\n\t\tassert.Equal(t, \"Hello, world\", string(result.Values()[0]))\n\t\tassert.Equal(t, len(result.Values()[0]), cap(result.Values()[0]))\n\t}\n\tassert.Equal(t, 1, rowCount)\n\tcommandTag, err := result.Close()\n\tassert.Equal(t, \"SELECT 1\", commandTag.String())\n\tassert.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\n// https://github.com/jackc/pgx/issues/1987\nfunc TestResultReaderReadNil(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresult := pgConn.ExecParams(ctx, \"select null::text\", nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Nil(t, result.Rows[0][0])\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecPrepared(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", \"select $1::text as msg\", nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, psd)\n\tassert.Len(t, psd.ParamOIDs, 1)\n\tassert.Len(t, psd.Fields, 1)\n\n\tresult := pgConn.ExecPrepared(ctx, \"ps1\", [][]byte{[]byte(\"Hello, world\")}, nil, nil)\n\trequire.Len(t, result.FieldDescriptions(), 1)\n\tassert.Equal(t, \"msg\", result.FieldDescriptions()[0].Name)\n\n\trowCount := 0\n\tfor result.NextRow() {\n\t\trowCount += 1\n\t\tassert.Equal(t, \"Hello, world\", string(result.Values()[0]))\n\t}\n\tassert.Equal(t, 1, rowCount)\n\tcommandTag, err := result.Close()\n\tassert.Equal(t, \"SELECT 1\", commandTag.String())\n\tassert.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecPreparedMaxNumberOfParams(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tparamCount := math.MaxUint16\n\tparams := make([]string, 0, paramCount)\n\targs := make([][]byte, 0, paramCount)\n\tfor i := range paramCount {\n\t\tparams = append(params, fmt.Sprintf(\"($%d::text)\", i+1))\n\t\targs = append(args, []byte(strconv.Itoa(i)))\n\t}\n\tsql := \"values\" + strings.Join(params, \", \")\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", sql, nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, psd)\n\tassert.Len(t, psd.ParamOIDs, paramCount)\n\tassert.Len(t, psd.Fields, 1)\n\n\tresult := pgConn.ExecPrepared(ctx, \"ps1\", args, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Len(t, result.Rows, paramCount)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecPreparedTooManyParams(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tparamCount := math.MaxUint16 + 1\n\tparams := make([]string, 0, paramCount)\n\targs := make([][]byte, 0, paramCount)\n\tfor i := range paramCount {\n\t\tparams = append(params, fmt.Sprintf(\"($%d::text)\", i+1))\n\t\targs = append(args, []byte(strconv.Itoa(i)))\n\t}\n\tsql := \"values\" + strings.Join(params, \", \")\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", sql, nil)\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// CockroachDB rejects preparing a statement with more than 65535 parameters.\n\t\trequire.EqualError(t, err, \"ERROR: more than 65535 arguments to prepared statement: 65536 (SQLSTATE 08P01)\")\n\t} else {\n\t\t// PostgreSQL accepts preparing a statement with more than 65535 parameters and only fails when executing it through the extended protocol.\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, psd)\n\t\tassert.Len(t, psd.ParamOIDs, paramCount)\n\t\tassert.Len(t, psd.Fields, 1)\n\n\t\tresult := pgConn.ExecPrepared(ctx, \"ps1\", args, nil, nil).Read()\n\t\trequire.EqualError(t, result.Err, \"extended protocol limited to 65535 parameters\")\n\t}\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecPreparedCanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"select current_database(), pg_sleep(1)\", nil)\n\trequire.NoError(t, err)\n\n\tctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond)\n\tdefer cancel()\n\tresult := pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil)\n\trowCount := 0\n\tfor result.NextRow() {\n\t\trowCount += 1\n\t}\n\tassert.Equal(t, 0, rowCount)\n\tcommandTag, err := result.Close()\n\tassert.Equal(t, pgconn.CommandTag{}, commandTag)\n\tassert.True(t, pgconn.Timeout(err))\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\nfunc TestConnExecPreparedPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"select current_database(), pg_sleep(1)\", nil)\n\trequire.NoError(t, err)\n\n\tcancel()\n\tresult := pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil).Read()\n\trequire.Error(t, result.Err)\n\tassert.True(t, errors.Is(result.Err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(result.Err))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecPreparedEmptySQL(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"\", nil)\n\trequire.NoError(t, err)\n\n\tresult := pgConn.ExecPrepared(ctx, \"ps1\", nil, nil, nil).Read()\n\tassert.Equal(t, pgconn.CommandTag{}, result.CommandTag)\n\tassert.Len(t, result.Rows, 0)\n\tassert.NoError(t, result.Err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecStatement(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", \"select $1::text as msg\", nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, psd)\n\tassert.Len(t, psd.ParamOIDs, 1)\n\tassert.Len(t, psd.Fields, 1)\n\n\tresult := pgConn.ExecStatement(ctx, psd, [][]byte{[]byte(\"Hello, world\")}, nil, nil)\n\trequire.Len(t, result.FieldDescriptions(), 1)\n\tassert.Equal(t, \"msg\", result.FieldDescriptions()[0].Name)\n\n\trowCount := 0\n\tfor result.NextRow() {\n\t\trowCount += 1\n\t\tassert.Equal(t, \"Hello, world\", string(result.Values()[0]))\n\t}\n\tassert.Equal(t, 1, rowCount)\n\tcommandTag, err := result.Close()\n\tassert.Equal(t, \"SELECT 1\", commandTag.String())\n\tassert.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\ntype byteCounterConn struct {\n\tconn         net.Conn\n\tbytesRead    int\n\tbytesWritten int\n}\n\nfunc (cbn *byteCounterConn) Read(b []byte) (n int, err error) {\n\tn, err = cbn.conn.Read(b)\n\tcbn.bytesRead += n\n\treturn n, err\n}\n\nfunc (cbn *byteCounterConn) Write(b []byte) (n int, err error) {\n\tn, err = cbn.conn.Write(b)\n\tcbn.bytesWritten += n\n\treturn n, err\n}\n\nfunc (cbn *byteCounterConn) Close() error {\n\treturn cbn.conn.Close()\n}\n\nfunc (cbn *byteCounterConn) LocalAddr() net.Addr {\n\treturn cbn.conn.LocalAddr()\n}\n\nfunc (cbn *byteCounterConn) RemoteAddr() net.Addr {\n\treturn cbn.conn.RemoteAddr()\n}\n\nfunc (cbn *byteCounterConn) SetDeadline(t time.Time) error {\n\treturn cbn.conn.SetDeadline(t)\n}\n\nfunc (cbn *byteCounterConn) SetReadDeadline(t time.Time) error {\n\treturn cbn.conn.SetReadDeadline(t)\n}\n\nfunc (cbn *byteCounterConn) SetWriteDeadline(t time.Time) error {\n\treturn cbn.conn.SetWriteDeadline(t)\n}\n\nfunc TestConnExecStatementNetworkUsage(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar counterConn *byteCounterConn\n\tconfig.AfterNetConnect = func(ctx context.Context, config *pgconn.Config, conn net.Conn) (net.Conn, error) {\n\t\tcounterConn = &byteCounterConn{conn: conn}\n\t\treturn counterConn, nil\n\t}\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\trequire.NotNil(t, counterConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server uses different number of bytes for same operations\")\n\t}\n\n\tpsd, err := pgConn.Prepare(ctx, \"ps1\", \"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\", nil)\n\trequire.NoError(t, err)\n\trequire.NotNil(t, psd)\n\tassert.Len(t, psd.ParamOIDs, 1)\n\tassert.Len(t, psd.Fields, 9)\n\n\tcounterConn.bytesWritten = 0\n\tcounterConn.bytesRead = 0\n\n\tresult := pgConn.ExecPrepared(ctx,\n\t\tpsd.Name,\n\t\t[][]byte{[]byte(\"1\")},\n\t\tnil,\n\t\t[]int16{pgx.BinaryFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode},\n\t).Read()\n\trequire.NoError(t, result.Err)\n\twithDescribeBytesWritten := counterConn.bytesWritten\n\twithDescribeBytesRead := counterConn.bytesRead\n\n\tcounterConn.bytesWritten = 0\n\tcounterConn.bytesRead = 0\n\n\tresult = pgConn.ExecStatement(\n\t\tctx,\n\t\tpsd,\n\t\t[][]byte{[]byte(\"1\")},\n\t\tnil,\n\t\t[]int16{pgx.BinaryFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, pgx.TextFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode, pgx.BinaryFormatCode},\n\t).Read()\n\trequire.NoError(t, result.Err)\n\tnoDescribeBytesWritten := counterConn.bytesWritten\n\tnoDescribeBytesRead := counterConn.bytesRead\n\n\tassert.Equal(t, 61, withDescribeBytesWritten)\n\tassert.Equal(t, 54, noDescribeBytesWritten)\n\tassert.Equal(t, 391, withDescribeBytesRead)\n\tassert.Equal(t, 153, noDescribeBytesRead)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"select $1::text\", nil)\n\trequire.NoError(t, err)\n\n\tsd, err := pgConn.Prepare(ctx, \"ps2\", \"select $1::text as name, $2::bigint as age\", nil)\n\trequire.NoError(t, err)\n\n\tbatch := &pgconn.Batch{}\n\n\tbatch.ExecParams(\"select $1::text\", [][]byte{[]byte(\"ExecParams 1\")}, nil, nil, nil)\n\tbatch.ExecPrepared(\"ps1\", [][]byte{[]byte(\"ExecPrepared 1\")}, nil, nil)\n\tbatch.ExecStatement(sd, [][]byte{[]byte(\"ExecStatement 1\"), []byte(\"42\")}, nil, nil)\n\tbatch.ExecStatement(sd, [][]byte{[]byte(\"ExecStatement 2\"), []byte(\"43\")}, nil, []int16{pgx.BinaryFormatCode})\n\tbatch.ExecStatement(sd, [][]byte{[]byte(\"ExecStatement 3\"), []byte(\"44\")}, nil, []int16{pgx.TextFormatCode, pgx.BinaryFormatCode})\n\tbatch.ExecParams(\"select $1::text\", [][]byte{[]byte(\"ExecParams 2\")}, nil, nil, nil)\n\tresults, err := pgConn.ExecBatch(ctx, batch).ReadAll()\n\trequire.NoError(t, err)\n\trequire.Len(t, results, 6)\n\n\trequire.Len(t, results[0].Rows, 1)\n\trequire.Equal(t, \"ExecParams 1\", string(results[0].Rows[0][0]))\n\tassert.Equal(t, \"SELECT 1\", results[0].CommandTag.String())\n\n\trequire.Len(t, results[1].Rows, 1)\n\trequire.Equal(t, \"ExecPrepared 1\", string(results[1].Rows[0][0]))\n\tassert.Equal(t, \"SELECT 1\", results[1].CommandTag.String())\n\n\trequire.Len(t, results[2].Rows, 1)\n\trequire.Equal(t, \"ExecStatement 1\", string(results[2].Rows[0][0]))\n\trequire.Equal(t, \"42\", string(results[2].Rows[0][1]))\n\tassert.Equal(t, \"SELECT 1\", results[2].CommandTag.String())\n\n\trequire.Len(t, results[3].Rows, 1)\n\trequire.Equal(t, \"ExecStatement 2\", string(results[3].Rows[0][0]))\n\trequire.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, 43}, results[3].Rows[0][1])\n\tassert.Equal(t, \"SELECT 1\", results[3].CommandTag.String())\n\n\trequire.Len(t, results[4].Rows, 1)\n\trequire.Equal(t, \"ExecStatement 3\", string(results[4].Rows[0][0]))\n\trequire.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, 44}, results[4].Rows[0][1])\n\tassert.Equal(t, \"SELECT 1\", results[4].CommandTag.String())\n\n\trequire.Len(t, results[5].Rows, 1)\n\trequire.Equal(t, \"ExecParams 2\", string(results[5].Rows[0][0]))\n\tassert.Equal(t, \"SELECT 1\", results[2].CommandTag.String())\n}\n\ntype mockConnection struct {\n\tnet.Conn\n\twriteLatency *time.Duration\n}\n\nfunc (m mockConnection) Write(b []byte) (n int, err error) {\n\ttime.Sleep(*m.writeLatency)\n\treturn m.Conn.Write(b)\n}\n\nfunc TestConnExecBatchWriteError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar mockConn mockConnection\n\twriteLatency := 0 * time.Second\n\tconfig.DialFunc = func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\tconn, err := net.Dial(network, address)\n\t\tmockConn = mockConnection{conn, &writeLatency}\n\t\treturn mockConn, err\n\t}\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tbatch := &pgconn.Batch{}\n\tpgConn.Conn()\n\n\tctx2, cancel2 := context.WithTimeout(context.Background(), 1*time.Second)\n\tdefer cancel2()\n\n\tbatch.ExecParams(\"select $1::text\", [][]byte{[]byte(\"ExecParams 1\")}, nil, nil, nil)\n\twriteLatency = 2 * time.Second\n\tmrr := pgConn.ExecBatch(ctx2, batch)\n\terr = mrr.Close()\n\trequire.Error(t, err)\n\tassert.ErrorIs(t, err, context.DeadlineExceeded)\n\trequire.True(t, pgConn.IsClosed())\n}\n\nfunc TestConnExecBatchDeferredError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\t}\n\n\tsetupSQL := `create temporary table t (\n\t\tid text primary key,\n\t\tn int not null,\n\t\tunique (n) deferrable initially deferred\n\t);\n\n\tinsert into t (id, n) values ('a', 1), ('b', 2), ('c', 3);`\n\n\t_, err = pgConn.Exec(ctx, setupSQL).ReadAll()\n\trequire.NoError(t, err)\n\n\tbatch := &pgconn.Batch{}\n\n\tbatch.ExecParams(`update t set n=n+1 where id='b' returning *`, nil, nil, nil, nil)\n\t_, err = pgConn.ExecBatch(ctx, batch).ReadAll()\n\trequire.NotNil(t, err)\n\tvar pgErr *pgconn.PgError\n\trequire.True(t, errors.As(err, &pgErr))\n\trequire.Equal(t, \"23505\", pgErr.Code)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnExecBatchPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Prepare(ctx, \"ps1\", \"select $1::text\", nil)\n\trequire.NoError(t, err)\n\n\tbatch := &pgconn.Batch{}\n\n\tbatch.ExecParams(\"select $1::text\", [][]byte{[]byte(\"ExecParams 1\")}, nil, nil, nil)\n\tbatch.ExecPrepared(\"ps1\", [][]byte{[]byte(\"ExecPrepared 1\")}, nil, nil)\n\tbatch.ExecParams(\"select $1::text\", [][]byte{[]byte(\"ExecParams 2\")}, nil, nil, nil)\n\n\tcancel()\n\t_, err = pgConn.ExecBatch(ctx, batch).ReadAll()\n\trequire.Error(t, err)\n\tassert.True(t, errors.Is(err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(err))\n\n\tensureConnValid(t, pgConn)\n}\n\n// Without concurrent reading and writing large batches can deadlock.\n//\n// See https://github.com/jackc/pgx/issues/374.\nfunc TestConnExecBatchHuge(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping test in short mode.\")\n\t}\n\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tbatch := &pgconn.Batch{}\n\n\tqueryCount := 100_000\n\targs := make([]string, queryCount)\n\n\tfor i := range args {\n\t\targs[i] = strconv.Itoa(i)\n\t\tbatch.ExecParams(\"select $1::text\", [][]byte{[]byte(args[i])}, nil, nil, nil)\n\t}\n\n\tresults, err := pgConn.ExecBatch(ctx, batch).ReadAll()\n\trequire.NoError(t, err)\n\trequire.Len(t, results, queryCount)\n\n\tfor i := range args {\n\t\trequire.Len(t, results[i].Rows, 1)\n\t\trequire.Equal(t, args[i], string(results[i].Rows[0][0]))\n\t\tassert.Equal(t, \"SELECT 1\", results[i].CommandTag.String())\n\t}\n}\n\nfunc TestConnExecBatchImplicitTransaction(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Skipping due to known server issue: (https://github.com/cockroachdb/cockroach/issues/44803)\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, \"create temporary table t(id int)\").ReadAll()\n\trequire.NoError(t, err)\n\n\tbatch := &pgconn.Batch{}\n\n\tbatch.ExecParams(\"insert into t(id) values(1)\", nil, nil, nil, nil)\n\tbatch.ExecParams(\"insert into t(id) values(2)\", nil, nil, nil, nil)\n\tbatch.ExecParams(\"insert into t(id) values(3)\", nil, nil, nil, nil)\n\tbatch.ExecParams(\"select 1/0\", nil, nil, nil, nil)\n\t_, err = pgConn.ExecBatch(ctx, batch).ReadAll()\n\trequire.Error(t, err)\n\n\tresult := pgConn.ExecParams(ctx, \"select count(*) from t\", nil, nil, nil, nil).Read()\n\trequire.Equal(t, \"0\", string(result.Rows[0][0]))\n}\n\nfunc TestConnLocking(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tmrr := pgConn.Exec(ctx, \"select 'Hello, world'\")\n\t_, err = pgConn.Exec(ctx, \"select 'Hello, world'\").ReadAll()\n\tassert.Error(t, err)\n\tassert.Equal(t, \"conn busy\", err.Error())\n\tassert.True(t, pgconn.SafeToRetry(err))\n\n\tresults, err := mrr.ReadAll()\n\tassert.NoError(t, err)\n\tassert.Len(t, results, 1)\n\tassert.Nil(t, results[0].Err)\n\tassert.Equal(t, \"SELECT 1\", results[0].CommandTag.String())\n\tassert.Len(t, results[0].Rows, 1)\n\tassert.Equal(t, \"Hello, world\", string(results[0].Rows[0][0]))\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnOnNotice(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar notice *pgconn.Notice\n\tconfig.OnNotice = func(c *pgconn.PgConn, n *pgconn.Notice) {\n\t\tnotice = n\n\t}\n\tconfig.RuntimeParams[\"client_min_messages\"] = \"notice\" // Ensure we only get the message we expect.\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support PL/PGSQL (https://github.com/cockroachdb/cockroach/issues/17511)\")\n\t}\n\n\tmultiResult := pgConn.Exec(ctx, `do $$\nbegin\n  raise notice 'hello, world';\nend$$;`)\n\terr = multiResult.Close()\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"NOTICE\", notice.SeverityUnlocalized)\n\tassert.Equal(t, \"hello, world\", notice.Message)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnOnNotification(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar msg string\n\tconfig.OnNotification = func(c *pgconn.PgConn, n *pgconn.Notification) {\n\t\tmsg = n.Payload\n\t}\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support LISTEN / NOTIFY (https://github.com/cockroachdb/cockroach/issues/41522)\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, \"listen foo\").ReadAll()\n\trequire.NoError(t, err)\n\n\tnotifier, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, notifier)\n\t_, err = notifier.Exec(ctx, \"notify foo, 'bar'\").ReadAll()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.Exec(ctx, \"select 1\").ReadAll()\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, \"bar\", msg)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnWaitForNotification(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar msg string\n\tconfig.OnNotification = func(c *pgconn.PgConn, n *pgconn.Notification) {\n\t\tmsg = n.Payload\n\t}\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support LISTEN / NOTIFY (https://github.com/cockroachdb/cockroach/issues/41522)\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, \"listen foo\").ReadAll()\n\trequire.NoError(t, err)\n\n\tnotifier, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, notifier)\n\t_, err = notifier.Exec(ctx, \"notify foo, 'bar'\").ReadAll()\n\trequire.NoError(t, err)\n\n\terr = pgConn.WaitForNotification(ctx)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, \"bar\", msg)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnWaitForNotificationPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tcancel()\n\terr = pgConn.WaitForNotification(ctx)\n\trequire.ErrorIs(t, err, context.Canceled)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnWaitForNotificationTimeout(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tctx, cancel = context.WithTimeout(ctx, 5*time.Millisecond)\n\terr = pgConn.WaitForNotification(ctx)\n\tcancel()\n\tassert.True(t, pgconn.Timeout(err))\n\tassert.ErrorIs(t, err, context.DeadlineExceeded)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyToSmall(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does support COPY TO\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int2,\n\t\tb int4,\n\t\tc int8,\n\t\td varchar,\n\t\te text,\n\t\tf date,\n\t\tg json\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.Exec(ctx, `insert into foo values (0, 1, 2, 'abc', 'efg', '2000-01-01', '{\"abc\":\"def\",\"foo\":\"bar\"}')`).ReadAll()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.Exec(ctx, `insert into foo values (null, null, null, null, null, null, null)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tinputBytes := []byte(\"0\\t1\\t2\\tabc\\tefg\\t2000-01-01\\t{\\\"abc\\\":\\\"def\\\",\\\"foo\\\":\\\"bar\\\"}\\n\" +\n\t\t\"\\\\N\\t\\\\N\\t\\\\N\\t\\\\N\\t\\\\N\\t\\\\N\\t\\\\N\\n\")\n\n\toutputWriter := bytes.NewBuffer(make([]byte, 0, len(inputBytes)))\n\n\tres, err := pgConn.CopyTo(ctx, outputWriter, \"copy foo to stdout\")\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, int64(2), res.RowsAffected())\n\tassert.Equal(t, inputBytes, outputWriter.Bytes())\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyToLarge(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does support COPY TO\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int2,\n\t\tb int4,\n\t\tc int8,\n\t\td varchar,\n\t\te text,\n\t\tf date,\n\t\tg json,\n\t\th bytea\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tinputBytes := make([]byte, 0)\n\n\tfor range 1000 {\n\t\t_, err = pgConn.Exec(ctx, `insert into foo values (0, 1, 2, 'abc', 'efg', '2000-01-01', '{\"abc\":\"def\",\"foo\":\"bar\"}', 'oooo')`).ReadAll()\n\t\trequire.NoError(t, err)\n\t\tinputBytes = append(inputBytes, \"0\\t1\\t2\\tabc\\tefg\\t2000-01-01\\t{\\\"abc\\\":\\\"def\\\",\\\"foo\\\":\\\"bar\\\"}\\t\\\\\\\\x6f6f6f6f\\n\"...)\n\t}\n\n\toutputWriter := bytes.NewBuffer(make([]byte, 0, len(inputBytes)))\n\n\tres, err := pgConn.CopyTo(ctx, outputWriter, \"copy foo to stdout\")\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, int64(1000), res.RowsAffected())\n\tassert.Equal(t, inputBytes, outputWriter.Bytes())\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyToQueryError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\toutputWriter := bytes.NewBuffer(make([]byte, 0))\n\n\tres, err := pgConn.CopyTo(ctx, outputWriter, \"cropy foo to stdout\")\n\trequire.Error(t, err)\n\tassert.IsType(t, &pgconn.PgError{}, err)\n\tassert.Equal(t, int64(0), res.RowsAffected())\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyToCanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support query cancellation (https://github.com/cockroachdb/cockroach/issues/41335)\")\n\t}\n\n\toutputWriter := &bytes.Buffer{}\n\n\tctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond)\n\tdefer cancel()\n\tres, err := pgConn.CopyTo(ctx, outputWriter, \"copy (select *, pg_sleep(0.01) from generate_series(1,1000)) to stdout\")\n\tassert.Error(t, err)\n\tassert.Equal(t, pgconn.CommandTag{}, res)\n\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\nfunc TestConnCopyToPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\toutputWriter := &bytes.Buffer{}\n\n\tcancel()\n\tres, err := pgConn.CopyTo(ctx, outputWriter, \"copy (select * from generate_series(1,1000)) to stdout\")\n\trequire.Error(t, err)\n\tassert.True(t, errors.Is(err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(err))\n\tassert.Equal(t, pgconn.CommandTag{}, res)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyFrom(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tsrcBuf := &bytes.Buffer{}\n\n\tinputRows := [][][]byte{}\n\tfor i := range 1000 {\n\t\ta := strconv.Itoa(i)\n\t\tb := \"foo \" + a + \" bar\"\n\t\tinputRows = append(inputRows, [][]byte{[]byte(a), []byte(b)})\n\t\t_, err = srcBuf.Write(fmt.Appendf(nil, \"%s,\\\"%s\\\"\\n\", a, b))\n\t\trequire.NoError(t, err)\n\t}\n\n\tcopySql := \"COPY foo FROM STDIN WITH (FORMAT csv)\"\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tcopySql = \"COPY foo FROM STDIN WITH CSV\"\n\t}\n\tct, err := pgConn.CopyFrom(ctx, srcBuf, copySql)\n\trequire.NoError(t, err)\n\tassert.Equal(t, int64(len(inputRows)), ct.RowsAffected())\n\n\tresult := pgConn.ExecParams(ctx, \"select * from foo\", nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\n\tassert.Equal(t, inputRows, result.Rows)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyFromBinary(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tbuf := []byte{}\n\tbuf = append(buf, \"PGCOPY\\n\\377\\r\\n\\000\"...)\n\tbuf = pgio.AppendInt32(buf, 0)\n\tbuf = pgio.AppendInt32(buf, 0)\n\n\tinputRows := [][][]byte{}\n\tfor i := range 1000 {\n\t\t// Number of elements in the tuple\n\t\tbuf = pgio.AppendInt16(buf, int16(2))\n\t\ta := i\n\n\t\t// Length of element for column `a int4`\n\t\tbuf = pgio.AppendInt32(buf, 4)\n\t\tbuf, err = pgtype.NewMap().Encode(pgtype.Int4OID, pgx.BinaryFormatCode, a, buf)\n\t\trequire.NoError(t, err)\n\n\t\tb := \"foo \" + strconv.Itoa(a) + \" bar\"\n\t\tlenB := int32(len([]byte(b)))\n\t\t// Length of element for column `b varchar`\n\t\tbuf = pgio.AppendInt32(buf, lenB)\n\t\tbuf, err = pgtype.NewMap().Encode(pgtype.VarcharOID, pgx.BinaryFormatCode, b, buf)\n\t\trequire.NoError(t, err)\n\n\t\tinputRows = append(inputRows, [][]byte{[]byte(strconv.Itoa(a)), []byte(b)})\n\t}\n\n\tsrcBuf := &bytes.Buffer{}\n\tsrcBuf.Write(buf)\n\tct, err := pgConn.CopyFrom(ctx, srcBuf, \"COPY foo (a, b) FROM STDIN BINARY;\")\n\trequire.NoError(t, err)\n\tassert.Equal(t, int64(len(inputRows)), ct.RowsAffected())\n\n\tresult := pgConn.ExecParams(ctx, \"select * from foo\", nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\n\tassert.Equal(t, inputRows, result.Rows)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyFromCanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\tfor i := range 1_000_000 {\n\t\t\ta := strconv.Itoa(i)\n\t\t\tb := \"foo \" + a + \" bar\"\n\t\t\t_, err := w.Write(fmt.Appendf(nil, \"%s,\\\"%s\\\"\\n\", a, b))\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(time.Microsecond)\n\t\t}\n\t}()\n\n\tctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond)\n\tcopySql := \"COPY foo FROM STDIN WITH (FORMAT csv)\"\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tcopySql = \"COPY foo FROM STDIN WITH CSV\"\n\t}\n\tct, err := pgConn.CopyFrom(ctx, r, copySql)\n\tcancel()\n\tassert.Equal(t, int64(0), ct.RowsAffected())\n\tassert.Error(t, err)\n\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\nfunc TestConnCopyFromPrecanceled(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\tfor i := range 1_000_000 {\n\t\t\ta := strconv.Itoa(i)\n\t\t\tb := \"foo \" + a + \" bar\"\n\t\t\t_, err := w.Write(fmt.Appendf(nil, \"%s,\\\"%s\\\"\\n\", a, b))\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(time.Microsecond)\n\t\t}\n\t}()\n\n\tctx, cancel = context.WithCancel(ctx)\n\tcancel()\n\tct, err := pgConn.CopyFrom(ctx, r, \"COPY foo FROM STDIN WITH (FORMAT csv)\")\n\trequire.Error(t, err)\n\tassert.True(t, errors.Is(err, context.Canceled))\n\tassert.True(t, pgconn.SafeToRetry(err))\n\tassert.Equal(t, pgconn.CommandTag{}, ct)\n\n\tensureConnValid(t, pgConn)\n}\n\n// https://github.com/jackc/pgx/issues/2364\nfunc TestConnCopyFromConnectionTerminated(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support pg_terminate_backend\")\n\t}\n\n\tcloserConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, closerConn)\n\terrChan := make(chan error, 1)\n\ttime.AfterFunc(500*time.Millisecond, func() {\n\t\terr := closerConn.ExecParams(ctx, \"select pg_terminate_backend($1)\", [][]byte{fmt.Appendf(nil, \"%d\", pgConn.PID())}, nil, nil, nil).Read().Err\n\t\terrChan <- err\n\t})\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tr, w := io.Pipe()\n\tgo func() {\n\t\tfor i := range 5_000 {\n\t\t\ta := strconv.Itoa(i)\n\t\t\tb := \"foo \" + a + \" bar\"\n\t\t\t_, err := w.Write(fmt.Appendf(nil, \"%s,\\\"%s\\\"\\n\", a, b))\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttime.Sleep(time.Millisecond)\n\t\t}\n\t}()\n\n\tcopySql := \"COPY foo FROM STDIN WITH (FORMAT csv)\"\n\tct, err := pgConn.CopyFrom(ctx, r, copySql)\n\tassert.Equal(t, int64(0), ct.RowsAffected())\n\tassert.Error(t, err)\n\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n\n\terr = <-errChan\n\trequire.NoError(t, err)\n}\n\nfunc TestConnCopyFromGzipReader(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not fully support COPY FROM (https://www.cockroachlabs.com/docs/v20.2/copy-from.html)\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tf, err := os.CreateTemp(t.TempDir(), \"*\")\n\trequire.NoError(t, err)\n\tdefer f.Close()\n\n\tgw := gzip.NewWriter(f)\n\n\tinputRows := [][][]byte{}\n\tfor i := range 1000 {\n\t\ta := strconv.Itoa(i)\n\t\tb := \"foo \" + a + \" bar\"\n\t\tinputRows = append(inputRows, [][]byte{[]byte(a), []byte(b)})\n\t\t_, err = gw.Write(fmt.Appendf(nil, \"%s,\\\"%s\\\"\\n\", a, b))\n\t\trequire.NoError(t, err)\n\t}\n\n\terr = gw.Close()\n\trequire.NoError(t, err)\n\n\t_, err = f.Seek(0, 0)\n\trequire.NoError(t, err)\n\n\tgr, err := gzip.NewReader(f)\n\trequire.NoError(t, err)\n\n\tcopySql := \"COPY foo FROM STDIN WITH (FORMAT csv)\"\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tcopySql = \"COPY foo FROM STDIN WITH CSV\"\n\t}\n\tct, err := pgConn.CopyFrom(ctx, gr, copySql)\n\trequire.NoError(t, err)\n\tassert.Equal(t, int64(len(inputRows)), ct.RowsAffected())\n\n\terr = gr.Close()\n\trequire.NoError(t, err)\n\n\tresult := pgConn.ExecParams(ctx, \"select * from foo\", nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\n\tassert.Equal(t, inputRows, result.Rows)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyFromQuerySyntaxError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, `create temporary table foo(\n\t\ta int4,\n\t\tb varchar\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\tsrcBuf := &bytes.Buffer{}\n\n\t// Send data even though the COPY FROM command will be rejected with a syntax error. This ensures that this does not\n\t// break the connection. See https://github.com/jackc/pgconn/pull/127 for context.\n\tinputRows := [][][]byte{}\n\tfor i := range 1000 {\n\t\ta := strconv.Itoa(i)\n\t\tb := \"foo \" + a + \" bar\"\n\t\tinputRows = append(inputRows, [][]byte{[]byte(a), []byte(b)})\n\t\t_, err = srcBuf.Write(fmt.Appendf(nil, \"%s,\\\"%s\\\"\\n\", a, b))\n\t\trequire.NoError(t, err)\n\t}\n\n\tres, err := pgConn.CopyFrom(ctx, srcBuf, \"cropy foo FROM STDIN WITH (FORMAT csv)\")\n\trequire.Error(t, err)\n\tassert.IsType(t, &pgconn.PgError{}, err)\n\tassert.Equal(t, int64(0), res.RowsAffected())\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCopyFromQueryNoTableError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tsrcBuf := &bytes.Buffer{}\n\n\tres, err := pgConn.CopyFrom(ctx, srcBuf, \"copy foo to stdout\")\n\trequire.Error(t, err)\n\tassert.IsType(t, &pgconn.PgError{}, err)\n\tassert.Equal(t, int64(0), res.RowsAffected())\n\n\tensureConnValid(t, pgConn)\n}\n\n// https://github.com/jackc/pgconn/issues/21\nfunc TestConnCopyFromNoticeResponseReceivedMidStream(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support triggers (https://github.com/cockroachdb/cockroach/issues/28296)\")\n\t}\n\n\t_, err = pgConn.Exec(ctx, `create temporary table sentences(\n\t\tt text,\n\t\tts tsvector\n\t)`).ReadAll()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.Exec(ctx, `create function pg_temp.sentences_trigger() returns trigger as $$\n\tbegin\n\t  new.ts := to_tsvector(new.t);\n\t\treturn new;\n\tend\n\t$$ language plpgsql;`).ReadAll()\n\trequire.NoError(t, err)\n\n\t_, err = pgConn.Exec(ctx, `create trigger sentences_update before insert on sentences for each row execute procedure pg_temp.sentences_trigger();`).ReadAll()\n\trequire.NoError(t, err)\n\n\tlongString := make([]byte, 10001)\n\tfor i := range longString {\n\t\tlongString[i] = 'x'\n\t}\n\n\tbuf := &bytes.Buffer{}\n\tfor range 1000 {\n\t\tbuf.Write(fmt.Appendf(nil, \"%s\\n\", string(longString)))\n\t}\n\n\t_, err = pgConn.CopyFrom(ctx, buf, \"COPY sentences(t) FROM STDIN WITH (FORMAT csv)\")\n\trequire.NoError(t, err)\n}\n\ntype delayedReader struct {\n\tr io.Reader\n}\n\nfunc (d delayedReader) Read(p []byte) (int, error) {\n\t// W/o sleep test passes, with sleep it fails.\n\ttime.Sleep(time.Millisecond)\n\treturn d.r.Read(p)\n}\n\n// https://github.com/jackc/pgconn/issues/128\nfunc TestConnCopyFromDataWriteAfterErrorAndReturn(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_DATABASE\")\n\t}\n\n\tconfig, err := pgconn.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not fully support COPY FROM\")\n\t}\n\n\tsetupSQL := `create temporary table t (\n\t\tid text primary key,\n\t\tn int not null\n\t);`\n\n\t_, err = pgConn.Exec(ctx, setupSQL).ReadAll()\n\tassert.NoError(t, err)\n\n\tr1 := delayedReader{r: strings.NewReader(`id\t0\\n`)}\n\t// Generate an error with a bogus COPY command\n\t_, err = pgConn.CopyFrom(ctx, r1, \"COPY nosuchtable FROM STDIN \")\n\tassert.Error(t, err)\n\n\tr2 := delayedReader{r: strings.NewReader(`id\t0\\n`)}\n\t_, err = pgConn.CopyFrom(ctx, r2, \"COPY t FROM STDIN\")\n\tassert.NoError(t, err)\n}\n\nfunc TestConnEscapeString(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\ttests := []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{in: \"\", out: \"\"},\n\t\t{in: \"42\", out: \"42\"},\n\t\t{in: \"'\", out: \"''\"},\n\t\t{in: \"hi'there\", out: \"hi''there\"},\n\t\t{in: \"'hi there'\", out: \"''hi there''\"},\n\t}\n\n\tfor i, tt := range tests {\n\t\tvalue, err := pgConn.EscapeString(tt.in)\n\t\tif assert.NoErrorf(t, err, \"%d.\", i) {\n\t\t\tassert.Equalf(t, tt.out, value, \"%d.\", i)\n\t\t}\n\t}\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCancelRequest(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tif pgConn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support query cancellation (https://github.com/cockroachdb/cockroach/issues/41335)\")\n\t}\n\n\tmultiResult := pgConn.Exec(ctx, \"select 'Hello, world', pg_sleep(25)\")\n\n\terrChan := make(chan error)\n\tgo func() {\n\t\t// The query is actually sent when multiResult.NextResult() is called. So wait to ensure it is sent.\n\t\t// Once Flush is available this could use that instead.\n\t\ttime.Sleep(1 * time.Second)\n\n\t\terr := pgConn.CancelRequest(ctx)\n\t\terrChan <- err\n\t}()\n\n\tfor multiResult.NextResult() {\n\t}\n\terr = multiResult.Close()\n\n\trequire.IsType(t, &pgconn.PgError{}, err)\n\trequire.Equal(t, \"57014\", err.(*pgconn.PgError).Code)\n\n\terr = <-errChan\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\n// https://github.com/jackc/pgx/issues/659\nfunc TestConnContextCanceledCancelsRunningQueryOnServer(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"postgres\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestConnContextCanceledCancelsRunningQueryOnServer(t, os.Getenv(\"PGX_TEST_DATABASE\"), \"postgres\")\n\t})\n\n\tt.Run(\"pgbouncer\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconnString := os.Getenv(pgbouncerConnStringEnvVar)\n\t\tif connString == \"\" {\n\t\t\tt.Skipf(\"Skipping due to missing environment variable %v\", pgbouncerConnStringEnvVar)\n\t\t}\n\n\t\ttestConnContextCanceledCancelsRunningQueryOnServer(t, connString, \"pgbouncer\")\n\t})\n}\n\nfunc testConnContextCanceledCancelsRunningQueryOnServer(t *testing.T, connString, dbType string) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond)\n\tdefer cancel()\n\n\t// Getting the actual PostgreSQL server process ID (PID) from a query executed through pgbouncer is not straightforward\n\t// because pgbouncer abstracts the underlying database connections, and it doesn't expose the PID of the PostgreSQL\n\t// server process to clients. However, we can check if the query is running by checking the generated query ID.\n\tqueryID := fmt.Sprintf(\"%s testConnContextCanceled %d\", dbType, time.Now().UnixNano())\n\n\tmultiResult := pgConn.Exec(ctx, fmt.Sprintf(`\n\t-- %v\n\tselect 'Hello, world', pg_sleep(30)\n\t`, queryID))\n\n\tfor multiResult.NextResult() {\n\t}\n\terr = multiResult.Close()\n\tassert.True(t, pgconn.Timeout(err))\n\tassert.True(t, pgConn.IsClosed())\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n\n\tctx, cancel = context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\totherConn, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, otherConn)\n\n\tctx, cancel = context.WithTimeout(ctx, time.Second*5)\n\tdefer cancel()\n\n\tfor {\n\t\tresult := otherConn.ExecParams(ctx,\n\t\t\t`select 1 from pg_stat_activity where query like $1`,\n\t\t\t[][]byte{[]byte(\"%\" + queryID + \"%\")},\n\t\t\tnil,\n\t\t\tnil,\n\t\t\tnil,\n\t\t).Read()\n\t\trequire.NoError(t, result.Err)\n\n\t\tif len(result.Rows) == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc TestHijackAndConstruct(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\torigConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\terr = origConn.SyncConn(ctx)\n\trequire.NoError(t, err)\n\n\thc, err := origConn.Hijack()\n\trequire.NoError(t, err)\n\n\t_, err = origConn.Exec(ctx, \"select 'Hello, world'\").ReadAll()\n\trequire.Error(t, err)\n\n\tnewConn, err := pgconn.Construct(hc)\n\trequire.NoError(t, err)\n\n\tdefer closeConn(t, newConn)\n\n\tresults, err := newConn.Exec(ctx, \"select 'Hello, world'\").ReadAll()\n\tassert.NoError(t, err)\n\n\tassert.Len(t, results, 1)\n\tassert.Nil(t, results[0].Err)\n\tassert.Equal(t, \"SELECT 1\", results[0].CommandTag.String())\n\tassert.Len(t, results[0].Rows, 1)\n\tassert.Equal(t, \"Hello, world\", string(results[0].Rows[0][0]))\n\n\tensureConnValid(t, newConn)\n}\n\nfunc TestConnCloseWhileCancellableQueryInProgress(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tpgConn.Exec(ctx, \"select n from generate_series(1,10) n\")\n\n\tcloseCtx, closeCancel := context.WithCancel(ctx)\n\tdefer closeCancel()\n\tpgConn.Close(closeCtx)\n\tselect {\n\tcase <-pgConn.CleanupDone():\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatal(\"Connection cleanup exceeded maximum time\")\n\t}\n}\n\n// https://github.com/jackc/pgx/issues/800\nfunc TestFatalErrorReceivedAfterCommandComplete(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tsteps := pgmock.AcceptUnauthenticatedConnRequestSteps()\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Parse{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Bind{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Describe{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Execute{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Sync{}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.RowDescription{Fields: []pgproto3.FieldDescription{\n\t\t{Name: []byte(\"mock\")},\n\t}}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.CommandComplete{CommandTag: []byte(\"SELECT 0\")}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.ErrorResponse{Severity: \"FATAL\", Code: \"57P01\"}))\n\n\tscript := &pgmock.Script{Steps: steps}\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\trequire.NoError(t, err)\n\tdefer ln.Close()\n\n\tserverErrChan := make(chan error, 1)\n\tgo func() {\n\t\tdefer close(serverErrChan)\n\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t\tdefer conn.Close()\n\n\t\terr = conn.SetDeadline(time.Now().Add(5 * time.Second))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\n\t\terr = script.Run(pgproto3.NewBackend(conn, conn))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t}()\n\n\thost, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s\", host, port)\n\n\tctx, cancel = context.WithTimeout(ctx, 5*time.Second)\n\tdefer cancel()\n\tconn, err := pgconn.Connect(ctx, connStr)\n\trequire.NoError(t, err)\n\n\trr := conn.ExecParams(ctx, \"mocked...\", nil, nil, nil, nil)\n\n\tfor rr.NextRow() {\n\t}\n\n\t_, err = rr.Close()\n\trequire.Error(t, err)\n}\n\n// https://github.com/jackc/pgconn/issues/27\nfunc TestConnLargeResponseWhileWritingDoesNotDeadlock(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, \"set client_min_messages = debug5\").ReadAll()\n\trequire.NoError(t, err)\n\n\t// The actual contents of this test aren't important. What's important is a large amount of data to be written and\n\t// because of client_min_messages = debug5 the server will return a large amount of data.\n\n\tparamCount := math.MaxUint16\n\tparams := make([]string, 0, paramCount)\n\targs := make([][]byte, 0, paramCount)\n\tfor i := range paramCount {\n\t\tparams = append(params, fmt.Sprintf(\"($%d::text)\", i+1))\n\t\targs = append(args, []byte(strconv.Itoa(i)))\n\t}\n\tsql := \"values\" + strings.Join(params, \", \")\n\n\tresult := pgConn.ExecParams(ctx, sql, args, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\trequire.Len(t, result.Rows, paramCount)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestConnCheckConn(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\t// Intentionally using TCP connection for more predictable close behavior. (Not sure if Unix domain sockets would behave subtly different.)\n\n\tconnString := os.Getenv(\"PGX_TEST_TCP_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TCP_CONN_STRING\")\n\t}\n\n\tc1, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\tdefer c1.Close(ctx)\n\n\tif c1.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)\")\n\t}\n\n\terr = c1.CheckConn()\n\trequire.NoError(t, err)\n\n\tc2, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\tdefer c2.Close(ctx)\n\n\t_, err = c2.Exec(ctx, fmt.Sprintf(\"select pg_terminate_backend(%d)\", c1.PID())).ReadAll()\n\trequire.NoError(t, err)\n\n\t// It may take a while for the server to kill the backend. Retry until the error is detected or the test context is\n\t// canceled.\n\tfor err == nil && ctx.Err() == nil {\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\terr = c1.CheckConn()\n\t}\n\trequire.Error(t, err)\n}\n\nfunc TestConnPing(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\t// Intentionally using TCP connection for more predictable close behavior. (Not sure if Unix domain sockets would behave subtly different.)\n\n\tconnString := os.Getenv(\"PGX_TEST_TCP_CONN_STRING\")\n\tif connString == \"\" {\n\t\tt.Skipf(\"Skipping due to missing environment variable %v\", \"PGX_TEST_TCP_CONN_STRING\")\n\t}\n\n\tc1, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\tdefer c1.Close(ctx)\n\n\tif c1.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)\")\n\t}\n\n\terr = c1.Exec(ctx, \"set log_statement = 'all'\").Close()\n\trequire.NoError(t, err)\n\n\terr = c1.Ping(ctx)\n\trequire.NoError(t, err)\n\n\tc2, err := pgconn.Connect(ctx, connString)\n\trequire.NoError(t, err)\n\tdefer c2.Close(ctx)\n\n\t_, err = c2.Exec(ctx, fmt.Sprintf(\"select pg_terminate_backend(%d)\", c1.PID())).ReadAll()\n\trequire.NoError(t, err)\n\n\t// Give a little time for the signal to actually kill the backend.\n\ttime.Sleep(500 * time.Millisecond)\n\n\terr = c1.Ping(ctx)\n\trequire.Error(t, err)\n}\n\nfunc TestPipelinePrepare(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresult := pgConn.ExecParams(ctx, `create temporary table t (id text primary key)`, nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"selectInt\", \"select $1::bigint as a\", nil)\n\tpipeline.SendPrepare(\"selectText\", \"select $1::text as b\", nil)\n\tpipeline.SendPrepare(\"selectNoParams\", \"select 42 as c\", nil)\n\tpipeline.SendPrepare(\"insertNoResults\", \"insert into t (id) values ($1)\", nil)\n\tpipeline.SendPrepare(\"insertNoParamsOrResults\", \"insert into t (id) values ('foo')\", nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok := results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"a\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok = results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"b\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok = results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"c\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok = results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 0)\n\trequire.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok = results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 0)\n\trequire.Len(t, sd.ParamOIDs, 0)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelinePrepareError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"selectInt\", \"select $1::bigint as a\", nil)\n\tpipeline.SendPrepare(\"selectError\", \"bad\", nil)\n\tpipeline.SendPrepare(\"selectText\", \"select $1::text as b\", nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok := results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"a\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, err, &pgErr)\n\trequire.Nil(t, results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelinePrepareAndDeallocate(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"selectInt\", \"select $1::bigint as a\", nil)\n\tpipeline.SendDeallocate(\"selectInt\")\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok := results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"a\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.CloseComplete)\n\trequire.Truef(t, ok, \"expected CloseComplete, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 2`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 3`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tpipeline.SendQueryParams(`select 4`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 5`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"2\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"3\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"4\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"5\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelinePrepareQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"ps\", \"select $1::text as msg\", nil)\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"hello\")}, nil, nil)\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"goodbye\")}, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok := results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"msg\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"hello\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"goodbye\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineQueryErrorBetweenSyncs(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 2`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tpipeline.SendQueryParams(`select 3`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 1/(3-n) from generate_series(1,10) n`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 4`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tpipeline.SendQueryParams(`select 5`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 6`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"2\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"3\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, readResult.Err, &pgErr)\n\trequire.Equal(t, \"22012\", pgErr.Code)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"5\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"6\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineFlushForSingleRequests(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\n\tpipeline.SendPrepare(\"ps\", \"select $1::text as msg\", nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok := results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"msg\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"hello\")}, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"hello\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendDeallocate(\"ps\")\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.CloseComplete)\n\trequire.Truef(t, ok, \"expected CloseComplete, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineFlushForRequestSeries(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"ps\", \"select $1::bigint as num\", nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\tsd, ok := results.(*pgconn.StatementDescription)\n\trequire.Truef(t, ok, \"expected StatementDescription, got: %#v\", results)\n\trequire.Len(t, sd.Fields, 1)\n\trequire.Equal(t, \"num\", string(sd.Fields[0].Name))\n\trequire.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"1\")}, nil, nil)\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"2\")}, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"2\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"3\")}, nil, nil)\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"4\")}, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"3\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"4\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"5\")}, nil, nil)\n\tpipeline.SendFlushRequest()\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryPrepared(`ps`, [][]byte{[]byte(\"6\")}, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"5\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"6\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineFlushWithError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 1/(3-n) from generate_series(1,10) n`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 2`, nil, nil, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\tvar pgErr *pgconn.PgError\n\trequire.ErrorAs(t, readResult.Err, &pgErr)\n\trequire.Equal(t, \"22012\", pgErr.Code)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryParams(`select 3`, nil, nil, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\tpipeline.SendQueryParams(`select 4`, nil, nil, nil, nil)\n\tpipeline.SendPipelineSync()\n\tpipeline.SendQueryParams(`select 5`, nil, nil, nil, nil)\n\tpipeline.SendFlushRequest()\n\terr = pipeline.Flush()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult = rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"5\", string(readResult.Rows[0][0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trequire.Nil(t, results)\n\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, ok = results.(*pgconn.PipelineSync)\n\trequire.Truef(t, ok, \"expected PipelineSync, got: %#v\", results)\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineGetResultsHandlesPartiallyReadResults(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tsd, err := pgConn.Prepare(ctx, \"ps\", \"select n from generate_series($1::int, $2::int) n\", nil)\n\trequire.NoError(t, err)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendQueryStatement(sd, [][]byte{[]byte(\"1\"), []byte(\"3\")}, nil, nil)\n\tpipeline.SendQueryStatement(sd, [][]byte{[]byte(\"5\"), []byte(\"7\")}, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\trequire.True(t, rr.NextRow())\n\trequire.Equal(t, \"1\", string(rr.Values()[0]))\n\n\tresults, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok = results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\trequire.True(t, rr.NextRow())\n\trequire.Equal(t, \"5\", string(rr.Values()[0]))\n\trequire.True(t, rr.NextRow())\n\trequire.Equal(t, \"6\", string(rr.Values()[0]))\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineCloseReadsUnreadResults(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tsd, err := pgConn.Prepare(ctx, \"ps\", \"select $1::text as msg\", nil)\n\trequire.NoError(t, err)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 2`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 3`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tpipeline.SendQueryParams(`select 4`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 5`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tpipeline.SendQueryStatement(sd, [][]byte{[]byte(\"6\")}, nil, nil)\n\tpipeline.SendQueryStatement(sd, [][]byte{[]byte(\"7\")}, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\terr = pipeline.Close()\n\trequire.NoError(t, err)\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc TestPipelineCloseDetectsUnsyncedRequests(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpipeline := pgConn.StartPipeline(ctx)\n\tpipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 2`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 3`, nil, nil, nil, nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\tpipeline.SendQueryParams(`select 4`, nil, nil, nil, nil)\n\tpipeline.SendQueryParams(`select 5`, nil, nil, nil, nil)\n\n\tresults, err := pipeline.GetResults()\n\trequire.NoError(t, err)\n\trr, ok := results.(*pgconn.ResultReader)\n\trequire.Truef(t, ok, \"expected ResultReader, got: %#v\", results)\n\treadResult := rr.Read()\n\trequire.NoError(t, readResult.Err)\n\trequire.Len(t, readResult.Rows, 1)\n\trequire.Len(t, readResult.Rows[0], 1)\n\trequire.Equal(t, \"1\", string(readResult.Rows[0][0]))\n\n\terr = pipeline.Close()\n\trequire.EqualError(t, err, \"pipeline has unsynced requests\")\n}\n\nfunc TestConnOnPgError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tconfig.OnPgError = func(c *pgconn.PgConn, pgErr *pgconn.PgError) bool {\n\t\trequire.NotNil(t, c)\n\t\trequire.NotNil(t, pgErr)\n\t\t// close connection on undefined tables only\n\t\tif pgErr.Code == \"42P01\" {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t}\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\t_, err = pgConn.Exec(ctx, \"select 'Hello, world'\").ReadAll()\n\tassert.NoError(t, err)\n\tassert.False(t, pgConn.IsClosed())\n\n\t_, err = pgConn.Exec(ctx, \"select 1/0\").ReadAll()\n\tassert.Error(t, err)\n\tassert.False(t, pgConn.IsClosed())\n\n\t_, err = pgConn.Exec(ctx, \"select * from non_existent_table\").ReadAll()\n\tassert.Error(t, err)\n\tassert.True(t, pgConn.IsClosed())\n}\n\nfunc TestConnCustomData(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tpgConn.CustomData()[\"foo\"] = \"bar\"\n\tassert.Equal(t, \"bar\", pgConn.CustomData()[\"foo\"])\n\n\tensureConnValid(t, pgConn)\n}\n\nfunc Example() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\tdefer pgConn.Close(ctx)\n\n\tresult := pgConn.ExecParams(ctx, \"select generate_series(1,3)\", nil, nil, nil, nil).Read()\n\tif result.Err != nil {\n\t\tlog.Fatalln(result.Err)\n\t}\n\n\tfor _, row := range result.Rows {\n\t\tfmt.Println(string(row[0]))\n\t}\n\n\tfmt.Println(result.CommandTag)\n\t// Output:\n\t// 1\n\t// 2\n\t// 3\n\t// SELECT 3\n}\n\nfunc GetSSLPassword(ctx context.Context) string {\n\tconnString := os.Getenv(\"PGX_SSL_PASSWORD\")\n\treturn connString\n}\n\nvar rsaCertPEM = `-----BEGIN CERTIFICATE-----\nMIIDCTCCAfGgAwIBAgIUQDlN1g1bzxIJ8KWkayNcQY5gzMEwDQYJKoZIhvcNAQEL\nBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDgxNTIxNDgyNloXDTIzMDgx\nNTIxNDgyNlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEA0vOppiT8zE+076acRORzD5JVbRYKMK3XlWLVrHua4+ct\nRm54WyP+3XsYU4JGGGKgb8E+u2UosGJYcSM+b+U1/5XPTcpuumS+pCiD9WP++A39\ntsukYwR7m65cgpiI4dlLEZI3EWpAW+Bb3230KiYW4sAmQ0Ih4PrN+oPvzcs86F4d\n9Y03CqVUxRKLBLaClZQAg8qz2Pawwj1FKKjDX7u2fRVR0wgOugpCMOBJMcCgz9pp\n0HSa4x3KZDHEZY7Pah5XwWrCfAEfRWsSTGcNaoN8gSxGFM1JOEJa8SAuPGjFcYIv\nMmVWdw0FXCgYlSDL02fzLE0uyvXBDibzSqOk770JhQIDAQABo1MwUTAdBgNVHQ4E\nFgQUiJ8JLENJ+2k1Xl4o6y2Lc/qHTh0wHwYDVR0jBBgwFoAUiJ8JLENJ+2k1Xl4o\n6y2Lc/qHTh0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAwjn2\ngnNAhFvh58VqLIjU6ftvn6rhz5B9dg2+XyY8sskLhhkO1nL9339BVZsRt+eI3a7I\n81GNIm9qHVM3MUAcQv3SZy+0UPVUT8DNH2LwHT3CHnYTBP8U+8n8TDNGSTMUhIBB\nRx+6KwODpwLdI79VGT3IkbU9bZwuepB9I9nM5t/tt5kS4gHmJFlO0aLJFCTO4Scf\nhp/WLPv4XQUH+I3cPfaJRxz2j0Kc8iOzMhFmvl1XOGByjX6X33LnOzY/LVeTSGyS\nVgC32BGtnMwuy5XZYgFAeUx9HKy4tG4OH2Ux6uPF/WAhsug6PXSjV7BK6wYT5i27\nMlascjupnaptKX/wMA==\n-----END CERTIFICATE-----\n`\n\nvar rsaKeyPEM = testingKey(`-----BEGIN TESTING KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDS86mmJPzMT7Tv\nppxE5HMPklVtFgowrdeVYtWse5rj5y1GbnhbI/7dexhTgkYYYqBvwT67ZSiwYlhx\nIz5v5TX/lc9Nym66ZL6kKIP1Y/74Df22y6RjBHubrlyCmIjh2UsRkjcRakBb4Fvf\nbfQqJhbiwCZDQiHg+s36g+/NyzzoXh31jTcKpVTFEosEtoKVlACDyrPY9rDCPUUo\nqMNfu7Z9FVHTCA66CkIw4EkxwKDP2mnQdJrjHcpkMcRljs9qHlfBasJ8AR9FaxJM\nZw1qg3yBLEYUzUk4QlrxIC48aMVxgi8yZVZ3DQVcKBiVIMvTZ/MsTS7K9cEOJvNK\no6TvvQmFAgMBAAECggEAKzTK54Ol33bn2TnnwdiElIjlRE2CUswYXrl6iDRc2hbs\nWAOiVRB/T/+5UMla7/2rXJhY7+rdNZs/ABU24ZYxxCJ77jPrD/Q4c8j0lhsgCtBa\nycjV543wf0dsHTd+ubtWu8eVzdRUUD0YtB+CJevdPh4a+CWgaMMV0xyYzi61T+Yv\nZ7Uc3awIAiT4Kw9JRmJiTnyMJg5vZqW3BBAX4ZIvS/54ipwEU+9sWLcuH7WmCR0B\nQCTqS6hfJDLm//dGC89Iyno57zfYuiT3PYCWH5crr/DH3LqnwlNaOGSBkhkXuIL+\nQvOaUMe2i0pjqxDrkBx05V554vyy9jEvK7i330HL4QKBgQDUJmouEr0+o7EMBApC\nCPPu58K04qY5t9aGciG/pOurN42PF99yNZ1CnynH6DbcnzSl8rjc6Y65tzTlWods\nbjwVfcmcokG7sPcivJvVjrjKpSQhL8xdZwSAjcqjN4yoJ/+ghm9w+SRmZr6oCQZ3\n1jREfJKT+PGiWTEjYcExPWUD2QKBgQD+jdgq4c3tFavU8Hjnlf75xbStr5qu+fp2\nSGLRRbX+msQwVbl2ZM9AJLoX9MTCl7D9zaI3ONhheMmfJ77lDTa3VMFtr3NevGA6\nMxbiCEfRtQpNkJnsqCixLckx3bskj5+IF9BWzw7y7nOzdhoWVFv/+TltTm3RB51G\nMcdlmmVjjQKBgQDSFAw2/YV6vtu2O1XxGC591/Bd8MaMBziev+wde3GHhaZfGVPC\nI8dLTpMwCwowpFKdNeLLl1gnHX161I+f1vUWjw4TVjVjaBUBx+VEr2Tb/nXtiwiD\nQV0a883CnGJjreAblKRMKdpasMmBWhaWmn39h6Iad3zHuCzJjaaiXNpn2QKBgQCf\nk1Q8LanmQnuh1c41f7aD5gjKCRezMUpt9BrejhD1NxheJJ9LNQ8nat6uPedLBcUS\nlmJms+AR2qKqf0QQWyQ98YgAtshgTz8TvQtPT1mWgSOgVFHqJdC8obNK63FyDgc4\nTZVxlgQNDqbBjfv0m5XA9f+mIlB9hYR2iKYzb4K30QKBgQC+LEJYZh00zsXttGHr\n5wU1RzbgDIEsNuu+nZ4MxsaCik8ILNRHNXdeQbnADKuo6ATfhdmDIQMVZLG8Mivi\nUwnwLd1GhizvqvLHa3ULnFphRyMGFxaLGV48axTT2ADoMX67ILrIY/yjycLqRZ3T\nz3w+CgS20UrbLIR1YXfqUXge1g==\n-----END TESTING KEY-----\n`)\n\nfunc testingKey(s string) string { return strings.ReplaceAll(s, \"TESTING KEY\", \"PRIVATE KEY\") }\n\nfunc TestSNISupport(t *testing.T) {\n\tt.Parallel()\n\ttests := []struct {\n\t\tname      string\n\t\tsni_param string\n\t\tsni_set   bool\n\t}{\n\t\t{\n\t\t\tname:      \"SNI is passed by default\",\n\t\t\tsni_param: \"\",\n\t\t\tsni_set:   true,\n\t\t},\n\t\t{\n\t\t\tname:      \"SNI is passed when asked for\",\n\t\t\tsni_param: \"sslsni=1\",\n\t\t\tsni_set:   true,\n\t\t},\n\t\t{\n\t\t\tname:      \"SNI is not passed when disabled\",\n\t\t\tsni_param: \"sslsni=0\",\n\t\t\tsni_set:   false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer ln.Close()\n\n\t\t\tserverErrChan := make(chan error, 1)\n\t\t\tserverSNINameChan := make(chan string, 1)\n\t\t\tdefer close(serverErrChan)\n\t\t\tdefer close(serverSNINameChan)\n\n\t\t\tgo func() {\n\t\t\t\tvar sniHost string\n\n\t\t\t\tconn, err := ln.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer conn.Close()\n\n\t\t\t\terr = conn.SetDeadline(time.Now().Add(5 * time.Second))\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tbackend := pgproto3.NewBackend(conn, conn)\n\t\t\t\tstartupMessage, err := backend.ReceiveStartupMessage()\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tswitch startupMessage.(type) {\n\t\t\t\tcase *pgproto3.SSLRequest:\n\t\t\t\t\t_, err = conn.Write([]byte(\"S\"))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tserverErrChan <- fmt.Errorf(\"unexpected startup message: %#v\", startupMessage)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tcert, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM))\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrChan <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tsrv := tls.Server(conn, &tls.Config{\n\t\t\t\t\tCertificates: []tls.Certificate{cert},\n\t\t\t\t\tGetConfigForClient: func(argHello *tls.ClientHelloInfo) (*tls.Config, error) {\n\t\t\t\t\t\tsniHost = argHello.ServerName\n\t\t\t\t\t\treturn nil, nil\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tdefer srv.Close()\n\n\t\t\t\tif err := srv.Handshake(); err != nil {\n\t\t\t\t\tserverErrChan <- fmt.Errorf(\"handshake: %w\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tsrv.Write(mustEncode((&pgproto3.AuthenticationOk{}).Encode(nil)))\n\t\t\t\tsrv.Write(mustEncode((&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: []byte{0, 0, 0, 0}}).Encode(nil)))\n\t\t\t\tsrv.Write(mustEncode((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(nil)))\n\n\t\t\t\tserverSNINameChan <- sniHost\n\t\t\t}()\n\n\t\t\t_, port, _ := strings.Cut(ln.Addr().String(), \":\")\n\t\t\tconnStr := fmt.Sprintf(\"sslmode=require host=localhost port=%s %s\", port, tt.sni_param)\n\t\t\t_, _ = pgconn.Connect(ctx, connStr)\n\n\t\t\tselect {\n\t\t\tcase sniHost := <-serverSNINameChan:\n\t\t\t\tif tt.sni_set {\n\t\t\t\t\trequire.Equal(t, \"localhost\", sniHost)\n\t\t\t\t} else {\n\t\t\t\t\trequire.Equal(t, \"\", sniHost)\n\t\t\t\t}\n\t\t\tcase err = <-serverErrChan:\n\t\t\t\tt.Fatalf(\"server failed with error: %+v\", err)\n\t\t\tcase <-time.After(time.Millisecond * 100):\n\t\t\t\tt.Fatal(\"exceeded connection timeout without erroring out\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectWithDirectSSLNegotiation(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname             string\n\t\tconnString       string\n\t\texpectDirectNego bool\n\t}{\n\t\t{\n\t\t\tname:             \"Default negotiation (postgres)\",\n\t\t\tconnString:       \"sslmode=require\",\n\t\t\texpectDirectNego: false,\n\t\t},\n\t\t{\n\t\t\tname:             \"Direct negotiation\",\n\t\t\tconnString:       \"sslmode=require sslnegotiation=direct\",\n\t\t\texpectDirectNego: true,\n\t\t},\n\t\t{\n\t\t\tname:             \"Explicit postgres negotiation\",\n\t\t\tconnString:       \"sslmode=require sslnegotiation=postgres\",\n\t\t\texpectDirectNego: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tscript := &pgmock.Script{\n\t\t\t\tSteps: pgmock.AcceptUnauthenticatedConnRequestSteps(),\n\t\t\t}\n\n\t\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer ln.Close()\n\n\t\t\t_, port, err := net.SplitHostPort(ln.Addr().String())\n\t\t\trequire.NoError(t, err)\n\n\t\t\tvar directNegoObserved atomic.Bool\n\n\t\t\tserverErrCh := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\tdefer close(serverErrCh)\n\n\t\t\t\tconn, err := ln.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrCh <- fmt.Errorf(\"accept error: %w\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer conn.Close()\n\n\t\t\t\tconn.SetDeadline(time.Now().Add(5 * time.Second))\n\n\t\t\t\tfirstByte := make([]byte, 1)\n\t\t\t\t_, err = conn.Read(firstByte)\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrCh <- fmt.Errorf(\"read first byte error: %w\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Check if TLS Client Hello (direct) or PostgreSQL SSLRequest\n\t\t\t\tisDirect := firstByte[0] >= 20 && firstByte[0] <= 23\n\t\t\t\tdirectNegoObserved.Store(isDirect)\n\n\t\t\t\tvar tlsConn *tls.Conn\n\n\t\t\t\tif !isDirect {\n\t\t\t\t\t// Handle standard PostgreSQL SSL negotiation\n\t\t\t\t\t// Read the rest of the SSL request message\n\t\t\t\t\tsslRequestRemainder := make([]byte, 7)\n\t\t\t\t\t_, err = io.ReadFull(conn, sslRequestRemainder)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tserverErrCh <- fmt.Errorf(\"read ssl request remainder error: %w\", err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Send SSL acceptance response\n\t\t\t\t\t_, err = conn.Write([]byte(\"S\"))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tserverErrCh <- fmt.Errorf(\"write ssl acceptance error: %w\", err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Setup TLS server without needing to reuse the first byte\n\t\t\t\t\tcert, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tserverErrCh <- fmt.Errorf(\"cert error: %w\", err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\ttlsConn = tls.Server(conn, &tls.Config{\n\t\t\t\t\t\tCertificates: []tls.Certificate{cert},\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\t// Handle direct TLS negotiation\n\t\t\t\t\t// Setup TLS server with the first byte already read\n\t\t\t\t\tcert, err := tls.X509KeyPair([]byte(rsaCertPEM), []byte(rsaKeyPEM))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tserverErrCh <- fmt.Errorf(\"cert error: %w\", err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\t// Use a wrapper to inject the first byte back into the TLS handshake\n\t\t\t\t\tbufConn := &prefixConn{\n\t\t\t\t\t\tConn:       conn,\n\t\t\t\t\t\tprefixData: firstByte,\n\t\t\t\t\t}\n\n\t\t\t\t\ttlsConn = tls.Server(bufConn, &tls.Config{\n\t\t\t\t\t\tCertificates: []tls.Certificate{cert},\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t// Complete TLS handshake\n\t\t\t\tif err := tlsConn.Handshake(); err != nil {\n\t\t\t\t\tserverErrCh <- fmt.Errorf(\"TLS handshake error: %w\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer tlsConn.Close()\n\n\t\t\t\terr = script.Run(pgproto3.NewBackend(tlsConn, tlsConn))\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErrCh <- fmt.Errorf(\"pgmock run error: %w\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tconnStr := fmt.Sprintf(\"%s host=localhost port=%s sslmode=require sslinsecure=1\",\n\t\t\t\ttt.connString, port)\n\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tconn, err := pgconn.Connect(ctx, connStr)\n\n\t\t\trequire.NoError(t, err)\n\n\t\t\tdefer conn.Close(ctx)\n\n\t\t\terr = <-serverErrCh\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Equal(t, tt.expectDirectNego, directNegoObserved.Load())\n\t\t})\n\t}\n}\n\n// prefixConn implements a net.Conn that prepends some data to the first Read\ntype prefixConn struct {\n\tnet.Conn\n\tprefixData     []byte\n\tprefixConsumed bool\n}\n\nfunc (c *prefixConn) Read(b []byte) (n int, err error) {\n\tif !c.prefixConsumed && len(c.prefixData) > 0 {\n\t\tn = copy(b, c.prefixData)\n\t\tc.prefixData = c.prefixData[n:]\n\t\tc.prefixConsumed = len(c.prefixData) == 0\n\t\treturn n, nil\n\t}\n\treturn c.Conn.Read(b)\n}\n\n// https://github.com/jackc/pgx/issues/1920\nfunc TestFatalErrorReceivedInPipelineMode(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tsteps := pgmock.AcceptUnauthenticatedConnRequestSteps()\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Parse{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Describe{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Parse{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Describe{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Parse{}))\n\tsteps = append(steps, pgmock.ExpectAnyMessage(&pgproto3.Describe{}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.ParseComplete{}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.ParameterDescription{}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.RowDescription{Fields: []pgproto3.FieldDescription{\n\t\t{Name: []byte(\"mock\")},\n\t}}))\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.ErrorResponse{Severity: \"FATAL\", Code: \"57P01\"}))\n\t// We shouldn't get anything after the first fatal error. But the reported issue was with PgBouncer so maybe that\n\t// causes the issue. Anyway, a FATAL error after the connection had already been killed could cause a panic.\n\tsteps = append(steps, pgmock.SendMessage(&pgproto3.ErrorResponse{Severity: \"FATAL\", Code: \"57P01\"}))\n\n\tscript := &pgmock.Script{Steps: steps}\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\trequire.NoError(t, err)\n\tdefer ln.Close()\n\n\tserverKeepAlive := make(chan struct{})\n\tdefer close(serverKeepAlive)\n\n\tserverErrChan := make(chan error, 1)\n\tgo func() {\n\t\tdefer close(serverErrChan)\n\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t\tdefer conn.Close()\n\n\t\terr = conn.SetDeadline(time.Now().Add(59 * time.Second))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\n\t\terr = script.Run(pgproto3.NewBackend(conn, conn))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\n\t\t<-serverKeepAlive\n\t}()\n\n\tparts := strings.Split(ln.Addr().String(), \":\")\n\thost := parts[0]\n\tport := parts[1]\n\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s\", host, port)\n\n\tctx, cancel = context.WithTimeout(ctx, 59*time.Second)\n\tdefer cancel()\n\tconn, err := pgconn.Connect(ctx, connStr)\n\trequire.NoError(t, err)\n\n\tpipeline := conn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"s1\", \"select 1\", nil)\n\tpipeline.SendPrepare(\"s2\", \"select 2\", nil)\n\tpipeline.SendPrepare(\"s3\", \"select 3\", nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\t_, err = pipeline.GetResults()\n\trequire.NoError(t, err)\n\t_, err = pipeline.GetResults()\n\trequire.Error(t, err)\n\n\terr = pipeline.Close()\n\trequire.Error(t, err)\n}\n\n// https://github.com/jackc/pgx/issues/2470\n// When the server sends multiple FATAL errors in a single batch (as PgBouncer can do when\n// terminating idle-in-transaction connections), Pipeline.Close() must not panic with\n// \"close of closed channel\" on cleanupDone. The first FATAL triggers OnPgError which closes\n// the connection and cleanupDone. The second FATAL, still in the read buffer, must not\n// attempt to close cleanupDone again.\n//\n// This test sends all server responses in a single TCP write to guarantee both FATAL errors\n// are in the chunkReader buffer simultaneously.\nfunc TestPipelineCloseDoesNotPanicOnMultipleFatalErrors(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:\")\n\trequire.NoError(t, err)\n\tdefer ln.Close()\n\n\tserverErrChan := make(chan error, 1)\n\tgo func() {\n\t\tdefer close(serverErrChan)\n\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t\tdefer conn.Close()\n\n\t\terr = conn.SetDeadline(time.Now().Add(59 * time.Second))\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\n\t\tbackend := pgproto3.NewBackend(conn, conn)\n\n\t\t// Handle startup\n\t\t_, err = backend.ReceiveStartupMessage()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t\tbackend.Send(&pgproto3.AuthenticationOk{})\n\t\tbackend.Send(&pgproto3.BackendKeyData{ProcessID: 0, SecretKey: []byte{0, 0, 0, 0}})\n\t\tbackend.Send(&pgproto3.ReadyForQuery{TxStatus: 'I'})\n\t\terr = backend.Flush()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\n\t\t// Read all client pipeline messages (Parse, Describe, Parse, Describe, Sync)\n\t\tfor i := 0; i < 5; i++ {\n\t\t\t_, err = backend.Receive()\n\t\t\tif err != nil {\n\t\t\t\tserverErrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// Send ALL responses in a single write so they all end up in the chunkReader buffer.\n\t\t// This simulates PgBouncer sending a FATAL and then the real PostgreSQL also sending\n\t\t// a FATAL, both arriving in the same TCP segment.\n\t\tbackend.Send(&pgproto3.ParseComplete{})\n\t\tbackend.Send(&pgproto3.ParameterDescription{})\n\t\tbackend.Send(&pgproto3.RowDescription{Fields: []pgproto3.FieldDescription{\n\t\t\t{Name: []byte(\"mock\")},\n\t\t}})\n\t\t// Two FATAL errors back-to-back in the same write buffer\n\t\tbackend.Send(&pgproto3.ErrorResponse{Severity: \"FATAL\", Code: \"57P01\", Message: \"terminating connection due to administrator command\"})\n\t\tbackend.Send(&pgproto3.ErrorResponse{Severity: \"FATAL\", Code: \"57P01\", Message: \"terminating connection due to administrator command\"})\n\t\terr = backend.Flush()\n\t\tif err != nil {\n\t\t\tserverErrChan <- err\n\t\t\treturn\n\t\t}\n\t}()\n\n\tparts := strings.Split(ln.Addr().String(), \":\")\n\thost := parts[0]\n\tport := parts[1]\n\tconnStr := fmt.Sprintf(\"sslmode=disable host=%s port=%s\", host, port)\n\n\tctx, cancel = context.WithTimeout(ctx, 59*time.Second)\n\tdefer cancel()\n\tconn, err := pgconn.Connect(ctx, connStr)\n\trequire.NoError(t, err)\n\n\tpipeline := conn.StartPipeline(ctx)\n\tpipeline.SendPrepare(\"s1\", \"select 1\", nil)\n\tpipeline.SendPrepare(\"s2\", \"select 2\", nil)\n\terr = pipeline.Sync()\n\trequire.NoError(t, err)\n\n\t// Do NOT call GetResults. Call Close() directly so it drains results via getResults().\n\t// The first FATAL closes the connection via OnPgError, including close(cleanupDone).\n\t// The second FATAL is still buffered in chunkReader. Without the fix, processing it\n\t// would attempt to close cleanupDone again, causing a panic.\n\terr = pipeline.Close()\n\trequire.Error(t, err)\n}\n\nfunc mustEncode(buf []byte, err error) []byte {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn buf\n}\n\nfunc TestDeadlineContextWatcherHandler(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"DeadlineExceeded with zero DeadlineDelay\", func(t *testing.T) {\n\t\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tconfig.BuildContextWatcherHandler = func(conn *pgconn.PgConn) ctxwatch.Handler {\n\t\t\treturn &pgconn.DeadlineContextWatcherHandler{Conn: conn.Conn()}\n\t\t}\n\t\tconfig.ConnectTimeout = 5 * time.Second\n\n\t\tpgConn, err := pgconn.ConnectConfig(context.Background(), config)\n\t\trequire.NoError(t, err)\n\t\tdefer closeConn(t, pgConn)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t_, err = pgConn.Exec(ctx, \"select 1, pg_sleep(1)\").ReadAll()\n\t\trequire.Error(t, err)\n\t\trequire.ErrorIs(t, err, context.DeadlineExceeded)\n\t\trequire.True(t, pgConn.IsClosed())\n\t})\n\n\tt.Run(\"DeadlineExceeded with DeadlineDelay\", func(t *testing.T) {\n\t\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tconfig.BuildContextWatcherHandler = func(conn *pgconn.PgConn) ctxwatch.Handler {\n\t\t\treturn &pgconn.DeadlineContextWatcherHandler{Conn: conn.Conn(), DeadlineDelay: 500 * time.Millisecond}\n\t\t}\n\t\tconfig.ConnectTimeout = 5 * time.Second\n\n\t\tpgConn, err := pgconn.ConnectConfig(context.Background(), config)\n\t\trequire.NoError(t, err)\n\t\tdefer closeConn(t, pgConn)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t_, err = pgConn.Exec(ctx, \"select 1, pg_sleep(0.250)\").ReadAll()\n\t\trequire.NoError(t, err)\n\n\t\tensureConnValid(t, pgConn)\n\t})\n}\n\nfunc TestCancelRequestContextWatcherHandler(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"DeadlineExceeded cancels request after CancelRequestDelay\", func(t *testing.T) {\n\t\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tconfig.BuildContextWatcherHandler = func(conn *pgconn.PgConn) ctxwatch.Handler {\n\t\t\treturn &pgconn.CancelRequestContextWatcherHandler{\n\t\t\t\tConn:               conn,\n\t\t\t\tCancelRequestDelay: 250 * time.Millisecond,\n\t\t\t\tDeadlineDelay:      5000 * time.Millisecond,\n\t\t\t}\n\t\t}\n\t\tconfig.ConnectTimeout = 5 * time.Second\n\n\t\tpgConn, err := pgconn.ConnectConfig(context.Background(), config)\n\t\trequire.NoError(t, err)\n\t\tdefer closeConn(t, pgConn)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t_, err = pgConn.Exec(ctx, \"select 1, pg_sleep(3)\").ReadAll()\n\t\trequire.Error(t, err)\n\t\tvar pgErr *pgconn.PgError\n\t\trequire.ErrorAs(t, err, &pgErr)\n\n\t\tensureConnValid(t, pgConn)\n\t})\n\n\tt.Run(\"DeadlineExceeded - do not send cancel request when query finishes in grace period\", func(t *testing.T) {\n\t\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tconfig.BuildContextWatcherHandler = func(conn *pgconn.PgConn) ctxwatch.Handler {\n\t\t\treturn &pgconn.CancelRequestContextWatcherHandler{\n\t\t\t\tConn:               conn,\n\t\t\t\tCancelRequestDelay: 1000 * time.Millisecond,\n\t\t\t\tDeadlineDelay:      5000 * time.Millisecond,\n\t\t\t}\n\t\t}\n\t\tconfig.ConnectTimeout = 5 * time.Second\n\n\t\tpgConn, err := pgconn.ConnectConfig(context.Background(), config)\n\t\trequire.NoError(t, err)\n\t\tdefer closeConn(t, pgConn)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t_, err = pgConn.Exec(ctx, \"select 1, pg_sleep(0.250)\").ReadAll()\n\t\trequire.NoError(t, err)\n\n\t\tensureConnValid(t, pgConn)\n\t})\n\n\tt.Run(\"DeadlineExceeded sets conn deadline with DeadlineDelay\", func(t *testing.T) {\n\t\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tconfig.BuildContextWatcherHandler = func(conn *pgconn.PgConn) ctxwatch.Handler {\n\t\t\treturn &pgconn.CancelRequestContextWatcherHandler{\n\t\t\t\tConn:               conn,\n\t\t\t\tCancelRequestDelay: 5000 * time.Millisecond, // purposely setting this higher than DeadlineDelay to ensure the cancel request never happens.\n\t\t\t\tDeadlineDelay:      250 * time.Millisecond,\n\t\t\t}\n\t\t}\n\t\tconfig.ConnectTimeout = 5 * time.Second\n\n\t\tpgConn, err := pgconn.ConnectConfig(context.Background(), config)\n\t\trequire.NoError(t, err)\n\t\tdefer closeConn(t, pgConn)\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t_, err = pgConn.Exec(ctx, \"select 1, pg_sleep(1)\").ReadAll()\n\t\trequire.Error(t, err)\n\t\trequire.ErrorIs(t, err, context.DeadlineExceeded)\n\t\trequire.True(t, pgConn.IsClosed())\n\t})\n\n\tfor i := range 10 {\n\t\tt.Run(fmt.Sprintf(\"Stress %d\", i), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tconfig.BuildContextWatcherHandler = func(conn *pgconn.PgConn) ctxwatch.Handler {\n\t\t\t\treturn &pgconn.CancelRequestContextWatcherHandler{\n\t\t\t\t\tConn:               conn,\n\t\t\t\t\tCancelRequestDelay: 5 * time.Millisecond,\n\t\t\t\t\tDeadlineDelay:      1000 * time.Millisecond,\n\t\t\t\t}\n\t\t\t}\n\t\t\tconfig.ConnectTimeout = 5 * time.Second\n\n\t\t\tpgConn, err := pgconn.ConnectConfig(context.Background(), config)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer closeConn(t, pgConn)\n\n\t\t\tfor range 20 {\n\t\t\t\tfunc() {\n\t\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 4*time.Millisecond)\n\t\t\t\t\tdefer cancel()\n\t\t\t\t\tpgConn.Exec(ctx, \"select 1, pg_sleep(0.010)\").ReadAll()\n\t\t\t\t\ttime.Sleep(100 * time.Millisecond) // ensure a cancel request that was a little late doesn't interrupt ensureConnValid.\n\t\t\t\t\tensureConnValid(t, pgConn)\n\t\t\t\t}()\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnectProtocolVersion32(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\t// Check if we're connecting to CockroachDB before attempting protocol v3.2, since CockroachDB\n\t// does not support protocol version 3.2 and will close the connection.\n\t{\n\t\tcheckConn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\tif err == nil {\n\t\t\tisCRDB := checkConn.ParameterStatus(\"crdb_version\") != \"\"\n\t\t\tcheckConn.Close(ctx)\n\t\t\tif isCRDB {\n\t\t\t\tt.Skip(\"CockroachDB does not support protocol version 3.2 yet\")\n\t\t\t}\n\t\t}\n\t}\n\n\tconfig, err := pgconn.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tconfig.MaxProtocolVersion = \"3.2\"\n\n\tpgConn, err := pgconn.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer closeConn(t, pgConn)\n\n\tresult, err := pgConn.Exec(context.Background(), \"show server_version_num\").ReadAll()\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Len(t, result[0].Rows, 1)\n\trequire.Len(t, result[0].Rows[0], 1)\n\tpgVersion, err := strconv.Atoi(string(result[0].Rows[0][0]))\n\trequire.NoError(t, err)\n\n\t// Check secret key length - PG18+ returns 32 bytes, older versions return 4\n\tsecretKey := pgConn.SecretKey()\n\n\tif pgVersion < 180000 {\n\t\tassert.Len(t, secretKey, 4)\n\t} else {\n\t\tassert.Len(t, secretKey, 32)\n\t}\n}\n"
  },
  {
    "path": "pgproto3/README.md",
    "content": "# pgproto3\n\nPackage pgproto3 is an encoder and decoder of the PostgreSQL wire protocol version 3.\n\npgproto3 can be used as a foundation for PostgreSQL drivers, proxies, mock servers, load balancers and more.\n\nSee example/pgfortune for a playful example of a fake PostgreSQL server.\n"
  },
  {
    "path": "pgproto3/authentication_cleartext_password.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationCleartextPassword is a message sent from the backend indicating that a clear-text password is required.\ntype AuthenticationCleartextPassword struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationCleartextPassword) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationCleartextPassword) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationCleartextPassword) Decode(src []byte) error {\n\tif len(src) != 4 {\n\t\treturn errors.New(\"bad authentication message size\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeCleartextPassword {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationCleartextPassword) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeCleartextPassword)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src AuthenticationCleartextPassword) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"AuthenticationCleartextPassword\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/authentication_gss.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype AuthenticationGSS struct{}\n\nfunc (a *AuthenticationGSS) Backend() {}\n\nfunc (a *AuthenticationGSS) AuthenticationResponse() {}\n\nfunc (a *AuthenticationGSS) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"authentication message too short\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeGSS {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\treturn nil\n}\n\nfunc (a *AuthenticationGSS) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeGSS)\n\treturn finishMessage(dst, sp)\n}\n\nfunc (a *AuthenticationGSS) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData []byte\n\t}{\n\t\tType: \"AuthenticationGSS\",\n\t})\n}\n\nfunc (a *AuthenticationGSS) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tType string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/authentication_gss_continue.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype AuthenticationGSSContinue struct {\n\tData []byte\n}\n\nfunc (a *AuthenticationGSSContinue) Backend() {}\n\nfunc (a *AuthenticationGSSContinue) AuthenticationResponse() {}\n\nfunc (a *AuthenticationGSSContinue) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"authentication message too short\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeGSSCont {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\ta.Data = src[4:]\n\treturn nil\n}\n\nfunc (a *AuthenticationGSSContinue) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeGSSCont)\n\tdst = append(dst, a.Data...)\n\treturn finishMessage(dst, sp)\n}\n\nfunc (a *AuthenticationGSSContinue) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData []byte\n\t}{\n\t\tType: \"AuthenticationGSSContinue\",\n\t\tData: a.Data,\n\t})\n}\n\nfunc (a *AuthenticationGSSContinue) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tType string\n\t\tData []byte\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\ta.Data = msg.Data\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/authentication_md5_password.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationMD5Password is a message sent from the backend indicating that an MD5 hashed password is required.\ntype AuthenticationMD5Password struct {\n\tSalt [4]byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationMD5Password) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationMD5Password) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationMD5Password) Decode(src []byte) error {\n\tif len(src) != 8 {\n\t\treturn errors.New(\"bad authentication message size\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeMD5Password {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\tcopy(dst.Salt[:], src[4:8])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationMD5Password) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeMD5Password)\n\tdst = append(dst, src.Salt[:]...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src AuthenticationMD5Password) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tSalt [4]byte\n\t}{\n\t\tType: \"AuthenticationMD5Password\",\n\t\tSalt: src.Salt,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *AuthenticationMD5Password) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tType string\n\t\tSalt [4]byte\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.Salt = msg.Salt\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/authentication_ok.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationOk is a message sent from the backend indicating that authentication was successful.\ntype AuthenticationOk struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationOk) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationOk) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationOk) Decode(src []byte) error {\n\tif len(src) != 4 {\n\t\treturn errors.New(\"bad authentication message size\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeOk {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationOk) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeOk)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src AuthenticationOk) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"AuthenticationOK\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/authentication_sasl.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationSASL is a message sent from the backend indicating that SASL authentication is required.\ntype AuthenticationSASL struct {\n\tAuthMechanisms []string\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationSASL) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationSASL) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationSASL) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"authentication message too short\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeSASL {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\tauthMechanisms := src[4:]\n\tfor len(authMechanisms) > 1 {\n\t\tidx := bytes.IndexByte(authMechanisms, 0)\n\t\tif idx == -1 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"AuthenticationSASL\", details: \"unterminated string\"}\n\t\t}\n\t\tdst.AuthMechanisms = append(dst.AuthMechanisms, string(authMechanisms[:idx]))\n\t\tauthMechanisms = authMechanisms[idx+1:]\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationSASL) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeSASL)\n\n\tfor _, s := range src.AuthMechanisms {\n\t\tdst = append(dst, []byte(s)...)\n\t\tdst = append(dst, 0)\n\t}\n\tdst = append(dst, 0)\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src AuthenticationSASL) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType           string\n\t\tAuthMechanisms []string\n\t}{\n\t\tType:           \"AuthenticationSASL\",\n\t\tAuthMechanisms: src.AuthMechanisms,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/authentication_sasl_continue.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationSASLContinue is a message sent from the backend containing a SASL challenge.\ntype AuthenticationSASLContinue struct {\n\tData []byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationSASLContinue) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationSASLContinue) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationSASLContinue) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"authentication message too short\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeSASLContinue {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\tdst.Data = src[4:]\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationSASLContinue) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeSASLContinue)\n\tdst = append(dst, src.Data...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src AuthenticationSASLContinue) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData string\n\t}{\n\t\tType: \"AuthenticationSASLContinue\",\n\t\tData: string(src.Data),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *AuthenticationSASLContinue) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tData string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.Data = []byte(msg.Data)\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/authentication_sasl_final.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// AuthenticationSASLFinal is a message sent from the backend indicating a SASL authentication has completed.\ntype AuthenticationSASLFinal struct {\n\tData []byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*AuthenticationSASLFinal) Backend() {}\n\n// Backend identifies this message as an authentication response.\nfunc (*AuthenticationSASLFinal) AuthenticationResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *AuthenticationSASLFinal) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"authentication message too short\")\n\t}\n\n\tauthType := binary.BigEndian.Uint32(src)\n\n\tif authType != AuthTypeSASLFinal {\n\t\treturn errors.New(\"bad auth type\")\n\t}\n\n\tdst.Data = src[4:]\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *AuthenticationSASLFinal) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'R')\n\tdst = pgio.AppendUint32(dst, AuthTypeSASLFinal)\n\tdst = append(dst, src.Data...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Unmarshaler.\nfunc (src AuthenticationSASLFinal) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData string\n\t}{\n\t\tType: \"AuthenticationSASLFinal\",\n\t\tData: string(src.Data),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *AuthenticationSASLFinal) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tData string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.Data = []byte(msg.Data)\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/backend.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// Backend acts as a server for the PostgreSQL wire protocol version 3.\ntype Backend struct {\n\tcr *chunkReader\n\tw  io.Writer\n\n\t// tracer is used to trace messages when Send or Receive is called. This means an outbound message is traced\n\t// before it is actually transmitted (i.e. before Flush).\n\ttracer *tracer\n\n\twbuf        []byte\n\tencodeError error\n\n\t// Frontend message flyweights\n\tbind           Bind\n\tcancelRequest  CancelRequest\n\t_close         Close\n\tcopyFail       CopyFail\n\tcopyData       CopyData\n\tcopyDone       CopyDone\n\tdescribe       Describe\n\texecute        Execute\n\tflush          Flush\n\tfunctionCall   FunctionCall\n\tgssEncRequest  GSSEncRequest\n\tparse          Parse\n\tquery          Query\n\tsslRequest     SSLRequest\n\tstartupMessage StartupMessage\n\tsync           Sync\n\tterminate      Terminate\n\n\tbodyLen    int\n\tmaxBodyLen int // maxBodyLen is the maximum length of a message body in octets. If a message body exceeds this length, Receive will return an error.\n\tmsgType    byte\n\tpartialMsg bool\n\tauthType   uint32\n}\n\nconst (\n\tminStartupPacketLen = 4      // minStartupPacketLen is a single 32-bit int version or code.\n\tmaxStartupPacketLen = 10_000 // maxStartupPacketLen is MAX_STARTUP_PACKET_LENGTH from PG source.\n)\n\n// NewBackend creates a new Backend.\nfunc NewBackend(r io.Reader, w io.Writer) *Backend {\n\tcr := newChunkReader(r, 0)\n\treturn &Backend{cr: cr, w: w}\n}\n\n// Send sends a message to the frontend (i.e. the client). The message is buffered until Flush is called. Any error\n// encountered will be returned from Flush.\nfunc (b *Backend) Send(msg BackendMessage) {\n\tif b.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(b.wbuf)\n\tnewBuf, err := msg.Encode(b.wbuf)\n\tif err != nil {\n\t\tb.encodeError = err\n\t\treturn\n\t}\n\tb.wbuf = newBuf\n\n\tif b.tracer != nil {\n\t\tb.tracer.traceMessage('B', int32(len(b.wbuf)-prevLen), msg)\n\t}\n}\n\n// Flush writes any pending messages to the frontend (i.e. the client).\nfunc (b *Backend) Flush() error {\n\tif err := b.encodeError; err != nil {\n\t\tb.encodeError = nil\n\t\tb.wbuf = b.wbuf[:0]\n\t\treturn &writeError{err: err, safeToRetry: true}\n\t}\n\n\tn, err := b.w.Write(b.wbuf)\n\n\tconst maxLen = 1024\n\tif len(b.wbuf) > maxLen {\n\t\tb.wbuf = make([]byte, 0, maxLen)\n\t} else {\n\t\tb.wbuf = b.wbuf[:0]\n\t}\n\n\tif err != nil {\n\t\treturn &writeError{err: err, safeToRetry: n == 0}\n\t}\n\n\treturn nil\n}\n\n// Trace starts tracing the message traffic to w. It writes in a similar format to that produced by the libpq function\n// PQtrace.\nfunc (b *Backend) Trace(w io.Writer, options TracerOptions) {\n\tb.tracer = &tracer{\n\t\tw:             w,\n\t\tbuf:           &bytes.Buffer{},\n\t\tTracerOptions: options,\n\t}\n}\n\n// Untrace stops tracing.\nfunc (b *Backend) Untrace() {\n\tb.tracer = nil\n}\n\n// ReceiveStartupMessage receives the initial connection message. This method is used of the normal Receive method\n// because the initial connection message is \"special\" and does not include the message type as the first byte. This\n// will return either a StartupMessage, SSLRequest, GSSEncRequest, or CancelRequest.\nfunc (b *Backend) ReceiveStartupMessage() (FrontendMessage, error) {\n\tbuf, err := b.cr.Next(4)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmsgSize := int(binary.BigEndian.Uint32(buf) - 4)\n\n\tif msgSize < minStartupPacketLen || msgSize > maxStartupPacketLen {\n\t\treturn nil, fmt.Errorf(\"invalid length of startup packet: %d\", msgSize)\n\t}\n\n\tbuf, err = b.cr.Next(msgSize)\n\tif err != nil {\n\t\treturn nil, translateEOFtoErrUnexpectedEOF(err)\n\t}\n\n\tcode := binary.BigEndian.Uint32(buf)\n\n\tswitch code {\n\tcase ProtocolVersion30, ProtocolVersion32:\n\t\terr = b.startupMessage.Decode(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &b.startupMessage, nil\n\tcase sslRequestNumber:\n\t\terr = b.sslRequest.Decode(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &b.sslRequest, nil\n\tcase cancelRequestCode:\n\t\terr = b.cancelRequest.Decode(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &b.cancelRequest, nil\n\tcase gssEncReqNumber:\n\t\terr = b.gssEncRequest.Decode(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &b.gssEncRequest, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown startup message code: %d\", code)\n\t}\n}\n\n// Receive receives a message from the frontend. The returned message is only valid until the next call to Receive.\nfunc (b *Backend) Receive() (FrontendMessage, error) {\n\tif !b.partialMsg {\n\t\theader, err := b.cr.Next(5)\n\t\tif err != nil {\n\t\t\treturn nil, translateEOFtoErrUnexpectedEOF(err)\n\t\t}\n\n\t\tb.msgType = header[0]\n\n\t\tmsgLength := int(binary.BigEndian.Uint32(header[1:]))\n\t\tif msgLength < 4 {\n\t\t\treturn nil, fmt.Errorf(\"invalid message length: %d\", msgLength)\n\t\t}\n\n\t\tb.bodyLen = msgLength - 4\n\t\tif b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {\n\t\t\treturn nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}\n\t\t}\n\t\tb.partialMsg = true\n\t}\n\n\tvar msg FrontendMessage\n\tswitch b.msgType {\n\tcase 'B':\n\t\tmsg = &b.bind\n\tcase 'C':\n\t\tmsg = &b._close\n\tcase 'D':\n\t\tmsg = &b.describe\n\tcase 'E':\n\t\tmsg = &b.execute\n\tcase 'F':\n\t\tmsg = &b.functionCall\n\tcase 'f':\n\t\tmsg = &b.copyFail\n\tcase 'd':\n\t\tmsg = &b.copyData\n\tcase 'c':\n\t\tmsg = &b.copyDone\n\tcase 'H':\n\t\tmsg = &b.flush\n\tcase 'P':\n\t\tmsg = &b.parse\n\tcase 'p':\n\t\tswitch b.authType {\n\t\tcase AuthTypeSASL:\n\t\t\tmsg = &SASLInitialResponse{}\n\t\tcase AuthTypeSASLContinue:\n\t\t\tmsg = &SASLResponse{}\n\t\tcase AuthTypeSASLFinal:\n\t\t\tmsg = &SASLResponse{}\n\t\tcase AuthTypeGSS, AuthTypeGSSCont:\n\t\t\tmsg = &GSSResponse{}\n\t\tcase AuthTypeCleartextPassword, AuthTypeMD5Password:\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\t// to maintain backwards compatibility\n\t\t\tmsg = &PasswordMessage{}\n\t\t}\n\tcase 'Q':\n\t\tmsg = &b.query\n\tcase 'S':\n\t\tmsg = &b.sync\n\tcase 'X':\n\t\tmsg = &b.terminate\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown message type: %c\", b.msgType)\n\t}\n\n\tmsgBody, err := b.cr.Next(b.bodyLen)\n\tif err != nil {\n\t\treturn nil, translateEOFtoErrUnexpectedEOF(err)\n\t}\n\n\tb.partialMsg = false\n\n\terr = msg.Decode(msgBody)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif b.tracer != nil {\n\t\tb.tracer.traceMessage('F', int32(5+len(msgBody)), msg)\n\t}\n\n\treturn msg, nil\n}\n\n// SetAuthType sets the authentication type in the backend.\n// Since multiple message types can start with 'p', SetAuthType allows\n// contextual identification of FrontendMessages. For example, in the\n// PG message flow documentation for PasswordMessage:\n//\n//\t\t\tByte1('p')\n//\n//\t     Identifies the message as a password response. Note that this is also used for\n//\t\t\tGSSAPI, SSPI and SASL response messages. The exact message type can be deduced from\n//\t\t\tthe context.\n//\n// Since the Frontend does not know about the state of a backend, it is important\n// to call SetAuthType() after an authentication request is received by the Frontend.\nfunc (b *Backend) SetAuthType(authType uint32) error {\n\tswitch authType {\n\tcase AuthTypeOk,\n\t\tAuthTypeCleartextPassword,\n\t\tAuthTypeMD5Password,\n\t\tAuthTypeSCMCreds,\n\t\tAuthTypeGSS,\n\t\tAuthTypeGSSCont,\n\t\tAuthTypeSSPI,\n\t\tAuthTypeSASL,\n\t\tAuthTypeSASLContinue,\n\t\tAuthTypeSASLFinal:\n\t\tb.authType = authType\n\tdefault:\n\t\treturn fmt.Errorf(\"authType not recognized: %d\", authType)\n\t}\n\n\treturn nil\n}\n\n// SetMaxBodyLen sets the maximum length of a message body in octets.\n// If a message body exceeds this length, Receive will return an error.\n// This is useful for protecting against malicious clients that send\n// large messages with the intent of causing memory exhaustion.\n// The default value is 0.\n// If maxBodyLen is 0, then no maximum is enforced.\nfunc (b *Backend) SetMaxBodyLen(maxBodyLen int) {\n\tb.maxBodyLen = maxBodyLen\n}\n"
  },
  {
    "path": "pgproto3/backend_key_data.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype BackendKeyData struct {\n\tProcessID uint32\n\tSecretKey []byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*BackendKeyData) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *BackendKeyData) Decode(src []byte) error {\n\tif len(src) < 8 {\n\t\treturn &invalidMessageLenErr{messageType: \"BackendKeyData\", expectedLen: 8, actualLen: len(src)}\n\t}\n\n\tdst.ProcessID = binary.BigEndian.Uint32(src[:4])\n\tdst.SecretKey = make([]byte, len(src)-4)\n\tcopy(dst.SecretKey, src[4:])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *BackendKeyData) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'K')\n\tdst = pgio.AppendUint32(dst, src.ProcessID)\n\tdst = append(dst, src.SecretKey...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src BackendKeyData) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType      string\n\t\tProcessID uint32\n\t\tSecretKey string\n\t}{\n\t\tType:      \"BackendKeyData\",\n\t\tProcessID: src.ProcessID,\n\t\tSecretKey: hex.EncodeToString(src.SecretKey),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *BackendKeyData) UnmarshalJSON(data []byte) error {\n\tvar msg struct {\n\t\tProcessID uint32\n\t\tSecretKey string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.ProcessID = msg.ProcessID\n\tsecretKey, err := hex.DecodeString(msg.SecretKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.SecretKey = secretKey\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/backend_key_data_test.go",
    "content": "package pgproto3\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBackendKeyDataDecodeProtocol30(t *testing.T) {\n\t// Protocol 3.0: 8 bytes (4 for ProcessID, 4 for SecretKey)\n\tsrc := []byte{\n\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t0xD9, 0x0C, 0xAE, 0xDB, // SecretKey\n\t}\n\n\tvar msg BackendKeyData\n\terr := msg.Decode(src)\n\trequire.NoError(t, err)\n\tassert.Equal(t, uint32(8864), msg.ProcessID)\n\texpectedKey := []byte{0xD9, 0x0C, 0xAE, 0xDB}\n\tassert.Equal(t, expectedKey, msg.SecretKey)\n}\n\nfunc TestBackendKeyDataDecodeProtocol32(t *testing.T) {\n\t// Protocol 3.2: variable-length key (using 32 bytes here)\n\tsecretKey := []byte{\n\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\n\t\t0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,\n\t\t0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,\n\t\t0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,\n\t}\n\n\tsrc := append([]byte{0x00, 0x00, 0x22, 0xA0}, secretKey...) // ProcessID: 8864\n\n\tvar msg BackendKeyData\n\terr := msg.Decode(src)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, uint32(8864), msg.ProcessID)\n\tassert.Equal(t, secretKey, msg.SecretKey)\n}\n\nfunc TestBackendKeyDataEncodeProtocol30(t *testing.T) {\n\tmsg := BackendKeyData{\n\t\tProcessID: 8864,\n\t\tSecretKey: []byte{0xD9, 0x0C, 0xAE, 0xDB},\n\t}\n\n\tbuf, err := msg.Encode(nil)\n\trequire.NoError(t, err)\n\n\texpected := []byte{\n\t\t'K',                    // message type\n\t\t0x00, 0x00, 0x00, 0x0C, // length: 12 (4 + 8)\n\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t0xD9, 0x0C, 0xAE, 0xDB, // SecretKey\n\t}\n\n\tassert.Equal(t, expected, buf)\n}\n\nfunc TestBackendKeyDataEncodeProtocol32(t *testing.T) {\n\tsecretKey := []byte{\n\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\n\t\t0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,\n\t\t0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,\n\t\t0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,\n\t}\n\n\tmsg := BackendKeyData{\n\t\tProcessID: 8864,\n\t\tSecretKey: secretKey,\n\t}\n\n\tbuf, err := msg.Encode(nil)\n\trequire.NoError(t, err)\n\n\t// 'K' + 4 byte length + 4 byte ProcessID + 32 byte SecretKey = 41 bytes total, length field is 40\n\texpected := append([]byte{\n\t\t'K',                    // message type\n\t\t0x00, 0x00, 0x00, 0x28, // length: 40 (4 + 4 + 32)\n\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t}, secretKey...)\n\n\tassert.Equal(t, expected, buf)\n}\n"
  },
  {
    "path": "pgproto3/backend_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBackendReceiveInterrupted(t *testing.T) {\n\tt.Parallel()\n\n\tserver := &interruptReader{}\n\tserver.push([]byte{'Q', 0, 0, 0, 6})\n\n\tbackend := pgproto3.NewBackend(server, nil)\n\n\tmsg, err := backend.Receive()\n\tif err == nil {\n\t\tt.Fatal(\"expected err\")\n\t}\n\tif msg != nil {\n\t\tt.Fatalf(\"did not expect msg, but %v\", msg)\n\t}\n\n\tserver.push([]byte{'I', 0})\n\n\tmsg, err = backend.Receive()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif msg, ok := msg.(*pgproto3.Query); !ok || msg.String != \"I\" {\n\t\tt.Fatalf(\"unexpected msg: %v\", msg)\n\t}\n}\n\nfunc TestBackendReceiveUnexpectedEOF(t *testing.T) {\n\tt.Parallel()\n\n\tserver := &interruptReader{}\n\tserver.push([]byte{'Q', 0, 0, 0, 6})\n\n\tbackend := pgproto3.NewBackend(server, nil)\n\n\t// Receive regular msg\n\tmsg, err := backend.Receive()\n\tassert.Nil(t, msg)\n\tassert.Equal(t, io.ErrUnexpectedEOF, err)\n\n\t// Receive StartupMessage msg\n\tdst := []byte{}\n\tdst = pgio.AppendUint32(dst, 1000) // tell the backend we expect 1000 bytes to be read\n\tdst = pgio.AppendUint32(dst, 1)    // only send 1 byte\n\tserver.push(dst)\n\n\tmsg, err = backend.ReceiveStartupMessage()\n\tassert.Nil(t, msg)\n\tassert.Equal(t, io.ErrUnexpectedEOF, err)\n}\n\nfunc TestStartupMessage(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"valid StartupMessage 3.0\", func(t *testing.T) {\n\t\twant := &pgproto3.StartupMessage{\n\t\t\tProtocolVersion: pgproto3.ProtocolVersion30,\n\t\t\tParameters:      map[string]string{\"username\": \"tester\"},\n\t\t}\n\t\tdst, err := want.Encode([]byte{})\n\t\trequire.NoError(t, err)\n\n\t\tserver := &interruptReader{}\n\t\tserver.push(dst)\n\n\t\tbackend := pgproto3.NewBackend(server, nil)\n\n\t\tmsg, err := backend.ReceiveStartupMessage()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, want, msg)\n\t})\n\n\tt.Run(\"valid StartupMessage 3.2\", func(t *testing.T) {\n\t\twant := &pgproto3.StartupMessage{\n\t\t\tProtocolVersion: pgproto3.ProtocolVersion32,\n\t\t\tParameters:      map[string]string{\"username\": \"tester\"},\n\t\t}\n\t\tdst, err := want.Encode([]byte{})\n\t\trequire.NoError(t, err)\n\n\t\tserver := &interruptReader{}\n\t\tserver.push(dst)\n\n\t\tbackend := pgproto3.NewBackend(server, nil)\n\n\t\tmsg, err := backend.ReceiveStartupMessage()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, want, msg)\n\t})\n\n\tt.Run(\"invalid packet length\", func(t *testing.T) {\n\t\twantErr := \"invalid length of startup packet\"\n\t\ttests := []struct {\n\t\t\tname      string\n\t\t\tpacketLen uint32\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"large packet length\",\n\t\t\t\t// Since the StartupMessage contains the \"Length of message contents\n\t\t\t\t//  in bytes, including self\", the max startup packet length is actually\n\t\t\t\t//  10000+4. Therefore, let's go past the limit with 10005\n\t\t\t\tpacketLen: 10005,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:      \"short packet length\",\n\t\t\t\tpacketLen: 3,\n\t\t\t},\n\t\t}\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tserver := &interruptReader{}\n\t\t\t\tdst := []byte{}\n\t\t\t\tdst = pgio.AppendUint32(dst, tt.packetLen)\n\t\t\t\tdst = pgio.AppendUint32(dst, pgproto3.ProtocolVersion30)\n\t\t\t\tserver.push(dst)\n\n\t\t\t\tbackend := pgproto3.NewBackend(server, nil)\n\n\t\t\t\tmsg, err := backend.ReceiveStartupMessage()\n\t\t\t\trequire.Error(t, err)\n\t\t\t\trequire.Nil(t, msg)\n\t\t\t\trequire.Contains(t, err.Error(), wantErr)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestBackendReceiveExceededMaxBodyLen(t *testing.T) {\n\tt.Parallel()\n\n\tserver := &interruptReader{}\n\tserver.push([]byte{'Q', 0, 0, 10, 10})\n\n\tbackend := pgproto3.NewBackend(server, nil)\n\n\t// Set max body len to 5\n\tbackend.SetMaxBodyLen(5)\n\n\t// Receive regular msg\n\tmsg, err := backend.Receive()\n\tassert.Nil(t, msg)\n\tvar invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr\n\tassert.ErrorAs(t, err, &invalidBodyLenErr)\n}\n"
  },
  {
    "path": "pgproto3/big_endian.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n)\n\ntype BigEndianBuf [8]byte\n\nfunc (b BigEndianBuf) Int16(n int16) []byte {\n\tbuf := b[0:2]\n\tbinary.BigEndian.PutUint16(buf, uint16(n))\n\treturn buf\n}\n\nfunc (b BigEndianBuf) Uint16(n uint16) []byte {\n\tbuf := b[0:2]\n\tbinary.BigEndian.PutUint16(buf, n)\n\treturn buf\n}\n\nfunc (b BigEndianBuf) Int32(n int32) []byte {\n\tbuf := b[0:4]\n\tbinary.BigEndian.PutUint32(buf, uint32(n))\n\treturn buf\n}\n\nfunc (b BigEndianBuf) Uint32(n uint32) []byte {\n\tbuf := b[0:4]\n\tbinary.BigEndian.PutUint32(buf, n)\n\treturn buf\n}\n\nfunc (b BigEndianBuf) Int64(n int64) []byte {\n\tbuf := b[0:8]\n\tbinary.BigEndian.PutUint64(buf, uint64(n))\n\treturn buf\n}\n"
  },
  {
    "path": "pgproto3/bind.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Bind struct {\n\tDestinationPortal    string\n\tPreparedStatement    string\n\tParameterFormatCodes []int16\n\tParameters           [][]byte\n\tResultFormatCodes    []int16\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Bind) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Bind) Decode(src []byte) error {\n\t*dst = Bind{}\n\n\tidx := bytes.IndexByte(src, 0)\n\tif idx < 0 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t}\n\tdst.DestinationPortal = string(src[:idx])\n\trp := idx + 1\n\n\tidx = bytes.IndexByte(src[rp:], 0)\n\tif idx < 0 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t}\n\tdst.PreparedStatement = string(src[rp : rp+idx])\n\trp += idx + 1\n\n\tif len(src[rp:]) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t}\n\tparameterFormatCodeCount := int(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\n\tif parameterFormatCodeCount > 0 {\n\t\tdst.ParameterFormatCodes = make([]int16, parameterFormatCodeCount)\n\n\t\tif len(src[rp:]) < len(dst.ParameterFormatCodes)*2 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t\t}\n\t\tfor i := range parameterFormatCodeCount {\n\t\t\tdst.ParameterFormatCodes[i] = int16(binary.BigEndian.Uint16(src[rp:]))\n\t\t\trp += 2\n\t\t}\n\t}\n\n\tif len(src[rp:]) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t}\n\tparameterCount := int(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\n\tif parameterCount > 0 {\n\t\tdst.Parameters = make([][]byte, parameterCount)\n\n\t\tfor i := range parameterCount {\n\t\t\tif len(src[rp:]) < 4 {\n\t\t\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t\t\t}\n\n\t\t\tmsgSize := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\t\trp += 4\n\n\t\t\t// null\n\t\t\tif msgSize == -1 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif msgSize < 0 || len(src[rp:]) < msgSize {\n\t\t\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t\t\t}\n\n\t\t\tdst.Parameters[i] = src[rp : rp+msgSize]\n\t\t\trp += msgSize\n\t\t}\n\t}\n\n\tif len(src[rp:]) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t}\n\tresultFormatCodeCount := int(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\n\tdst.ResultFormatCodes = make([]int16, resultFormatCodeCount)\n\tif len(src[rp:]) < len(dst.ResultFormatCodes)*2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Bind\"}\n\t}\n\tfor i := range resultFormatCodeCount {\n\t\tdst.ResultFormatCodes[i] = int16(binary.BigEndian.Uint16(src[rp:]))\n\t\trp += 2\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Bind) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'B')\n\n\tdst = append(dst, src.DestinationPortal...)\n\tdst = append(dst, 0)\n\tdst = append(dst, src.PreparedStatement...)\n\tdst = append(dst, 0)\n\n\tif len(src.ParameterFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many parameter format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ParameterFormatCodes)))\n\tfor _, fc := range src.ParameterFormatCodes {\n\t\tdst = pgio.AppendInt16(dst, fc)\n\t}\n\n\tif len(src.Parameters) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many parameters\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.Parameters)))\n\tfor _, p := range src.Parameters {\n\t\tif p == nil {\n\t\t\tdst = pgio.AppendInt32(dst, -1)\n\t\t\tcontinue\n\t\t}\n\n\t\tdst = pgio.AppendInt32(dst, int32(len(p)))\n\t\tdst = append(dst, p...)\n\t}\n\n\tif len(src.ResultFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many result format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ResultFormatCodes)))\n\tfor _, fc := range src.ResultFormatCodes {\n\t\tdst = pgio.AppendInt16(dst, fc)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Bind) MarshalJSON() ([]byte, error) {\n\tformattedParameters := make([]map[string]string, len(src.Parameters))\n\tfor i, p := range src.Parameters {\n\t\tif p == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\ttextFormat := true\n\t\tif len(src.ParameterFormatCodes) == 1 {\n\t\t\ttextFormat = src.ParameterFormatCodes[0] == 0\n\t\t} else if len(src.ParameterFormatCodes) > 1 {\n\t\t\ttextFormat = src.ParameterFormatCodes[i] == 0\n\t\t}\n\n\t\tif textFormat {\n\t\t\tformattedParameters[i] = map[string]string{\"text\": string(p)}\n\t\t} else {\n\t\t\tformattedParameters[i] = map[string]string{\"binary\": hex.EncodeToString(p)}\n\t\t}\n\t}\n\n\treturn json.Marshal(struct {\n\t\tType                 string\n\t\tDestinationPortal    string\n\t\tPreparedStatement    string\n\t\tParameterFormatCodes []int16\n\t\tParameters           []map[string]string\n\t\tResultFormatCodes    []int16\n\t}{\n\t\tType:                 \"Bind\",\n\t\tDestinationPortal:    src.DestinationPortal,\n\t\tPreparedStatement:    src.PreparedStatement,\n\t\tParameterFormatCodes: src.ParameterFormatCodes,\n\t\tParameters:           formattedParameters,\n\t\tResultFormatCodes:    src.ResultFormatCodes,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *Bind) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tDestinationPortal    string\n\t\tPreparedStatement    string\n\t\tParameterFormatCodes []int16\n\t\tParameters           []map[string]string\n\t\tResultFormatCodes    []int16\n\t}\n\terr := json.Unmarshal(data, &msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.DestinationPortal = msg.DestinationPortal\n\tdst.PreparedStatement = msg.PreparedStatement\n\tdst.ParameterFormatCodes = msg.ParameterFormatCodes\n\tdst.Parameters = make([][]byte, len(msg.Parameters))\n\tdst.ResultFormatCodes = msg.ResultFormatCodes\n\tfor n, parameter := range msg.Parameters {\n\t\tdst.Parameters[n], err = getValueFromJSON(parameter)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot get param %d: %w\", n, err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/bind_complete.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype BindComplete struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*BindComplete) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *BindComplete) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"BindComplete\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *BindComplete) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, '2', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src BindComplete) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"BindComplete\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/bind_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// https://github.com/jackc/pgx/issues/2519\nfunc TestBindDecodeNegativeParameterLength(t *testing.T) {\n\tt.Parallel()\n\n\t// Craft a Bind message with a negative parameter length that is not -1.\n\t// This should return an error, not panic.\n\t//\n\t// Message layout:\n\t//   - destination portal: \"\" (1 byte null terminator)\n\t//   - prepared statement: \"\" (1 byte null terminator)\n\t//   - parameter format code count: 0 (2 bytes)\n\t//   - parameter count: 1 (2 bytes)\n\t//   - parameter 0 length: -2 (4 bytes, 0xFFFFFFFE)\n\tsrc := []byte{\n\t\t0,    // destination portal null terminator\n\t\t0,    // prepared statement null terminator\n\t\t0, 0, // parameter format code count = 0\n\t\t0, 1, // parameter count = 1\n\t\t0xFF, 0xFF, 0xFF, 0xFE, // parameter length = -2\n\t}\n\n\tvar bind pgproto3.Bind\n\terr := bind.Decode(src)\n\trequire.Error(t, err, \"Bind.Decode should reject negative parameter length other than -1\")\n}\n\nfunc TestBindBiggerThanMaxMessageBodyLen(t *testing.T) {\n\tt.Parallel()\n\n\t// Maximum allowed size.\n\t_, err := (&pgproto3.Bind{Parameters: [][]byte{make([]byte, pgproto3.MaxMessageBodyLen-16)}}).Encode(nil)\n\trequire.NoError(t, err)\n\n\t// 1 byte too big\n\t_, err = (&pgproto3.Bind{Parameters: [][]byte{make([]byte, pgproto3.MaxMessageBodyLen-15)}}).Encode(nil)\n\trequire.Error(t, err)\n}\n"
  },
  {
    "path": "pgproto3/cancel_request.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst cancelRequestCode = 80877102\n\ntype CancelRequest struct {\n\tProcessID uint32\n\tSecretKey []byte\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*CancelRequest) Frontend() {}\n\nfunc (dst *CancelRequest) Decode(src []byte) error {\n\tif len(src) < 12 {\n\t\treturn errors.New(\"cancel request too short\")\n\t}\n\tif len(src) > 264 {\n\t\treturn errors.New(\"cancel request too long\")\n\t}\n\n\trequestCode := binary.BigEndian.Uint32(src)\n\tif requestCode != cancelRequestCode {\n\t\treturn errors.New(\"bad cancel request code\")\n\t}\n\n\tdst.ProcessID = binary.BigEndian.Uint32(src[4:])\n\tdst.SecretKey = make([]byte, len(src)-8)\n\tcopy(dst.SecretKey, src[8:])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 4 byte message length.\nfunc (src *CancelRequest) Encode(dst []byte) ([]byte, error) {\n\tif len(src.SecretKey) > 256 {\n\t\treturn nil, errors.New(\"secret key too long\")\n\t}\n\tmsgLen := int32(12 + len(src.SecretKey))\n\tdst = pgio.AppendInt32(dst, msgLen)\n\tdst = pgio.AppendInt32(dst, cancelRequestCode)\n\tdst = pgio.AppendUint32(dst, src.ProcessID)\n\tdst = append(dst, src.SecretKey...)\n\treturn dst, nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CancelRequest) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType      string\n\t\tProcessID uint32\n\t\tSecretKey string\n\t}{\n\t\tType:      \"CancelRequest\",\n\t\tProcessID: src.ProcessID,\n\t\tSecretKey: hex.EncodeToString(src.SecretKey),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *CancelRequest) UnmarshalJSON(data []byte) error {\n\tvar msg struct {\n\t\tProcessID uint32\n\t\tSecretKey string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.ProcessID = msg.ProcessID\n\tsecretKey, err := hex.DecodeString(msg.SecretKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.SecretKey = secretKey\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/cancel_request_test.go",
    "content": "package pgproto3\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCancelRequestDecode(t *testing.T) {\n\tsecretKey32 := []byte{\n\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\n\t\t0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,\n\t\t0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,\n\t\t0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,\n\t}\n\n\ttests := []struct {\n\t\tname              string\n\t\tsrc               []byte\n\t\texpectedProcessID uint32\n\t\texpectedSecretKey []byte\n\t\texpectError       bool\n\t}{\n\t\t{\n\t\t\tname: \"Protocol 3.0 (16 bytes total)\",\n\t\t\tsrc: []byte{\n\t\t\t\t0x04, 0xD2, 0x16, 0x2E, // cancelRequestCode: 80877102\n\t\t\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t\t\t0xD9, 0x0C, 0xAE, 0xDB, // SecretKey\n\t\t\t},\n\t\t\texpectedProcessID: 8864,\n\t\t\texpectedSecretKey: []byte{0xD9, 0x0C, 0xAE, 0xDB},\n\t\t},\n\t\t{\n\t\t\tname: \"Protocol 3.2 (variable-length 32-byte key)\",\n\t\t\tsrc: append([]byte{\n\t\t\t\t0x04, 0xD2, 0x16, 0x2E, // cancelRequestCode: 80877102\n\t\t\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t\t}, secretKey32...),\n\t\t\texpectedProcessID: 8864,\n\t\t\texpectedSecretKey: secretKey32,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid length (too short)\",\n\t\t\tsrc: []byte{\n\t\t\t\t0x00, 0x00, 0x00, 0x00, // invalid length\n\t\t\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t\t\t0xD9, 0x0C, 0xAE, 0xDB, // SecretKey\n\t\t\t},\n\t\t\texpectError: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid length (too long)\",\n\t\t\tsrc: append([]byte{\n\t\t\t\t0x00, 0x00, 0x01, 0x09, // invalid length: 265\n\t\t\t\t0x04, 0xD2, 0x16, 0x2E, // cancelRequestCode: 80877102\n\t\t\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t\t}, make([]byte, 257)...), // 257 bytes secret key (1 byte too many)\n\t\t\texpectError: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar msg CancelRequest\n\t\t\terr := msg.Decode(tt.src)\n\n\t\t\tif tt.expectError {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.expectedProcessID, msg.ProcessID)\n\t\t\tassert.Equal(t, tt.expectedSecretKey, msg.SecretKey)\n\t\t})\n\t}\n}\n\nfunc TestCancelRequestEncode(t *testing.T) {\n\tsecretKey32 := []byte{\n\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,\n\t\t0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,\n\t\t0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,\n\t\t0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,\n\t}\n\n\ttests := []struct {\n\t\tname        string\n\t\tmsg         CancelRequest\n\t\texpected    []byte\n\t\texpectError bool\n\t}{\n\t\t{\n\t\t\tname: \"Protocol 3.0 (4-byte key)\",\n\t\t\tmsg: CancelRequest{\n\t\t\t\tProcessID: 8864,\n\t\t\t\tSecretKey: []byte{0xD9, 0x0C, 0xAE, 0xDB},\n\t\t\t},\n\t\t\texpected: []byte{\n\t\t\t\t0x00, 0x00, 0x00, 0x10, // length: 16\n\t\t\t\t0x04, 0xD2, 0x16, 0x2E, // cancelRequestCode: 80877102\n\t\t\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t\t\t0xD9, 0x0C, 0xAE, 0xDB, // SecretKey\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Protocol 3.2 (32-byte key)\",\n\t\t\tmsg: CancelRequest{\n\t\t\t\tProcessID: 8864,\n\t\t\t\tSecretKey: secretKey32,\n\t\t\t},\n\t\t\t// 4 byte length + 4 byte code + 4 byte ProcessID + 32 byte SecretKey = 44 bytes total\n\t\t\texpected: append([]byte{\n\t\t\t\t0x00, 0x00, 0x00, 0x2C, // length: 44 (12 + 32)\n\t\t\t\t0x04, 0xD2, 0x16, 0x2E, // cancelRequestCode: 80877102\n\t\t\t\t0x00, 0x00, 0x22, 0xA0, // ProcessID: 8864\n\t\t\t}, secretKey32...),\n\t\t},\n\t\t{\n\t\t\tname: \"Too long secret key\",\n\t\t\tmsg: CancelRequest{\n\t\t\t\tProcessID: 8864,\n\t\t\t\tSecretKey: make([]byte, 257), // 1 byte too many\n\t\t\t},\n\t\t\texpectError: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbuf, err := tt.msg.Encode(nil)\n\t\t\tif tt.expectError {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.expected, buf)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgproto3/chunkreader.go",
    "content": "package pgproto3\n\nimport (\n\t\"io\"\n\n\t\"github.com/jackc/pgx/v5/internal/iobufpool\"\n)\n\n// chunkReader is a io.Reader wrapper that minimizes IO reads and memory allocations. It allocates memory in chunks and\n// will read as much as will fit in the current buffer in a single call regardless of how large a read is actually\n// requested. The memory returned via Next is only valid until the next call to Next.\n//\n// This is roughly equivalent to a bufio.Reader that only uses Peek and Discard to never copy bytes.\ntype chunkReader struct {\n\tr io.Reader\n\n\tbuf    *[]byte\n\trp, wp int // buf read position and write position\n\n\tminBufSize int\n}\n\n// newChunkReader creates and returns a new chunkReader for r with default configuration. If minBufSize is <= 0 it uses\n// a default value.\nfunc newChunkReader(r io.Reader, minBufSize int) *chunkReader {\n\tif minBufSize <= 0 {\n\t\t// By historical reasons Postgres currently has 8KB send buffer inside,\n\t\t// so here we want to have at least the same size buffer.\n\t\t// @see https://github.com/postgres/postgres/blob/249d64999615802752940e017ee5166e726bc7cd/src/backend/libpq/pqcomm.c#L134\n\t\t// @see https://www.postgresql.org/message-id/0cdc5485-cb3c-5e16-4a46-e3b2f7a41322%40ya.ru\n\t\t//\n\t\t// In addition, testing has found no benefit of any larger buffer.\n\t\tminBufSize = 8192\n\t}\n\n\treturn &chunkReader{\n\t\tr:          r,\n\t\tminBufSize: minBufSize,\n\t\tbuf:        iobufpool.Get(minBufSize),\n\t}\n}\n\n// Next returns buf filled with the next n bytes. buf is only valid until next call of Next. If an error occurs, buf\n// will be nil.\nfunc (r *chunkReader) Next(n int) (buf []byte, err error) {\n\t// Reset the buffer if it is empty\n\tif r.rp == r.wp {\n\t\tif len(*r.buf) != r.minBufSize {\n\t\t\tiobufpool.Put(r.buf)\n\t\t\tr.buf = iobufpool.Get(r.minBufSize)\n\t\t}\n\t\tr.rp = 0\n\t\tr.wp = 0\n\t}\n\n\t// n bytes already in buf\n\tif (r.wp - r.rp) >= n {\n\t\tbuf = (*r.buf)[r.rp : r.rp+n : r.rp+n]\n\t\tr.rp += n\n\t\treturn buf, err\n\t}\n\n\t// buf is smaller than requested number of bytes\n\tif len(*r.buf) < n {\n\t\tbigBuf := iobufpool.Get(n)\n\t\tr.wp = copy((*bigBuf), (*r.buf)[r.rp:r.wp])\n\t\tr.rp = 0\n\t\tiobufpool.Put(r.buf)\n\t\tr.buf = bigBuf\n\t}\n\n\t// buf is large enough, but need to shift filled area to start to make enough contiguous space\n\tminReadCount := n - (r.wp - r.rp)\n\tif (len(*r.buf) - r.wp) < minReadCount {\n\t\tr.wp = copy((*r.buf), (*r.buf)[r.rp:r.wp])\n\t\tr.rp = 0\n\t}\n\n\t// Read at least the required number of bytes from the underlying io.Reader\n\treadBytesCount, err := io.ReadAtLeast(r.r, (*r.buf)[r.wp:], minReadCount)\n\tr.wp += readBytesCount\n\t// fmt.Println(\"read\", n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbuf = (*r.buf)[r.rp : r.rp+n : r.rp+n]\n\tr.rp += n\n\treturn buf, nil\n}\n"
  },
  {
    "path": "pgproto3/chunkreader_test.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"math/rand/v2\"\n\t\"testing\"\n)\n\nfunc TestChunkReaderNextDoesNotReadIfAlreadyBuffered(t *testing.T) {\n\tserver := &bytes.Buffer{}\n\tr := newChunkReader(server, 4)\n\n\tsrc := []byte{1, 2, 3, 4}\n\tserver.Write(src)\n\n\tn1, err := r.Next(2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(n1, src[0:2]) {\n\t\tt.Fatalf(\"Expected read bytes to be %v, but they were %v\", src[0:2], n1)\n\t}\n\n\tn2, err := r.Next(2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif !bytes.Equal(n2, src[2:4]) {\n\t\tt.Fatalf(\"Expected read bytes to be %v, but they were %v\", src[2:4], n2)\n\t}\n\n\tif !bytes.Equal((*r.buf)[:len(src)], src) {\n\t\tt.Fatalf(\"Expected r.buf to be %v, but it was %v\", src, r.buf)\n\t}\n\n\t_, err = r.Next(0) // Trigger the buffer reset.\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif r.rp != 0 {\n\t\tt.Fatalf(\"Expected r.rp to be %v, but it was %v\", 0, r.rp)\n\t}\n\tif r.wp != 0 {\n\t\tt.Fatalf(\"Expected r.wp to be %v, but it was %v\", 0, r.wp)\n\t}\n}\n\ntype randomReader struct {\n\trnd *rand.Rand\n}\n\n// Read reads a random number of random bytes.\nfunc (r *randomReader) Read(p []byte) (n int, err error) {\n\tn = r.rnd.IntN(len(p) + 1)\n\tfor i := 0; i < n; i++ {\n\t\tp[i] = byte(r.rnd.Uint64())\n\t}\n\treturn n, nil\n}\n\nfunc TestChunkReaderNextFuzz(t *testing.T) {\n\trr := &randomReader{rnd: rand.New(rand.NewPCG(1, 0))}\n\tr := newChunkReader(rr, 8192)\n\n\trandomSizes := rand.New(rand.NewPCG(0, 0))\n\n\tfor range 100_000 {\n\t\tsize := randomSizes.IntN(16384) + 1\n\t\tbuf, err := r.Next(size)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif len(buf) != size {\n\t\t\tt.Fatalf(\"Expected to get %v bytes but got %v bytes\", size, len(buf))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgproto3/close.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n)\n\ntype Close struct {\n\tObjectType byte // 'S' = prepared statement, 'P' = portal\n\tName       string\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Close) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Close) Decode(src []byte) error {\n\tif len(src) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Close\"}\n\t}\n\n\tdst.ObjectType = src[0]\n\trp := 1\n\n\tidx := bytes.IndexByte(src[rp:], 0)\n\tif idx != len(src[rp:])-1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Close\"}\n\t}\n\n\tdst.Name = string(src[rp : len(src)-1])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Close) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'C')\n\tdst = append(dst, src.ObjectType)\n\tdst = append(dst, src.Name...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Close) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType       string\n\t\tObjectType string\n\t\tName       string\n\t}{\n\t\tType:       \"Close\",\n\t\tObjectType: string(src.ObjectType),\n\t\tName:       src.Name,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *Close) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tObjectType string\n\t\tName       string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tif len(msg.ObjectType) != 1 {\n\t\treturn errors.New(\"invalid length for Close.ObjectType\")\n\t}\n\n\tdst.ObjectType = byte(msg.ObjectType[0])\n\tdst.Name = msg.Name\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/close_complete.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype CloseComplete struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*CloseComplete) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CloseComplete) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"CloseComplete\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CloseComplete) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, '3', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CloseComplete) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"CloseComplete\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/command_complete.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n)\n\ntype CommandComplete struct {\n\tCommandTag []byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*CommandComplete) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CommandComplete) Decode(src []byte) error {\n\tidx := bytes.IndexByte(src, 0)\n\tif idx == -1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CommandComplete\", details: \"unterminated string\"}\n\t}\n\tif idx != len(src)-1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CommandComplete\", details: \"string terminated too early\"}\n\t}\n\n\tdst.CommandTag = src[:idx]\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CommandComplete) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'C')\n\tdst = append(dst, src.CommandTag...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CommandComplete) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType       string\n\t\tCommandTag string\n\t}{\n\t\tType:       \"CommandComplete\",\n\t\tCommandTag: string(src.CommandTag),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *CommandComplete) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tCommandTag string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.CommandTag = []byte(msg.CommandTag)\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/copy_both_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype CopyBothResponse struct {\n\tOverallFormat     byte\n\tColumnFormatCodes []uint16\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*CopyBothResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CopyBothResponse) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tif buf.Len() < 3 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyBothResponse\"}\n\t}\n\n\toverallFormat := buf.Next(1)[0]\n\n\tcolumnCount := int(binary.BigEndian.Uint16(buf.Next(2)))\n\tif buf.Len() != columnCount*2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyBothResponse\"}\n\t}\n\n\tcolumnFormatCodes := make([]uint16, columnCount)\n\tfor i := range columnCount {\n\t\tcolumnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))\n\t}\n\n\t*dst = CopyBothResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyBothResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'W')\n\tdst = append(dst, src.OverallFormat)\n\tif len(src.ColumnFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many column format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))\n\tfor _, fc := range src.ColumnFormatCodes {\n\t\tdst = pgio.AppendUint16(dst, fc)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyBothResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType              string\n\t\tColumnFormatCodes []uint16\n\t}{\n\t\tType:              \"CopyBothResponse\",\n\t\tColumnFormatCodes: src.ColumnFormatCodes,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *CopyBothResponse) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tOverallFormat     string\n\t\tColumnFormatCodes []uint16\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tif len(msg.OverallFormat) != 1 {\n\t\treturn errors.New(\"invalid length for CopyBothResponse.OverallFormat\")\n\t}\n\n\tdst.OverallFormat = msg.OverallFormat[0]\n\tdst.ColumnFormatCodes = msg.ColumnFormatCodes\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/copy_both_response_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestEncodeDecode(t *testing.T) {\n\tsrcBytes := []byte{'W', 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}\n\tdstResp := pgproto3.CopyBothResponse{}\n\terr := dstResp.Decode(srcBytes[5:])\n\tassert.NoError(t, err, \"No errors on decode\")\n\tdstBytes := []byte{}\n\tdstBytes, err = dstResp.Encode(dstBytes)\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, srcBytes, dstBytes, \"Expecting src & dest bytes to match\")\n}\n"
  },
  {
    "path": "pgproto3/copy_data.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n)\n\ntype CopyData struct {\n\tData []byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*CopyData) Backend() {}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*CopyData) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CopyData) Decode(src []byte) error {\n\tdst.Data = src\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyData) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'd')\n\tdst = append(dst, src.Data...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyData) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData string\n\t}{\n\t\tType: \"CopyData\",\n\t\tData: hex.EncodeToString(src.Data),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *CopyData) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tData string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.Data = []byte(msg.Data)\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/copy_done.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype CopyDone struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*CopyDone) Backend() {}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*CopyDone) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CopyDone) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"CopyDone\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyDone) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'c', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyDone) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"CopyDone\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/copy_fail.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n)\n\ntype CopyFail struct {\n\tMessage string\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*CopyFail) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CopyFail) Decode(src []byte) error {\n\tif len(src) == 0 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyFail\"}\n\t}\n\n\tidx := bytes.IndexByte(src, 0)\n\tif idx != len(src)-1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyFail\"}\n\t}\n\n\tdst.Message = string(src[:idx])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyFail) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'f')\n\tdst = append(dst, src.Message...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyFail) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType    string\n\t\tMessage string\n\t}{\n\t\tType:    \"CopyFail\",\n\t\tMessage: src.Message,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/copy_in_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype CopyInResponse struct {\n\tOverallFormat     byte\n\tColumnFormatCodes []uint16\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*CopyInResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CopyInResponse) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tif buf.Len() < 3 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyInResponse\"}\n\t}\n\n\toverallFormat := buf.Next(1)[0]\n\n\tcolumnCount := int(binary.BigEndian.Uint16(buf.Next(2)))\n\tif buf.Len() != columnCount*2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyInResponse\"}\n\t}\n\n\tcolumnFormatCodes := make([]uint16, columnCount)\n\tfor i := range columnCount {\n\t\tcolumnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))\n\t}\n\n\t*dst = CopyInResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyInResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'G')\n\n\tdst = append(dst, src.OverallFormat)\n\tif len(src.ColumnFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many column format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))\n\tfor _, fc := range src.ColumnFormatCodes {\n\t\tdst = pgio.AppendUint16(dst, fc)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyInResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType              string\n\t\tColumnFormatCodes []uint16\n\t}{\n\t\tType:              \"CopyInResponse\",\n\t\tColumnFormatCodes: src.ColumnFormatCodes,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *CopyInResponse) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tOverallFormat     string\n\t\tColumnFormatCodes []uint16\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tif len(msg.OverallFormat) != 1 {\n\t\treturn errors.New(\"invalid length for CopyInResponse.OverallFormat\")\n\t}\n\n\tdst.OverallFormat = msg.OverallFormat[0]\n\tdst.ColumnFormatCodes = msg.ColumnFormatCodes\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/copy_out_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype CopyOutResponse struct {\n\tOverallFormat     byte\n\tColumnFormatCodes []uint16\n}\n\nfunc (*CopyOutResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *CopyOutResponse) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tif buf.Len() < 3 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyOutResponse\"}\n\t}\n\n\toverallFormat := buf.Next(1)[0]\n\n\tcolumnCount := int(binary.BigEndian.Uint16(buf.Next(2)))\n\tif buf.Len() != columnCount*2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"CopyOutResponse\"}\n\t}\n\n\tcolumnFormatCodes := make([]uint16, columnCount)\n\tfor i := range columnCount {\n\t\tcolumnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))\n\t}\n\n\t*dst = CopyOutResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyOutResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'H')\n\n\tdst = append(dst, src.OverallFormat)\n\n\tif len(src.ColumnFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many column format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))\n\tfor _, fc := range src.ColumnFormatCodes {\n\t\tdst = pgio.AppendUint16(dst, fc)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyOutResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType              string\n\t\tColumnFormatCodes []uint16\n\t}{\n\t\tType:              \"CopyOutResponse\",\n\t\tColumnFormatCodes: src.ColumnFormatCodes,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *CopyOutResponse) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tOverallFormat     string\n\t\tColumnFormatCodes []uint16\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tif len(msg.OverallFormat) != 1 {\n\t\treturn errors.New(\"invalid length for CopyOutResponse.OverallFormat\")\n\t}\n\n\tdst.OverallFormat = msg.OverallFormat[0]\n\tdst.ColumnFormatCodes = msg.ColumnFormatCodes\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/data_row.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype DataRow struct {\n\tValues [][]byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*DataRow) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *DataRow) Decode(src []byte) error {\n\tif len(src) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"DataRow\"}\n\t}\n\trp := 0\n\tfieldCount := int(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\n\t// If the capacity of the values slice is too small OR substantially too\n\t// large reallocate. This is too avoid one row with many columns from\n\t// permanently allocating memory.\n\tif cap(dst.Values) < fieldCount || cap(dst.Values)-fieldCount > 32 {\n\t\tnewCap := max(32, fieldCount)\n\t\tdst.Values = make([][]byte, fieldCount, newCap)\n\t} else {\n\t\tdst.Values = dst.Values[:fieldCount]\n\t}\n\n\tfor i := range fieldCount {\n\t\tif len(src[rp:]) < 4 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"DataRow\"}\n\t\t}\n\n\t\tvalueLen := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\trp += 4\n\n\t\t// null\n\t\tif valueLen == -1 {\n\t\t\tdst.Values[i] = nil\n\t\t} else {\n\t\t\tif len(src[rp:]) < valueLen || valueLen < 0 {\n\t\t\t\treturn &invalidMessageFormatErr{messageType: \"DataRow\"}\n\t\t\t}\n\n\t\t\tdst.Values[i] = src[rp : rp+valueLen : rp+valueLen]\n\t\t\trp += valueLen\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *DataRow) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'D')\n\n\tif len(src.Values) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many values\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.Values)))\n\tfor _, v := range src.Values {\n\t\tif v == nil {\n\t\t\tdst = pgio.AppendInt32(dst, -1)\n\t\t\tcontinue\n\t\t}\n\n\t\tdst = pgio.AppendInt32(dst, int32(len(v)))\n\t\tdst = append(dst, v...)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src DataRow) MarshalJSON() ([]byte, error) {\n\tformattedValues := make([]map[string]string, len(src.Values))\n\tfor i, v := range src.Values {\n\t\tif v == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar hasNonPrintable bool\n\t\tfor _, b := range v {\n\t\t\tif b < 32 {\n\t\t\t\thasNonPrintable = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif hasNonPrintable {\n\t\t\tformattedValues[i] = map[string]string{\"binary\": hex.EncodeToString(v)}\n\t\t} else {\n\t\t\tformattedValues[i] = map[string]string{\"text\": string(v)}\n\t\t}\n\t}\n\n\treturn json.Marshal(struct {\n\t\tType   string\n\t\tValues []map[string]string\n\t}{\n\t\tType:   \"DataRow\",\n\t\tValues: formattedValues,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *DataRow) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tValues []map[string]string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.Values = make([][]byte, len(msg.Values))\n\tfor n, parameter := range msg.Values {\n\t\tvar err error\n\t\tdst.Values[n], err = getValueFromJSON(parameter)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/describe.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n)\n\ntype Describe struct {\n\tObjectType byte // 'S' = prepared statement, 'P' = portal\n\tName       string\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Describe) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Describe) Decode(src []byte) error {\n\tif len(src) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Describe\"}\n\t}\n\n\tdst.ObjectType = src[0]\n\trp := 1\n\n\tidx := bytes.IndexByte(src[rp:], 0)\n\tif idx != len(src[rp:])-1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Describe\"}\n\t}\n\n\tdst.Name = string(src[rp : len(src)-1])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Describe) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'D')\n\tdst = append(dst, src.ObjectType)\n\tdst = append(dst, src.Name...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Describe) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType       string\n\t\tObjectType string\n\t\tName       string\n\t}{\n\t\tType:       \"Describe\",\n\t\tObjectType: string(src.ObjectType),\n\t\tName:       src.Name,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *Describe) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tObjectType string\n\t\tName       string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\tif len(msg.ObjectType) != 1 {\n\t\treturn errors.New(\"invalid length for Describe.ObjectType\")\n\t}\n\n\tdst.ObjectType = byte(msg.ObjectType[0])\n\tdst.Name = msg.Name\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/doc.go",
    "content": "// Package pgproto3 is an encoder and decoder of the PostgreSQL wire protocol version 3.\n//\n// The primary interfaces are Frontend and Backend. They correspond to a client and server respectively. Messages are\n// sent with Send (or a specialized Send variant). Messages are automatically buffered to minimize small writes. Call\n// Flush to ensure a message has actually been sent.\n//\n// The Trace method of Frontend and Backend can be used to examine the wire-level message traffic. It outputs in a\n// similar format to the PQtrace function in libpq.\n//\n// See https://www.postgresql.org/docs/current/protocol-message-formats.html for meanings of the different messages.\npackage pgproto3\n"
  },
  {
    "path": "pgproto3/empty_query_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype EmptyQueryResponse struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*EmptyQueryResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *EmptyQueryResponse) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"EmptyQueryResponse\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *EmptyQueryResponse) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'I', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src EmptyQueryResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"EmptyQueryResponse\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/error_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strconv\"\n)\n\ntype ErrorResponse struct {\n\tSeverity            string\n\tSeverityUnlocalized string // only in 9.6 and greater\n\tCode                string\n\tMessage             string\n\tDetail              string\n\tHint                string\n\tPosition            int32\n\tInternalPosition    int32\n\tInternalQuery       string\n\tWhere               string\n\tSchemaName          string\n\tTableName           string\n\tColumnName          string\n\tDataTypeName        string\n\tConstraintName      string\n\tFile                string\n\tLine                int32\n\tRoutine             string\n\n\tUnknownFields map[byte]string\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*ErrorResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *ErrorResponse) Decode(src []byte) error {\n\t*dst = ErrorResponse{}\n\n\tbuf := bytes.NewBuffer(src)\n\n\tfor {\n\t\tk, err := buf.ReadByte()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif k == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tvb, err := buf.ReadBytes(0)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tv := string(vb[:len(vb)-1])\n\n\t\tswitch k {\n\t\tcase 'S':\n\t\t\tdst.Severity = v\n\t\tcase 'V':\n\t\t\tdst.SeverityUnlocalized = v\n\t\tcase 'C':\n\t\t\tdst.Code = v\n\t\tcase 'M':\n\t\t\tdst.Message = v\n\t\tcase 'D':\n\t\t\tdst.Detail = v\n\t\tcase 'H':\n\t\t\tdst.Hint = v\n\t\tcase 'P':\n\t\t\ts := v\n\t\t\tn, _ := strconv.ParseInt(s, 10, 32)\n\t\t\tdst.Position = int32(n)\n\t\tcase 'p':\n\t\t\ts := v\n\t\t\tn, _ := strconv.ParseInt(s, 10, 32)\n\t\t\tdst.InternalPosition = int32(n)\n\t\tcase 'q':\n\t\t\tdst.InternalQuery = v\n\t\tcase 'W':\n\t\t\tdst.Where = v\n\t\tcase 's':\n\t\t\tdst.SchemaName = v\n\t\tcase 't':\n\t\t\tdst.TableName = v\n\t\tcase 'c':\n\t\t\tdst.ColumnName = v\n\t\tcase 'd':\n\t\t\tdst.DataTypeName = v\n\t\tcase 'n':\n\t\t\tdst.ConstraintName = v\n\t\tcase 'F':\n\t\t\tdst.File = v\n\t\tcase 'L':\n\t\t\ts := v\n\t\t\tn, _ := strconv.ParseInt(s, 10, 32)\n\t\t\tdst.Line = int32(n)\n\t\tcase 'R':\n\t\t\tdst.Routine = v\n\n\t\tdefault:\n\t\t\tif dst.UnknownFields == nil {\n\t\t\t\tdst.UnknownFields = make(map[byte]string)\n\t\t\t}\n\t\t\tdst.UnknownFields[k] = v\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *ErrorResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'E')\n\tdst = src.appendFields(dst)\n\treturn finishMessage(dst, sp)\n}\n\nfunc (src *ErrorResponse) appendFields(dst []byte) []byte {\n\tif src.Severity != \"\" {\n\t\tdst = append(dst, 'S')\n\t\tdst = append(dst, src.Severity...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.SeverityUnlocalized != \"\" {\n\t\tdst = append(dst, 'V')\n\t\tdst = append(dst, src.SeverityUnlocalized...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Code != \"\" {\n\t\tdst = append(dst, 'C')\n\t\tdst = append(dst, src.Code...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Message != \"\" {\n\t\tdst = append(dst, 'M')\n\t\tdst = append(dst, src.Message...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Detail != \"\" {\n\t\tdst = append(dst, 'D')\n\t\tdst = append(dst, src.Detail...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Hint != \"\" {\n\t\tdst = append(dst, 'H')\n\t\tdst = append(dst, src.Hint...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Position != 0 {\n\t\tdst = append(dst, 'P')\n\t\tdst = append(dst, strconv.Itoa(int(src.Position))...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.InternalPosition != 0 {\n\t\tdst = append(dst, 'p')\n\t\tdst = append(dst, strconv.Itoa(int(src.InternalPosition))...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.InternalQuery != \"\" {\n\t\tdst = append(dst, 'q')\n\t\tdst = append(dst, src.InternalQuery...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Where != \"\" {\n\t\tdst = append(dst, 'W')\n\t\tdst = append(dst, src.Where...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.SchemaName != \"\" {\n\t\tdst = append(dst, 's')\n\t\tdst = append(dst, src.SchemaName...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.TableName != \"\" {\n\t\tdst = append(dst, 't')\n\t\tdst = append(dst, src.TableName...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.ColumnName != \"\" {\n\t\tdst = append(dst, 'c')\n\t\tdst = append(dst, src.ColumnName...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.DataTypeName != \"\" {\n\t\tdst = append(dst, 'd')\n\t\tdst = append(dst, src.DataTypeName...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.ConstraintName != \"\" {\n\t\tdst = append(dst, 'n')\n\t\tdst = append(dst, src.ConstraintName...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.File != \"\" {\n\t\tdst = append(dst, 'F')\n\t\tdst = append(dst, src.File...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Line != 0 {\n\t\tdst = append(dst, 'L')\n\t\tdst = append(dst, strconv.Itoa(int(src.Line))...)\n\t\tdst = append(dst, 0)\n\t}\n\tif src.Routine != \"\" {\n\t\tdst = append(dst, 'R')\n\t\tdst = append(dst, src.Routine...)\n\t\tdst = append(dst, 0)\n\t}\n\n\tfor k, v := range src.UnknownFields {\n\t\tdst = append(dst, k)\n\t\tdst = append(dst, v...)\n\t\tdst = append(dst, 0)\n\t}\n\n\tdst = append(dst, 0)\n\n\treturn dst\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src ErrorResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType                string\n\t\tSeverity            string\n\t\tSeverityUnlocalized string // only in 9.6 and greater\n\t\tCode                string\n\t\tMessage             string\n\t\tDetail              string\n\t\tHint                string\n\t\tPosition            int32\n\t\tInternalPosition    int32\n\t\tInternalQuery       string\n\t\tWhere               string\n\t\tSchemaName          string\n\t\tTableName           string\n\t\tColumnName          string\n\t\tDataTypeName        string\n\t\tConstraintName      string\n\t\tFile                string\n\t\tLine                int32\n\t\tRoutine             string\n\n\t\tUnknownFields map[byte]string\n\t}{\n\t\tType:                \"ErrorResponse\",\n\t\tSeverity:            src.Severity,\n\t\tSeverityUnlocalized: src.SeverityUnlocalized,\n\t\tCode:                src.Code,\n\t\tMessage:             src.Message,\n\t\tDetail:              src.Detail,\n\t\tHint:                src.Hint,\n\t\tPosition:            src.Position,\n\t\tInternalPosition:    src.InternalPosition,\n\t\tInternalQuery:       src.InternalQuery,\n\t\tWhere:               src.Where,\n\t\tSchemaName:          src.SchemaName,\n\t\tTableName:           src.TableName,\n\t\tColumnName:          src.ColumnName,\n\t\tDataTypeName:        src.DataTypeName,\n\t\tConstraintName:      src.ConstraintName,\n\t\tFile:                src.File,\n\t\tLine:                src.Line,\n\t\tRoutine:             src.Routine,\n\t\tUnknownFields:       src.UnknownFields,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *ErrorResponse) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tType                string\n\t\tSeverity            string\n\t\tSeverityUnlocalized string // only in 9.6 and greater\n\t\tCode                string\n\t\tMessage             string\n\t\tDetail              string\n\t\tHint                string\n\t\tPosition            int32\n\t\tInternalPosition    int32\n\t\tInternalQuery       string\n\t\tWhere               string\n\t\tSchemaName          string\n\t\tTableName           string\n\t\tColumnName          string\n\t\tDataTypeName        string\n\t\tConstraintName      string\n\t\tFile                string\n\t\tLine                int32\n\t\tRoutine             string\n\n\t\tUnknownFields map[byte]string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.Severity = msg.Severity\n\tdst.SeverityUnlocalized = msg.SeverityUnlocalized\n\tdst.Code = msg.Code\n\tdst.Message = msg.Message\n\tdst.Detail = msg.Detail\n\tdst.Hint = msg.Hint\n\tdst.Position = msg.Position\n\tdst.InternalPosition = msg.InternalPosition\n\tdst.InternalQuery = msg.InternalQuery\n\tdst.Where = msg.Where\n\tdst.SchemaName = msg.SchemaName\n\tdst.TableName = msg.TableName\n\tdst.ColumnName = msg.ColumnName\n\tdst.DataTypeName = msg.DataTypeName\n\tdst.ConstraintName = msg.ConstraintName\n\tdst.File = msg.File\n\tdst.Line = msg.Line\n\tdst.Routine = msg.Routine\n\n\tdst.UnknownFields = msg.UnknownFields\n\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/example/pgfortune/README.md",
    "content": "# pgfortune\n\npgfortune is a mock PostgreSQL server that responds to every query with a fortune.\n\n## Installation\n\nInstall `fortune` and `cowsay`. They should be available in any Unix package manager (apt, yum, brew, etc.)\n\n```\ngo get -u github.com/jackc/pgproto3/example/pgfortune\n```\n\n## Usage\n\n```\n$ pgfortune\n```\n\nBy default pgfortune listens on 127.0.0.1:15432 and responds to queries with `fortune | cowsay -f elephant`. These are\nconfigurable with the `listen` and `response-command` arguments respectively.\n\nWhile `pgfortune` is running connect to it with `psql`.\n\n```\n$ psql -h 127.0.0.1 -p 15432\nTiming is on.\nNull display is \"∅\".\nLine style is unicode.\npsql (11.5, server 0.0.0)\nType \"help\" for help.\n\njack@127.0.0.1:15432 jack=# select foo;\n                   fortune\n─────────────────────────────────────────────\n  _________________________________________ ↵\n / Ships are safe in harbor, but they were \\↵\n \\ never meant to stay there.              /↵\n  ----------------------------------------- ↵\n  \\     /\\  ___  /\\                         ↵\n   \\   // \\/   \\/ \\\\                        ↵\n      ((    O O    ))                       ↵\n       \\\\ /     \\ //                        ↵\n        \\/  | |  \\/                         ↵\n         |  | |  |                          ↵\n         |  | |  |                          ↵\n         |   o   |                          ↵\n         | |   | |                          ↵\n         |m|   |m|                          ↵\n\n(1 row)\n\nTime: 28.161 ms\n```\n"
  },
  {
    "path": "pgproto3/example/pgfortune/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n)\n\nvar options struct {\n\tlistenAddress   string\n\tresponseCommand string\n}\n\nfunc main() {\n\tflag.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, \"usage:  %s [options]\\n\", os.Args[0])\n\t\tflag.PrintDefaults()\n\t}\n\n\tflag.StringVar(&options.listenAddress, \"listen\", \"127.0.0.1:15432\", \"Listen address\")\n\tflag.StringVar(&options.responseCommand, \"response-command\", \"fortune | cowsay -f elephant\", \"Command to execute to generate query response\")\n\tflag.Parse()\n\n\tln, err := net.Listen(\"tcp\", options.listenAddress)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tlog.Println(\"Listening on\", ln.Addr())\n\n\tfor {\n\t\tconn, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tlog.Println(\"Accepted connection from\", conn.RemoteAddr())\n\n\t\tb := NewPgFortuneBackend(conn, func() ([]byte, error) {\n\t\t\treturn exec.Command(\"sh\", \"-c\", options.responseCommand).CombinedOutput()\n\t\t})\n\t\tgo func() {\n\t\t\terr := b.Run()\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(err)\n\t\t\t}\n\t\t\tlog.Println(\"Closed connection from\", conn.RemoteAddr())\n\t\t}()\n\t}\n}\n"
  },
  {
    "path": "pgproto3/example/pgfortune/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n)\n\ntype PgFortuneBackend struct {\n\tbackend   *pgproto3.Backend\n\tconn      net.Conn\n\tresponder func() ([]byte, error)\n}\n\nfunc NewPgFortuneBackend(conn net.Conn, responder func() ([]byte, error)) *PgFortuneBackend {\n\tbackend := pgproto3.NewBackend(conn, conn)\n\n\tconnHandler := &PgFortuneBackend{\n\t\tbackend:   backend,\n\t\tconn:      conn,\n\t\tresponder: responder,\n\t}\n\n\treturn connHandler\n}\n\nfunc (p *PgFortuneBackend) Run() error {\n\tdefer p.Close()\n\n\terr := p.handleStartup()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tmsg, err := p.backend.Receive()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error receiving message: %w\", err)\n\t\t}\n\n\t\tswitch msg.(type) {\n\t\tcase *pgproto3.Query:\n\t\t\tresponse, err := p.responder()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error generating query response: %w\", err)\n\t\t\t}\n\n\t\t\tbuf := mustEncode((&pgproto3.RowDescription{Fields: []pgproto3.FieldDescription{\n\t\t\t\t{\n\t\t\t\t\tName:                 []byte(\"fortune\"),\n\t\t\t\t\tTableOID:             0,\n\t\t\t\t\tTableAttributeNumber: 0,\n\t\t\t\t\tDataTypeOID:          25,\n\t\t\t\t\tDataTypeSize:         -1,\n\t\t\t\t\tTypeModifier:         -1,\n\t\t\t\t\tFormat:               0,\n\t\t\t\t},\n\t\t\t}}).Encode(nil))\n\t\t\tbuf = mustEncode((&pgproto3.DataRow{Values: [][]byte{response}}).Encode(buf))\n\t\t\tbuf = mustEncode((&pgproto3.CommandComplete{CommandTag: []byte(\"SELECT 1\")}).Encode(buf))\n\t\t\tbuf = mustEncode((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(buf))\n\t\t\t_, err = p.conn.Write(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error writing query response: %w\", err)\n\t\t\t}\n\t\tcase *pgproto3.Terminate:\n\t\t\treturn nil\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"received message other than Query from client: %#v\", msg)\n\t\t}\n\t}\n}\n\nfunc (p *PgFortuneBackend) handleStartup() error {\n\tstartupMessage, err := p.backend.ReceiveStartupMessage()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error receiving startup message: %w\", err)\n\t}\n\n\tswitch startupMessage.(type) {\n\tcase *pgproto3.StartupMessage:\n\t\tbuf := mustEncode((&pgproto3.AuthenticationOk{}).Encode(nil))\n\t\tbuf = mustEncode((&pgproto3.ReadyForQuery{TxStatus: 'I'}).Encode(buf))\n\t\t_, err = p.conn.Write(buf)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error sending ready for query: %w\", err)\n\t\t}\n\tcase *pgproto3.SSLRequest:\n\t\t_, err = p.conn.Write([]byte(\"N\"))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error sending deny SSL request: %w\", err)\n\t\t}\n\t\treturn p.handleStartup()\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown startup message: %#v\", startupMessage)\n\t}\n\n\treturn nil\n}\n\nfunc (p *PgFortuneBackend) Close() error {\n\treturn p.conn.Close()\n}\n\nfunc mustEncode(buf []byte, err error) []byte {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn buf\n}\n"
  },
  {
    "path": "pgproto3/execute.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Execute struct {\n\tPortal  string\n\tMaxRows uint32\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Execute) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Execute) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tb, err := buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.Portal = string(b[:len(b)-1])\n\n\tif buf.Len() < 4 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Execute\"}\n\t}\n\tdst.MaxRows = binary.BigEndian.Uint32(buf.Next(4))\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Execute) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'E')\n\tdst = append(dst, src.Portal...)\n\tdst = append(dst, 0)\n\tdst = pgio.AppendUint32(dst, src.MaxRows)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Execute) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType    string\n\t\tPortal  string\n\t\tMaxRows uint32\n\t}{\n\t\tType:    \"Execute\",\n\t\tPortal:  src.Portal,\n\t\tMaxRows: src.MaxRows,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/flush.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Flush struct{}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Flush) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Flush) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"Flush\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Flush) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'H', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Flush) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"Flush\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/frontend.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// Frontend acts as a client for the PostgreSQL wire protocol version 3.\ntype Frontend struct {\n\tcr *chunkReader\n\tw  io.Writer\n\n\t// tracer is used to trace messages when Send or Receive is called. This means an outbound message is traced\n\t// before it is actually transmitted (i.e. before Flush). It is safe to change this variable when the Frontend is\n\t// idle. Setting and unsetting tracer provides equivalent functionality to PQtrace and PQuntrace in libpq.\n\ttracer *tracer\n\n\twbuf        []byte\n\tencodeError error\n\n\t// Backend message flyweights\n\tauthenticationOk                AuthenticationOk\n\tauthenticationCleartextPassword AuthenticationCleartextPassword\n\tauthenticationMD5Password       AuthenticationMD5Password\n\tauthenticationGSS               AuthenticationGSS\n\tauthenticationGSSContinue       AuthenticationGSSContinue\n\tauthenticationSASL              AuthenticationSASL\n\tauthenticationSASLContinue      AuthenticationSASLContinue\n\tauthenticationSASLFinal         AuthenticationSASLFinal\n\tbackendKeyData                  BackendKeyData\n\tbindComplete                    BindComplete\n\tcloseComplete                   CloseComplete\n\tcommandComplete                 CommandComplete\n\tcopyBothResponse                CopyBothResponse\n\tcopyData                        CopyData\n\tcopyInResponse                  CopyInResponse\n\tcopyOutResponse                 CopyOutResponse\n\tcopyDone                        CopyDone\n\tdataRow                         DataRow\n\temptyQueryResponse              EmptyQueryResponse\n\terrorResponse                   ErrorResponse\n\tfunctionCallResponse            FunctionCallResponse\n\tnoData                          NoData\n\tnoticeResponse                  NoticeResponse\n\tnotificationResponse            NotificationResponse\n\tparameterDescription            ParameterDescription\n\tparameterStatus                 ParameterStatus\n\tparseComplete                   ParseComplete\n\treadyForQuery                   ReadyForQuery\n\trowDescription                  RowDescription\n\tportalSuspended                 PortalSuspended\n\tnegotiateProtocolVersion        NegotiateProtocolVersion\n\n\tbodyLen    int\n\tmaxBodyLen int // maxBodyLen is the maximum length of a message body in octets. If a message body exceeds this length, Receive will return an error.\n\tmsgType    byte\n\tpartialMsg bool\n\tauthType   uint32\n}\n\n// NewFrontend creates a new Frontend.\nfunc NewFrontend(r io.Reader, w io.Writer) *Frontend {\n\tcr := newChunkReader(r, 0)\n\treturn &Frontend{cr: cr, w: w}\n}\n\n// Send sends a message to the backend (i.e. the server). The message is buffered until Flush is called. Any error\n// encountered will be returned from Flush.\n//\n// Send can work with any FrontendMessage. Some commonly used message types such as Bind have specialized send methods\n// such as SendBind. These methods should be preferred when the type of message is known up front (e.g. when building an\n// extended query protocol query) as they may be faster due to knowing the type of msg rather than it being hidden\n// behind an interface.\nfunc (f *Frontend) Send(msg FrontendMessage) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceMessage('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// Flush writes any pending messages to the backend (i.e. the server).\nfunc (f *Frontend) Flush() error {\n\tif err := f.encodeError; err != nil {\n\t\tf.encodeError = nil\n\t\tf.wbuf = f.wbuf[:0]\n\t\treturn &writeError{err: err, safeToRetry: true}\n\t}\n\n\tif len(f.wbuf) == 0 {\n\t\treturn nil\n\t}\n\n\tn, err := f.w.Write(f.wbuf)\n\n\tconst maxLen = 1024\n\tif len(f.wbuf) > maxLen {\n\t\tf.wbuf = make([]byte, 0, maxLen)\n\t} else {\n\t\tf.wbuf = f.wbuf[:0]\n\t}\n\n\tif err != nil {\n\t\treturn &writeError{err: err, safeToRetry: n == 0}\n\t}\n\n\treturn nil\n}\n\n// Trace starts tracing the message traffic to w. It writes in a similar format to that produced by the libpq function\n// PQtrace.\nfunc (f *Frontend) Trace(w io.Writer, options TracerOptions) {\n\tf.tracer = &tracer{\n\t\tw:             w,\n\t\tbuf:           &bytes.Buffer{},\n\t\tTracerOptions: options,\n\t}\n}\n\n// Untrace stops tracing.\nfunc (f *Frontend) Untrace() {\n\tf.tracer = nil\n}\n\n// SendBind sends a Bind message to the backend (i.e. the server). The message is buffered until Flush is called. Any\n// error encountered will be returned from Flush.\nfunc (f *Frontend) SendBind(msg *Bind) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceBind('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendParse sends a Parse message to the backend (i.e. the server). The message is buffered until Flush is called. Any\n// error encountered will be returned from Flush.\nfunc (f *Frontend) SendParse(msg *Parse) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceParse('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendClose sends a Close message to the backend (i.e. the server). The message is buffered until Flush is called. Any\n// error encountered will be returned from Flush.\nfunc (f *Frontend) SendClose(msg *Close) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceClose('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendDescribe sends a Describe message to the backend (i.e. the server). The message is buffered until Flush is\n// called. Any error encountered will be returned from Flush.\nfunc (f *Frontend) SendDescribe(msg *Describe) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceDescribe('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendExecute sends an Execute message to the backend (i.e. the server). The message is buffered until Flush is called.\n// Any error encountered will be returned from Flush.\nfunc (f *Frontend) SendExecute(msg *Execute) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.TraceQueryute('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendSync sends a Sync message to the backend (i.e. the server). The message is buffered until Flush is called. Any\n// error encountered will be returned from Flush.\nfunc (f *Frontend) SendSync(msg *Sync) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceSync('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendQuery sends a Query message to the backend (i.e. the server). The message is buffered until Flush is called. Any\n// error encountered will be returned from Flush.\nfunc (f *Frontend) SendQuery(msg *Query) {\n\tif f.encodeError != nil {\n\t\treturn\n\t}\n\n\tprevLen := len(f.wbuf)\n\tnewBuf, err := msg.Encode(f.wbuf)\n\tif err != nil {\n\t\tf.encodeError = err\n\t\treturn\n\t}\n\tf.wbuf = newBuf\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceQuery('F', int32(len(f.wbuf)-prevLen), msg)\n\t}\n}\n\n// SendUnbufferedEncodedCopyData immediately sends an encoded CopyData message to the backend (i.e. the server). This method\n// is more efficient than sending a CopyData message with Send as the message data is not copied to the internal buffer\n// before being written out. The internal buffer is flushed before the message is sent.\nfunc (f *Frontend) SendUnbufferedEncodedCopyData(msg []byte) error {\n\terr := f.Flush()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tn, err := f.w.Write(msg)\n\tif err != nil {\n\t\treturn &writeError{err: err, safeToRetry: n == 0}\n\t}\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceCopyData('F', int32(len(msg)-1), &CopyData{})\n\t}\n\n\treturn nil\n}\n\nfunc translateEOFtoErrUnexpectedEOF(err error) error {\n\tif err == io.EOF {\n\t\treturn io.ErrUnexpectedEOF\n\t}\n\treturn err\n}\n\n// Receive receives a message from the backend. The returned message is only valid until the next call to Receive.\nfunc (f *Frontend) Receive() (BackendMessage, error) {\n\tif !f.partialMsg {\n\t\theader, err := f.cr.Next(5)\n\t\tif err != nil {\n\t\t\treturn nil, translateEOFtoErrUnexpectedEOF(err)\n\t\t}\n\n\t\tf.msgType = header[0]\n\n\t\tmsgLength := int(binary.BigEndian.Uint32(header[1:]))\n\t\tif msgLength < 4 {\n\t\t\treturn nil, fmt.Errorf(\"invalid message length: %d\", msgLength)\n\t\t}\n\n\t\tf.bodyLen = msgLength - 4\n\t\tif f.maxBodyLen > 0 && f.bodyLen > f.maxBodyLen {\n\t\t\treturn nil, &ExceededMaxBodyLenErr{f.maxBodyLen, f.bodyLen}\n\t\t}\n\t\tf.partialMsg = true\n\t}\n\n\tmsgBody, err := f.cr.Next(f.bodyLen)\n\tif err != nil {\n\t\treturn nil, translateEOFtoErrUnexpectedEOF(err)\n\t}\n\n\tf.partialMsg = false\n\n\tvar msg BackendMessage\n\tswitch f.msgType {\n\tcase '1':\n\t\tmsg = &f.parseComplete\n\tcase '2':\n\t\tmsg = &f.bindComplete\n\tcase '3':\n\t\tmsg = &f.closeComplete\n\tcase 'A':\n\t\tmsg = &f.notificationResponse\n\tcase 'c':\n\t\tmsg = &f.copyDone\n\tcase 'C':\n\t\tmsg = &f.commandComplete\n\tcase 'd':\n\t\tmsg = &f.copyData\n\tcase 'D':\n\t\tmsg = &f.dataRow\n\tcase 'E':\n\t\tmsg = &f.errorResponse\n\tcase 'G':\n\t\tmsg = &f.copyInResponse\n\tcase 'H':\n\t\tmsg = &f.copyOutResponse\n\tcase 'I':\n\t\tmsg = &f.emptyQueryResponse\n\tcase 'K':\n\t\tmsg = &f.backendKeyData\n\tcase 'n':\n\t\tmsg = &f.noData\n\tcase 'N':\n\t\tmsg = &f.noticeResponse\n\tcase 'R':\n\t\tvar err error\n\t\tmsg, err = f.findAuthenticationMessageType(msgBody)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase 's':\n\t\tmsg = &f.portalSuspended\n\tcase 'S':\n\t\tmsg = &f.parameterStatus\n\tcase 't':\n\t\tmsg = &f.parameterDescription\n\tcase 'T':\n\t\tmsg = &f.rowDescription\n\tcase 'V':\n\t\tmsg = &f.functionCallResponse\n\tcase 'W':\n\t\tmsg = &f.copyBothResponse\n\tcase 'Z':\n\t\tmsg = &f.readyForQuery\n\tcase 'v':\n\t\tmsg = &f.negotiateProtocolVersion\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown message type: %c\", f.msgType)\n\t}\n\n\terr = msg.Decode(msgBody)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif f.tracer != nil {\n\t\tf.tracer.traceMessage('B', int32(5+len(msgBody)), msg)\n\t}\n\n\treturn msg, nil\n}\n\n// Authentication message type constants.\n// See src/include/libpq/pqcomm.h for all\n// constants.\nconst (\n\tAuthTypeOk                = 0\n\tAuthTypeCleartextPassword = 3\n\tAuthTypeMD5Password       = 5\n\tAuthTypeSCMCreds          = 6\n\tAuthTypeGSS               = 7\n\tAuthTypeGSSCont           = 8\n\tAuthTypeSSPI              = 9\n\tAuthTypeSASL              = 10\n\tAuthTypeSASLContinue      = 11\n\tAuthTypeSASLFinal         = 12\n)\n\nfunc (f *Frontend) findAuthenticationMessageType(src []byte) (BackendMessage, error) {\n\tif len(src) < 4 {\n\t\treturn nil, errors.New(\"authentication message too short\")\n\t}\n\tf.authType = binary.BigEndian.Uint32(src[:4])\n\n\tswitch f.authType {\n\tcase AuthTypeOk:\n\t\treturn &f.authenticationOk, nil\n\tcase AuthTypeCleartextPassword:\n\t\treturn &f.authenticationCleartextPassword, nil\n\tcase AuthTypeMD5Password:\n\t\treturn &f.authenticationMD5Password, nil\n\tcase AuthTypeSCMCreds:\n\t\treturn nil, errors.New(\"AuthTypeSCMCreds is unimplemented\")\n\tcase AuthTypeGSS:\n\t\treturn &f.authenticationGSS, nil\n\tcase AuthTypeGSSCont:\n\t\treturn &f.authenticationGSSContinue, nil\n\tcase AuthTypeSSPI:\n\t\treturn nil, errors.New(\"AuthTypeSSPI is unimplemented\")\n\tcase AuthTypeSASL:\n\t\treturn &f.authenticationSASL, nil\n\tcase AuthTypeSASLContinue:\n\t\treturn &f.authenticationSASLContinue, nil\n\tcase AuthTypeSASLFinal:\n\t\treturn &f.authenticationSASLFinal, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown authentication type: %d\", f.authType)\n\t}\n}\n\n// GetAuthType returns the authType used in the current state of the frontend.\n// See SetAuthType for more information.\nfunc (f *Frontend) GetAuthType() uint32 {\n\treturn f.authType\n}\n\nfunc (f *Frontend) ReadBufferLen() int {\n\treturn f.cr.wp - f.cr.rp\n}\n\n// SetMaxBodyLen sets the maximum length of a message body in octets.\n// If a message body exceeds this length, Receive will return an error.\n// This is useful for protecting against a corrupted server that sends\n// messages with incorrect length, which can cause memory exhaustion.\n// The default value is 0.\n// If maxBodyLen is 0, then no maximum is enforced.\nfunc (f *Frontend) SetMaxBodyLen(maxBodyLen int) {\n\tf.maxBodyLen = maxBodyLen\n}\n"
  },
  {
    "path": "pgproto3/frontend_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype interruptReader struct {\n\tchunks [][]byte\n}\n\nfunc (ir *interruptReader) Read(p []byte) (n int, err error) {\n\tif len(ir.chunks) == 0 {\n\t\treturn 0, io.EOF\n\t}\n\n\tn = copy(p, ir.chunks[0])\n\tif n != len(ir.chunks[0]) {\n\t\tpanic(\"this test reader doesn't support partial reads of chunks\")\n\t}\n\n\tir.chunks = ir.chunks[1:]\n\n\treturn n, nil\n}\n\nfunc (ir *interruptReader) push(p []byte) {\n\tir.chunks = append(ir.chunks, p)\n}\n\nfunc TestFrontendReceiveInterrupted(t *testing.T) {\n\tt.Parallel()\n\n\tserver := &interruptReader{}\n\tserver.push([]byte{'Z', 0, 0, 0, 5})\n\n\tfrontend := pgproto3.NewFrontend(server, nil)\n\n\tmsg, err := frontend.Receive()\n\tif err == nil {\n\t\tt.Fatal(\"expected err\")\n\t}\n\tif msg != nil {\n\t\tt.Fatalf(\"did not expect msg, but %v\", msg)\n\t}\n\n\tserver.push([]byte{'I'})\n\n\tmsg, err = frontend.Receive()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif msg, ok := msg.(*pgproto3.ReadyForQuery); !ok || msg.TxStatus != 'I' {\n\t\tt.Fatalf(\"unexpected msg: %v\", msg)\n\t}\n}\n\nfunc TestFrontendReceiveUnexpectedEOF(t *testing.T) {\n\tt.Parallel()\n\n\tserver := &interruptReader{}\n\tserver.push([]byte{'Z', 0, 0, 0, 5})\n\n\tfrontend := pgproto3.NewFrontend(server, nil)\n\n\tmsg, err := frontend.Receive()\n\tif err == nil {\n\t\tt.Fatal(\"expected err\")\n\t}\n\tif msg != nil {\n\t\tt.Fatalf(\"did not expect msg, but %v\", msg)\n\t}\n\n\tmsg, err = frontend.Receive()\n\tassert.Nil(t, msg)\n\tassert.Equal(t, io.ErrUnexpectedEOF, err)\n}\n\nfunc TestErrorResponse(t *testing.T) {\n\tt.Parallel()\n\n\twant := &pgproto3.ErrorResponse{\n\t\tSeverity:            \"ERROR\",\n\t\tSeverityUnlocalized: \"ERROR\",\n\t\tMessage:             `column \"foo\" does not exist`,\n\t\tFile:                \"parse_relation.c\",\n\t\tCode:                \"42703\",\n\t\tPosition:            8,\n\t\tLine:                3513,\n\t\tRoutine:             \"errorMissingColumn\",\n\t}\n\n\traw := []byte{\n\t\t'E', 0, 0, 0, 'f',\n\t\t'S', 'E', 'R', 'R', 'O', 'R', 0,\n\t\t'V', 'E', 'R', 'R', 'O', 'R', 0,\n\t\t'C', '4', '2', '7', '0', '3', 0,\n\t\t'M', 'c', 'o', 'l', 'u', 'm', 'n', 32, '\"', 'f', 'o', 'o', '\"', 32, 'd', 'o', 'e', 's', 32, 'n', 'o', 't', 32, 'e', 'x', 'i', 's', 't', 0,\n\t\t'P', '8', 0,\n\t\t'F', 'p', 'a', 'r', 's', 'e', '_', 'r', 'e', 'l', 'a', 't', 'i', 'o', 'n', '.', 'c', 0,\n\t\t'L', '3', '5', '1', '3', 0,\n\t\t'R', 'e', 'r', 'r', 'o', 'r', 'M', 'i', 's', 's', 'i', 'n', 'g', 'C', 'o', 'l', 'u', 'm', 'n', 0, 0,\n\t}\n\n\tserver := &interruptReader{}\n\tserver.push(raw)\n\n\tfrontend := pgproto3.NewFrontend(server, nil)\n\n\tgot, err := frontend.Receive()\n\trequire.NoError(t, err)\n\tassert.Equal(t, want, got)\n}\n\nfunc TestFrontendReceiveExceededMaxBodyLen(t *testing.T) {\n\tt.Parallel()\n\n\tclient := &interruptReader{}\n\tclient.push([]byte{'D', 0, 0, 10, 10})\n\n\tfrontend := pgproto3.NewFrontend(client, nil)\n\n\t// Set max body len to 5\n\tfrontend.SetMaxBodyLen(5)\n\n\t// Receive regular msg\n\tmsg, err := frontend.Receive()\n\tassert.Nil(t, msg)\n\tvar invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr\n\tassert.ErrorAs(t, err, &invalidBodyLenErr)\n}\n"
  },
  {
    "path": "pgproto3/function_call.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype FunctionCall struct {\n\tFunction         uint32\n\tArgFormatCodes   []uint16\n\tArguments        [][]byte\n\tResultFormatCode uint16\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*FunctionCall) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *FunctionCall) Decode(src []byte) error {\n\t*dst = FunctionCall{}\n\trp := 0\n\n\tif len(src) < 8 {\n\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t}\n\n\t// Specifies the object ID of the function to call.\n\tdst.Function = binary.BigEndian.Uint32(src[rp:])\n\trp += 4\n\t// The number of argument format codes that follow (denoted C below).\n\t// This can be zero to indicate that there are no arguments or that the arguments all use the default format (text);\n\t// or one, in which case the specified format code is applied to all arguments;\n\t// or it can equal the actual number of arguments.\n\tnArgumentCodes := int(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\n\tif len(src[rp:]) < nArgumentCodes*2+2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t}\n\n\targumentCodes := make([]uint16, nArgumentCodes)\n\tfor i := range nArgumentCodes {\n\t\t// The argument format codes. Each must presently be zero (text) or one (binary).\n\t\tac := binary.BigEndian.Uint16(src[rp:])\n\t\tif ac != 0 && ac != 1 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t\t}\n\t\targumentCodes[i] = ac\n\t\trp += 2\n\t}\n\tdst.ArgFormatCodes = argumentCodes\n\n\t// Specifies the number of arguments being supplied to the function.\n\tnArguments := int(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\targuments := make([][]byte, nArguments)\n\tfor i := range nArguments {\n\t\tif len(src[rp:]) < 4 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t\t}\n\t\t// The length of the argument value, in bytes (this count does not include itself). Can be zero.\n\t\t// As a special case, -1 indicates a NULL argument value. No value bytes follow in the NULL case.\n\t\targumentLength := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\trp += 4\n\t\tif argumentLength == -1 {\n\t\t\targuments[i] = nil\n\t\t} else if argumentLength < 0 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t\t} else {\n\t\t\tif len(src[rp:]) < argumentLength {\n\t\t\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t\t\t}\n\t\t\t// The value of the argument, in the format indicated by the associated format code. n is the above length.\n\t\t\targumentValue := src[rp : rp+argumentLength]\n\t\t\trp += argumentLength\n\t\t\targuments[i] = argumentValue\n\t\t}\n\t}\n\tdst.Arguments = arguments\n\t// The format code for the function result. Must presently be zero (text) or one (binary).\n\tif len(src[rp:]) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t}\n\tresultFormatCode := binary.BigEndian.Uint16(src[rp:])\n\tif resultFormatCode != 0 && resultFormatCode != 1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCall\"}\n\t}\n\tdst.ResultFormatCode = resultFormatCode\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *FunctionCall) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'F')\n\tdst = pgio.AppendUint32(dst, src.Function)\n\n\tif len(src.ArgFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many arg format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ArgFormatCodes)))\n\tfor _, argFormatCode := range src.ArgFormatCodes {\n\t\tdst = pgio.AppendUint16(dst, argFormatCode)\n\t}\n\n\tif len(src.Arguments) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many arguments\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.Arguments)))\n\tfor _, argument := range src.Arguments {\n\t\tif argument == nil {\n\t\t\tdst = pgio.AppendInt32(dst, -1)\n\t\t} else {\n\t\t\tdst = pgio.AppendInt32(dst, int32(len(argument)))\n\t\t\tdst = append(dst, argument...)\n\t\t}\n\t}\n\tdst = pgio.AppendUint16(dst, src.ResultFormatCode)\n\treturn finishMessage(dst, sp)\n}\n"
  },
  {
    "path": "pgproto3/function_call_decode_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// github.com/jackc/pgx/issues/2520\nfunc TestFunctionCallDecodeNegativeArgumentLength(t *testing.T) {\n\tt.Parallel()\n\n\t// Craft a FunctionCall message with a negative argument length that is not -1.\n\t//\n\t// Message layout:\n\t//   - function OID: 1 (4 bytes)\n\t//   - arg format code count: 0 (2 bytes)\n\t//   - argument count: 1 (2 bytes)\n\t//   - argument 0 length: -2 (4 bytes, 0xFFFFFFFE)\n\tsrc := []byte{\n\t\t0, 0, 0, 1, // function OID = 1\n\t\t0, 0, // arg format code count = 0\n\t\t0, 1, // argument count = 1\n\t\t0xFF, 0xFF, 0xFF, 0xFE, // argument length = -2\n\t}\n\n\tvar msg pgproto3.FunctionCall\n\terr := msg.Decode(src)\n\trequire.Error(t, err, \"FunctionCall.Decode should reject negative argument length other than -1\")\n}\n"
  },
  {
    "path": "pgproto3/function_call_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype FunctionCallResponse struct {\n\tResult []byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*FunctionCallResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *FunctionCallResponse) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCallResponse\"}\n\t}\n\trp := 0\n\tresultSize := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\trp += 4\n\n\tif resultSize == -1 {\n\t\tdst.Result = nil\n\t\treturn nil\n\t}\n\n\tif resultSize < 0 || len(src[rp:]) != resultSize {\n\t\treturn &invalidMessageFormatErr{messageType: \"FunctionCallResponse\"}\n\t}\n\n\tdst.Result = src[rp:]\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *FunctionCallResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'V')\n\n\tif src.Result == nil {\n\t\tdst = pgio.AppendInt32(dst, -1)\n\t} else {\n\t\tdst = pgio.AppendInt32(dst, int32(len(src.Result)))\n\t\tdst = append(dst, src.Result...)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src FunctionCallResponse) MarshalJSON() ([]byte, error) {\n\tvar formattedValue map[string]string\n\tvar hasNonPrintable bool\n\tfor _, b := range src.Result {\n\t\tif b < 32 {\n\t\t\thasNonPrintable = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif hasNonPrintable {\n\t\tformattedValue = map[string]string{\"binary\": hex.EncodeToString(src.Result)}\n\t} else {\n\t\tformattedValue = map[string]string{\"text\": string(src.Result)}\n\t}\n\n\treturn json.Marshal(struct {\n\t\tType   string\n\t\tResult map[string]string\n\t}{\n\t\tType:   \"FunctionCallResponse\",\n\t\tResult: formattedValue,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *FunctionCallResponse) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tResult map[string]string\n\t}\n\terr := json.Unmarshal(data, &msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.Result, err = getValueFromJSON(msg.Result)\n\treturn err\n}\n"
  },
  {
    "path": "pgproto3/function_call_response_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunctionCallResponseDecodeNullResult(t *testing.T) {\n\tt.Parallel()\n\n\t// A valid NULL function call response has result size = -1 (0xFFFFFFFF).\n\t// Without the int32 cast, int(uint32(0xFFFFFFFF)) is 4294967295 on 64-bit,\n\t// so the -1 sentinel check is dead code and a valid NULL is incorrectly rejected.\n\tsrc := []byte{\n\t\t0xFF, 0xFF, 0xFF, 0xFF, // result size = -1 (NULL)\n\t}\n\n\tvar msg pgproto3.FunctionCallResponse\n\terr := msg.Decode(src)\n\trequire.NoError(t, err, \"FunctionCallResponse.Decode should accept -1 as NULL result\")\n\trequire.Nil(t, msg.Result)\n}\n\nfunc TestFunctionCallResponseDecodeNegativeResultSize(t *testing.T) {\n\tt.Parallel()\n\n\t// A result size that is negative but not -1 should be rejected.\n\tsrc := []byte{\n\t\t0xFF, 0xFF, 0xFF, 0xFE, // result size = -2\n\t}\n\n\tvar msg pgproto3.FunctionCallResponse\n\terr := msg.Decode(src)\n\trequire.Error(t, err, \"FunctionCallResponse.Decode should reject negative result size other than -1\")\n}\n"
  },
  {
    "path": "pgproto3/function_call_test.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFunctionCall_EncodeDecode(t *testing.T) {\n\ttype fields struct {\n\t\tFunction         uint32\n\t\tArgFormatCodes   []uint16\n\t\tArguments        [][]byte\n\t\tResultFormatCode uint16\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{\"valid\", fields{uint32(123), []uint16{0, 1, 0, 1}, [][]byte{[]byte(\"foo\"), []byte(\"bar\"), []byte(\"baz\")}, uint16(1)}, false},\n\t\t{\"invalid format code\", fields{uint32(123), []uint16{2, 1, 0, 1}, [][]byte{[]byte(\"foo\"), []byte(\"bar\"), []byte(\"baz\")}, uint16(0)}, true},\n\t\t{\"invalid result format code\", fields{uint32(123), []uint16{1, 1, 0, 1}, [][]byte{[]byte(\"foo\"), []byte(\"bar\"), []byte(\"baz\")}, uint16(2)}, true},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tsrc := &FunctionCall{\n\t\t\t\tFunction:         tt.fields.Function,\n\t\t\t\tArgFormatCodes:   tt.fields.ArgFormatCodes,\n\t\t\t\tArguments:        tt.fields.Arguments,\n\t\t\t\tResultFormatCode: tt.fields.ResultFormatCode,\n\t\t\t}\n\t\t\tencoded, err := src.Encode([]byte{})\n\t\t\trequire.NoError(t, err)\n\t\t\tdst := &FunctionCall{}\n\t\t\t// Check the header\n\t\t\tmsgTypeCode := encoded[0]\n\t\t\tif msgTypeCode != 'F' {\n\t\t\t\tt.Errorf(\"msgTypeCode %v should be 'F'\", msgTypeCode)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Check length, does not include type code character\n\t\t\tl := binary.BigEndian.Uint32(encoded[1:5])\n\t\t\tif int(l) != (len(encoded) - 1) {\n\t\t\t\tt.Errorf(\"Incorrect message length, got = %v, wanted = %v\", l, len(encoded))\n\t\t\t}\n\t\t\t// Check decoding works as expected\n\t\t\terr = dst.Decode(encoded[5:])\n\t\t\tif err != nil {\n\t\t\t\tif !tt.wantErr {\n\t\t\t\t\tt.Errorf(\"FunctionCall.Decode() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(src, dst) {\n\t\t\t\tt.Error(\"difference after encode / decode cycle\")\n\t\t\t\tt.Errorf(\"src = %v\", src)\n\t\t\t\tt.Errorf(\"dst = %v\", dst)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgproto3/fuzz_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc FuzzFrontend(f *testing.F) {\n\ttestcases := []struct {\n\t\tmsgType byte\n\t\tmsgLen  uint32\n\t\tmsgBody []byte\n\t}{\n\t\t{\n\t\t\tmsgType: 'Z',\n\t\t\tmsgLen:  2,\n\t\t\tmsgBody: []byte{'I'},\n\t\t},\n\t\t{\n\t\t\tmsgType: 'Z',\n\t\t\tmsgLen:  5,\n\t\t\tmsgBody: []byte{'I'},\n\t\t},\n\t}\n\tfor _, tc := range testcases {\n\t\tf.Add(tc.msgType, tc.msgLen, tc.msgBody)\n\t}\n\tf.Fuzz(func(t *testing.T, msgType byte, msgLen uint32, msgBody []byte) {\n\t\t// Prune any msgLen > len(msgBody) because they would hang the test waiting for more input.\n\t\tif int(msgLen) > len(msgBody)+4 {\n\t\t\treturn\n\t\t}\n\n\t\t// Prune any messages that are too long.\n\t\tif msgLen > 128 || len(msgBody) > 128 {\n\t\t\treturn\n\t\t}\n\n\t\tr := &bytes.Buffer{}\n\t\tw := &bytes.Buffer{}\n\t\tfe := pgproto3.NewFrontend(r, w)\n\n\t\tvar encodedMsg []byte\n\t\tencodedMsg = append(encodedMsg, msgType)\n\t\tencodedMsg = pgio.AppendUint32(encodedMsg, msgLen)\n\t\tencodedMsg = append(encodedMsg, msgBody...)\n\t\t_, err := r.Write(encodedMsg)\n\t\trequire.NoError(t, err)\n\n\t\t// Not checking anything other than no panic.\n\t\tfe.Receive()\n\t})\n}\n\nfunc FuzzBackend(f *testing.F) {\n\ttestcases := []struct {\n\t\tmsgType byte\n\t\tmsgLen  uint32\n\t\tmsgBody []byte\n\t}{\n\t\t{msgType: 'B', msgLen: 14, msgBody: []byte{0, 0, 0, 0, 0, 1, 0, 0, 0, 1}},             // Bind\n\t\t{msgType: 'P', msgLen: 8, msgBody: []byte{0, 0, 0, 0}},                                // Parse\n\t\t{msgType: 'Q', msgLen: 6, msgBody: []byte{0}},                                         // Query\n\t\t{msgType: 'F', msgLen: 18, msgBody: []byte{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1}}, // FunctionCall\n\t}\n\tfor _, tc := range testcases {\n\t\tf.Add(tc.msgType, tc.msgLen, tc.msgBody)\n\t}\n\tf.Fuzz(func(t *testing.T, msgType byte, msgLen uint32, msgBody []byte) {\n\t\t// Prune any msgLen > len(msgBody) because they would hang the test waiting for more input.\n\t\tif int(msgLen) > len(msgBody)+4 {\n\t\t\treturn\n\t\t}\n\n\t\t// Prune any messages that are too long.\n\t\tif msgLen > 128 || len(msgBody) > 128 {\n\t\t\treturn\n\t\t}\n\n\t\tr := &bytes.Buffer{}\n\t\tw := &bytes.Buffer{}\n\t\tbe := pgproto3.NewBackend(r, w)\n\n\t\tvar encodedMsg []byte\n\t\tencodedMsg = append(encodedMsg, msgType)\n\t\tencodedMsg = pgio.AppendUint32(encodedMsg, msgLen)\n\t\tencodedMsg = append(encodedMsg, msgBody...)\n\t\t_, err := r.Write(encodedMsg)\n\t\trequire.NoError(t, err)\n\n\t\t// Not checking anything other than no panic.\n\t\tbe.Receive()\n\t})\n}\n\n// Fuzz individual Decode methods directly. This provides better coverage than\n// going through Frontend/Backend.Receive because there is no message framing\n// overhead and the fuzzer can explore the full input space of each decoder.\n\nfunc FuzzBind(f *testing.F) {\n\tf.Add([]byte{0, 0, 0, 0, 0, 1, 0, 0, 0, 1})\n\tf.Add([]byte{0, 0, 0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0xFF}) // NULL param\n\tf.Add([]byte{0, 0, 0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0xFE}) // negative param length\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.Bind\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzDataRow(f *testing.F) {\n\tf.Add([]byte{0, 1, 0, 0, 0, 3, 'a', 'b', 'c'})\n\tf.Add([]byte{0, 1, 0xFF, 0xFF, 0xFF, 0xFF}) // NULL\n\tf.Add([]byte{0, 1, 0xFF, 0xFF, 0xFF, 0xFE}) // negative length\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.DataRow\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzRowDescription(f *testing.F) {\n\tf.Add([]byte{0, 1, 'n', 'a', 'm', 'e', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})\n\tf.Add([]byte{0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.RowDescription\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzFunctionCall(f *testing.F) {\n\tf.Add([]byte{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1})\n\tf.Add([]byte{0, 0, 0, 1, 0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0, 1}) // NULL arg\n\tf.Add([]byte{0, 0, 0, 1, 0, 0, 0, 1, 0xFF, 0xFF, 0xFF, 0xFE, 0, 1}) // negative arg length\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.FunctionCall\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzFunctionCallResponse(f *testing.F) {\n\tf.Add([]byte{0, 0, 0, 3, 'a', 'b', 'c'})\n\tf.Add([]byte{0xFF, 0xFF, 0xFF, 0xFF}) // NULL\n\tf.Add([]byte{0xFF, 0xFF, 0xFF, 0xFE}) // negative length\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.FunctionCallResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzParse(f *testing.F) {\n\tf.Add([]byte{0, 0, 0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.Parse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzQuery(f *testing.F) {\n\tf.Add([]byte(\"SELECT 1\\x00\"))\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.Query\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzClose(f *testing.F) {\n\tf.Add([]byte{'S', 't', 'e', 's', 't', 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.Close\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzDescribe(f *testing.F) {\n\tf.Add([]byte{'S', 't', 'e', 's', 't', 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.Describe\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzExecute(f *testing.F) {\n\tf.Add([]byte{'t', 'e', 's', 't', 0, 0, 0, 0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.Execute\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzCopyInResponse(f *testing.F) {\n\tf.Add([]byte{0, 0, 1, 0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.CopyInResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzCopyOutResponse(f *testing.F) {\n\tf.Add([]byte{0, 0, 1, 0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.CopyOutResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzCopyBothResponse(f *testing.F) {\n\tf.Add([]byte{0, 0, 1, 0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.CopyBothResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzErrorResponse(f *testing.F) {\n\tf.Add([]byte{'S', 'E', 'R', 'R', 'O', 'R', 0, 'M', 't', 'e', 's', 't', 0, 0})\n\tf.Add([]byte{0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.ErrorResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzNotificationResponse(f *testing.F) {\n\tf.Add([]byte{0, 0, 0, 1, 'c', 'h', 0, 'p', 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.NotificationResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzParameterDescription(f *testing.F) {\n\tf.Add([]byte{0, 1, 0, 0, 0, 23})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.ParameterDescription\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzParameterStatus(f *testing.F) {\n\tf.Add([]byte{'k', 'e', 'y', 0, 'v', 'a', 'l', 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.ParameterStatus\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzReadyForQuery(f *testing.F) {\n\tf.Add([]byte{'I'})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.ReadyForQuery\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzSASLInitialResponse(f *testing.F) {\n\tf.Add([]byte(\"SCRAM-SHA-256\\x00\\x00\\x00\\x00\\x04test\"))\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.SASLInitialResponse\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzStartupMessage(f *testing.F) {\n\tf.Add([]byte{0, 3, 0, 0, 'u', 's', 'e', 'r', 0, 'p', 'g', 0, 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.StartupMessage\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n\nfunc FuzzNegotiateProtocolVersion(f *testing.F) {\n\tf.Add([]byte{0, 0, 0, 1, 0, 0, 0, 1, 'o', 'p', 't', 0})\n\tf.Add([]byte{})\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tvar msg pgproto3.NegotiateProtocolVersion\n\t\tmsg.Decode(data) // must not panic\n\t})\n}\n"
  },
  {
    "path": "pgproto3/gss_enc_request.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst gssEncReqNumber = 80877104\n\ntype GSSEncRequest struct{}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*GSSEncRequest) Frontend() {}\n\nfunc (dst *GSSEncRequest) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"gss encoding request too short\")\n\t}\n\n\trequestCode := binary.BigEndian.Uint32(src)\n\n\tif requestCode != gssEncReqNumber {\n\t\treturn errors.New(\"bad gss encoding request code\")\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 4 byte message length.\nfunc (src *GSSEncRequest) Encode(dst []byte) ([]byte, error) {\n\tdst = pgio.AppendInt32(dst, 8)\n\tdst = pgio.AppendInt32(dst, gssEncReqNumber)\n\treturn dst, nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src GSSEncRequest) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType            string\n\t\tProtocolVersion uint32\n\t\tParameters      map[string]string\n\t}{\n\t\tType: \"GSSEncRequest\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/gss_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype GSSResponse struct {\n\tData []byte\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (g *GSSResponse) Frontend() {}\n\nfunc (g *GSSResponse) Decode(data []byte) error {\n\tg.Data = data\n\treturn nil\n}\n\nfunc (g *GSSResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'p')\n\tdst = append(dst, g.Data...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (g *GSSResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData []byte\n\t}{\n\t\tType: \"GSSResponse\",\n\t\tData: g.Data,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (g *GSSResponse) UnmarshalJSON(data []byte) error {\n\tvar msg struct {\n\t\tData []byte\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\tg.Data = msg.Data\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/json_test.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestJSONUnmarshalAuthenticationMD5Password(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationMD5Password\", \"Salt\":[97,98,99,100]}`)\n\twant := AuthenticationMD5Password{\n\t\tSalt: [4]byte{'a', 'b', 'c', 'd'},\n\t}\n\n\tvar got AuthenticationMD5Password\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationMD5Password struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalAuthenticationSASL(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationSASL\",\"AuthMechanisms\":[\"SCRAM-SHA-256\"]}`)\n\twant := AuthenticationSASL{\n\t\t[]string{\"SCRAM-SHA-256\"},\n\t}\n\n\tvar got AuthenticationSASL\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationSASL struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalAuthenticationGSS(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationGSS\"}`)\n\twant := AuthenticationGSS{}\n\n\tvar got AuthenticationGSS\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationGSS struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalAuthenticationGSSContinue(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationGSSContinue\",\"Data\":[1,2,3,4]}`)\n\twant := AuthenticationGSSContinue{Data: []byte{1, 2, 3, 4}}\n\n\tvar got AuthenticationGSSContinue\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationGSSContinue struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalAuthenticationSASLContinue(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationSASLContinue\", \"Data\":\"1\"}`)\n\twant := AuthenticationSASLContinue{\n\t\tData: []byte{'1'},\n\t}\n\n\tvar got AuthenticationSASLContinue\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationSASLContinue struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalAuthenticationSASLFinal(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationSASLFinal\", \"Data\":\"1\"}`)\n\twant := AuthenticationSASLFinal{\n\t\tData: []byte{'1'},\n\t}\n\n\tvar got AuthenticationSASLFinal\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationSASLFinal struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalBackendKeyData30(t *testing.T) {\n\t// SecretKey is now hex-encoded: d90caedb = 3641487067 in big-endian\n\tdata := []byte(`{\"Type\":\"BackendKeyData\",\"ProcessID\":8864,\"SecretKey\":\"d90caedb\"}`)\n\twant := BackendKeyData{\n\t\tProcessID: 8864,\n\t\tSecretKey: []byte{0xd9, 0x0c, 0xae, 0xdb},\n\t}\n\n\tvar got BackendKeyData\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled BackendKeyData struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalBackendKeyData32(t *testing.T) {\n\t// 32-byte key as hex\n\tdata := []byte(`{\"Type\":\"BackendKeyData\",\"ProcessID\":8864,\"SecretKey\":\"0102030405060708091011121314151617181920212223242526272829303132\"}`)\n\twant := BackendKeyData{\n\t\tProcessID: 8864,\n\t\tSecretKey: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32},\n\t}\n\n\tvar got BackendKeyData\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled BackendKeyData struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCommandComplete(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"CommandComplete\",\"CommandTag\":\"SELECT 1\"}`)\n\twant := CommandComplete{\n\t\tCommandTag: []byte(\"SELECT 1\"),\n\t}\n\n\tvar got CommandComplete\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CommandComplete struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCopyBothResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"CopyBothResponse\", \"OverallFormat\": \"W\"}`)\n\twant := CopyBothResponse{\n\t\tOverallFormat: 'W',\n\t}\n\n\tvar got CopyBothResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CopyBothResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCopyData(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"CopyData\"}`)\n\twant := CopyData{\n\t\tData: []byte{},\n\t}\n\n\tvar got CopyData\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CopyData struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCopyInResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"CopyBothResponse\", \"OverallFormat\": \"W\"}`)\n\twant := CopyBothResponse{\n\t\tOverallFormat: 'W',\n\t}\n\n\tvar got CopyBothResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CopyBothResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCopyOutResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"CopyOutResponse\", \"OverallFormat\": \"W\"}`)\n\twant := CopyOutResponse{\n\t\tOverallFormat: 'W',\n\t}\n\n\tvar got CopyOutResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CopyOutResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalDataRow(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"DataRow\",\"Values\":[{\"text\":\"abc\"},{\"text\":\"this is a test\"},{\"binary\":\"000263d3114d2e34\"}]}`)\n\twant := DataRow{\n\t\tValues: [][]byte{\n\t\t\t[]byte(\"abc\"),\n\t\t\t[]byte(\"this is a test\"),\n\t\t\t{0, 2, 99, 211, 17, 77, 46, 52},\n\t\t},\n\t}\n\n\tvar got DataRow\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled DataRow struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalErrorResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"ErrorResponse\", \"UnknownFields\": {\"97\": \"foo\"}}`)\n\twant := ErrorResponse{\n\t\tUnknownFields: map[byte]string{\n\t\t\t'a': \"foo\",\n\t\t},\n\t}\n\n\tvar got ErrorResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled ErrorResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalFunctionCallResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"FunctionCallResponse\"}`)\n\twant := FunctionCallResponse{}\n\n\tvar got FunctionCallResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled FunctionCallResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalNoticeResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"NoticeResponse\", \"UnknownFields\": {\"97\": \"foo\"}}`)\n\twant := NoticeResponse{\n\t\tUnknownFields: map[byte]string{\n\t\t\t'a': \"foo\",\n\t\t},\n\t}\n\n\tvar got NoticeResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled NoticeResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalNotificationResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"NotificationResponse\"}`)\n\twant := NotificationResponse{}\n\n\tvar got NotificationResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled NotificationResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalParameterDescription(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"ParameterDescription\", \"ParameterOIDs\": [25]}`)\n\twant := ParameterDescription{\n\t\tParameterOIDs: []uint32{25},\n\t}\n\n\tvar got ParameterDescription\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled ParameterDescription struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalParameterStatus(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"ParameterStatus\",\"Name\":\"TimeZone\",\"Value\":\"Europe/Amsterdam\"}`)\n\twant := ParameterStatus{\n\t\tName:  \"TimeZone\",\n\t\tValue: \"Europe/Amsterdam\",\n\t}\n\n\tvar got ParameterStatus\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled ParameterDescription struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalReadyForQuery(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"ReadyForQuery\",\"TxStatus\":\"I\"}`)\n\twant := ReadyForQuery{\n\t\tTxStatus: 'I',\n\t}\n\n\tvar got ReadyForQuery\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled ParameterDescription struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalRowDescription(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"RowDescription\",\"Fields\":[{\"Name\":\"generate_series\",\"TableOID\":0,\"TableAttributeNumber\":0,\"DataTypeOID\":23,\"DataTypeSize\":4,\"TypeModifier\":-1,\"Format\":0}]}`)\n\twant := RowDescription{\n\t\tFields: []FieldDescription{\n\t\t\t{\n\t\t\t\tName:         []byte(\"generate_series\"),\n\t\t\t\tDataTypeOID:  23,\n\t\t\t\tDataTypeSize: 4,\n\t\t\t\tTypeModifier: -1,\n\t\t\t},\n\t\t},\n\t}\n\n\tvar got RowDescription\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled RowDescription struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalBind(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc string\n\t\tdata []byte\n\t}{\n\t\t{\n\t\t\t\"textual\",\n\t\t\t[]byte(`{\"Type\":\"Bind\",\"DestinationPortal\":\"\",\"PreparedStatement\":\"lrupsc_1_0\",\"ParameterFormatCodes\":[0],\"Parameters\":[{\"text\":\"ABC-123\"}],\"ResultFormatCodes\":[0,0,0,0,0,1,1]}`),\n\t\t},\n\t\t{\n\t\t\t\"binary\",\n\t\t\t[]byte(`{\"Type\":\"Bind\",\"DestinationPortal\":\"\",\"PreparedStatement\":\"lrupsc_1_0\",\"ParameterFormatCodes\":[0],\"Parameters\":[{\"binary\":\"` + hex.EncodeToString([]byte(\"ABC-123\")) + `\"}],\"ResultFormatCodes\":[0,0,0,0,0,1,1]}`),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\twant := Bind{\n\t\t\t\tPreparedStatement:    \"lrupsc_1_0\",\n\t\t\t\tParameterFormatCodes: []int16{0},\n\t\t\t\tParameters:           [][]byte{[]byte(\"ABC-123\")},\n\t\t\t\tResultFormatCodes:    []int16{0, 0, 0, 0, 0, 1, 1},\n\t\t\t}\n\n\t\t\tvar got Bind\n\t\t\tif err := json.Unmarshal(tc.data, &got); err != nil {\n\t\t\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, want) {\n\t\t\t\tt.Error(\"unmarshaled Bind struct doesn't match expected value\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestJSONUnmarshalCancelRequest(t *testing.T) {\n\t// SecretKey is now hex-encoded: d90caedb = 3641487067 in big-endian\n\tdata := []byte(`{\"Type\":\"CancelRequest\",\"ProcessID\":8864,\"SecretKey\":\"d90caedb\"}`)\n\twant := CancelRequest{\n\t\tProcessID: 8864,\n\t\tSecretKey: []byte{0xd9, 0x0c, 0xae, 0xdb},\n\t}\n\n\tvar got CancelRequest\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CancelRequest struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCancelRequestLongKey(t *testing.T) {\n\t// 32-byte key as hex\n\tdata := []byte(`{\"Type\":\"CancelRequest\",\"ProcessID\":8864,\"SecretKey\":\"0102030405060708091011121314151617181920212223242526272829303132\"}`)\n\twant := CancelRequest{\n\t\tProcessID: 8864,\n\t\tSecretKey: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32},\n\t}\n\n\tvar got CancelRequest\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CancelRequest struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalClose(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"Close\",\"ObjectType\":\"S\",\"Name\":\"abc\"}`)\n\twant := Close{\n\t\tObjectType: 'S',\n\t\tName:       \"abc\",\n\t}\n\n\tvar got Close\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled Close struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalCopyFail(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"CopyFail\",\"Message\":\"abc\"}`)\n\twant := CopyFail{\n\t\tMessage: \"abc\",\n\t}\n\n\tvar got CopyFail\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled CopyFail struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalDescribe(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"Describe\",\"ObjectType\":\"S\",\"Name\":\"abc\"}`)\n\twant := Describe{\n\t\tObjectType: 'S',\n\t\tName:       \"abc\",\n\t}\n\n\tvar got Describe\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled Describe struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalExecute(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"Execute\",\"Portal\":\"\",\"MaxRows\":0}`)\n\twant := Execute{}\n\n\tvar got Execute\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled Execute struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalParse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"Parse\",\"Name\":\"lrupsc_1_0\",\"Query\":\"SELECT id, name FROM t WHERE id = $1\",\"ParameterOIDs\":null}`)\n\twant := Parse{\n\t\tName:  \"lrupsc_1_0\",\n\t\tQuery: \"SELECT id, name FROM t WHERE id = $1\",\n\t}\n\n\tvar got Parse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled Parse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalPasswordMessage(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"PasswordMessage\",\"Password\":\"abcdef\"}`)\n\twant := PasswordMessage{\n\t\tPassword: \"abcdef\",\n\t}\n\n\tvar got PasswordMessage\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled PasswordMessage struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalQuery(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"Query\",\"String\":\"SELECT 1\"}`)\n\twant := Query{\n\t\tString: \"SELECT 1\",\n\t}\n\n\tvar got Query\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled Query struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalSASLInitialResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"SASLInitialResponse\", \"AuthMechanism\":\"SCRAM-SHA-256\", \"Data\": \"6D\"}`)\n\twant := SASLInitialResponse{\n\t\tAuthMechanism: \"SCRAM-SHA-256\",\n\t\tData:          []byte{109},\n\t}\n\n\tvar got SASLInitialResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled SASLInitialResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalSASLResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"SASLResponse\",\"Message\":\"abc\"}`)\n\twant := SASLResponse{}\n\n\tvar got SASLResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled SASLResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalStartupMessage(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"StartupMessage\",\"ProtocolVersion\":196608,\"Parameters\":{\"database\":\"testing\",\"user\":\"postgres\"}}`)\n\twant := StartupMessage{\n\t\tProtocolVersion: 196608,\n\t\tParameters: map[string]string{\n\t\t\t\"database\": \"testing\",\n\t\t\t\"user\":     \"postgres\",\n\t\t},\n\t}\n\n\tvar got StartupMessage\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled StartupMessage struct doesn't match expected value\")\n\t}\n}\n\nfunc TestAuthenticationOK(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationOK\"}`)\n\twant := AuthenticationOk{}\n\n\tvar got AuthenticationOk\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationOK struct doesn't match expected value\")\n\t}\n}\n\nfunc TestAuthenticationCleartextPassword(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationCleartextPassword\"}`)\n\twant := AuthenticationCleartextPassword{}\n\n\tvar got AuthenticationCleartextPassword\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationCleartextPassword struct doesn't match expected value\")\n\t}\n}\n\nfunc TestAuthenticationMD5Password(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"AuthenticationMD5Password\",\"Salt\":[1,2,3,4]}`)\n\twant := AuthenticationMD5Password{\n\t\tSalt: [4]byte{1, 2, 3, 4},\n\t}\n\n\tvar got AuthenticationMD5Password\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled AuthenticationMD5Password struct doesn't match expected value\")\n\t}\n}\n\nfunc TestJSONUnmarshalGSSResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"GSSResponse\",\"Data\":[10,20,30,40]}`)\n\twant := GSSResponse{Data: []byte{10, 20, 30, 40}}\n\n\tvar got GSSResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled GSSResponse struct doesn't match expected value\")\n\t}\n}\n\nfunc TestErrorResponse(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"ErrorResponse\",\"UnknownFields\":{\"112\":\"foo\"},\"Code\": \"Fail\",\"Position\":1,\"Message\":\"this is an error\"}`)\n\twant := ErrorResponse{\n\t\tUnknownFields: map[byte]string{\n\t\t\t'p': \"foo\",\n\t\t},\n\t\tCode:     \"Fail\",\n\t\tPosition: 1,\n\t\tMessage:  \"this is an error\",\n\t}\n\n\tvar got ErrorResponse\n\tif err := json.Unmarshal(data, &got); err != nil {\n\t\tt.Errorf(\"cannot JSON unmarshal %v\", err)\n\t}\n\tif !reflect.DeepEqual(got, want) {\n\t\tt.Error(\"unmarshaled ErrorResponse struct doesn't match expected value\")\n\t}\n}\n"
  },
  {
    "path": "pgproto3/negotiate_protocol_version.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype NegotiateProtocolVersion struct {\n\tNewestMinorProtocol uint32\n\tUnrecognizedOptions []string\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*NegotiateProtocolVersion) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *NegotiateProtocolVersion) Decode(src []byte) error {\n\tif len(src) < 8 {\n\t\treturn &invalidMessageLenErr{messageType: \"NegotiateProtocolVersion\", expectedLen: 8, actualLen: len(src)}\n\t}\n\n\tdst.NewestMinorProtocol = binary.BigEndian.Uint32(src[:4])\n\toptionCount := int(binary.BigEndian.Uint32(src[4:8]))\n\n\trp := 8\n\n\t// Use the remaining message size as an upper bound for capacity to prevent\n\t// malicious optionCount values from causing excessive memory allocation.\n\tcapHint := optionCount\n\tif remaining := len(src) - rp; capHint > remaining {\n\t\tcapHint = remaining\n\t}\n\tdst.UnrecognizedOptions = make([]string, 0, capHint)\n\tfor i := 0; i < optionCount; i++ {\n\t\tif rp >= len(src) {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"NegotiateProtocolVersion\"}\n\t\t}\n\t\tend := rp\n\t\tfor end < len(src) && src[end] != 0 {\n\t\t\tend++\n\t\t}\n\t\tif end >= len(src) {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"NegotiateProtocolVersion\"}\n\t\t}\n\t\tdst.UnrecognizedOptions = append(dst.UnrecognizedOptions, string(src[rp:end]))\n\t\trp = end + 1\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *NegotiateProtocolVersion) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'v')\n\tdst = pgio.AppendUint32(dst, src.NewestMinorProtocol)\n\tdst = pgio.AppendUint32(dst, uint32(len(src.UnrecognizedOptions)))\n\tfor _, option := range src.UnrecognizedOptions {\n\t\tdst = append(dst, option...)\n\t\tdst = append(dst, 0)\n\t}\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src NegotiateProtocolVersion) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType                string\n\t\tNewestMinorProtocol uint32\n\t\tUnrecognizedOptions []string\n\t}{\n\t\tType:                \"NegotiateProtocolVersion\",\n\t\tNewestMinorProtocol: src.NewestMinorProtocol,\n\t\tUnrecognizedOptions: src.UnrecognizedOptions,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *NegotiateProtocolVersion) UnmarshalJSON(data []byte) error {\n\tvar msg struct {\n\t\tNewestMinorProtocol uint32\n\t\tUnrecognizedOptions []string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\n\tdst.NewestMinorProtocol = msg.NewestMinorProtocol\n\tdst.UnrecognizedOptions = msg.UnrecognizedOptions\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/negotiate_protocol_version_test.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestNegotiateProtocolVersionDecode(t *testing.T) {\n\tsrc := []byte{\n\t\t0x00, 0x00, 0x00, 0x00, // NewestMinorProtocol: 0\n\t\t0x00, 0x00, 0x00, 0x02, // Option count: 2\n\t\t'o', 'p', 't', '1', 0x00, // \"opt1\"\n\t\t'o', 'p', 't', '2', 0x00, // \"opt2\"\n\t}\n\n\tvar msg NegotiateProtocolVersion\n\terr := msg.Decode(src)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, uint32(0), msg.NewestMinorProtocol)\n\tassert.Equal(t, []string{\"opt1\", \"opt2\"}, msg.UnrecognizedOptions)\n}\n\nfunc TestNegotiateProtocolVersionDecodeNoOptions(t *testing.T) {\n\t// Message: minor version 2, no unrecognized options\n\tsrc := []byte{\n\t\t0x00, 0x00, 0x00, 0x02, // NewestMinorProtocol: 2\n\t\t0x00, 0x00, 0x00, 0x00, // Option count: 0\n\t}\n\n\tvar msg NegotiateProtocolVersion\n\terr := msg.Decode(src)\n\trequire.NoError(t, err)\n\tassert.Equal(t, uint32(2), msg.NewestMinorProtocol)\n\tassert.Equal(t, 0, len(msg.UnrecognizedOptions))\n}\n\nfunc TestNegotiateProtocolVersionEncode(t *testing.T) {\n\tmsg := NegotiateProtocolVersion{\n\t\tNewestMinorProtocol: 0,\n\t\tUnrecognizedOptions: []string{\"opt1\", \"opt2\"},\n\t}\n\n\tbuf, err := msg.Encode(nil)\n\trequire.NoError(t, err)\n\n\texpected := []byte{\n\t\t'v',                    // message type\n\t\t0x00, 0x00, 0x00, 0x16, // length: 22 (4 for length + 4 + 4 + 5 + 5)\n\t\t0x00, 0x00, 0x00, 0x00, // NewestMinorProtocol: 0\n\t\t0x00, 0x00, 0x00, 0x02, // Option count: 2\n\t\t'o', 'p', 't', '1', 0x00,\n\t\t'o', 'p', 't', '2', 0x00,\n\t}\n\n\trequire.Equal(t, expected, buf)\n}\n\nfunc TestNegotiateProtocolVersionJSON(t *testing.T) {\n\tmsg := NegotiateProtocolVersion{\n\t\tNewestMinorProtocol: 0,\n\t\tUnrecognizedOptions: []string{\"opt1\"},\n\t}\n\n\tdata, err := json.Marshal(msg)\n\trequire.NoError(t, err)\n\n\tvar decoded NegotiateProtocolVersion\n\terr = json.Unmarshal(data, &decoded)\n\trequire.NoError(t, err)\n\n\tassert.Equal(t, msg, decoded)\n}\n\nfunc TestJSONUnmarshalNegotiateProtocolVersion(t *testing.T) {\n\tdata := []byte(`{\"Type\":\"NegotiateProtocolVersion\",\"NewestMinorProtocol\":0,\"UnrecognizedOptions\":[\"opt1\"]}`)\n\twant := NegotiateProtocolVersion{\n\t\tNewestMinorProtocol: 0,\n\t\tUnrecognizedOptions: []string{\"opt1\"},\n\t}\n\n\tvar got NegotiateProtocolVersion\n\terr := json.Unmarshal(data, &got)\n\trequire.NoError(t, err)\n\tassert.Equal(t, want, got)\n}\n"
  },
  {
    "path": "pgproto3/no_data.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype NoData struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*NoData) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *NoData) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"NoData\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *NoData) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'n', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src NoData) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"NoData\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/notice_response.go",
    "content": "package pgproto3\n\ntype NoticeResponse ErrorResponse\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*NoticeResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *NoticeResponse) Decode(src []byte) error {\n\treturn (*ErrorResponse)(dst).Decode(src)\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *NoticeResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'N')\n\tdst = (*ErrorResponse)(src).appendFields(dst)\n\treturn finishMessage(dst, sp)\n}\n"
  },
  {
    "path": "pgproto3/notification_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype NotificationResponse struct {\n\tPID     uint32\n\tChannel string\n\tPayload string\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*NotificationResponse) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *NotificationResponse) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tif buf.Len() < 4 {\n\t\treturn &invalidMessageFormatErr{messageType: \"NotificationResponse\", details: \"too short\"}\n\t}\n\n\tpid := binary.BigEndian.Uint32(buf.Next(4))\n\n\tb, err := buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tchannel := string(b[:len(b)-1])\n\n\tb, err = buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpayload := string(b[:len(b)-1])\n\n\t*dst = NotificationResponse{PID: pid, Channel: channel, Payload: payload}\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *NotificationResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'A')\n\tdst = pgio.AppendUint32(dst, src.PID)\n\tdst = append(dst, src.Channel...)\n\tdst = append(dst, 0)\n\tdst = append(dst, src.Payload...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src NotificationResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType    string\n\t\tPID     uint32\n\t\tChannel string\n\t\tPayload string\n\t}{\n\t\tType:    \"NotificationResponse\",\n\t\tPID:     src.PID,\n\t\tChannel: src.Channel,\n\t\tPayload: src.Payload,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/parameter_description.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype ParameterDescription struct {\n\tParameterOIDs []uint32\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*ParameterDescription) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *ParameterDescription) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tif buf.Len() < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"ParameterDescription\"}\n\t}\n\n\t// Reported parameter count will be incorrect when number of args is greater than uint16\n\tbuf.Next(2)\n\t// Instead infer parameter count by remaining size of message\n\tparameterCount := buf.Len() / 4\n\n\t*dst = ParameterDescription{ParameterOIDs: make([]uint32, parameterCount)}\n\n\tfor i := range parameterCount {\n\t\tdst.ParameterOIDs[i] = binary.BigEndian.Uint32(buf.Next(4))\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *ParameterDescription) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 't')\n\n\tif len(src.ParameterOIDs) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many parameter oids\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))\n\tfor _, oid := range src.ParameterOIDs {\n\t\tdst = pgio.AppendUint32(dst, oid)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src ParameterDescription) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType          string\n\t\tParameterOIDs []uint32\n\t}{\n\t\tType:          \"ParameterDescription\",\n\t\tParameterOIDs: src.ParameterOIDs,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/parameter_status.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n)\n\ntype ParameterStatus struct {\n\tName  string\n\tValue string\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*ParameterStatus) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *ParameterStatus) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tb, err := buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tname := string(b[:len(b)-1])\n\n\tb, err = buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvalue := string(b[:len(b)-1])\n\n\t*dst = ParameterStatus{Name: name, Value: value}\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *ParameterStatus) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'S')\n\tdst = append(dst, src.Name...)\n\tdst = append(dst, 0)\n\tdst = append(dst, src.Value...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (ps ParameterStatus) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType  string\n\t\tName  string\n\t\tValue string\n\t}{\n\t\tType:  \"ParameterStatus\",\n\t\tName:  ps.Name,\n\t\tValue: ps.Value,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/parse.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Parse struct {\n\tName          string\n\tQuery         string\n\tParameterOIDs []uint32\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Parse) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Parse) Decode(src []byte) error {\n\t*dst = Parse{}\n\n\tbuf := bytes.NewBuffer(src)\n\n\tb, err := buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.Name = string(b[:len(b)-1])\n\n\tb, err = buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.Query = string(b[:len(b)-1])\n\n\tif buf.Len() < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Parse\"}\n\t}\n\tparameterOIDCount := int(binary.BigEndian.Uint16(buf.Next(2)))\n\n\tfor range parameterOIDCount {\n\t\tif buf.Len() < 4 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"Parse\"}\n\t\t}\n\t\tdst.ParameterOIDs = append(dst.ParameterOIDs, binary.BigEndian.Uint32(buf.Next(4)))\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Parse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'P')\n\n\tdst = append(dst, src.Name...)\n\tdst = append(dst, 0)\n\tdst = append(dst, src.Query...)\n\tdst = append(dst, 0)\n\n\tif len(src.ParameterOIDs) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many parameter oids\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))\n\tfor _, oid := range src.ParameterOIDs {\n\t\tdst = pgio.AppendUint32(dst, oid)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Parse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType          string\n\t\tName          string\n\t\tQuery         string\n\t\tParameterOIDs []uint32\n\t}{\n\t\tType:          \"Parse\",\n\t\tName:          src.Name,\n\t\tQuery:         src.Query,\n\t\tParameterOIDs: src.ParameterOIDs,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/parse_complete.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype ParseComplete struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*ParseComplete) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *ParseComplete) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"ParseComplete\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *ParseComplete) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, '1', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src ParseComplete) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"ParseComplete\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/password_message.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n)\n\ntype PasswordMessage struct {\n\tPassword string\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*PasswordMessage) Frontend() {}\n\n// InitialResponse identifies this message as an authentication response.\nfunc (*PasswordMessage) InitialResponse() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *PasswordMessage) Decode(src []byte) error {\n\tbuf := bytes.NewBuffer(src)\n\n\tb, err := buf.ReadBytes(0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdst.Password = string(b[:len(b)-1])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *PasswordMessage) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'p')\n\tdst = append(dst, src.Password...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src PasswordMessage) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType     string\n\t\tPassword string\n\t}{\n\t\tType:     \"PasswordMessage\",\n\t\tPassword: src.Password,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/pgproto3.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// maxMessageBodyLen is the maximum length of a message body in bytes. See PG_LARGE_MESSAGE_LIMIT in the PostgreSQL\n// source. It is defined as (MaxAllocSize - 1). MaxAllocSize is defined as 0x3fffffff.\nconst maxMessageBodyLen = (0x3fffffff - 1)\n\n// Message is the interface implemented by an object that can decode and encode\n// a particular PostgreSQL message.\ntype Message interface {\n\t// Decode is allowed and expected to retain a reference to data after\n\t// returning (unlike encoding.BinaryUnmarshaler).\n\tDecode(data []byte) error\n\n\t// Encode appends itself to dst and returns the new buffer.\n\tEncode(dst []byte) ([]byte, error)\n}\n\n// FrontendMessage is a message sent by the frontend (i.e. the client).\ntype FrontendMessage interface {\n\tMessage\n\tFrontend() // no-op method to distinguish frontend from backend methods\n}\n\n// BackendMessage is a message sent by the backend (i.e. the server).\ntype BackendMessage interface {\n\tMessage\n\tBackend() // no-op method to distinguish frontend from backend methods\n}\n\ntype AuthenticationResponseMessage interface {\n\tBackendMessage\n\tAuthenticationResponse() // no-op method to distinguish authentication responses\n}\n\ntype invalidMessageLenErr struct {\n\tmessageType string\n\texpectedLen int\n\tactualLen   int\n}\n\nfunc (e *invalidMessageLenErr) Error() string {\n\treturn fmt.Sprintf(\"%s body must have length of %d, but it is %d\", e.messageType, e.expectedLen, e.actualLen)\n}\n\ntype invalidMessageFormatErr struct {\n\tmessageType string\n\tdetails     string\n}\n\nfunc (e *invalidMessageFormatErr) Error() string {\n\treturn fmt.Sprintf(\"%s body is invalid %s\", e.messageType, e.details)\n}\n\ntype writeError struct {\n\terr         error\n\tsafeToRetry bool\n}\n\nfunc (e *writeError) Error() string {\n\treturn fmt.Sprintf(\"write failed: %s\", e.err.Error())\n}\n\nfunc (e *writeError) SafeToRetry() bool {\n\treturn e.safeToRetry\n}\n\nfunc (e *writeError) Unwrap() error {\n\treturn e.err\n}\n\ntype ExceededMaxBodyLenErr struct {\n\tMaxExpectedBodyLen int\n\tActualBodyLen      int\n}\n\nfunc (e *ExceededMaxBodyLenErr) Error() string {\n\treturn fmt.Sprintf(\"invalid body length: expected at most %d, but got %d\", e.MaxExpectedBodyLen, e.ActualBodyLen)\n}\n\n// getValueFromJSON gets the value from a protocol message representation in JSON.\nfunc getValueFromJSON(v map[string]string) ([]byte, error) {\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\tif text, ok := v[\"text\"]; ok {\n\t\treturn []byte(text), nil\n\t}\n\tif binary, ok := v[\"binary\"]; ok {\n\t\treturn hex.DecodeString(binary)\n\t}\n\treturn nil, errors.New(\"unknown protocol representation\")\n}\n\n// beginMessage begins a new message of type t. It appends the message type and a placeholder for the message length to\n// dst. It returns the new buffer and the position of the message length placeholder.\nfunc beginMessage(dst []byte, t byte) ([]byte, int) {\n\tdst = append(dst, t)\n\tsp := len(dst)\n\tdst = pgio.AppendInt32(dst, -1)\n\treturn dst, sp\n}\n\n// finishMessage finishes a message that was started with beginMessage. It computes the message length and writes it to\n// dst[sp]. If the message length is too large it returns an error. Otherwise it returns the final message buffer.\nfunc finishMessage(dst []byte, sp int) ([]byte, error) {\n\tmessageBodyLen := len(dst[sp:])\n\tif messageBodyLen > maxMessageBodyLen {\n\t\treturn nil, errors.New(\"message body too large\")\n\t}\n\tpgio.SetInt32(dst[sp:], int32(messageBodyLen))\n\treturn dst, nil\n}\n"
  },
  {
    "path": "pgproto3/pgproto3_private_test.go",
    "content": "package pgproto3\n\nconst MaxMessageBodyLen = maxMessageBodyLen\n"
  },
  {
    "path": "pgproto3/portal_suspended.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype PortalSuspended struct{}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*PortalSuspended) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *PortalSuspended) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"PortalSuspended\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *PortalSuspended) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 's', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src PortalSuspended) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"PortalSuspended\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/query.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n)\n\ntype Query struct {\n\tString string\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Query) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Query) Decode(src []byte) error {\n\tif len(src) == 0 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Query\"}\n\t}\n\n\ti := bytes.IndexByte(src, 0)\n\tif i != len(src)-1 {\n\t\treturn &invalidMessageFormatErr{messageType: \"Query\"}\n\t}\n\n\tdst.String = string(src[:i])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Query) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'Q')\n\tdst = append(dst, src.String...)\n\tdst = append(dst, 0)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Query) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType   string\n\t\tString string\n\t}{\n\t\tType:   \"Query\",\n\t\tString: src.String,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/query_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestQueryBiggerThanMaxMessageBodyLen(t *testing.T) {\n\tt.Parallel()\n\n\t// Maximum allowed size. 4 bytes for size and 1 byte for 0 terminated string.\n\t_, err := (&pgproto3.Query{String: string(make([]byte, pgproto3.MaxMessageBodyLen-5))}).Encode(nil)\n\trequire.NoError(t, err)\n\n\t// 1 byte too big\n\t_, err = (&pgproto3.Query{String: string(make([]byte, pgproto3.MaxMessageBodyLen-4))}).Encode(nil)\n\trequire.Error(t, err)\n}\n"
  },
  {
    "path": "pgproto3/ready_for_query.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n)\n\ntype ReadyForQuery struct {\n\tTxStatus byte\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*ReadyForQuery) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *ReadyForQuery) Decode(src []byte) error {\n\tif len(src) != 1 {\n\t\treturn &invalidMessageLenErr{messageType: \"ReadyForQuery\", expectedLen: 1, actualLen: len(src)}\n\t}\n\n\tdst.TxStatus = src[0]\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *ReadyForQuery) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'Z', 0, 0, 0, 5, src.TxStatus), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src ReadyForQuery) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType     string\n\t\tTxStatus string\n\t}{\n\t\tType:     \"ReadyForQuery\",\n\t\tTxStatus: string(src.TxStatus),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *ReadyForQuery) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tTxStatus string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\tif len(msg.TxStatus) != 1 {\n\t\treturn errors.New(\"invalid length for ReadyForQuery.TxStatus\")\n\t}\n\tdst.TxStatus = msg.TxStatus[0]\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/row_description.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst (\n\tTextFormat   = 0\n\tBinaryFormat = 1\n)\n\ntype FieldDescription struct {\n\tName                 []byte\n\tTableOID             uint32\n\tTableAttributeNumber uint16\n\tDataTypeOID          uint32\n\tDataTypeSize         int16\n\tTypeModifier         int32\n\tFormat               int16\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (fd FieldDescription) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tName                 string\n\t\tTableOID             uint32\n\t\tTableAttributeNumber uint16\n\t\tDataTypeOID          uint32\n\t\tDataTypeSize         int16\n\t\tTypeModifier         int32\n\t\tFormat               int16\n\t}{\n\t\tName:                 string(fd.Name),\n\t\tTableOID:             fd.TableOID,\n\t\tTableAttributeNumber: fd.TableAttributeNumber,\n\t\tDataTypeOID:          fd.DataTypeOID,\n\t\tDataTypeSize:         fd.DataTypeSize,\n\t\tTypeModifier:         fd.TypeModifier,\n\t\tFormat:               fd.Format,\n\t})\n}\n\ntype RowDescription struct {\n\tFields []FieldDescription\n}\n\n// Backend identifies this message as sendable by the PostgreSQL backend.\nfunc (*RowDescription) Backend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *RowDescription) Decode(src []byte) error {\n\tif len(src) < 2 {\n\t\treturn &invalidMessageFormatErr{messageType: \"RowDescription\"}\n\t}\n\tfieldCount := int(binary.BigEndian.Uint16(src))\n\trp := 2\n\n\tdst.Fields = dst.Fields[0:0]\n\n\tfor range fieldCount {\n\t\tvar fd FieldDescription\n\n\t\tidx := bytes.IndexByte(src[rp:], 0)\n\t\tif idx < 0 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"RowDescription\"}\n\t\t}\n\t\tfd.Name = src[rp : rp+idx]\n\t\trp += idx + 1\n\n\t\t// Since buf.Next() doesn't return an error if we hit the end of the buffer\n\t\t// check Len ahead of time\n\t\tif len(src[rp:]) < 18 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"RowDescription\"}\n\t\t}\n\n\t\tfd.TableOID = binary.BigEndian.Uint32(src[rp:])\n\t\trp += 4\n\t\tfd.TableAttributeNumber = binary.BigEndian.Uint16(src[rp:])\n\t\trp += 2\n\t\tfd.DataTypeOID = binary.BigEndian.Uint32(src[rp:])\n\t\trp += 4\n\t\tfd.DataTypeSize = int16(binary.BigEndian.Uint16(src[rp:]))\n\t\trp += 2\n\t\tfd.TypeModifier = int32(binary.BigEndian.Uint32(src[rp:]))\n\t\trp += 4\n\t\tfd.Format = int16(binary.BigEndian.Uint16(src[rp:]))\n\t\trp += 2\n\n\t\tdst.Fields = append(dst.Fields, fd)\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *RowDescription) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'T')\n\n\tif len(src.Fields) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many fields\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.Fields)))\n\tfor _, fd := range src.Fields {\n\t\tdst = append(dst, fd.Name...)\n\t\tdst = append(dst, 0)\n\n\t\tdst = pgio.AppendUint32(dst, fd.TableOID)\n\t\tdst = pgio.AppendUint16(dst, fd.TableAttributeNumber)\n\t\tdst = pgio.AppendUint32(dst, fd.DataTypeOID)\n\t\tdst = pgio.AppendInt16(dst, fd.DataTypeSize)\n\t\tdst = pgio.AppendInt32(dst, fd.TypeModifier)\n\t\tdst = pgio.AppendInt16(dst, fd.Format)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src RowDescription) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType   string\n\t\tFields []FieldDescription\n\t}{\n\t\tType:   \"RowDescription\",\n\t\tFields: src.Fields,\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *RowDescription) UnmarshalJSON(data []byte) error {\n\tvar msg struct {\n\t\tFields []struct {\n\t\t\tName                 string\n\t\t\tTableOID             uint32\n\t\t\tTableAttributeNumber uint16\n\t\t\tDataTypeOID          uint32\n\t\t\tDataTypeSize         int16\n\t\t\tTypeModifier         int32\n\t\t\tFormat               int16\n\t\t}\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\tdst.Fields = make([]FieldDescription, len(msg.Fields))\n\tfor n, field := range msg.Fields {\n\t\tdst.Fields[n] = FieldDescription{\n\t\t\tName:                 []byte(field.Name),\n\t\t\tTableOID:             field.TableOID,\n\t\t\tTableAttributeNumber: field.TableAttributeNumber,\n\t\t\tDataTypeOID:          field.DataTypeOID,\n\t\t\tDataTypeSize:         field.DataTypeSize,\n\t\t\tTypeModifier:         field.TypeModifier,\n\t\t\tFormat:               field.Format,\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/sasl_initial_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype SASLInitialResponse struct {\n\tAuthMechanism string\n\tData          []byte\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*SASLInitialResponse) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *SASLInitialResponse) Decode(src []byte) error {\n\t*dst = SASLInitialResponse{}\n\n\trp := 0\n\n\tidx := bytes.IndexByte(src, 0)\n\tif idx < 0 {\n\t\treturn errors.New(\"invalid SASLInitialResponse\")\n\t}\n\n\tdst.AuthMechanism = string(src[rp:idx])\n\trp = idx + 1\n\n\tif len(src[rp:]) < 4 {\n\t\treturn errors.New(\"invalid SASLInitialResponse\")\n\t}\n\trp += 4 // The rest of the message is data so we can just skip the size\n\tdst.Data = src[rp:]\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *SASLInitialResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'p')\n\n\tdst = append(dst, []byte(src.AuthMechanism)...)\n\tdst = append(dst, 0)\n\n\tdst = pgio.AppendInt32(dst, int32(len(src.Data)))\n\tdst = append(dst, src.Data...)\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src SASLInitialResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType          string\n\t\tAuthMechanism string\n\t\tData          string\n\t}{\n\t\tType:          \"SASLInitialResponse\",\n\t\tAuthMechanism: src.AuthMechanism,\n\t\tData:          string(src.Data),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *SASLInitialResponse) UnmarshalJSON(data []byte) error {\n\t// Ignore null, like in the main JSON package.\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar msg struct {\n\t\tAuthMechanism string\n\t\tData          string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\tdst.AuthMechanism = msg.AuthMechanism\n\tif msg.Data != \"\" {\n\t\tdecoded, err := hex.DecodeString(msg.Data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdst.Data = decoded\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/sasl_response.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n)\n\ntype SASLResponse struct {\n\tData []byte\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*SASLResponse) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *SASLResponse) Decode(src []byte) error {\n\t*dst = SASLResponse{Data: src}\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *SASLResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'p')\n\tdst = append(dst, src.Data...)\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src SASLResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t\tData string\n\t}{\n\t\tType: \"SASLResponse\",\n\t\tData: string(src.Data),\n\t})\n}\n\n// UnmarshalJSON implements encoding/json.Unmarshaler.\nfunc (dst *SASLResponse) UnmarshalJSON(data []byte) error {\n\tvar msg struct {\n\t\tData string\n\t}\n\tif err := json.Unmarshal(data, &msg); err != nil {\n\t\treturn err\n\t}\n\tif msg.Data != \"\" {\n\t\tdecoded, err := hex.DecodeString(msg.Data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdst.Data = decoded\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pgproto3/ssl_request.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst sslRequestNumber = 80877103\n\ntype SSLRequest struct{}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*SSLRequest) Frontend() {}\n\nfunc (dst *SSLRequest) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"ssl request too short\")\n\t}\n\n\trequestCode := binary.BigEndian.Uint32(src)\n\n\tif requestCode != sslRequestNumber {\n\t\treturn errors.New(\"bad ssl request code\")\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 4 byte message length.\nfunc (src *SSLRequest) Encode(dst []byte) ([]byte, error) {\n\tdst = pgio.AppendInt32(dst, 8)\n\tdst = pgio.AppendInt32(dst, sslRequestNumber)\n\treturn dst, nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src SSLRequest) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType            string\n\t\tProtocolVersion uint32\n\t\tParameters      map[string]string\n\t}{\n\t\tType: \"SSLRequest\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/startup_message.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst (\n\tProtocolVersion30     = 196608            // 3.0\n\tProtocolVersion32     = 196610            // 3.2\n\tProtocolVersionNumber = ProtocolVersion30 // Default is still 3.0\n)\n\ntype StartupMessage struct {\n\tProtocolVersion uint32\n\tParameters      map[string]string\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*StartupMessage) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *StartupMessage) Decode(src []byte) error {\n\tif len(src) < 4 {\n\t\treturn errors.New(\"startup message too short\")\n\t}\n\n\tdst.ProtocolVersion = binary.BigEndian.Uint32(src)\n\trp := 4\n\n\tif dst.ProtocolVersion != ProtocolVersion30 && dst.ProtocolVersion != ProtocolVersion32 {\n\t\treturn fmt.Errorf(\"Bad startup message version number. Expected %d or %d, got %d\", ProtocolVersion30, ProtocolVersion32, dst.ProtocolVersion)\n\t}\n\n\tdst.Parameters = make(map[string]string)\n\tfor {\n\t\tidx := bytes.IndexByte(src[rp:], 0)\n\t\tif idx < 0 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"StartupMessage\"}\n\t\t}\n\t\tkey := string(src[rp : rp+idx])\n\t\trp += idx + 1\n\n\t\tidx = bytes.IndexByte(src[rp:], 0)\n\t\tif idx < 0 {\n\t\t\treturn &invalidMessageFormatErr{messageType: \"StartupMessage\"}\n\t\t}\n\t\tvalue := string(src[rp : rp+idx])\n\t\trp += idx + 1\n\n\t\tdst.Parameters[key] = value\n\n\t\tif len(src[rp:]) == 1 {\n\t\t\tif src[rp] != 0 {\n\t\t\t\treturn fmt.Errorf(\"Bad startup message last byte. Expected 0, got %d\", src[rp])\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *StartupMessage) Encode(dst []byte) ([]byte, error) {\n\tsp := len(dst)\n\tdst = pgio.AppendInt32(dst, -1)\n\n\tdst = pgio.AppendUint32(dst, src.ProtocolVersion)\n\tfor k, v := range src.Parameters {\n\t\tdst = append(dst, k...)\n\t\tdst = append(dst, 0)\n\t\tdst = append(dst, v...)\n\t\tdst = append(dst, 0)\n\t}\n\tdst = append(dst, 0)\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src StartupMessage) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType            string\n\t\tProtocolVersion uint32\n\t\tParameters      map[string]string\n\t}{\n\t\tType:            \"StartupMessage\",\n\t\tProtocolVersion: src.ProtocolVersion,\n\t\tParameters:      src.Parameters,\n\t})\n}\n"
  },
  {
    "path": "pgproto3/sync.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Sync struct{}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Sync) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Sync) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"Sync\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Sync) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'S', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Sync) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"Sync\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/terminate.go",
    "content": "package pgproto3\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Terminate struct{}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*Terminate) Frontend() {}\n\n// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message\n// type identifier and 4 byte message length.\nfunc (dst *Terminate) Decode(src []byte) error {\n\tif len(src) != 0 {\n\t\treturn &invalidMessageLenErr{messageType: \"Terminate\", expectedLen: 0, actualLen: len(src)}\n\t}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Terminate) Encode(dst []byte) ([]byte, error) {\n\treturn append(dst, 'X', 0, 0, 0, 4), nil\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src Terminate) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType string\n\t}{\n\t\tType: \"Terminate\",\n\t})\n}\n"
  },
  {
    "path": "pgproto3/testdata/fuzz/FuzzFrontend/39c5e864da4707fc15fea48f7062d6a07796fdc43b33e0ba9dbd7074a0211fa6",
    "content": "go test fuzz v1\nbyte('A')\nuint32(5)\n[]byte(\"0\")\n"
  },
  {
    "path": "pgproto3/testdata/fuzz/FuzzFrontend/9b06792b1aaac8a907dbfa04d526ae14326c8573b7409032caac8461e83065f7",
    "content": "go test fuzz v1\nbyte('D')\nuint32(21)\n[]byte(\"00\\xb300000000000000\")\n"
  },
  {
    "path": "pgproto3/testdata/fuzz/FuzzFrontend/a661fb98e802839f0a7361160fbc6e28794612a411d00bde104364ee281c4214",
    "content": "go test fuzz v1\nbyte('C')\nuint32(4)\n[]byte(\"0\")\n"
  },
  {
    "path": "pgproto3/testdata/fuzz/FuzzFrontend/fc98dcd487a5173b38763a5f7dd023933f3a86ab566e3f2b091eb36248107eb4",
    "content": "go test fuzz v1\nbyte('R')\nuint32(13)\n[]byte(\"\\x00\\x00\\x00\\n0\\x12\\xebG\\x8dI']G\\xdac\\x95\\xb7\\x18\\xb0\\x02\\xe8m\\xc2\\x00\\xef\\x03\\x12\\x1b\\xbdj\\x10\\x9f\\xf9\\xeb\\xb8\")\n"
  },
  {
    "path": "pgproto3/trace.go",
    "content": "package pgproto3\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// tracer traces the messages send to and from a Backend or Frontend. The format it produces roughly mimics the\n// format produced by the libpq C function PQtrace.\ntype tracer struct {\n\tTracerOptions\n\n\tmux sync.Mutex\n\tw   io.Writer\n\tbuf *bytes.Buffer\n}\n\n// TracerOptions controls tracing behavior. It is roughly equivalent to the libpq function PQsetTraceFlags.\ntype TracerOptions struct {\n\t// SuppressTimestamps prevents printing of timestamps.\n\tSuppressTimestamps bool\n\n\t// RegressMode redacts fields that may be vary between executions.\n\tRegressMode bool\n}\n\nfunc (t *tracer) traceMessage(sender byte, encodedLen int32, msg Message) {\n\tswitch msg := msg.(type) {\n\tcase *AuthenticationCleartextPassword:\n\t\tt.traceAuthenticationCleartextPassword(sender, encodedLen, msg)\n\tcase *AuthenticationGSS:\n\t\tt.traceAuthenticationGSS(sender, encodedLen, msg)\n\tcase *AuthenticationGSSContinue:\n\t\tt.traceAuthenticationGSSContinue(sender, encodedLen, msg)\n\tcase *AuthenticationMD5Password:\n\t\tt.traceAuthenticationMD5Password(sender, encodedLen, msg)\n\tcase *AuthenticationOk:\n\t\tt.traceAuthenticationOk(sender, encodedLen, msg)\n\tcase *AuthenticationSASL:\n\t\tt.traceAuthenticationSASL(sender, encodedLen, msg)\n\tcase *AuthenticationSASLContinue:\n\t\tt.traceAuthenticationSASLContinue(sender, encodedLen, msg)\n\tcase *AuthenticationSASLFinal:\n\t\tt.traceAuthenticationSASLFinal(sender, encodedLen, msg)\n\tcase *BackendKeyData:\n\t\tt.traceBackendKeyData(sender, encodedLen, msg)\n\tcase *Bind:\n\t\tt.traceBind(sender, encodedLen, msg)\n\tcase *BindComplete:\n\t\tt.traceBindComplete(sender, encodedLen, msg)\n\tcase *CancelRequest:\n\t\tt.traceCancelRequest(sender, encodedLen, msg)\n\tcase *Close:\n\t\tt.traceClose(sender, encodedLen, msg)\n\tcase *CloseComplete:\n\t\tt.traceCloseComplete(sender, encodedLen, msg)\n\tcase *CommandComplete:\n\t\tt.traceCommandComplete(sender, encodedLen, msg)\n\tcase *CopyBothResponse:\n\t\tt.traceCopyBothResponse(sender, encodedLen, msg)\n\tcase *CopyData:\n\t\tt.traceCopyData(sender, encodedLen, msg)\n\tcase *CopyDone:\n\t\tt.traceCopyDone(sender, encodedLen, msg)\n\tcase *CopyFail:\n\t\tt.traceCopyFail(sender, encodedLen, msg)\n\tcase *CopyInResponse:\n\t\tt.traceCopyInResponse(sender, encodedLen, msg)\n\tcase *CopyOutResponse:\n\t\tt.traceCopyOutResponse(sender, encodedLen, msg)\n\tcase *DataRow:\n\t\tt.traceDataRow(sender, encodedLen, msg)\n\tcase *Describe:\n\t\tt.traceDescribe(sender, encodedLen, msg)\n\tcase *EmptyQueryResponse:\n\t\tt.traceEmptyQueryResponse(sender, encodedLen, msg)\n\tcase *ErrorResponse:\n\t\tt.traceErrorResponse(sender, encodedLen, msg)\n\tcase *Execute:\n\t\tt.TraceQueryute(sender, encodedLen, msg)\n\tcase *Flush:\n\t\tt.traceFlush(sender, encodedLen, msg)\n\tcase *FunctionCall:\n\t\tt.traceFunctionCall(sender, encodedLen, msg)\n\tcase *FunctionCallResponse:\n\t\tt.traceFunctionCallResponse(sender, encodedLen, msg)\n\tcase *GSSEncRequest:\n\t\tt.traceGSSEncRequest(sender, encodedLen, msg)\n\tcase *NoData:\n\t\tt.traceNoData(sender, encodedLen, msg)\n\tcase *NoticeResponse:\n\t\tt.traceNoticeResponse(sender, encodedLen, msg)\n\tcase *NotificationResponse:\n\t\tt.traceNotificationResponse(sender, encodedLen, msg)\n\tcase *ParameterDescription:\n\t\tt.traceParameterDescription(sender, encodedLen, msg)\n\tcase *ParameterStatus:\n\t\tt.traceParameterStatus(sender, encodedLen, msg)\n\tcase *Parse:\n\t\tt.traceParse(sender, encodedLen, msg)\n\tcase *ParseComplete:\n\t\tt.traceParseComplete(sender, encodedLen, msg)\n\tcase *PortalSuspended:\n\t\tt.tracePortalSuspended(sender, encodedLen, msg)\n\tcase *Query:\n\t\tt.traceQuery(sender, encodedLen, msg)\n\tcase *ReadyForQuery:\n\t\tt.traceReadyForQuery(sender, encodedLen, msg)\n\tcase *RowDescription:\n\t\tt.traceRowDescription(sender, encodedLen, msg)\n\tcase *SSLRequest:\n\t\tt.traceSSLRequest(sender, encodedLen, msg)\n\tcase *StartupMessage:\n\t\tt.traceStartupMessage(sender, encodedLen, msg)\n\tcase *Sync:\n\t\tt.traceSync(sender, encodedLen, msg)\n\tcase *Terminate:\n\t\tt.traceTerminate(sender, encodedLen, msg)\n\tdefault:\n\t\tt.writeTrace(sender, encodedLen, \"Unknown\", nil)\n\t}\n}\n\nfunc (t *tracer) traceAuthenticationCleartextPassword(sender byte, encodedLen int32, msg *AuthenticationCleartextPassword) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationCleartextPassword\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationGSS(sender byte, encodedLen int32, msg *AuthenticationGSS) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationGSS\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationGSSContinue(sender byte, encodedLen int32, msg *AuthenticationGSSContinue) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationGSSContinue\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationMD5Password(sender byte, encodedLen int32, msg *AuthenticationMD5Password) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationMD5Password\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationOk(sender byte, encodedLen int32, msg *AuthenticationOk) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationOk\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationSASL(sender byte, encodedLen int32, msg *AuthenticationSASL) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationSASL\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationSASLContinue(sender byte, encodedLen int32, msg *AuthenticationSASLContinue) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationSASLContinue\", nil)\n}\n\nfunc (t *tracer) traceAuthenticationSASLFinal(sender byte, encodedLen int32, msg *AuthenticationSASLFinal) {\n\tt.writeTrace(sender, encodedLen, \"AuthenticationSASLFinal\", nil)\n}\n\nfunc (t *tracer) traceBackendKeyData(sender byte, encodedLen int32, msg *BackendKeyData) {\n\tt.writeTrace(sender, encodedLen, \"BackendKeyData\", func() {\n\t\tif t.RegressMode {\n\t\t\tt.buf.WriteString(\"\\t NNNN NNNN\")\n\t\t} else {\n\t\t\tfmt.Fprintf(t.buf, \"\\t %d %d\", msg.ProcessID, msg.SecretKey)\n\t\t}\n\t})\n}\n\nfunc (t *tracer) traceBind(sender byte, encodedLen int32, msg *Bind) {\n\tt.writeTrace(sender, encodedLen, \"Bind\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s %s %d\", traceDoubleQuotedString([]byte(msg.DestinationPortal)), traceDoubleQuotedString([]byte(msg.PreparedStatement)), len(msg.ParameterFormatCodes))\n\t\tfor _, fc := range msg.ParameterFormatCodes {\n\t\t\tfmt.Fprintf(t.buf, \" %d\", fc)\n\t\t}\n\t\tfmt.Fprintf(t.buf, \" %d\", len(msg.Parameters))\n\t\tfor _, p := range msg.Parameters {\n\t\t\tfmt.Fprintf(t.buf, \" %s\", traceSingleQuotedString(p))\n\t\t}\n\t\tfmt.Fprintf(t.buf, \" %d\", len(msg.ResultFormatCodes))\n\t\tfor _, fc := range msg.ResultFormatCodes {\n\t\t\tfmt.Fprintf(t.buf, \" %d\", fc)\n\t\t}\n\t})\n}\n\nfunc (t *tracer) traceBindComplete(sender byte, encodedLen int32, msg *BindComplete) {\n\tt.writeTrace(sender, encodedLen, \"BindComplete\", nil)\n}\n\nfunc (t *tracer) traceCancelRequest(sender byte, encodedLen int32, msg *CancelRequest) {\n\tt.writeTrace(sender, encodedLen, \"CancelRequest\", nil)\n}\n\nfunc (t *tracer) traceClose(sender byte, encodedLen int32, msg *Close) {\n\tt.writeTrace(sender, encodedLen, \"Close\", nil)\n}\n\nfunc (t *tracer) traceCloseComplete(sender byte, encodedLen int32, msg *CloseComplete) {\n\tt.writeTrace(sender, encodedLen, \"CloseComplete\", nil)\n}\n\nfunc (t *tracer) traceCommandComplete(sender byte, encodedLen int32, msg *CommandComplete) {\n\tt.writeTrace(sender, encodedLen, \"CommandComplete\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s\", traceDoubleQuotedString(msg.CommandTag))\n\t})\n}\n\nfunc (t *tracer) traceCopyBothResponse(sender byte, encodedLen int32, msg *CopyBothResponse) {\n\tt.writeTrace(sender, encodedLen, \"CopyBothResponse\", nil)\n}\n\nfunc (t *tracer) traceCopyData(sender byte, encodedLen int32, msg *CopyData) {\n\tt.writeTrace(sender, encodedLen, \"CopyData\", nil)\n}\n\nfunc (t *tracer) traceCopyDone(sender byte, encodedLen int32, msg *CopyDone) {\n\tt.writeTrace(sender, encodedLen, \"CopyDone\", nil)\n}\n\nfunc (t *tracer) traceCopyFail(sender byte, encodedLen int32, msg *CopyFail) {\n\tt.writeTrace(sender, encodedLen, \"CopyFail\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s\", traceDoubleQuotedString([]byte(msg.Message)))\n\t})\n}\n\nfunc (t *tracer) traceCopyInResponse(sender byte, encodedLen int32, msg *CopyInResponse) {\n\tt.writeTrace(sender, encodedLen, \"CopyInResponse\", nil)\n}\n\nfunc (t *tracer) traceCopyOutResponse(sender byte, encodedLen int32, msg *CopyOutResponse) {\n\tt.writeTrace(sender, encodedLen, \"CopyOutResponse\", nil)\n}\n\nfunc (t *tracer) traceDataRow(sender byte, encodedLen int32, msg *DataRow) {\n\tt.writeTrace(sender, encodedLen, \"DataRow\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %d\", len(msg.Values))\n\t\tfor _, v := range msg.Values {\n\t\t\tif v == nil {\n\t\t\t\tt.buf.WriteString(\" -1\")\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(t.buf, \" %d %s\", len(v), traceSingleQuotedString(v))\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc (t *tracer) traceDescribe(sender byte, encodedLen int32, msg *Describe) {\n\tt.writeTrace(sender, encodedLen, \"Describe\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %c %s\", msg.ObjectType, traceDoubleQuotedString([]byte(msg.Name)))\n\t})\n}\n\nfunc (t *tracer) traceEmptyQueryResponse(sender byte, encodedLen int32, msg *EmptyQueryResponse) {\n\tt.writeTrace(sender, encodedLen, \"EmptyQueryResponse\", nil)\n}\n\nfunc (t *tracer) traceErrorResponse(sender byte, encodedLen int32, msg *ErrorResponse) {\n\tt.writeTrace(sender, encodedLen, \"ErrorResponse\", nil)\n}\n\nfunc (t *tracer) TraceQueryute(sender byte, encodedLen int32, msg *Execute) {\n\tt.writeTrace(sender, encodedLen, \"Execute\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s %d\", traceDoubleQuotedString([]byte(msg.Portal)), msg.MaxRows)\n\t})\n}\n\nfunc (t *tracer) traceFlush(sender byte, encodedLen int32, msg *Flush) {\n\tt.writeTrace(sender, encodedLen, \"Flush\", nil)\n}\n\nfunc (t *tracer) traceFunctionCall(sender byte, encodedLen int32, msg *FunctionCall) {\n\tt.writeTrace(sender, encodedLen, \"FunctionCall\", nil)\n}\n\nfunc (t *tracer) traceFunctionCallResponse(sender byte, encodedLen int32, msg *FunctionCallResponse) {\n\tt.writeTrace(sender, encodedLen, \"FunctionCallResponse\", nil)\n}\n\nfunc (t *tracer) traceGSSEncRequest(sender byte, encodedLen int32, msg *GSSEncRequest) {\n\tt.writeTrace(sender, encodedLen, \"GSSEncRequest\", nil)\n}\n\nfunc (t *tracer) traceNoData(sender byte, encodedLen int32, msg *NoData) {\n\tt.writeTrace(sender, encodedLen, \"NoData\", nil)\n}\n\nfunc (t *tracer) traceNoticeResponse(sender byte, encodedLen int32, msg *NoticeResponse) {\n\tt.writeTrace(sender, encodedLen, \"NoticeResponse\", nil)\n}\n\nfunc (t *tracer) traceNotificationResponse(sender byte, encodedLen int32, msg *NotificationResponse) {\n\tt.writeTrace(sender, encodedLen, \"NotificationResponse\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %d %s %s\", msg.PID, traceDoubleQuotedString([]byte(msg.Channel)), traceDoubleQuotedString([]byte(msg.Payload)))\n\t})\n}\n\nfunc (t *tracer) traceParameterDescription(sender byte, encodedLen int32, msg *ParameterDescription) {\n\tt.writeTrace(sender, encodedLen, \"ParameterDescription\", nil)\n}\n\nfunc (t *tracer) traceParameterStatus(sender byte, encodedLen int32, msg *ParameterStatus) {\n\tt.writeTrace(sender, encodedLen, \"ParameterStatus\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s %s\", traceDoubleQuotedString([]byte(msg.Name)), traceDoubleQuotedString([]byte(msg.Value)))\n\t})\n}\n\nfunc (t *tracer) traceParse(sender byte, encodedLen int32, msg *Parse) {\n\tt.writeTrace(sender, encodedLen, \"Parse\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s %s %d\", traceDoubleQuotedString([]byte(msg.Name)), traceDoubleQuotedString([]byte(msg.Query)), len(msg.ParameterOIDs))\n\t\tfor _, oid := range msg.ParameterOIDs {\n\t\t\tfmt.Fprintf(t.buf, \" %d\", oid)\n\t\t}\n\t})\n}\n\nfunc (t *tracer) traceParseComplete(sender byte, encodedLen int32, msg *ParseComplete) {\n\tt.writeTrace(sender, encodedLen, \"ParseComplete\", nil)\n}\n\nfunc (t *tracer) tracePortalSuspended(sender byte, encodedLen int32, msg *PortalSuspended) {\n\tt.writeTrace(sender, encodedLen, \"PortalSuspended\", nil)\n}\n\nfunc (t *tracer) traceQuery(sender byte, encodedLen int32, msg *Query) {\n\tt.writeTrace(sender, encodedLen, \"Query\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %s\", traceDoubleQuotedString([]byte(msg.String)))\n\t})\n}\n\nfunc (t *tracer) traceReadyForQuery(sender byte, encodedLen int32, msg *ReadyForQuery) {\n\tt.writeTrace(sender, encodedLen, \"ReadyForQuery\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %c\", msg.TxStatus)\n\t})\n}\n\nfunc (t *tracer) traceRowDescription(sender byte, encodedLen int32, msg *RowDescription) {\n\tt.writeTrace(sender, encodedLen, \"RowDescription\", func() {\n\t\tfmt.Fprintf(t.buf, \"\\t %d\", len(msg.Fields))\n\t\tfor _, fd := range msg.Fields {\n\t\t\tfmt.Fprintf(t.buf, ` %s %d %d %d %d %d %d`, traceDoubleQuotedString(fd.Name), fd.TableOID, fd.TableAttributeNumber, fd.DataTypeOID, fd.DataTypeSize, fd.TypeModifier, fd.Format)\n\t\t}\n\t})\n}\n\nfunc (t *tracer) traceSSLRequest(sender byte, encodedLen int32, msg *SSLRequest) {\n\tt.writeTrace(sender, encodedLen, \"SSLRequest\", nil)\n}\n\nfunc (t *tracer) traceStartupMessage(sender byte, encodedLen int32, msg *StartupMessage) {\n\tt.writeTrace(sender, encodedLen, \"StartupMessage\", nil)\n}\n\nfunc (t *tracer) traceSync(sender byte, encodedLen int32, msg *Sync) {\n\tt.writeTrace(sender, encodedLen, \"Sync\", nil)\n}\n\nfunc (t *tracer) traceTerminate(sender byte, encodedLen int32, msg *Terminate) {\n\tt.writeTrace(sender, encodedLen, \"Terminate\", nil)\n}\n\nfunc (t *tracer) writeTrace(sender byte, encodedLen int32, msgType string, writeDetails func()) {\n\tt.mux.Lock()\n\tdefer t.mux.Unlock()\n\tdefer func() {\n\t\tif t.buf.Cap() > 1024 {\n\t\t\tt.buf = &bytes.Buffer{}\n\t\t} else {\n\t\t\tt.buf.Reset()\n\t\t}\n\t}()\n\n\tif !t.SuppressTimestamps {\n\t\tnow := time.Now()\n\t\tt.buf.WriteString(now.Format(\"2006-01-02 15:04:05.000000\"))\n\t\tt.buf.WriteByte('\\t')\n\t}\n\n\tt.buf.WriteByte(sender)\n\tt.buf.WriteByte('\\t')\n\tt.buf.WriteString(msgType)\n\tt.buf.WriteByte('\\t')\n\tt.buf.WriteString(strconv.FormatInt(int64(encodedLen), 10))\n\n\tif writeDetails != nil {\n\t\twriteDetails()\n\t}\n\n\tt.buf.WriteByte('\\n')\n\tt.buf.WriteTo(t.w)\n}\n\n// traceDoubleQuotedString returns t.buf as a double-quoted string without any escaping. It is roughly equivalent to\n// pqTraceOutputString in libpq.\nfunc traceDoubleQuotedString(buf []byte) string {\n\treturn `\"` + string(buf) + `\"`\n}\n\n// traceSingleQuotedString returns buf as a single-quoted string with non-printable characters hex-escaped. It is\n// roughly equivalent to pqTraceOutputNchar in libpq.\nfunc traceSingleQuotedString(buf []byte) string {\n\tsb := &strings.Builder{}\n\n\tsb.WriteByte('\\'')\n\tfor _, b := range buf {\n\t\tif b < 32 || b > 126 {\n\t\t\tfmt.Fprintf(sb, `\\x%x`, b)\n\t\t} else {\n\t\t\tsb.WriteByte(b)\n\t\t}\n\t}\n\tsb.WriteByte('\\'')\n\n\treturn sb.String()\n}\n"
  },
  {
    "path": "pgproto3/trace_test.go",
    "content": "package pgproto3_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgproto3\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTrace(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgconn.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer conn.Close(ctx)\n\n\tif conn.ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Skipping message trace on CockroachDB as it varies slightly from PostgreSQL\")\n\t}\n\n\ttraceOutput := &bytes.Buffer{}\n\tconn.Frontend().Trace(traceOutput, pgproto3.TracerOptions{\n\t\tSuppressTimestamps: true,\n\t\tRegressMode:        true,\n\t})\n\n\tresult := conn.ExecParams(ctx, \"select n from generate_series(1,5) n\", nil, nil, nil, nil).Read()\n\trequire.NoError(t, result.Err)\n\n\texpected := `F\tParse\t45\t \"\" \"select n from generate_series(1,5) n\" 0\nF\tBind\t13\t \"\" \"\" 0 0 0\nF\tDescribe\t7\t P \"\"\nF\tExecute\t10\t \"\" 0\nF\tSync\t5\nB\tParseComplete\t5\nB\tBindComplete\t5\nB\tRowDescription\t27\t 1 \"n\" 0 0 23 4 -1 0\nB\tDataRow\t12\t 1 1 '1'\nB\tDataRow\t12\t 1 1 '2'\nB\tDataRow\t12\t 1 1 '3'\nB\tDataRow\t12\t 1 1 '4'\nB\tDataRow\t12\t 1 1 '5'\nB\tCommandComplete\t14\t \"SELECT 5\"\nB\tReadyForQuery\t6\t I\n`\n\n\trequire.Equal(t, expected, traceOutput.String())\n}\n"
  },
  {
    "path": "pgtype/array.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// Information on the internals of PostgreSQL arrays can be found in\n// src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of\n// particular interest is the array_send function.\n\ntype arrayHeader struct {\n\tContainsNull bool\n\tElementOID   uint32\n\tDimensions   []ArrayDimension\n}\n\ntype ArrayDimension struct {\n\tLength     int32\n\tLowerBound int32\n}\n\n// cardinality returns the number of elements in an array of dimensions size.\nfunc cardinality(dimensions []ArrayDimension) int {\n\tif len(dimensions) == 0 {\n\t\treturn 0\n\t}\n\n\telementCount := int(dimensions[0].Length)\n\tfor _, d := range dimensions[1:] {\n\t\telementCount *= int(d.Length)\n\t}\n\n\treturn elementCount\n}\n\nfunc (dst *arrayHeader) DecodeBinary(m *Map, src []byte) (int, error) {\n\tif len(src) < 12 {\n\t\treturn 0, fmt.Errorf(\"array header too short: %d\", len(src))\n\t}\n\n\trp := 0\n\n\tnumDims := int(binary.BigEndian.Uint32(src[rp:]))\n\trp += 4\n\n\tdst.ContainsNull = binary.BigEndian.Uint32(src[rp:]) == 1\n\trp += 4\n\n\tdst.ElementOID = binary.BigEndian.Uint32(src[rp:])\n\trp += 4\n\n\tdst.Dimensions = make([]ArrayDimension, numDims)\n\tif len(src) < 12+numDims*8 {\n\t\treturn 0, fmt.Errorf(\"array header too short for %d dimensions: %d\", numDims, len(src))\n\t}\n\tfor i := range dst.Dimensions {\n\t\tdst.Dimensions[i].Length = int32(binary.BigEndian.Uint32(src[rp:]))\n\t\trp += 4\n\n\t\tdst.Dimensions[i].LowerBound = int32(binary.BigEndian.Uint32(src[rp:]))\n\t\trp += 4\n\t}\n\n\treturn rp, nil\n}\n\nfunc (src arrayHeader) EncodeBinary(buf []byte) []byte {\n\tbuf = pgio.AppendInt32(buf, int32(len(src.Dimensions)))\n\n\tvar containsNull int32\n\tif src.ContainsNull {\n\t\tcontainsNull = 1\n\t}\n\tbuf = pgio.AppendInt32(buf, containsNull)\n\n\tbuf = pgio.AppendUint32(buf, src.ElementOID)\n\n\tfor i := range src.Dimensions {\n\t\tbuf = pgio.AppendInt32(buf, src.Dimensions[i].Length)\n\t\tbuf = pgio.AppendInt32(buf, src.Dimensions[i].LowerBound)\n\t}\n\n\treturn buf\n}\n\ntype untypedTextArray struct {\n\tElements   []string\n\tQuoted     []bool\n\tDimensions []ArrayDimension\n}\n\nfunc parseUntypedTextArray(src string) (*untypedTextArray, error) {\n\tdst := &untypedTextArray{\n\t\tElements:   []string{},\n\t\tQuoted:     []bool{},\n\t\tDimensions: []ArrayDimension{},\n\t}\n\n\tbuf := bytes.NewBufferString(src)\n\n\tskipWhitespace(buf)\n\n\tr, _, err := buf.ReadRune()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t}\n\n\tvar explicitDimensions []ArrayDimension\n\n\t// Array has explicit dimensions\n\tif r == '[' {\n\t\tbuf.UnreadRune()\n\n\t\tfor {\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t\t}\n\n\t\t\tif r == '=' {\n\t\t\t\tbreak\n\t\t\t} else if r != '[' {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array, expected '[' or '=' got %v\", r)\n\t\t\t}\n\n\t\t\tlower, err := arrayParseInteger(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t\t}\n\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t\t}\n\n\t\t\tif r != ':' {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array, expected ':' got %v\", r)\n\t\t\t}\n\n\t\t\tupper, err := arrayParseInteger(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t\t}\n\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t\t}\n\n\t\t\tif r != ']' {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array, expected ']' got %v\", r)\n\t\t\t}\n\n\t\t\texplicitDimensions = append(explicitDimensions, ArrayDimension{LowerBound: lower, Length: upper - lower + 1})\n\t\t}\n\n\t\tr, _, err = buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t}\n\t}\n\n\tif r != '{' {\n\t\treturn nil, fmt.Errorf(\"invalid array, expected '{' got %v\", r)\n\t}\n\n\timplicitDimensions := []ArrayDimension{{LowerBound: 1, Length: 0}}\n\n\t// Consume all initial opening brackets. This provides number of dimensions.\n\tfor {\n\t\tr, _, err = buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t}\n\n\t\tif r == '{' {\n\t\t\timplicitDimensions[len(implicitDimensions)-1].Length = 1\n\t\t\timplicitDimensions = append(implicitDimensions, ArrayDimension{LowerBound: 1})\n\t\t} else {\n\t\t\tbuf.UnreadRune()\n\t\t\tbreak\n\t\t}\n\t}\n\tcurrentDim := len(implicitDimensions) - 1\n\tcounterDim := currentDim\n\n\tfor {\n\t\tr, _, err = buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t\t}\n\n\t\tswitch r {\n\t\tcase '{':\n\t\t\tif currentDim == counterDim {\n\t\t\t\timplicitDimensions[currentDim].Length++\n\t\t\t}\n\t\t\tcurrentDim++\n\t\tcase ',':\n\t\tcase '}':\n\t\t\tcurrentDim--\n\t\t\tif currentDim < counterDim {\n\t\t\t\tcounterDim = currentDim\n\t\t\t}\n\t\tdefault:\n\t\t\tbuf.UnreadRune()\n\t\t\tvalue, quoted, err := arrayParseValue(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid array value: %w\", err)\n\t\t\t}\n\t\t\tif currentDim == counterDim {\n\t\t\t\timplicitDimensions[currentDim].Length++\n\t\t\t}\n\t\t\tdst.Quoted = append(dst.Quoted, quoted)\n\t\t\tdst.Elements = append(dst.Elements, value)\n\t\t}\n\n\t\tif currentDim < 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tskipWhitespace(buf)\n\n\tif buf.Len() > 0 {\n\t\treturn nil, fmt.Errorf(\"unexpected trailing data: %v\", buf.String())\n\t}\n\n\tif len(dst.Elements) == 0 {\n\t} else if len(explicitDimensions) > 0 {\n\t\tdst.Dimensions = explicitDimensions\n\t} else {\n\t\tdst.Dimensions = implicitDimensions\n\t}\n\n\treturn dst, nil\n}\n\nfunc skipWhitespace(buf *bytes.Buffer) {\n\tvar r rune\n\tvar err error\n\tfor r, _, _ = buf.ReadRune(); unicode.IsSpace(r); r, _, _ = buf.ReadRune() {\n\t}\n\n\tif err != io.EOF {\n\t\tbuf.UnreadRune()\n\t}\n}\n\nfunc arrayParseValue(buf *bytes.Buffer) (string, bool, error) {\n\tr, _, err := buf.ReadRune()\n\tif err != nil {\n\t\treturn \"\", false, err\n\t}\n\tif r == '\"' {\n\t\treturn arrayParseQuotedValue(buf)\n\t}\n\tbuf.UnreadRune()\n\n\ts := &bytes.Buffer{}\n\n\tfor {\n\t\tr, _, err := buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn \"\", false, err\n\t\t}\n\n\t\tswitch r {\n\t\tcase ',', '}':\n\t\t\tbuf.UnreadRune()\n\t\t\treturn s.String(), false, nil\n\t\t}\n\n\t\ts.WriteRune(r)\n\t}\n}\n\nfunc arrayParseQuotedValue(buf *bytes.Buffer) (string, bool, error) {\n\ts := &bytes.Buffer{}\n\n\tfor {\n\t\tr, _, err := buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn \"\", false, err\n\t\t}\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", false, err\n\t\t\t}\n\t\tcase '\"':\n\t\t\t_, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", false, err\n\t\t\t}\n\t\t\tbuf.UnreadRune()\n\t\t\treturn s.String(), true, nil\n\t\t}\n\t\ts.WriteRune(r)\n\t}\n}\n\nfunc arrayParseInteger(buf *bytes.Buffer) (int32, error) {\n\ts := &bytes.Buffer{}\n\n\tfor {\n\t\tr, _, err := buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tif ('0' <= r && r <= '9') || r == '-' {\n\t\t\ts.WriteRune(r)\n\t\t} else {\n\t\t\tbuf.UnreadRune()\n\t\t\tn, err := strconv.ParseInt(s.String(), 10, 32)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\treturn int32(n), nil\n\t\t}\n\t}\n}\n\nfunc encodeTextArrayDimensions(buf []byte, dimensions []ArrayDimension) []byte {\n\tvar customDimensions bool\n\tfor _, dim := range dimensions {\n\t\tif dim.LowerBound != 1 {\n\t\t\tcustomDimensions = true\n\t\t}\n\t}\n\n\tif !customDimensions {\n\t\treturn buf\n\t}\n\n\tfor _, dim := range dimensions {\n\t\tbuf = append(buf, '[')\n\t\tbuf = append(buf, strconv.FormatInt(int64(dim.LowerBound), 10)...)\n\t\tbuf = append(buf, ':')\n\t\tbuf = append(buf, strconv.FormatInt(int64(dim.LowerBound+dim.Length-1), 10)...)\n\t\tbuf = append(buf, ']')\n\t}\n\n\treturn append(buf, '=')\n}\n\nvar quoteArrayReplacer = strings.NewReplacer(`\\`, `\\\\`, `\"`, `\\\"`)\n\nfunc quoteArrayElement(src string) string {\n\treturn `\"` + quoteArrayReplacer.Replace(src) + `\"`\n}\n\nfunc isSpace(ch byte) bool {\n\t// see array_isspace:\n\t// https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/arrayfuncs.c\n\treturn ch == ' ' || ch == '\\t' || ch == '\\n' || ch == '\\r' || ch == '\\v' || ch == '\\f'\n}\n\nfunc quoteArrayElementIfNeeded(src string) string {\n\tif src == \"\" || (len(src) == 4 && strings.EqualFold(src, \"null\")) || isSpace(src[0]) || isSpace(src[len(src)-1]) || strings.ContainsAny(src, `{},\"\\`) {\n\t\treturn quoteArrayElement(src)\n\t}\n\treturn src\n}\n\n// Array represents a PostgreSQL array for T. It implements the [ArrayGetter] and [ArraySetter] interfaces. It preserves\n// PostgreSQL dimensions and custom lower bounds. Use [FlatArray] if these are not needed.\ntype Array[T any] struct {\n\tElements []T\n\tDims     []ArrayDimension\n\tValid    bool\n}\n\nfunc (a Array[T]) Dimensions() []ArrayDimension {\n\treturn a.Dims\n}\n\nfunc (a Array[T]) Index(i int) any {\n\treturn a.Elements[i]\n}\n\nfunc (a Array[T]) IndexType() any {\n\tvar el T\n\treturn el\n}\n\nfunc (a *Array[T]) SetDimensions(dimensions []ArrayDimension) error {\n\tif dimensions == nil {\n\t\t*a = Array[T]{}\n\t\treturn nil\n\t}\n\n\telementCount := cardinality(dimensions)\n\t*a = Array[T]{\n\t\tElements: make([]T, elementCount),\n\t\tDims:     dimensions,\n\t\tValid:    true,\n\t}\n\n\treturn nil\n}\n\nfunc (a Array[T]) ScanIndex(i int) any {\n\treturn &a.Elements[i]\n}\n\nfunc (a Array[T]) ScanIndexType() any {\n\treturn new(T)\n}\n\n// FlatArray implements the [ArrayGetter] and [ArraySetter] interfaces for any slice of T. It ignores PostgreSQL dimensions\n// and custom lower bounds. Use [Array] to preserve these.\ntype FlatArray[T any] []T\n\nfunc (a FlatArray[T]) Dimensions() []ArrayDimension {\n\tif a == nil {\n\t\treturn nil\n\t}\n\n\treturn []ArrayDimension{{Length: int32(len(a)), LowerBound: 1}}\n}\n\nfunc (a FlatArray[T]) Index(i int) any {\n\treturn a[i]\n}\n\nfunc (a FlatArray[T]) IndexType() any {\n\tvar el T\n\treturn el\n}\n\nfunc (a *FlatArray[T]) SetDimensions(dimensions []ArrayDimension) error {\n\tif dimensions == nil {\n\t\t*a = nil\n\t\treturn nil\n\t}\n\n\telementCount := cardinality(dimensions)\n\t*a = make(FlatArray[T], elementCount)\n\treturn nil\n}\n\nfunc (a FlatArray[T]) ScanIndex(i int) any {\n\treturn &a[i]\n}\n\nfunc (a FlatArray[T]) ScanIndexType() any {\n\treturn new(T)\n}\n"
  },
  {
    "path": "pgtype/array_codec.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// ArrayGetter is a type that can be converted into a PostgreSQL array.\ntype ArrayGetter interface {\n\t// Dimensions returns the array dimensions. If array is nil then nil is returned.\n\tDimensions() []ArrayDimension\n\n\t// Index returns the element at i.\n\tIndex(i int) any\n\n\t// IndexType returns a non-nil scan target of the type Index will return. This is used by ArrayCodec.PlanEncode.\n\tIndexType() any\n}\n\n// ArraySetter is a type can be set from a PostgreSQL array.\ntype ArraySetter interface {\n\t// SetDimensions prepares the value such that ScanIndex can be called for each element. This will remove any existing\n\t// elements. dimensions may be nil to indicate a NULL array. If unable to exactly preserve dimensions SetDimensions\n\t// may return an error or silently flatten the array dimensions.\n\tSetDimensions(dimensions []ArrayDimension) error\n\n\t// ScanIndex returns a value usable as a scan target for i. SetDimensions must be called before ScanIndex.\n\tScanIndex(i int) any\n\n\t// ScanIndexType returns a non-nil scan target of the type ScanIndex will return. This is used by\n\t// ArrayCodec.PlanScan.\n\tScanIndexType() any\n}\n\n// ArrayCodec is a codec for any array type.\ntype ArrayCodec struct {\n\tElementType *Type\n}\n\nfunc (c *ArrayCodec) FormatSupported(format int16) bool {\n\treturn c.ElementType.Codec.FormatSupported(format)\n}\n\nfunc (c *ArrayCodec) PreferredFormat() int16 {\n\t// The binary format should always be preferred for arrays if it is supported. Usually, this will happen automatically\n\t// because most types that support binary prefer it. However, text, json, and jsonb support binary but prefer the text\n\t// format. This is because it is simpler for jsonb and PostgreSQL can be significantly faster using the text format\n\t// for text-like data types than binary. However, arrays appear to always be faster in binary.\n\t//\n\t// https://www.postgresql.org/message-id/CAMovtNoHFod2jMAKQjjxv209PCTJx5Kc66anwWvX0mEiaXwgmA%40mail.gmail.com\n\tif c.ElementType.Codec.FormatSupported(BinaryFormatCode) {\n\t\treturn BinaryFormatCode\n\t}\n\treturn TextFormatCode\n}\n\nfunc (c *ArrayCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tarrayValuer, ok := value.(ArrayGetter)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\telementType := arrayValuer.IndexType()\n\n\telementEncodePlan := m.PlanEncode(c.ElementType.OID, format, elementType)\n\tif elementEncodePlan == nil {\n\t\tif reflect.TypeOf(elementType) != nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn &encodePlanArrayCodecBinary{ac: c, m: m, oid: oid}\n\tcase TextFormatCode:\n\t\treturn &encodePlanArrayCodecText{ac: c, m: m, oid: oid}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanArrayCodecText struct {\n\tac  *ArrayCodec\n\tm   *Map\n\toid uint32\n}\n\nfunc (p *encodePlanArrayCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tarray := value.(ArrayGetter)\n\n\tdimensions := array.Dimensions()\n\tif dimensions == nil {\n\t\treturn nil, nil\n\t}\n\n\telementCount := cardinality(dimensions)\n\tif elementCount == 0 {\n\t\treturn append(buf, '{', '}'), nil\n\t}\n\n\tbuf = encodeTextArrayDimensions(buf, dimensions)\n\n\t// dimElemCounts is the multiples of elements that each array lies on. For\n\t// example, a single dimension array of length 4 would have a dimElemCounts of\n\t// [4]. A multi-dimensional array of lengths [3,5,2] would have a\n\t// dimElemCounts of [30,10,2]. This is used to simplify when to render a '{'\n\t// or '}'.\n\tdimElemCounts := make([]int, len(dimensions))\n\tdimElemCounts[len(dimensions)-1] = int(dimensions[len(dimensions)-1].Length)\n\tfor i := len(dimensions) - 2; i > -1; i-- {\n\t\tdimElemCounts[i] = int(dimensions[i].Length) * dimElemCounts[i+1]\n\t}\n\n\tvar encodePlan EncodePlan\n\tvar lastElemType reflect.Type\n\tinElemBuf := make([]byte, 0, 32)\n\tfor i := range elementCount {\n\t\tif i > 0 {\n\t\t\tbuf = append(buf, ',')\n\t\t}\n\n\t\tfor _, dec := range dimElemCounts {\n\t\t\tif i%dec == 0 {\n\t\t\t\tbuf = append(buf, '{')\n\t\t\t}\n\t\t}\n\n\t\telem := array.Index(i)\n\t\tvar elemBuf []byte\n\t\tif isNil, _ := isNilDriverValuer(elem); !isNil {\n\t\t\telemType := reflect.TypeOf(elem)\n\t\t\tif lastElemType != elemType {\n\t\t\t\tlastElemType = elemType\n\t\t\t\tencodePlan = p.m.PlanEncode(p.ac.ElementType.OID, TextFormatCode, elem)\n\t\t\t\tif encodePlan == nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unable to encode %v\", array.Index(i))\n\t\t\t\t}\n\t\t\t}\n\t\t\telemBuf, err = encodePlan.Encode(elem, inElemBuf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif elemBuf == nil {\n\t\t\tbuf = append(buf, `NULL`...)\n\t\t} else {\n\t\t\tbuf = append(buf, quoteArrayElementIfNeeded(string(elemBuf))...)\n\t\t}\n\n\t\tfor _, dec := range dimElemCounts {\n\t\t\tif (i+1)%dec == 0 {\n\t\t\t\tbuf = append(buf, '}')\n\t\t\t}\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanArrayCodecBinary struct {\n\tac  *ArrayCodec\n\tm   *Map\n\toid uint32\n}\n\nfunc (p *encodePlanArrayCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tarray := value.(ArrayGetter)\n\n\tdimensions := array.Dimensions()\n\tif dimensions == nil {\n\t\treturn nil, nil\n\t}\n\n\tarrayHeader := arrayHeader{\n\t\tDimensions: dimensions,\n\t\tElementOID: p.ac.ElementType.OID,\n\t}\n\n\tcontainsNullIndex := len(buf) + 4\n\n\tbuf = arrayHeader.EncodeBinary(buf)\n\n\telementCount := cardinality(dimensions)\n\n\tvar encodePlan EncodePlan\n\tvar lastElemType reflect.Type\n\tfor i := range elementCount {\n\t\tsp := len(buf)\n\t\tbuf = pgio.AppendInt32(buf, -1)\n\n\t\telem := array.Index(i)\n\t\tvar elemBuf []byte\n\t\tif isNil, _ := isNilDriverValuer(elem); !isNil {\n\t\t\telemType := reflect.TypeOf(elem)\n\t\t\tif lastElemType != elemType {\n\t\t\t\tlastElemType = elemType\n\t\t\t\tencodePlan = p.m.PlanEncode(p.ac.ElementType.OID, BinaryFormatCode, elem)\n\t\t\t\tif encodePlan == nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unable to encode %v\", array.Index(i))\n\t\t\t\t}\n\t\t\t}\n\t\t\telemBuf, err = encodePlan.Encode(elem, buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif elemBuf == nil {\n\t\t\tpgio.SetInt32(buf[containsNullIndex:], 1)\n\t\t} else {\n\t\t\tbuf = elemBuf\n\t\t\tpgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (c *ArrayCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tarrayScanner, ok := target.(ArraySetter)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\t// target / arrayScanner might be a pointer to a nil. If it is create one so we can call ScanIndexType to plan the\n\t// scan of the elements.\n\tif isNil, _ := isNilDriverValuer(target); isNil {\n\t\tarrayScanner = reflect.New(reflect.TypeOf(target).Elem()).Interface().(ArraySetter)\n\t}\n\n\telementType := arrayScanner.ScanIndexType()\n\n\telementScanPlan := m.PlanScan(c.ElementType.OID, format, elementType)\n\tif _, ok := elementScanPlan.(*scanPlanFail); ok {\n\t\treturn nil\n\t}\n\n\treturn &scanPlanArrayCodec{\n\t\tarrayCodec: c,\n\t\tm:          m,\n\t\toid:        oid,\n\t\tformatCode: format,\n\t}\n}\n\nfunc (c *ArrayCodec) decodeBinary(m *Map, arrayOID uint32, src []byte, array ArraySetter) error {\n\tvar arrayHeader arrayHeader\n\trp, err := arrayHeader.DecodeBinary(m, src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = array.SetDimensions(arrayHeader.Dimensions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\telementCount := cardinality(arrayHeader.Dimensions)\n\tif elementCount == 0 {\n\t\treturn nil\n\t}\n\n\telementScanPlan := c.ElementType.Codec.PlanScan(m, c.ElementType.OID, BinaryFormatCode, array.ScanIndex(0))\n\tif elementScanPlan == nil {\n\t\telementScanPlan = m.PlanScan(c.ElementType.OID, BinaryFormatCode, array.ScanIndex(0))\n\t}\n\n\tfor i := range elementCount {\n\t\telem := array.ScanIndex(i)\n\t\telemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\trp += 4\n\t\tvar elemSrc []byte\n\t\tif elemLen >= 0 {\n\t\t\telemSrc = src[rp : rp+elemLen]\n\t\t\trp += elemLen\n\t\t}\n\t\terr = elementScanPlan.Scan(elemSrc, elem)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to scan array element %d: %w\", i, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *ArrayCodec) decodeText(m *Map, arrayOID uint32, src []byte, array ArraySetter) error {\n\tuta, err := parseUntypedTextArray(string(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = array.SetDimensions(uta.Dimensions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(uta.Elements) == 0 {\n\t\treturn nil\n\t}\n\n\telementScanPlan := c.ElementType.Codec.PlanScan(m, c.ElementType.OID, TextFormatCode, array.ScanIndex(0))\n\tif elementScanPlan == nil {\n\t\telementScanPlan = m.PlanScan(c.ElementType.OID, TextFormatCode, array.ScanIndex(0))\n\t}\n\n\tfor i, s := range uta.Elements {\n\t\telem := array.ScanIndex(i)\n\t\tvar elemSrc []byte\n\t\tif s != \"NULL\" || uta.Quoted[i] {\n\t\t\telemSrc = []byte(s)\n\t\t}\n\n\t\terr = elementScanPlan.Scan(elemSrc, elem)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanArrayCodec struct {\n\tarrayCodec      *ArrayCodec\n\tm               *Map\n\toid             uint32\n\tformatCode      int16\n\telementScanPlan ScanPlan\n}\n\nfunc (spac *scanPlanArrayCodec) Scan(src []byte, dst any) error {\n\tc := spac.arrayCodec\n\tm := spac.m\n\toid := spac.oid\n\tformatCode := spac.formatCode\n\n\tarray := dst.(ArraySetter)\n\n\tif src == nil {\n\t\treturn array.SetDimensions(nil)\n\t}\n\n\tswitch formatCode {\n\tcase BinaryFormatCode:\n\t\treturn c.decodeBinary(m, oid, src, array)\n\tcase TextFormatCode:\n\t\treturn c.decodeText(m, oid, src, array)\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown format code %d\", formatCode)\n\t}\n}\n\nfunc (c *ArrayCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn string(src), nil\n\tcase BinaryFormatCode:\n\t\tbuf := make([]byte, len(src))\n\t\tcopy(buf, src)\n\t\treturn buf, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n\nfunc (c *ArrayCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar slice []any\n\terr := m.PlanScan(oid, format, &slice).Scan(src, &slice)\n\treturn slice, err\n}\n\nfunc isRagged(slice reflect.Value) bool {\n\tif slice.Type().Elem().Kind() != reflect.Slice {\n\t\treturn false\n\t}\n\n\tsliceLen := slice.Len()\n\tinnerLen := 0\n\tfor i := range sliceLen {\n\t\tif i == 0 {\n\t\t\tinnerLen = slice.Index(i).Len()\n\t\t} else {\n\t\t\tif slice.Index(i).Len() != innerLen {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\tif isRagged(slice.Index(i)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pgtype/array_codec_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestArrayCodec(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tfor i, tt := range []struct {\n\t\t\texpected any\n\t\t}{\n\t\t\t{[]int16(nil)},\n\t\t\t{[]int16{}},\n\t\t\t{[]int16{1, 2, 3}},\n\t\t} {\n\t\t\tvar actual []int16\n\t\t\terr := conn.QueryRow(\n\t\t\t\tctx,\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\n\t\tnewInt16 := func(n int16) *int16 { return &n }\n\n\t\tfor i, tt := range []struct {\n\t\t\texpected any\n\t\t}{\n\t\t\t{[]*int16{newInt16(1), nil, newInt16(3), nil, newInt16(5)}},\n\t\t} {\n\t\t\tvar actual []*int16\n\t\t\terr := conn.QueryRow(\n\t\t\t\tctx,\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t})\n}\n\nfunc TestArrayCodecFlatArrayString(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput []string\n\t}{\n\t\t{nil},\n\t\t{[]string{}},\n\t\t{[]string{\"a\"}},\n\t\t{[]string{\"a\", \"b\"}},\n\t\t// previously had a bug with whitespace handling\n\t\t{[]string{\"\\v\", \"\\t\", \"\\n\", \"\\r\", \"\\f\", \" \"}},\n\t\t{[]string{\"a\\vb\", \"a\\tb\", \"a\\nb\", \"a\\rb\", \"a\\fb\", \"a b\"}},\n\t}\n\n\tqueryModes := []pgx.QueryExecMode{pgx.QueryExecModeSimpleProtocol, pgx.QueryExecModeDescribeExec}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tfor i, testCase := range testCases {\n\t\t\tfor _, queryMode := range queryModes {\n\t\t\t\tvar out []string\n\t\t\t\terr := conn.QueryRow(ctx, \"select $1::text[]\", queryMode, testCase.input).Scan(&out)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"i=%d input=%#v queryMode=%s: Scan failed: %s\",\n\t\t\t\t\t\ti, testCase.input, queryMode, err)\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(out, testCase.input) {\n\t\t\t\t\tt.Errorf(\"i=%d input=%#v queryMode=%s: not equal output=%#v\",\n\t\t\t\t\t\ti, testCase.input, queryMode, out)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestArrayCodecArray(t *testing.T) {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support multi-dimensional arrays\")\n\t}\n\n\tctr.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tfor i, tt := range []struct {\n\t\t\texpected any\n\t\t}{\n\t\t\t{pgtype.Array[int32]{\n\t\t\t\tElements: []int32{1, 2, 3, 4},\n\t\t\t\tDims: []pgtype.ArrayDimension{\n\t\t\t\t\t{Length: 2, LowerBound: 2},\n\t\t\t\t\t{Length: 2, LowerBound: 2},\n\t\t\t\t},\n\t\t\t\tValid: true,\n\t\t\t}},\n\t\t} {\n\t\t\tvar actual pgtype.Array[int32]\n\t\t\terr := conn.QueryRow(\n\t\t\t\tctx,\n\t\t\t\t\"select $1::int[]\",\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t})\n}\n\nfunc TestArrayCodecPointerToNil(t *testing.T) {\n\tpgxtest.RunWithQueryExecModes(context.Background(), t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tn := int32(42)\n\t\tinput := []*int32{&n, nil}\n\t\tvar actual []*int32\n\t\terr := conn.QueryRow(\n\t\t\tctx,\n\t\t\t\"select $1::int[]\",\n\t\t\tinput,\n\t\t).Scan(&actual)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, input, actual)\n\t})\n}\n\nfunc TestArrayCodecNamedSliceType(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttype _int16Slice []int16\n\n\t\tfor i, tt := range []struct {\n\t\t\texpected any\n\t\t}{\n\t\t\t{_int16Slice(nil)},\n\t\t\t{_int16Slice{}},\n\t\t\t{_int16Slice{1, 2, 3}},\n\t\t} {\n\t\t\tvar actual _int16Slice\n\t\t\terr := conn.QueryRow(\n\t\t\t\tctx,\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1488\nfunc TestArrayCodecAnySliceArgument(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttype _int16Slice []int16\n\n\t\tfor i, tt := range []struct {\n\t\t\targ      any\n\t\t\texpected []int16\n\t\t}{\n\t\t\t{[]any{1, 2, 3}, []int16{1, 2, 3}},\n\t\t} {\n\t\t\tvar actual []int16\n\t\t\terr := conn.QueryRow(\n\t\t\t\tctx,\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\ttt.arg,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1442\nfunc TestArrayCodecAnyArray(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttype _point3 [3]float32\n\n\t\tfor i, tt := range []struct {\n\t\t\texpected any\n\t\t}{\n\t\t\t{_point3{0, 0, 0}},\n\t\t\t{_point3{1, 2, 3}},\n\t\t} {\n\t\t\tvar actual _point3\n\t\t\terr := conn.QueryRow(\n\t\t\t\tctx,\n\t\t\t\t\"select $1::float4[]\",\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1273#issuecomment-1218262703\nfunc TestArrayCodecSliceArgConversion(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\targ := []string{\n\t\t\t\"3ad95bfd-ecea-4032-83c3-0c823cafb372\",\n\t\t\t\"951baf11-c0cc-4afc-a779-abff0611dbf1\",\n\t\t\t\"8327f244-7e2f-45e7-a10b-fbdc9d6f3378\",\n\t\t}\n\n\t\tvar expected []pgtype.UUID\n\n\t\tfor _, s := range arg {\n\t\t\tbuf, err := hex.DecodeString(strings.ReplaceAll(s, \"-\", \"\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tvar u pgtype.UUID\n\t\t\tcopy(u.Bytes[:], buf)\n\t\t\tu.Valid = true\n\t\t\texpected = append(expected, u)\n\t\t}\n\n\t\tvar actual []pgtype.UUID\n\t\terr := conn.QueryRow(\n\t\t\tctx,\n\t\t\t\"select $1::uuid[]\",\n\t\t\targ,\n\t\t).Scan(&actual)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, expected, actual)\n\t})\n}\n\nfunc TestArrayCodecDecodeValue(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tfor _, tt := range []struct {\n\t\t\tsql      string\n\t\t\texpected any\n\t\t}{\n\t\t\t{\n\t\t\t\tsql:      `select '{}'::int4[]`,\n\t\t\t\texpected: []any{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select '{1,2}'::int8[]`,\n\t\t\t\texpected: []any{int64(1), int64(2)},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select '{foo,bar}'::text[]`,\n\t\t\t\texpected: []any{\"foo\", \"bar\"},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(tt.sql, func(t *testing.T) {\n\t\t\t\trows, err := conn.Query(ctx, tt.sql)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\tvalues, err := rows.Values()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, values, 1)\n\t\t\t\t\trequire.Equal(t, tt.expected, values[0])\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, rows.Err())\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestArrayCodecScanMultipleDimensions(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, err := conn.Query(ctx, `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)\n\t\trequire.NoError(t, err)\n\n\t\tfor rows.Next() {\n\t\t\tvar ss [][]int32\n\t\t\terr := rows.Scan(&ss)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, ss)\n\t\t}\n\n\t\trequire.NoError(t, rows.Err())\n\t})\n}\n\nfunc TestArrayCodecScanMultipleDimensionsEmpty(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, err := conn.Query(ctx, `select '{}'::int4[]`)\n\t\trequire.NoError(t, err)\n\n\t\tfor rows.Next() {\n\t\t\tvar ss [][]int32\n\t\t\terr := rows.Scan(&ss)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, [][]int32{}, ss)\n\t\t}\n\n\t\trequire.NoError(t, rows.Err())\n\t})\n}\n\nfunc TestArrayCodecScanWrongMultipleDimensions(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, err := conn.Query(ctx, `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)\n\t\trequire.NoError(t, err)\n\n\t\tfor rows.Next() {\n\t\t\tvar ss [][][]int32\n\t\t\terr := rows.Scan(&ss)\n\t\t\trequire.Error(t, err, \"can't scan into dest[0]: PostgreSQL array has 2 dimensions but slice has 3 dimensions\")\n\t\t}\n\t})\n}\n\nfunc TestArrayCodecEncodeMultipleDimensions(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, err := conn.Query(ctx, `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}})\n\t\trequire.NoError(t, err)\n\n\t\tfor rows.Next() {\n\t\t\tvar ss [][]int32\n\t\t\terr := rows.Scan(&ss)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, ss)\n\t\t}\n\n\t\trequire.NoError(t, rows.Err())\n\t})\n}\n\nfunc TestArrayCodecEncodeMultipleDimensionsRagged(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, err := conn.Query(ctx, `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5}, {9, 10, 11, 12}})\n\t\trequire.Error(t, err, \"cannot convert [][]int32 to ArrayGetter because it is a ragged multi-dimensional\")\n\t\tdefer rows.Close()\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1494\nfunc TestArrayCodecDecodeTextArrayWithTextOfNULL(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t{\n\t\t\tvar actual []string\n\t\t\terr := conn.QueryRow(ctx, `select '{\"foo\", \"NULL\", \" NULL \"}'::text[]`).Scan(&actual)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, []string{\"foo\", \"NULL\", \" NULL \"}, actual)\n\t\t}\n\n\t\t{\n\t\t\tvar actual []pgtype.Text\n\t\t\terr := conn.QueryRow(ctx, `select '{\"foo\", \"NULL\", NULL, \" NULL \"}'::text[]`).Scan(&actual)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, []pgtype.Text{\n\t\t\t\t{String: \"foo\", Valid: true},\n\t\t\t\t{String: \"NULL\", Valid: true},\n\t\t\t\t{},\n\t\t\t\t{String: \" NULL \", Valid: true},\n\t\t\t}, actual)\n\t\t}\n\t})\n}\n\nfunc TestArrayCodecDecodeTextArrayPrefersBinaryFormat(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsd, err := conn.Prepare(ctx, \"\", `select '{\"foo\", \"NULL\", \" NULL \"}'::text[]`)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, int16(1), conn.TypeMap().FormatCodeForOID(sd.Fields[0].DataTypeOID))\n\t})\n}\n"
  },
  {
    "path": "pgtype/array_test.go",
    "content": "package pgtype\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestParseUntypedTextArray(t *testing.T) {\n\ttests := []struct {\n\t\tsource string\n\t\tresult untypedTextArray\n\t}{\n\t\t{\n\t\t\tsource: \"{}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{},\n\t\t\t\tQuoted:     []bool{},\n\t\t\t\tDimensions: []ArrayDimension{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"{1}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"1\"},\n\t\t\t\tQuoted:     []bool{false},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"{a,b}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"a\", \"b\"},\n\t\t\t\tQuoted:     []bool{false, false},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 2, LowerBound: 1}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: `{\"NULL\"}`,\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"NULL\"},\n\t\t\t\tQuoted:     []bool{true},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: `{\"\"}`,\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"\"},\n\t\t\t\tQuoted:     []bool{true},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: `{\"He said, \\\"Hello.\\\"\"}`,\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{`He said, \"Hello.\"`},\n\t\t\t\tQuoted:     []bool{true},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"{{a,b},{c,d},{e,f}}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"},\n\t\t\t\tQuoted:     []bool{false, false, false, false, false, false},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"{{{a,b},{c,d},{e,f}},{{a,b},{c,d},{e,f}}}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements: []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\"},\n\t\t\t\tQuoted:   []bool{false, false, false, false, false, false, false, false, false, false, false, false},\n\t\t\t\tDimensions: []ArrayDimension{\n\t\t\t\t\t{Length: 2, LowerBound: 1},\n\t\t\t\t\t{Length: 3, LowerBound: 1},\n\t\t\t\t\t{Length: 2, LowerBound: 1},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"[4:4]={1}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"1\"},\n\t\t\t\tQuoted:     []bool{false},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 1, LowerBound: 4}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"[4:5][2:3]={{a,b},{c,d}}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements: []string{\"a\", \"b\", \"c\", \"d\"},\n\t\t\t\tQuoted:   []bool{false, false, false, false},\n\t\t\t\tDimensions: []ArrayDimension{\n\t\t\t\t\t{Length: 2, LowerBound: 4},\n\t\t\t\t\t{Length: 2, LowerBound: 2},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tsource: \"[-4:-2]={1,2,3}\",\n\t\t\tresult: untypedTextArray{\n\t\t\t\tElements:   []string{\"1\", \"2\", \"3\"},\n\t\t\t\tQuoted:     []bool{false, false, false},\n\t\t\t\tDimensions: []ArrayDimension{{Length: 3, LowerBound: -4}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tr, err := parseUntypedTextArray(tt.source)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif !reflect.DeepEqual(*r, tt.result) {\n\t\t\tt.Errorf(\"%d: expected %+v to be parsed to %+v, but it was %+v\", i, tt.source, tt.result, *r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/bits.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype BitsScanner interface {\n\tScanBits(v Bits) error\n}\n\ntype BitsValuer interface {\n\tBitsValue() (Bits, error)\n}\n\n// Bits represents the PostgreSQL bit and varbit types.\ntype Bits struct {\n\tBytes []byte\n\tLen   int32 // Number of bits\n\tValid bool\n}\n\n// ScanBits implements the [BitsScanner] interface.\nfunc (b *Bits) ScanBits(v Bits) error {\n\t*b = v\n\treturn nil\n}\n\n// BitsValue implements the [BitsValuer] interface.\nfunc (b Bits) BitsValue() (Bits, error) {\n\treturn b, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Bits) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Bits{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToBitsScanner{}.Scan([]byte(src), dst)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Bits) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := BitsCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype BitsCodec struct{}\n\nfunc (BitsCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (BitsCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (BitsCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(BitsValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanBitsCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanBitsCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanBitsCodecBinary struct{}\n\nfunc (encodePlanBitsCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tbits, err := value.(BitsValuer).BitsValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !bits.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendInt32(buf, bits.Len)\n\treturn append(buf, bits.Bytes...), nil\n}\n\ntype encodePlanBitsCodecText struct{}\n\nfunc (encodePlanBitsCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tbits, err := value.(BitsValuer).BitsValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !bits.Valid {\n\t\treturn nil, nil\n\t}\n\n\tfor i := int32(0); i < bits.Len; i++ {\n\t\tbyteIdx := i / 8\n\t\tbitMask := byte(128 >> byte(i%8))\n\t\tchar := byte('0')\n\t\tif bits.Bytes[byteIdx]&bitMask > 0 {\n\t\t\tchar = '1'\n\t\t}\n\t\tbuf = append(buf, char)\n\t}\n\n\treturn buf, nil\n}\n\nfunc (BitsCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase BitsScanner:\n\t\t\treturn scanPlanBinaryBitsToBitsScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase BitsScanner:\n\t\t\treturn scanPlanTextAnyToBitsScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c BitsCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c BitsCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar box Bits\n\terr := codecScan(c, m, oid, format, src, &box)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn box, nil\n}\n\ntype scanPlanBinaryBitsToBitsScanner struct{}\n\nfunc (scanPlanBinaryBitsToBitsScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(BitsScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanBits(Bits{})\n\t}\n\n\tif len(src) < 4 {\n\t\treturn fmt.Errorf(\"invalid length for bit/varbit: %v\", len(src))\n\t}\n\n\tbitLen := int32(binary.BigEndian.Uint32(src))\n\trp := 4\n\tbuf := make([]byte, len(src[rp:]))\n\tcopy(buf, src[rp:])\n\n\treturn scanner.ScanBits(Bits{Bytes: buf, Len: bitLen, Valid: true})\n}\n\ntype scanPlanTextAnyToBitsScanner struct{}\n\nfunc (scanPlanTextAnyToBitsScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(BitsScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanBits(Bits{})\n\t}\n\n\tbitLen := len(src)\n\tbyteLen := bitLen / 8\n\tif bitLen%8 > 0 {\n\t\tbyteLen++\n\t}\n\tbuf := make([]byte, byteLen)\n\n\tfor i, b := range src {\n\t\tif b == '1' {\n\t\t\tbyteIdx := i / 8\n\t\t\tbitIdx := uint(i % 8)\n\t\t\tbuf[byteIdx] = buf[byteIdx] | (128 >> bitIdx)\n\t\t}\n\t}\n\n\treturn scanner.ScanBits(Bits{Bytes: buf, Len: int32(bitLen), Valid: true})\n}\n"
  },
  {
    "path": "pgtype/bits_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqBits(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tab := a.(pgtype.Bits)\n\t\tvb := v.(pgtype.Bits)\n\t\treturn bytes.Equal(ab.Bytes, vb.Bytes) && ab.Len == vb.Len && ab.Valid == vb.Valid\n\t}\n}\n\nfunc TestBitsCodecBit(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"bit(40)\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Bits{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Valid: true},\n\t\t\tResult: new(pgtype.Bits),\n\t\t\tTest:   isExpectedEqBits(pgtype.Bits{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Bits{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Valid: true},\n\t\t\tResult: new(pgtype.Bits),\n\t\t\tTest:   isExpectedEqBits(pgtype.Bits{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Bits{}, Result: new(pgtype.Bits), Test: isExpectedEqBits(pgtype.Bits{})},\n\t\t{Param: nil, Result: new(pgtype.Bits), Test: isExpectedEqBits(pgtype.Bits{})},\n\t})\n}\n\nfunc TestBitsCodecVarbit(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"varbit\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Bits{Bytes: []byte{}, Len: 0, Valid: true},\n\t\t\tResult: new(pgtype.Bits),\n\t\t\tTest:   isExpectedEqBits(pgtype.Bits{Bytes: []byte{}, Len: 0, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Bits{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Valid: true},\n\t\t\tResult: new(pgtype.Bits),\n\t\t\tTest:   isExpectedEqBits(pgtype.Bits{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Bits{Bytes: []byte{0, 1, 128, 254, 128}, Len: 33, Valid: true},\n\t\t\tResult: new(pgtype.Bits),\n\t\t\tTest:   isExpectedEqBits(pgtype.Bits{Bytes: []byte{0, 1, 128, 254, 128}, Len: 33, Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Bits{}, Result: new(pgtype.Bits), Test: isExpectedEqBits(pgtype.Bits{})},\n\t\t{Param: nil, Result: new(pgtype.Bits), Test: isExpectedEqBits(pgtype.Bits{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/bool.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype BoolScanner interface {\n\tScanBool(v Bool) error\n}\n\ntype BoolValuer interface {\n\tBoolValue() (Bool, error)\n}\n\ntype Bool struct {\n\tBool  bool\n\tValid bool\n}\n\n// ScanBool implements the [BoolScanner] interface.\nfunc (b *Bool) ScanBool(v Bool) error {\n\t*b = v\n\treturn nil\n}\n\n// BoolValue implements the [BoolValuer] interface.\nfunc (b Bool) BoolValue() (Bool, error) {\n\treturn b, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Bool) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Bool{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase bool:\n\t\t*dst = Bool{Bool: src, Valid: true}\n\t\treturn nil\n\tcase string:\n\t\tb, err := strconv.ParseBool(src)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*dst = Bool{Bool: b, Valid: true}\n\t\treturn nil\n\tcase []byte:\n\t\tb, err := strconv.ParseBool(string(src))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*dst = Bool{Bool: b, Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Bool) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn src.Bool, nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Bool) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tif src.Bool {\n\t\treturn []byte(\"true\"), nil\n\t} else {\n\t\treturn []byte(\"false\"), nil\n\t}\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Bool) UnmarshalJSON(b []byte) error {\n\tvar v *bool\n\terr := json.Unmarshal(b, &v)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif v == nil {\n\t\t*dst = Bool{}\n\t} else {\n\t\t*dst = Bool{Bool: *v, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype BoolCodec struct{}\n\nfunc (BoolCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (BoolCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (BoolCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase bool:\n\t\t\treturn encodePlanBoolCodecBinaryBool{}\n\t\tcase BoolValuer:\n\t\t\treturn encodePlanBoolCodecBinaryBoolValuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase bool:\n\t\t\treturn encodePlanBoolCodecTextBool{}\n\t\tcase BoolValuer:\n\t\t\treturn encodePlanBoolCodecTextBoolValuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanBoolCodecBinaryBool struct{}\n\nfunc (encodePlanBoolCodecBinaryBool) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv := value.(bool)\n\n\tif v {\n\t\tbuf = append(buf, 1)\n\t} else {\n\t\tbuf = append(buf, 0)\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanBoolCodecTextBoolValuer struct{}\n\nfunc (encodePlanBoolCodecTextBoolValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb, err := value.(BoolValuer).BoolValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !b.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif b.Bool {\n\t\tbuf = append(buf, 't')\n\t} else {\n\t\tbuf = append(buf, 'f')\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanBoolCodecBinaryBoolValuer struct{}\n\nfunc (encodePlanBoolCodecBinaryBoolValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb, err := value.(BoolValuer).BoolValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !b.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif b.Bool {\n\t\tbuf = append(buf, 1)\n\t} else {\n\t\tbuf = append(buf, 0)\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanBoolCodecTextBool struct{}\n\nfunc (encodePlanBoolCodecTextBool) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv := value.(bool)\n\n\tif v {\n\t\tbuf = append(buf, 't')\n\t} else {\n\t\tbuf = append(buf, 'f')\n\t}\n\n\treturn buf, nil\n}\n\nfunc (BoolCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *bool:\n\t\t\treturn scanPlanBinaryBoolToBool{}\n\t\tcase BoolScanner:\n\t\t\treturn scanPlanBinaryBoolToBoolScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *bool:\n\t\t\treturn scanPlanTextAnyToBool{}\n\t\tcase BoolScanner:\n\t\t\treturn scanPlanTextAnyToBoolScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c BoolCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn c.DecodeValue(m, oid, format, src)\n}\n\nfunc (c BoolCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar b bool\n\terr := codecScan(c, m, oid, format, src, &b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn b, nil\n}\n\ntype scanPlanBinaryBoolToBool struct{}\n\nfunc (scanPlanBinaryBoolToBool) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 1 {\n\t\treturn fmt.Errorf(\"invalid length for bool: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*bool)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = src[0] == 1\n\n\treturn nil\n}\n\ntype scanPlanTextAnyToBool struct{}\n\nfunc (scanPlanTextAnyToBool) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) == 0 {\n\t\treturn fmt.Errorf(\"cannot scan empty string into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*bool)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tv, err := planTextToBool(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = v\n\n\treturn nil\n}\n\ntype scanPlanBinaryBoolToBoolScanner struct{}\n\nfunc (scanPlanBinaryBoolToBoolScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(BoolScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanBool(Bool{})\n\t}\n\n\tif len(src) != 1 {\n\t\treturn fmt.Errorf(\"invalid length for bool: %v\", len(src))\n\t}\n\n\treturn s.ScanBool(Bool{Bool: src[0] == 1, Valid: true})\n}\n\ntype scanPlanTextAnyToBoolScanner struct{}\n\nfunc (scanPlanTextAnyToBoolScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(BoolScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanBool(Bool{})\n\t}\n\n\tif len(src) == 0 {\n\t\treturn fmt.Errorf(\"cannot scan empty string into %T\", dst)\n\t}\n\n\tv, err := planTextToBool(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.ScanBool(Bool{Bool: v, Valid: true})\n}\n\n// https://www.postgresql.org/docs/current/datatype-boolean.html\nfunc planTextToBool(src []byte) (bool, error) {\n\ts := string(bytes.ToLower(bytes.TrimSpace(src)))\n\n\tswitch {\n\tcase strings.HasPrefix(\"true\", s), strings.HasPrefix(\"yes\", s), s == \"on\", s == \"1\":\n\t\treturn true, nil\n\tcase strings.HasPrefix(\"false\", s), strings.HasPrefix(\"no\", s), strings.HasPrefix(\"off\", s), s == \"0\":\n\t\treturn false, nil\n\tdefault:\n\t\treturn false, fmt.Errorf(\"unknown boolean string representation %q\", src)\n\t}\n}\n"
  },
  {
    "path": "pgtype/bool_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestBoolCodec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"bool\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: true, Result: new(bool), Test: isExpectedEq(true)},\n\t\t{Param: false, Result: new(bool), Test: isExpectedEq(false)},\n\t\t{Param: true, Result: new(pgtype.Bool), Test: isExpectedEq(pgtype.Bool{Bool: true, Valid: true})},\n\t\t{Param: pgtype.Bool{}, Result: new(pgtype.Bool), Test: isExpectedEq(pgtype.Bool{})},\n\t\t{Param: nil, Result: new(*bool), Test: isExpectedEq((*bool)(nil))},\n\t})\n}\n\nfunc TestBoolMarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Bool\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Bool{}, result: \"null\"},\n\t\t{source: pgtype.Bool{Bool: true, Valid: true}, result: \"true\"},\n\t\t{source: pgtype.Bool{Bool: false, Valid: true}, result: \"false\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestBoolUnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Bool\n\t}{\n\t\t{source: \"null\", result: pgtype.Bool{}},\n\t\t{source: \"true\", result: pgtype.Bool{Bool: true, Valid: true}},\n\t\t{source: \"false\", result: pgtype.Bool{Bool: false, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Bool\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/box.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype BoxScanner interface {\n\tScanBox(v Box) error\n}\n\ntype BoxValuer interface {\n\tBoxValue() (Box, error)\n}\n\ntype Box struct {\n\tP     [2]Vec2\n\tValid bool\n}\n\n// ScanBox implements the [BoxScanner] interface.\nfunc (b *Box) ScanBox(v Box) error {\n\t*b = v\n\treturn nil\n}\n\n// BoxValue implements the [BoxValuer] interface.\nfunc (b Box) BoxValue() (Box, error) {\n\treturn b, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Box) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Box{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToBoxScanner{}.Scan([]byte(src), dst)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Box) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := BoxCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype BoxCodec struct{}\n\nfunc (BoxCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (BoxCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (BoxCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(BoxValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanBoxCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanBoxCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanBoxCodecBinary struct{}\n\nfunc (encodePlanBoxCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tbox, err := value.(BoxValuer).BoxValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !box.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(box.P[0].X))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(box.P[0].Y))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(box.P[1].X))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(box.P[1].Y))\n\treturn buf, nil\n}\n\ntype encodePlanBoxCodecText struct{}\n\nfunc (encodePlanBoxCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tbox, err := value.(BoxValuer).BoxValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !box.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`,\n\t\tstrconv.FormatFloat(box.P[0].X, 'f', -1, 64),\n\t\tstrconv.FormatFloat(box.P[0].Y, 'f', -1, 64),\n\t\tstrconv.FormatFloat(box.P[1].X, 'f', -1, 64),\n\t\tstrconv.FormatFloat(box.P[1].Y, 'f', -1, 64),\n\t)...)\n\treturn buf, nil\n}\n\nfunc (BoxCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase BoxScanner:\n\t\t\treturn scanPlanBinaryBoxToBoxScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase BoxScanner:\n\t\t\treturn scanPlanTextAnyToBoxScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryBoxToBoxScanner struct{}\n\nfunc (scanPlanBinaryBoxToBoxScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(BoxScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanBox(Box{})\n\t}\n\n\tif len(src) != 32 {\n\t\treturn fmt.Errorf(\"invalid length for Box: %v\", len(src))\n\t}\n\n\tx1 := binary.BigEndian.Uint64(src)\n\ty1 := binary.BigEndian.Uint64(src[8:])\n\tx2 := binary.BigEndian.Uint64(src[16:])\n\ty2 := binary.BigEndian.Uint64(src[24:])\n\n\treturn scanner.ScanBox(Box{\n\t\tP: [2]Vec2{\n\t\t\t{math.Float64frombits(x1), math.Float64frombits(y1)},\n\t\t\t{math.Float64frombits(x2), math.Float64frombits(y2)},\n\t\t},\n\t\tValid: true,\n\t})\n}\n\ntype scanPlanTextAnyToBoxScanner struct{}\n\nfunc (scanPlanTextAnyToBoxScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(BoxScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanBox(Box{})\n\t}\n\n\tif len(src) < 11 {\n\t\treturn fmt.Errorf(\"invalid length for Box: %v\", len(src))\n\t}\n\n\tstr := string(src[1:])\n\n\tvar end int\n\tend = strings.IndexByte(str, ',')\n\n\tx1, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+1:]\n\tend = strings.IndexByte(str, ')')\n\n\ty1, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+3:]\n\tend = strings.IndexByte(str, ',')\n\n\tx2, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+1 : len(str)-1]\n\n\ty2, err := strconv.ParseFloat(str, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanBox(Box{P: [2]Vec2{{x1, y1}, {x2, y2}}, Valid: true})\n}\n\nfunc (c BoxCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c BoxCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar box Box\n\terr := codecScan(c, m, oid, format, src, &box)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn box, nil\n}\n"
  },
  {
    "path": "pgtype/box_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestBoxCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support box type\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"box\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam: pgtype.Box{\n\t\t\t\tP:     [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Box),\n\t\t\tTest: isExpectedEq(pgtype.Box{\n\t\t\t\tP:     [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}},\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Box{\n\t\t\t\tP:     [2]pgtype.Vec2{{7.1, 5.2345678}, {-13.14, -5.234}},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Box),\n\t\t\tTest: isExpectedEq(pgtype.Box{\n\t\t\t\tP:     [2]pgtype.Vec2{{7.1, 5.2345678}, {-13.14, -5.234}},\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Box{}, Result: new(pgtype.Box), Test: isExpectedEq(pgtype.Box{})},\n\t\t{Param: nil, Result: new(pgtype.Box), Test: isExpectedEq(pgtype.Box{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/builtin_wrappers.go",
    "content": "package pgtype\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n\t\"net\"\n\t\"net/netip\"\n\t\"reflect\"\n\t\"time\"\n)\n\ntype int8Wrapper int8\n\nfunc (w int8Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *int8Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *int8\")\n\t}\n\n\tif v.Int64 < math.MinInt8 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int8\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxInt8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int8\", v.Int64)\n\t}\n\t*w = int8Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w int8Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype int16Wrapper int16\n\nfunc (w int16Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *int16Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *int16\")\n\t}\n\n\tif v.Int64 < math.MinInt16 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int16\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int16\", v.Int64)\n\t}\n\t*w = int16Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w int16Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype int32Wrapper int32\n\nfunc (w int32Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *int32Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *int32\")\n\t}\n\n\tif v.Int64 < math.MinInt32 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int32\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxInt32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int32\", v.Int64)\n\t}\n\t*w = int32Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w int32Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype int64Wrapper int64\n\nfunc (w int64Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *int64Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *int64\")\n\t}\n\n\t*w = int64Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w int64Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype intWrapper int\n\nfunc (w intWrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *intWrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *int\")\n\t}\n\n\tif v.Int64 < math.MinInt {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxInt {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int\", v.Int64)\n\t}\n\n\t*w = intWrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w intWrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype uint8Wrapper uint8\n\nfunc (w uint8Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *uint8Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint8\")\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint8\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxUint8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint8\", v.Int64)\n\t}\n\t*w = uint8Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w uint8Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype uint16Wrapper uint16\n\nfunc (w uint16Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *uint16Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint16\")\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint16\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxUint16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint16\", v.Int64)\n\t}\n\t*w = uint16Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w uint16Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype uint32Wrapper uint32\n\nfunc (w uint32Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *uint32Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint32\")\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint32\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxUint32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint32\", v.Int64)\n\t}\n\t*w = uint32Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w uint32Wrapper) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\ntype uint64Wrapper uint64\n\nfunc (w uint64Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *uint64Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint64\")\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint64\", v.Int64)\n\t}\n\n\t*w = uint64Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w uint64Wrapper) Int64Value() (Int8, error) {\n\tif uint64(w) > uint64(math.MaxInt64) {\n\t\treturn Int8{}, fmt.Errorf(\"%d is greater than maximum value for int64\", w)\n\t}\n\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\nfunc (w *uint64Wrapper) ScanNumeric(v Numeric) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint64\")\n\t}\n\n\tbi, err := v.toBigInt()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot scan into *uint64: %w\", err)\n\t}\n\n\tif !bi.IsUint64() {\n\t\treturn fmt.Errorf(\"cannot scan %v into *uint64\", bi.String())\n\t}\n\n\t*w = uint64Wrapper(bi.Uint64())\n\n\treturn nil\n}\n\nfunc (w uint64Wrapper) NumericValue() (Numeric, error) {\n\treturn Numeric{Int: new(big.Int).SetUint64(uint64(w)), Valid: true}, nil\n}\n\ntype uintWrapper uint\n\nfunc (w uintWrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *uintWrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint64\")\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint64\", v.Int64)\n\t}\n\n\tif uint64(v.Int64) > math.MaxUint {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint\", v.Int64)\n\t}\n\n\t*w = uintWrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w uintWrapper) Int64Value() (Int8, error) {\n\tif uint64(w) > uint64(math.MaxInt64) {\n\t\treturn Int8{}, fmt.Errorf(\"%d is greater than maximum value for int64\", w)\n\t}\n\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\nfunc (w *uintWrapper) ScanNumeric(v Numeric) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *uint\")\n\t}\n\n\tbi, err := v.toBigInt()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot scan into *uint: %w\", err)\n\t}\n\n\tif !bi.IsUint64() {\n\t\treturn fmt.Errorf(\"cannot scan %v into *uint\", bi.String())\n\t}\n\n\tui := bi.Uint64()\n\n\tif math.MaxUint < ui {\n\t\treturn fmt.Errorf(\"cannot scan %v into *uint\", ui)\n\t}\n\n\t*w = uintWrapper(ui)\n\n\treturn nil\n}\n\nfunc (w uintWrapper) NumericValue() (Numeric, error) {\n\treturn Numeric{Int: new(big.Int).SetUint64(uint64(w)), Valid: true}, nil\n}\n\ntype float32Wrapper float32\n\nfunc (w float32Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *float32Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *float32\")\n\t}\n\n\t*w = float32Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w float32Wrapper) Int64Value() (Int8, error) {\n\tif w > math.MaxInt64 {\n\t\treturn Int8{}, fmt.Errorf(\"%f is greater than maximum value for int64\", w)\n\t}\n\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\nfunc (w *float32Wrapper) ScanFloat64(v Float8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *float32\")\n\t}\n\n\t*w = float32Wrapper(v.Float64)\n\n\treturn nil\n}\n\nfunc (w float32Wrapper) Float64Value() (Float8, error) {\n\treturn Float8{Float64: float64(w), Valid: true}, nil\n}\n\ntype float64Wrapper float64\n\nfunc (w float64Wrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *float64Wrapper) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *float64\")\n\t}\n\n\t*w = float64Wrapper(v.Int64)\n\n\treturn nil\n}\n\nfunc (w float64Wrapper) Int64Value() (Int8, error) {\n\tif w > math.MaxInt64 {\n\t\treturn Int8{}, fmt.Errorf(\"%f is greater than maximum value for int64\", w)\n\t}\n\n\treturn Int8{Int64: int64(w), Valid: true}, nil\n}\n\nfunc (w *float64Wrapper) ScanFloat64(v Float8) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *float64\")\n\t}\n\n\t*w = float64Wrapper(v.Float64)\n\n\treturn nil\n}\n\nfunc (w float64Wrapper) Float64Value() (Float8, error) {\n\treturn Float8{Float64: float64(w), Valid: true}, nil\n}\n\ntype stringWrapper string\n\nfunc (w stringWrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *stringWrapper) ScanText(v Text) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *string\")\n\t}\n\n\t*w = stringWrapper(v.String)\n\treturn nil\n}\n\nfunc (w stringWrapper) TextValue() (Text, error) {\n\treturn Text{String: string(w), Valid: true}, nil\n}\n\ntype timeWrapper time.Time\n\nfunc (w *timeWrapper) ScanDate(v Date) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *time.Time\")\n\t}\n\n\tswitch v.InfinityModifier {\n\tcase Finite:\n\t\t*w = timeWrapper(v.Time)\n\t\treturn nil\n\tcase Infinity:\n\t\treturn fmt.Errorf(\"cannot scan Infinity into *time.Time\")\n\tcase NegativeInfinity:\n\t\treturn fmt.Errorf(\"cannot scan -Infinity into *time.Time\")\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid InfinityModifier: %v\", v.InfinityModifier)\n\t}\n}\n\nfunc (w timeWrapper) DateValue() (Date, error) {\n\treturn Date{Time: time.Time(w), Valid: true}, nil\n}\n\nfunc (w *timeWrapper) ScanTimestamp(v Timestamp) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *time.Time\")\n\t}\n\n\tswitch v.InfinityModifier {\n\tcase Finite:\n\t\t*w = timeWrapper(v.Time)\n\t\treturn nil\n\tcase Infinity:\n\t\treturn fmt.Errorf(\"cannot scan Infinity into *time.Time\")\n\tcase NegativeInfinity:\n\t\treturn fmt.Errorf(\"cannot scan -Infinity into *time.Time\")\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid InfinityModifier: %v\", v.InfinityModifier)\n\t}\n}\n\nfunc (w timeWrapper) TimestampValue() (Timestamp, error) {\n\treturn Timestamp{Time: time.Time(w), Valid: true}, nil\n}\n\nfunc (w *timeWrapper) ScanTimestamptz(v Timestamptz) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *time.Time\")\n\t}\n\n\tswitch v.InfinityModifier {\n\tcase Finite:\n\t\t*w = timeWrapper(v.Time)\n\t\treturn nil\n\tcase Infinity:\n\t\treturn fmt.Errorf(\"cannot scan Infinity into *time.Time\")\n\tcase NegativeInfinity:\n\t\treturn fmt.Errorf(\"cannot scan -Infinity into *time.Time\")\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid InfinityModifier: %v\", v.InfinityModifier)\n\t}\n}\n\nfunc (w timeWrapper) TimestamptzValue() (Timestamptz, error) {\n\treturn Timestamptz{Time: time.Time(w), Valid: true}, nil\n}\n\nfunc (w *timeWrapper) ScanTime(v Time) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *time.Time\")\n\t}\n\n\t// 24:00:00 is max allowed time in PostgreSQL, but time.Time will normalize that to 00:00:00 the next day.\n\tvar maxRepresentableByTime int64 = 24*60*60*1000000 - 1\n\tif v.Microseconds > maxRepresentableByTime {\n\t\treturn fmt.Errorf(\"%d microseconds cannot be represented as time.Time\", v.Microseconds)\n\t}\n\n\tusec := v.Microseconds\n\thours := usec / microsecondsPerHour\n\tusec -= hours * microsecondsPerHour\n\tminutes := usec / microsecondsPerMinute\n\tusec -= minutes * microsecondsPerMinute\n\tseconds := usec / microsecondsPerSecond\n\tusec -= seconds * microsecondsPerSecond\n\tns := usec * 1000\n\t*w = timeWrapper(time.Date(2000, 1, 1, int(hours), int(minutes), int(seconds), int(ns), time.UTC))\n\treturn nil\n}\n\nfunc (w timeWrapper) TimeValue() (Time, error) {\n\tt := time.Time(w)\n\tusec := int64(t.Hour())*microsecondsPerHour +\n\t\tint64(t.Minute())*microsecondsPerMinute +\n\t\tint64(t.Second())*microsecondsPerSecond +\n\t\tint64(t.Nanosecond())/1000\n\treturn Time{Microseconds: usec, Valid: true}, nil\n}\n\ntype durationWrapper time.Duration\n\nfunc (w durationWrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *durationWrapper) ScanInterval(v Interval) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *time.Interval\")\n\t}\n\n\tus := int64(v.Months)*microsecondsPerMonth + int64(v.Days)*microsecondsPerDay + v.Microseconds\n\t*w = durationWrapper(time.Duration(us) * time.Microsecond)\n\treturn nil\n}\n\nfunc (w durationWrapper) IntervalValue() (Interval, error) {\n\treturn Interval{Microseconds: int64(w) / 1000, Valid: true}, nil\n}\n\ntype netIPNetWrapper net.IPNet\n\nfunc (w *netIPNetWrapper) ScanNetipPrefix(v netip.Prefix) error {\n\tif !v.IsValid() {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *net.IPNet\")\n\t}\n\n\t*w = netIPNetWrapper{\n\t\tIP:   v.Addr().AsSlice(),\n\t\tMask: net.CIDRMask(v.Bits(), v.Addr().BitLen()),\n\t}\n\n\treturn nil\n}\n\nfunc (w netIPNetWrapper) NetipPrefixValue() (netip.Prefix, error) {\n\tip, ok := netip.AddrFromSlice(w.IP)\n\tif !ok {\n\t\treturn netip.Prefix{}, errors.New(\"invalid net.IPNet\")\n\t}\n\n\tones, _ := w.Mask.Size()\n\n\treturn netip.PrefixFrom(ip, ones), nil\n}\n\ntype netIPWrapper net.IP\n\nfunc (w netIPWrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *netIPWrapper) ScanNetipPrefix(v netip.Prefix) error {\n\tif !v.IsValid() {\n\t\t*w = nil\n\t\treturn nil\n\t}\n\n\tif v.Addr().BitLen() != v.Bits() {\n\t\treturn fmt.Errorf(\"cannot scan %v to *net.IP\", v)\n\t}\n\n\t*w = netIPWrapper(v.Addr().AsSlice())\n\treturn nil\n}\n\nfunc (w netIPWrapper) NetipPrefixValue() (netip.Prefix, error) {\n\tif w == nil {\n\t\treturn netip.Prefix{}, nil\n\t}\n\n\taddr, ok := netip.AddrFromSlice([]byte(w))\n\tif !ok {\n\t\treturn netip.Prefix{}, errors.New(\"invalid net.IP\")\n\t}\n\n\treturn netip.PrefixFrom(addr, addr.BitLen()), nil\n}\n\ntype netipPrefixWrapper netip.Prefix\n\nfunc (w *netipPrefixWrapper) ScanNetipPrefix(v netip.Prefix) error {\n\t*w = netipPrefixWrapper(v)\n\treturn nil\n}\n\nfunc (w netipPrefixWrapper) NetipPrefixValue() (netip.Prefix, error) {\n\treturn netip.Prefix(w), nil\n}\n\ntype netipAddrWrapper netip.Addr\n\nfunc (w *netipAddrWrapper) ScanNetipPrefix(v netip.Prefix) error {\n\tif !v.IsValid() {\n\t\t*w = netipAddrWrapper(netip.Addr{})\n\t\treturn nil\n\t}\n\n\tif v.Addr().BitLen() != v.Bits() {\n\t\treturn fmt.Errorf(\"cannot scan %v to netip.Addr\", v)\n\t}\n\n\t*w = netipAddrWrapper(v.Addr())\n\n\treturn nil\n}\n\nfunc (w netipAddrWrapper) NetipPrefixValue() (netip.Prefix, error) {\n\taddr := (netip.Addr)(w)\n\tif !addr.IsValid() {\n\t\treturn netip.Prefix{}, nil\n\t}\n\n\treturn netip.PrefixFrom(addr, addr.BitLen()), nil\n}\n\ntype mapStringToPointerStringWrapper map[string]*string\n\nfunc (w *mapStringToPointerStringWrapper) ScanHstore(v Hstore) error {\n\t*w = mapStringToPointerStringWrapper(v)\n\treturn nil\n}\n\nfunc (w mapStringToPointerStringWrapper) HstoreValue() (Hstore, error) {\n\treturn Hstore(w), nil\n}\n\ntype mapStringToStringWrapper map[string]string\n\nfunc (w *mapStringToStringWrapper) ScanHstore(v Hstore) error {\n\t*w = make(mapStringToStringWrapper, len(v))\n\tfor k, v := range v {\n\t\tif v == nil {\n\t\t\treturn fmt.Errorf(\"cannot scan NULL to string\")\n\t\t}\n\t\t(*w)[k] = *v\n\t}\n\treturn nil\n}\n\nfunc (w mapStringToStringWrapper) HstoreValue() (Hstore, error) {\n\tif w == nil {\n\t\treturn nil, nil\n\t}\n\n\thstore := make(Hstore, len(w))\n\tfor k, v := range w {\n\t\ts := v\n\t\thstore[k] = &s\n\t}\n\treturn hstore, nil\n}\n\ntype fmtStringerWrapper struct {\n\ts fmt.Stringer\n}\n\nfunc (w fmtStringerWrapper) TextValue() (Text, error) {\n\treturn Text{String: w.s.String(), Valid: true}, nil\n}\n\ntype byte16Wrapper [16]byte\n\nfunc (w *byte16Wrapper) ScanUUID(v UUID) error {\n\tif !v.Valid {\n\t\treturn fmt.Errorf(\"cannot scan NULL into *[16]byte\")\n\t}\n\t*w = byte16Wrapper(v.Bytes)\n\treturn nil\n}\n\nfunc (w byte16Wrapper) UUIDValue() (UUID, error) {\n\treturn UUID{Bytes: [16]byte(w), Valid: true}, nil\n}\n\ntype byteSliceWrapper []byte\n\nfunc (w byteSliceWrapper) SkipUnderlyingTypePlan() {}\n\nfunc (w *byteSliceWrapper) ScanText(v Text) error {\n\tif !v.Valid {\n\t\t*w = nil\n\t\treturn nil\n\t}\n\n\t*w = byteSliceWrapper(v.String)\n\treturn nil\n}\n\nfunc (w byteSliceWrapper) TextValue() (Text, error) {\n\tif w == nil {\n\t\treturn Text{}, nil\n\t}\n\n\treturn Text{String: string(w), Valid: true}, nil\n}\n\nfunc (w *byteSliceWrapper) ScanUUID(v UUID) error {\n\tif !v.Valid {\n\t\t*w = nil\n\t\treturn nil\n\t}\n\t*w = make(byteSliceWrapper, 16)\n\tcopy(*w, v.Bytes[:])\n\treturn nil\n}\n\nfunc (w byteSliceWrapper) UUIDValue() (UUID, error) {\n\tif w == nil {\n\t\treturn UUID{}, nil\n\t}\n\n\tuuid := UUID{Valid: true}\n\tcopy(uuid.Bytes[:], w)\n\treturn uuid, nil\n}\n\n// structWrapper implements CompositeIndexGetter for a struct.\ntype structWrapper struct {\n\ts              any\n\texportedFields []reflect.Value\n}\n\nfunc (w structWrapper) IsNull() bool {\n\treturn w.s == nil\n}\n\nfunc (w structWrapper) Index(i int) any {\n\tif i >= len(w.exportedFields) {\n\t\treturn fmt.Errorf(\"%#v only has %d public fields - %d is out of bounds\", w.s, len(w.exportedFields), i)\n\t}\n\n\treturn w.exportedFields[i].Interface()\n}\n\n// ptrStructWrapper implements CompositeIndexScanner for a pointer to a struct.\ntype ptrStructWrapper struct {\n\ts              any\n\texportedFields []reflect.Value\n}\n\nfunc (w *ptrStructWrapper) ScanNull() error {\n\treturn fmt.Errorf(\"cannot scan NULL into %#v\", w.s)\n}\n\nfunc (w *ptrStructWrapper) ScanIndex(i int) any {\n\tif i >= len(w.exportedFields) {\n\t\treturn fmt.Errorf(\"%#v only has %d public fields - %d is out of bounds\", w.s, len(w.exportedFields), i)\n\t}\n\n\treturn w.exportedFields[i].Addr().Interface()\n}\n\ntype anySliceArrayReflect struct {\n\tslice reflect.Value\n}\n\nfunc (a anySliceArrayReflect) Dimensions() []ArrayDimension {\n\tif a.slice.IsNil() {\n\t\treturn nil\n\t}\n\n\treturn []ArrayDimension{{Length: int32(a.slice.Len()), LowerBound: 1}}\n}\n\nfunc (a anySliceArrayReflect) Index(i int) any {\n\treturn a.slice.Index(i).Interface()\n}\n\nfunc (a anySliceArrayReflect) IndexType() any {\n\treturn reflect.New(a.slice.Type().Elem()).Elem().Interface()\n}\n\nfunc (a *anySliceArrayReflect) SetDimensions(dimensions []ArrayDimension) error {\n\tsliceType := a.slice.Type()\n\n\tif dimensions == nil {\n\t\ta.slice.Set(reflect.Zero(sliceType))\n\t\treturn nil\n\t}\n\n\telementCount := cardinality(dimensions)\n\tslice := reflect.MakeSlice(sliceType, elementCount, elementCount)\n\ta.slice.Set(slice)\n\treturn nil\n}\n\nfunc (a *anySliceArrayReflect) ScanIndex(i int) any {\n\treturn a.slice.Index(i).Addr().Interface()\n}\n\nfunc (a *anySliceArrayReflect) ScanIndexType() any {\n\treturn reflect.New(a.slice.Type().Elem()).Interface()\n}\n\ntype anyMultiDimSliceArray struct {\n\tslice reflect.Value\n\tdims  []ArrayDimension\n}\n\nfunc (a *anyMultiDimSliceArray) Dimensions() []ArrayDimension {\n\tif a.slice.IsNil() {\n\t\treturn nil\n\t}\n\n\ts := a.slice\n\tfor {\n\t\ta.dims = append(a.dims, ArrayDimension{Length: int32(s.Len()), LowerBound: 1})\n\t\tif s.Len() > 0 {\n\t\t\ts = s.Index(0)\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t\tif s.Type().Kind() == reflect.Slice {\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn a.dims\n}\n\nfunc (a *anyMultiDimSliceArray) Index(i int) any {\n\tif len(a.dims) == 1 {\n\t\treturn a.slice.Index(i).Interface()\n\t}\n\n\tindexes := make([]int, len(a.dims))\n\tfor j := len(a.dims) - 1; j >= 0; j-- {\n\t\tdimLen := int(a.dims[j].Length)\n\t\tindexes[j] = i % dimLen\n\t\ti = i / dimLen\n\t}\n\n\tv := a.slice\n\tfor _, si := range indexes {\n\t\tv = v.Index(si)\n\t}\n\n\treturn v.Interface()\n}\n\nfunc (a *anyMultiDimSliceArray) IndexType() any {\n\tlowestSliceType := a.slice.Type()\n\tfor ; lowestSliceType.Elem().Kind() == reflect.Slice; lowestSliceType = lowestSliceType.Elem() {\n\t}\n\treturn reflect.New(lowestSliceType.Elem()).Elem().Interface()\n}\n\nfunc (a *anyMultiDimSliceArray) SetDimensions(dimensions []ArrayDimension) error {\n\tsliceType := a.slice.Type()\n\n\tif dimensions == nil {\n\t\ta.slice.Set(reflect.Zero(sliceType))\n\t\treturn nil\n\t}\n\n\tswitch len(dimensions) {\n\tcase 0:\n\t\t// Empty, but non-nil array\n\t\tslice := reflect.MakeSlice(sliceType, 0, 0)\n\t\ta.slice.Set(slice)\n\t\treturn nil\n\tcase 1:\n\t\telementCount := cardinality(dimensions)\n\t\tslice := reflect.MakeSlice(sliceType, elementCount, elementCount)\n\t\ta.slice.Set(slice)\n\t\treturn nil\n\tdefault:\n\t\tsliceDimensionCount := 1\n\t\tlowestSliceType := sliceType\n\t\tfor ; lowestSliceType.Elem().Kind() == reflect.Slice; lowestSliceType = lowestSliceType.Elem() {\n\t\t\tsliceDimensionCount++\n\t\t}\n\n\t\tif sliceDimensionCount != len(dimensions) {\n\t\t\treturn fmt.Errorf(\"PostgreSQL array has %d dimensions but slice has %d dimensions\", len(dimensions), sliceDimensionCount)\n\t\t}\n\n\t\telementCount := cardinality(dimensions)\n\t\tflatSlice := reflect.MakeSlice(lowestSliceType, elementCount, elementCount)\n\n\t\tmultiDimSlice := a.makeMultidimensionalSlice(sliceType, dimensions, flatSlice, 0)\n\t\ta.slice.Set(multiDimSlice)\n\n\t\t// Now that a.slice is a multi-dimensional slice with the underlying data pointed at flatSlice change a.slice to\n\t\t// flatSlice so ScanIndex only has to handle simple one dimensional slices.\n\t\ta.slice = flatSlice\n\n\t\treturn nil\n\t}\n}\n\nfunc (a *anyMultiDimSliceArray) makeMultidimensionalSlice(sliceType reflect.Type, dimensions []ArrayDimension, flatSlice reflect.Value, flatSliceIdx int) reflect.Value {\n\tif len(dimensions) == 1 {\n\t\tendIdx := flatSliceIdx + int(dimensions[0].Length)\n\t\treturn flatSlice.Slice3(flatSliceIdx, endIdx, endIdx)\n\t}\n\n\tsliceLen := int(dimensions[0].Length)\n\tslice := reflect.MakeSlice(sliceType, sliceLen, sliceLen)\n\tfor i := range sliceLen {\n\t\tsubSlice := a.makeMultidimensionalSlice(sliceType.Elem(), dimensions[1:], flatSlice, flatSliceIdx+(i*int(dimensions[1].Length)))\n\t\tslice.Index(i).Set(subSlice)\n\t}\n\n\treturn slice\n}\n\nfunc (a *anyMultiDimSliceArray) ScanIndex(i int) any {\n\treturn a.slice.Index(i).Addr().Interface()\n}\n\nfunc (a *anyMultiDimSliceArray) ScanIndexType() any {\n\tlowestSliceType := a.slice.Type()\n\tfor ; lowestSliceType.Elem().Kind() == reflect.Slice; lowestSliceType = lowestSliceType.Elem() {\n\t}\n\treturn reflect.New(lowestSliceType.Elem()).Interface()\n}\n\ntype anyArrayArrayReflect struct {\n\tarray reflect.Value\n}\n\nfunc (a anyArrayArrayReflect) Dimensions() []ArrayDimension {\n\treturn []ArrayDimension{{Length: int32(a.array.Len()), LowerBound: 1}}\n}\n\nfunc (a anyArrayArrayReflect) Index(i int) any {\n\treturn a.array.Index(i).Interface()\n}\n\nfunc (a anyArrayArrayReflect) IndexType() any {\n\treturn reflect.New(a.array.Type().Elem()).Elem().Interface()\n}\n\nfunc (a *anyArrayArrayReflect) SetDimensions(dimensions []ArrayDimension) error {\n\tif dimensions == nil {\n\t\treturn fmt.Errorf(\"anyArrayArrayReflect: cannot scan NULL into %v\", a.array.Type().String())\n\t}\n\n\tif len(dimensions) != 1 {\n\t\treturn fmt.Errorf(\"anyArrayArrayReflect: cannot scan multi-dimensional array into %v\", a.array.Type().String())\n\t}\n\n\tif int(dimensions[0].Length) != a.array.Len() {\n\t\treturn fmt.Errorf(\"anyArrayArrayReflect: cannot scan array with length %v into %v\", dimensions[0].Length, a.array.Type().String())\n\t}\n\n\treturn nil\n}\n\nfunc (a *anyArrayArrayReflect) ScanIndex(i int) any {\n\treturn a.array.Index(i).Addr().Interface()\n}\n\nfunc (a *anyArrayArrayReflect) ScanIndexType() any {\n\treturn reflect.New(a.array.Type().Elem()).Interface()\n}\n"
  },
  {
    "path": "pgtype/bytea.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/hex\"\n\t\"fmt\"\n)\n\ntype BytesScanner interface {\n\t// ScanBytes receives a byte slice of driver memory that is only valid until the next database method call.\n\tScanBytes(v []byte) error\n}\n\ntype BytesValuer interface {\n\t// BytesValue returns a byte slice of the byte data. The caller must not change the returned slice.\n\tBytesValue() ([]byte, error)\n}\n\n// DriverBytes is a byte slice that holds a reference to memory owned by the driver. It is only valid from the time it\n// is scanned until Rows.Next or Rows.Close is called. It is never safe to use DriverBytes with QueryRow as Row.Scan\n// internally calls Rows.Close before returning.\ntype DriverBytes []byte\n\nfunc (b *DriverBytes) ScanBytes(v []byte) error {\n\t*b = v\n\treturn nil\n}\n\n// PreallocBytes is a byte slice of preallocated memory that scanned bytes will be copied to. If it is too small a new\n// slice will be allocated.\ntype PreallocBytes []byte\n\nfunc (b *PreallocBytes) ScanBytes(v []byte) error {\n\tif v == nil {\n\t\t*b = nil\n\t\treturn nil\n\t}\n\n\tif len(v) <= len(*b) {\n\t\t*b = (*b)[:len(v)]\n\t} else {\n\t\t*b = make(PreallocBytes, len(v))\n\t}\n\tcopy(*b, v)\n\treturn nil\n}\n\n// UndecodedBytes can be used as a scan target to get the raw bytes from PostgreSQL without any decoding.\ntype UndecodedBytes []byte\n\ntype scanPlanAnyToUndecodedBytes struct{}\n\nfunc (scanPlanAnyToUndecodedBytes) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*UndecodedBytes)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\t*dstBuf = make([]byte, len(src))\n\tcopy(*dstBuf, src)\n\treturn nil\n}\n\ntype ByteaCodec struct{}\n\nfunc (ByteaCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (ByteaCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (ByteaCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase []byte:\n\t\t\treturn encodePlanBytesCodecBinaryBytes{}\n\t\tcase BytesValuer:\n\t\t\treturn encodePlanBytesCodecBinaryBytesValuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase []byte:\n\t\t\treturn encodePlanBytesCodecTextBytes{}\n\t\tcase BytesValuer:\n\t\t\treturn encodePlanBytesCodecTextBytesValuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanBytesCodecBinaryBytes struct{}\n\nfunc (encodePlanBytesCodecBinaryBytes) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb := value.([]byte)\n\tif b == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, b...), nil\n}\n\ntype encodePlanBytesCodecBinaryBytesValuer struct{}\n\nfunc (encodePlanBytesCodecBinaryBytesValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb, err := value.(BytesValuer).BytesValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif b == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, b...), nil\n}\n\ntype encodePlanBytesCodecTextBytes struct{}\n\nfunc (encodePlanBytesCodecTextBytes) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb := value.([]byte)\n\tif b == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, `\\x`...)\n\tbuf = append(buf, hex.EncodeToString(b)...)\n\treturn buf, nil\n}\n\ntype encodePlanBytesCodecTextBytesValuer struct{}\n\nfunc (encodePlanBytesCodecTextBytesValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb, err := value.(BytesValuer).BytesValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif b == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, `\\x`...)\n\tbuf = append(buf, hex.EncodeToString(b)...)\n\treturn buf, nil\n}\n\nfunc (ByteaCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *[]byte:\n\t\t\treturn scanPlanBinaryBytesToBytes{}\n\t\tcase BytesScanner:\n\t\t\treturn scanPlanBinaryBytesToBytesScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *[]byte:\n\t\t\treturn scanPlanTextByteaToBytes{}\n\t\tcase BytesScanner:\n\t\t\treturn scanPlanTextByteaToBytesScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryBytesToBytes struct{}\n\nfunc (scanPlanBinaryBytesToBytes) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*[]byte)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\t*dstBuf = make([]byte, len(src))\n\tcopy(*dstBuf, src)\n\treturn nil\n}\n\ntype scanPlanBinaryBytesToBytesScanner struct{}\n\nfunc (scanPlanBinaryBytesToBytesScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(BytesScanner)\n\treturn scanner.ScanBytes(src)\n}\n\ntype scanPlanTextByteaToBytes struct{}\n\nfunc (scanPlanTextByteaToBytes) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*[]byte)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\tbuf, err := decodeHexBytea(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*dstBuf = buf\n\n\treturn nil\n}\n\ntype scanPlanTextByteaToBytesScanner struct{}\n\nfunc (scanPlanTextByteaToBytesScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(BytesScanner)\n\tbuf, err := decodeHexBytea(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn scanner.ScanBytes(buf)\n}\n\nfunc decodeHexBytea(src []byte) ([]byte, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tif len(src) < 2 || src[0] != '\\\\' || src[1] != 'x' {\n\t\treturn nil, fmt.Errorf(\"invalid hex format\")\n\t}\n\n\tbuf := make([]byte, (len(src)-2)/2)\n\t_, err := hex.Decode(buf, src[2:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf, nil\n}\n\nfunc (c ByteaCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn c.DecodeValue(m, oid, format, src)\n}\n\nfunc (c ByteaCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar buf []byte\n\terr := codecScan(c, m, oid, format, src, &buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf, nil\n}\n"
  },
  {
    "path": "pgtype/bytea_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc isExpectedEqBytes(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tab := a.([]byte)\n\t\tvb := v.([]byte)\n\n\t\tif (ab == nil) != (vb == nil) {\n\t\t\treturn false\n\t\t}\n\n\t\tif ab == nil {\n\t\t\treturn true\n\t\t}\n\n\t\treturn bytes.Equal(ab, vb)\n\t}\n}\n\nfunc TestByteaCodec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"bytea\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: []byte{1, 2, 3}, Result: new([]byte), Test: isExpectedEqBytes([]byte{1, 2, 3})},\n\t\t{Param: []byte{}, Result: new([]byte), Test: isExpectedEqBytes([]byte{})},\n\t\t{Param: []byte(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: nil, Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t})\n}\n\nfunc TestDriverBytesQueryRow(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar buf []byte\n\t\terr := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.DriverBytes)(&buf))\n\t\trequire.EqualError(t, err, \"cannot scan into *pgtype.DriverBytes from QueryRow\")\n\t})\n}\n\nfunc TestDriverBytes(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\targBuf := make([]byte, 128)\n\t\tfor i := range argBuf {\n\t\t\targBuf[i] = byte(i)\n\t\t}\n\n\t\trows, err := conn.Query(ctx, `select $1::bytea from generate_series(1, 1000)`, argBuf)\n\t\trequire.NoError(t, err)\n\t\tdefer rows.Close()\n\n\t\trowCount := 0\n\t\tresultBuf := argBuf\n\t\tdetectedResultMutation := false\n\t\tfor rows.Next() {\n\t\t\trowCount++\n\n\t\t\t// At some point the buffer should be reused and change.\n\t\t\tif !bytes.Equal(argBuf, resultBuf) {\n\t\t\t\tdetectedResultMutation = true\n\t\t\t}\n\n\t\t\terr = rows.Scan((*pgtype.DriverBytes)(&resultBuf))\n\t\t\trequire.NoError(t, err)\n\n\t\t\trequire.Len(t, resultBuf, len(argBuf))\n\t\t\trequire.Equal(t, resultBuf, argBuf)\n\t\t\trequire.Equalf(t, cap(resultBuf), len(resultBuf), \"cap(resultBuf) is larger than len(resultBuf)\")\n\t\t}\n\n\t\trequire.True(t, detectedResultMutation)\n\n\t\terr = rows.Err()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestPreallocBytes(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\torigBuf := []byte{5, 6, 7, 8}\n\t\tbuf := origBuf\n\t\terr := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.PreallocBytes)(&buf))\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, buf, 2)\n\t\trequire.Equal(t, 4, cap(buf))\n\t\trequire.Equal(t, []byte{1, 2}, buf)\n\n\t\trequire.Equal(t, []byte{1, 2, 7, 8}, origBuf)\n\n\t\terr = conn.QueryRow(ctx, `select $1::bytea`, []byte{3, 4, 5, 6, 7}).Scan((*pgtype.PreallocBytes)(&buf))\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, buf, 5)\n\t\trequire.Equal(t, 5, cap(buf))\n\n\t\trequire.Equal(t, []byte{1, 2, 7, 8}, origBuf)\n\t})\n}\n\nfunc TestUndecodedBytes(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar buf []byte\n\t\terr := conn.QueryRow(ctx, `select 1::int4`).Scan((*pgtype.UndecodedBytes)(&buf))\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, buf, 4)\n\t\trequire.Equal(t, []byte{0, 0, 0, 1}, buf)\n\t})\n}\n\nfunc TestByteaCodecDecodeDatabaseSQLValue(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar buf []byte\n\t\terr := conn.QueryRow(ctx, `select '\\xa1b2c3d4'::bytea`).Scan(sqlScannerFunc(func(src any) error {\n\t\t\tswitch src := src.(type) {\n\t\t\tcase []byte:\n\t\t\t\tbuf = make([]byte, len(src))\n\t\t\t\tcopy(buf, src)\n\t\t\t\treturn nil\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"expected []byte, got %T\", src)\n\t\t\t}\n\t\t}))\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, buf, 4)\n\t\trequire.Equal(t, []byte{0xa1, 0xb2, 0xc3, 0xd4}, buf)\n\t})\n}\n"
  },
  {
    "path": "pgtype/circle.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype CircleScanner interface {\n\tScanCircle(v Circle) error\n}\n\ntype CircleValuer interface {\n\tCircleValue() (Circle, error)\n}\n\ntype Circle struct {\n\tP     Vec2\n\tR     float64\n\tValid bool\n}\n\n// ScanCircle implements the [CircleScanner] interface.\nfunc (c *Circle) ScanCircle(v Circle) error {\n\t*c = v\n\treturn nil\n}\n\n// CircleValue implements the [CircleValuer] interface.\nfunc (c Circle) CircleValue() (Circle, error) {\n\treturn c, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Circle) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Circle{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToCircleScanner{}.Scan([]byte(src), dst)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Circle) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := CircleCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype CircleCodec struct{}\n\nfunc (CircleCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (CircleCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (CircleCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(CircleValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanCircleCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanCircleCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanCircleCodecBinary struct{}\n\nfunc (encodePlanCircleCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tcircle, err := value.(CircleValuer).CircleValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !circle.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(circle.P.X))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(circle.P.Y))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(circle.R))\n\treturn buf, nil\n}\n\ntype encodePlanCircleCodecText struct{}\n\nfunc (encodePlanCircleCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tcircle, err := value.(CircleValuer).CircleValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !circle.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, fmt.Sprintf(`<(%s,%s),%s>`,\n\t\tstrconv.FormatFloat(circle.P.X, 'f', -1, 64),\n\t\tstrconv.FormatFloat(circle.P.Y, 'f', -1, 64),\n\t\tstrconv.FormatFloat(circle.R, 'f', -1, 64),\n\t)...)\n\treturn buf, nil\n}\n\nfunc (CircleCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase CircleScanner:\n\t\t\treturn scanPlanBinaryCircleToCircleScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase CircleScanner:\n\t\t\treturn scanPlanTextAnyToCircleScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c CircleCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c CircleCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar circle Circle\n\terr := codecScan(c, m, oid, format, src, &circle)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn circle, nil\n}\n\ntype scanPlanBinaryCircleToCircleScanner struct{}\n\nfunc (scanPlanBinaryCircleToCircleScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(CircleScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanCircle(Circle{})\n\t}\n\n\tif len(src) != 24 {\n\t\treturn fmt.Errorf(\"invalid length for Circle: %v\", len(src))\n\t}\n\n\tx := binary.BigEndian.Uint64(src)\n\ty := binary.BigEndian.Uint64(src[8:])\n\tr := binary.BigEndian.Uint64(src[16:])\n\n\treturn scanner.ScanCircle(Circle{\n\t\tP:     Vec2{math.Float64frombits(x), math.Float64frombits(y)},\n\t\tR:     math.Float64frombits(r),\n\t\tValid: true,\n\t})\n}\n\ntype scanPlanTextAnyToCircleScanner struct{}\n\nfunc (scanPlanTextAnyToCircleScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(CircleScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanCircle(Circle{})\n\t}\n\n\tif len(src) < 9 {\n\t\treturn fmt.Errorf(\"invalid length for Circle: %v\", len(src))\n\t}\n\n\tstr := string(src[2:])\n\tend := strings.IndexByte(str, ',')\n\tx, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+1:]\n\tend = strings.IndexByte(str, ')')\n\n\ty, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+2 : len(str)-1]\n\n\tr, err := strconv.ParseFloat(str, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanCircle(Circle{P: Vec2{x, y}, R: r, Valid: true})\n}\n"
  },
  {
    "path": "pgtype/circle_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestCircleTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support box type\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"circle\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true},\n\t\t\tResult: new(pgtype.Circle),\n\t\t\tTest:   isExpectedEq(pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true},\n\t\t\tResult: new(pgtype.Circle),\n\t\t\tTest:   isExpectedEq(pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Circle{}, Result: new(pgtype.Circle), Test: isExpectedEq(pgtype.Circle{})},\n\t\t{Param: nil, Result: new(pgtype.Circle), Test: isExpectedEq(pgtype.Circle{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/composite.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// CompositeIndexGetter is a type accessed by index that can be converted into a PostgreSQL composite.\ntype CompositeIndexGetter interface {\n\t// IsNull returns true if the value is SQL NULL.\n\tIsNull() bool\n\n\t// Index returns the element at i.\n\tIndex(i int) any\n}\n\n// CompositeIndexScanner is a type accessed by index that can be scanned from a PostgreSQL composite.\ntype CompositeIndexScanner interface {\n\t// ScanNull sets the value to SQL NULL.\n\tScanNull() error\n\n\t// ScanIndex returns a value usable as a scan target for i.\n\tScanIndex(i int) any\n}\n\ntype CompositeCodecField struct {\n\tName string\n\tType *Type\n}\n\ntype CompositeCodec struct {\n\tFields []CompositeCodecField\n}\n\nfunc (c *CompositeCodec) FormatSupported(format int16) bool {\n\tfor _, f := range c.Fields {\n\t\tif !f.Type.Codec.FormatSupported(format) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc (c *CompositeCodec) PreferredFormat() int16 {\n\tif c.FormatSupported(BinaryFormatCode) {\n\t\treturn BinaryFormatCode\n\t}\n\treturn TextFormatCode\n}\n\nfunc (c *CompositeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(CompositeIndexGetter); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn &encodePlanCompositeCodecCompositeIndexGetterToBinary{cc: c, m: m}\n\tcase TextFormatCode:\n\t\treturn &encodePlanCompositeCodecCompositeIndexGetterToText{cc: c, m: m}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanCompositeCodecCompositeIndexGetterToBinary struct {\n\tcc *CompositeCodec\n\tm  *Map\n}\n\nfunc (plan *encodePlanCompositeCodecCompositeIndexGetterToBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tgetter := value.(CompositeIndexGetter)\n\n\tif getter.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\tbuilder := NewCompositeBinaryBuilder(plan.m, buf)\n\tfor i, field := range plan.cc.Fields {\n\t\tbuilder.AppendValue(field.Type.OID, getter.Index(i))\n\t}\n\n\treturn builder.Finish()\n}\n\ntype encodePlanCompositeCodecCompositeIndexGetterToText struct {\n\tcc *CompositeCodec\n\tm  *Map\n}\n\nfunc (plan *encodePlanCompositeCodecCompositeIndexGetterToText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tgetter := value.(CompositeIndexGetter)\n\n\tif getter.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\tb := NewCompositeTextBuilder(plan.m, buf)\n\tfor i, field := range plan.cc.Fields {\n\t\tb.AppendValue(field.Type.OID, getter.Index(i))\n\t}\n\n\treturn b.Finish()\n}\n\nfunc (c *CompositeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase CompositeIndexScanner:\n\t\t\treturn &scanPlanBinaryCompositeToCompositeIndexScanner{cc: c, m: m}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase CompositeIndexScanner:\n\t\t\treturn &scanPlanTextCompositeToCompositeIndexScanner{cc: c, m: m}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryCompositeToCompositeIndexScanner struct {\n\tcc *CompositeCodec\n\tm  *Map\n}\n\nfunc (plan *scanPlanBinaryCompositeToCompositeIndexScanner) Scan(src []byte, target any) error {\n\ttargetScanner := (target).(CompositeIndexScanner)\n\n\tif src == nil {\n\t\treturn targetScanner.ScanNull()\n\t}\n\n\tscanner := NewCompositeBinaryScanner(plan.m, src)\n\tfor i, field := range plan.cc.Fields {\n\t\tif scanner.Next() {\n\t\t\tfieldTarget := targetScanner.ScanIndex(i)\n\t\t\tif fieldTarget != nil {\n\t\t\t\tfieldPlan := plan.m.PlanScan(field.Type.OID, BinaryFormatCode, fieldTarget)\n\t\t\t\tif fieldPlan == nil {\n\t\t\t\t\treturn fmt.Errorf(\"unable to encode %v into OID %d in binary format\", field, field.Type.OID)\n\t\t\t\t}\n\n\t\t\t\terr := fieldPlan.Scan(scanner.Bytes(), fieldTarget)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"read past end of composite\")\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\ntype scanPlanTextCompositeToCompositeIndexScanner struct {\n\tcc *CompositeCodec\n\tm  *Map\n}\n\nfunc (plan *scanPlanTextCompositeToCompositeIndexScanner) Scan(src []byte, target any) error {\n\ttargetScanner := (target).(CompositeIndexScanner)\n\n\tif src == nil {\n\t\treturn targetScanner.ScanNull()\n\t}\n\n\tscanner := NewCompositeTextScanner(plan.m, src)\n\tfor i, field := range plan.cc.Fields {\n\t\tif scanner.Next() {\n\t\t\tfieldTarget := targetScanner.ScanIndex(i)\n\t\t\tif fieldTarget != nil {\n\t\t\t\tfieldPlan := plan.m.PlanScan(field.Type.OID, TextFormatCode, fieldTarget)\n\t\t\t\tif fieldPlan == nil {\n\t\t\t\t\treturn fmt.Errorf(\"unable to encode %v into OID %d in text format\", field, field.Type.OID)\n\t\t\t\t}\n\n\t\t\t\terr := fieldPlan.Scan(scanner.Bytes(), fieldTarget)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn errors.New(\"read past end of composite\")\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (c *CompositeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn string(src), nil\n\tcase BinaryFormatCode:\n\t\tbuf := make([]byte, len(src))\n\t\tcopy(buf, src)\n\t\treturn buf, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n\nfunc (c *CompositeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\tscanner := NewCompositeTextScanner(m, src)\n\t\tvalues := make(map[string]any, len(c.Fields))\n\t\tfor i := 0; scanner.Next() && i < len(c.Fields); i++ {\n\t\t\tvar v any\n\t\t\tfieldPlan := m.PlanScan(c.Fields[i].Type.OID, TextFormatCode, &v)\n\t\t\tif fieldPlan == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to scan OID %d in text format into %v\", c.Fields[i].Type.OID, v)\n\t\t\t}\n\n\t\t\terr := fieldPlan.Scan(scanner.Bytes(), &v)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tvalues[c.Fields[i].Name] = v\n\t\t}\n\n\t\tif err := scanner.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn values, nil\n\tcase BinaryFormatCode:\n\t\tscanner := NewCompositeBinaryScanner(m, src)\n\t\tvalues := make(map[string]any, len(c.Fields))\n\t\tfor i := 0; scanner.Next() && i < len(c.Fields); i++ {\n\t\t\tvar v any\n\t\t\tfieldPlan := m.PlanScan(scanner.OID(), BinaryFormatCode, &v)\n\t\t\tif fieldPlan == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to scan OID %d in binary format into %v\", scanner.OID(), v)\n\t\t\t}\n\n\t\t\terr := fieldPlan.Scan(scanner.Bytes(), &v)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tvalues[c.Fields[i].Name] = v\n\t\t}\n\n\t\tif err := scanner.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn values, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n\ntype CompositeBinaryScanner struct {\n\tm   *Map\n\trp  int\n\tsrc []byte\n\n\tfieldCount int32\n\tfieldBytes []byte\n\tfieldOID   uint32\n\terr        error\n}\n\n// NewCompositeBinaryScanner a scanner over a binary encoded composite value.\nfunc NewCompositeBinaryScanner(m *Map, src []byte) *CompositeBinaryScanner {\n\trp := 0\n\tif len(src[rp:]) < 4 {\n\t\treturn &CompositeBinaryScanner{err: fmt.Errorf(\"Record incomplete %v\", src)}\n\t}\n\n\tfieldCount := int32(binary.BigEndian.Uint32(src[rp:]))\n\trp += 4\n\n\treturn &CompositeBinaryScanner{\n\t\tm:          m,\n\t\trp:         rp,\n\t\tsrc:        src,\n\t\tfieldCount: fieldCount,\n\t}\n}\n\n// Next advances the scanner to the next field. It returns false after the last field is read or an error occurs. After\n// Next returns false, the Err method can be called to check if any errors occurred.\nfunc (cfs *CompositeBinaryScanner) Next() bool {\n\tif cfs.err != nil {\n\t\treturn false\n\t}\n\n\tif cfs.rp == len(cfs.src) {\n\t\treturn false\n\t}\n\n\tif len(cfs.src[cfs.rp:]) < 8 {\n\t\tcfs.err = fmt.Errorf(\"Record incomplete %v\", cfs.src)\n\t\treturn false\n\t}\n\tcfs.fieldOID = binary.BigEndian.Uint32(cfs.src[cfs.rp:])\n\tcfs.rp += 4\n\n\tfieldLen := int(int32(binary.BigEndian.Uint32(cfs.src[cfs.rp:])))\n\tcfs.rp += 4\n\n\tif fieldLen >= 0 {\n\t\tif len(cfs.src[cfs.rp:]) < fieldLen {\n\t\t\tcfs.err = fmt.Errorf(\"Record incomplete rp=%d src=%v\", cfs.rp, cfs.src)\n\t\t\treturn false\n\t\t}\n\t\tcfs.fieldBytes = cfs.src[cfs.rp : cfs.rp+fieldLen]\n\t\tcfs.rp += fieldLen\n\t} else {\n\t\tcfs.fieldBytes = nil\n\t}\n\n\treturn true\n}\n\nfunc (cfs *CompositeBinaryScanner) FieldCount() int {\n\treturn int(cfs.fieldCount)\n}\n\n// Bytes returns the bytes of the field most recently read by Scan().\nfunc (cfs *CompositeBinaryScanner) Bytes() []byte {\n\treturn cfs.fieldBytes\n}\n\n// OID returns the OID of the field most recently read by Scan().\nfunc (cfs *CompositeBinaryScanner) OID() uint32 {\n\treturn cfs.fieldOID\n}\n\n// Err returns any error encountered by the scanner.\nfunc (cfs *CompositeBinaryScanner) Err() error {\n\treturn cfs.err\n}\n\ntype CompositeTextScanner struct {\n\tm   *Map\n\trp  int\n\tsrc []byte\n\n\tfieldBytes []byte\n\terr        error\n}\n\n// NewCompositeTextScanner a scanner over a text encoded composite value.\nfunc NewCompositeTextScanner(m *Map, src []byte) *CompositeTextScanner {\n\tif len(src) < 2 {\n\t\treturn &CompositeTextScanner{err: fmt.Errorf(\"Record incomplete %v\", src)}\n\t}\n\n\tif src[0] != '(' {\n\t\treturn &CompositeTextScanner{err: fmt.Errorf(\"composite text format must start with '('\")}\n\t}\n\n\tif src[len(src)-1] != ')' {\n\t\treturn &CompositeTextScanner{err: fmt.Errorf(\"composite text format must end with ')'\")}\n\t}\n\n\treturn &CompositeTextScanner{\n\t\tm:   m,\n\t\trp:  1,\n\t\tsrc: src,\n\t}\n}\n\n// Next advances the scanner to the next field. It returns false after the last field is read or an error occurs. After\n// Next returns false, the Err method can be called to check if any errors occurred.\nfunc (cfs *CompositeTextScanner) Next() bool {\n\tif cfs.err != nil {\n\t\treturn false\n\t}\n\n\tif cfs.rp == len(cfs.src) {\n\t\treturn false\n\t}\n\n\tswitch cfs.src[cfs.rp] {\n\tcase ',', ')': // null\n\t\tcfs.rp++\n\t\tcfs.fieldBytes = nil\n\t\treturn true\n\tcase '\"': // quoted value\n\t\tcfs.rp++\n\t\tcfs.fieldBytes = make([]byte, 0, 16)\n\t\tfor {\n\t\t\tch := cfs.src[cfs.rp]\n\n\t\t\tif ch == '\"' {\n\t\t\t\tcfs.rp++\n\t\t\t\tif cfs.src[cfs.rp] == '\"' {\n\t\t\t\t\tcfs.fieldBytes = append(cfs.fieldBytes, '\"')\n\t\t\t\t\tcfs.rp++\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t} else if ch == '\\\\' {\n\t\t\t\tcfs.rp++\n\t\t\t\tcfs.fieldBytes = append(cfs.fieldBytes, cfs.src[cfs.rp])\n\t\t\t\tcfs.rp++\n\t\t\t} else {\n\t\t\t\tcfs.fieldBytes = append(cfs.fieldBytes, ch)\n\t\t\t\tcfs.rp++\n\t\t\t}\n\t\t}\n\t\tcfs.rp++\n\t\treturn true\n\tdefault: // unquoted value\n\t\tstart := cfs.rp\n\t\tfor {\n\t\t\tch := cfs.src[cfs.rp]\n\t\t\tif ch == ',' || ch == ')' {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcfs.rp++\n\t\t}\n\t\tcfs.fieldBytes = cfs.src[start:cfs.rp]\n\t\tcfs.rp++\n\t\treturn true\n\t}\n}\n\n// Bytes returns the bytes of the field most recently read by Scan().\nfunc (cfs *CompositeTextScanner) Bytes() []byte {\n\treturn cfs.fieldBytes\n}\n\n// Err returns any error encountered by the scanner.\nfunc (cfs *CompositeTextScanner) Err() error {\n\treturn cfs.err\n}\n\ntype CompositeBinaryBuilder struct {\n\tm          *Map\n\tbuf        []byte\n\tstartIdx   int\n\tfieldCount uint32\n\terr        error\n}\n\nfunc NewCompositeBinaryBuilder(m *Map, buf []byte) *CompositeBinaryBuilder {\n\tstartIdx := len(buf)\n\tbuf = append(buf, 0, 0, 0, 0) // allocate room for number of fields\n\treturn &CompositeBinaryBuilder{m: m, buf: buf, startIdx: startIdx}\n}\n\nfunc (b *CompositeBinaryBuilder) AppendValue(oid uint32, field any) {\n\tif b.err != nil {\n\t\treturn\n\t}\n\n\tif isNil, _ := isNilDriverValuer(field); isNil {\n\t\tb.buf = pgio.AppendUint32(b.buf, oid)\n\t\tb.buf = pgio.AppendInt32(b.buf, -1)\n\t\tb.fieldCount++\n\t\treturn\n\t}\n\n\tplan := b.m.PlanEncode(oid, BinaryFormatCode, field)\n\tif plan == nil {\n\t\tb.err = fmt.Errorf(\"unable to encode %v into OID %d in binary format\", field, oid)\n\t\treturn\n\t}\n\n\tb.buf = pgio.AppendUint32(b.buf, oid)\n\tlengthPos := len(b.buf)\n\tb.buf = pgio.AppendInt32(b.buf, -1)\n\tfieldBuf, err := plan.Encode(field, b.buf)\n\tif err != nil {\n\t\tb.err = err\n\t\treturn\n\t}\n\tif fieldBuf != nil {\n\t\tbinary.BigEndian.PutUint32(fieldBuf[lengthPos:], uint32(len(fieldBuf)-len(b.buf)))\n\t\tb.buf = fieldBuf\n\t}\n\n\tb.fieldCount++\n}\n\nfunc (b *CompositeBinaryBuilder) Finish() ([]byte, error) {\n\tif b.err != nil {\n\t\treturn nil, b.err\n\t}\n\n\tbinary.BigEndian.PutUint32(b.buf[b.startIdx:], b.fieldCount)\n\treturn b.buf, nil\n}\n\ntype CompositeTextBuilder struct {\n\tm          *Map\n\tbuf        []byte\n\tstartIdx   int\n\tfieldCount uint32\n\terr        error\n\tfieldBuf   [32]byte\n}\n\nfunc NewCompositeTextBuilder(m *Map, buf []byte) *CompositeTextBuilder {\n\tbuf = append(buf, '(') // allocate room for number of fields\n\treturn &CompositeTextBuilder{m: m, buf: buf}\n}\n\nfunc (b *CompositeTextBuilder) AppendValue(oid uint32, field any) {\n\tif b.err != nil {\n\t\treturn\n\t}\n\n\tif isNil, _ := isNilDriverValuer(field); isNil {\n\t\tb.buf = append(b.buf, ',')\n\t\treturn\n\t}\n\n\tplan := b.m.PlanEncode(oid, TextFormatCode, field)\n\tif plan == nil {\n\t\tb.err = fmt.Errorf(\"unable to encode %v into OID %d in text format\", field, oid)\n\t\treturn\n\t}\n\n\tfieldBuf, err := plan.Encode(field, b.fieldBuf[0:0])\n\tif err != nil {\n\t\tb.err = err\n\t\treturn\n\t}\n\tif fieldBuf != nil {\n\t\tb.buf = append(b.buf, quoteCompositeFieldIfNeeded(string(fieldBuf))...)\n\t}\n\n\tb.buf = append(b.buf, ',')\n}\n\nfunc (b *CompositeTextBuilder) Finish() ([]byte, error) {\n\tif b.err != nil {\n\t\treturn nil, b.err\n\t}\n\n\tb.buf[len(b.buf)-1] = ')'\n\treturn b.buf, nil\n}\n\nvar quoteCompositeReplacer = strings.NewReplacer(`\\`, `\\\\`, `\"`, `\\\"`)\n\nfunc quoteCompositeField(src string) string {\n\treturn `\"` + quoteCompositeReplacer.Replace(src) + `\"`\n}\n\nfunc quoteCompositeFieldIfNeeded(src string) string {\n\tif src == \"\" || src[0] == ' ' || src[len(src)-1] == ' ' || strings.ContainsAny(src, `(),\"\\`) {\n\t\treturn quoteCompositeField(src)\n\t}\n\treturn src\n}\n\n// CompositeFields represents the values of a composite value. It can be used as an encoding source or as a scan target.\n// It cannot scan a NULL, but the composite fields can be NULL.\ntype CompositeFields []any\n\nfunc (cf CompositeFields) SkipUnderlyingTypePlan() {}\n\nfunc (cf CompositeFields) IsNull() bool {\n\treturn cf == nil\n}\n\nfunc (cf CompositeFields) Index(i int) any {\n\treturn cf[i]\n}\n\nfunc (cf CompositeFields) ScanNull() error {\n\treturn fmt.Errorf(\"cannot scan NULL into CompositeFields\")\n}\n\nfunc (cf CompositeFields) ScanIndex(i int) any {\n\treturn cf[i]\n}\n"
  },
  {
    "path": "pgtype/composite_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestCompositeCodecTranscode(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop type if exists ct_test;\n\ncreate type ct_test as (\n\ta text,\n  b int4\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type ct_test\")\n\n\t\tdt, err := conn.LoadType(ctx, \"ct_test\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tvar a string\n\t\t\tvar b int32\n\n\t\t\terr := conn.QueryRow(ctx, \"select $1::ct_test\", pgx.QueryResultFormats{format.code},\n\t\t\t\tpgtype.CompositeFields{\"hi\", int32(42)},\n\t\t\t).Scan(\n\t\t\t\tpgtype.CompositeFields{&a, &b},\n\t\t\t)\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.EqualValuesf(t, \"hi\", a, \"%v\", format.name)\n\t\t\trequire.EqualValuesf(t, 42, b, \"%v\", format.name)\n\t\t}\n\t})\n}\n\ntype point3d struct {\n\tX, Y, Z float64\n}\n\nfunc (p point3d) IsNull() bool {\n\treturn false\n}\n\nfunc (p point3d) Index(i int) any {\n\tswitch i {\n\tcase 0:\n\t\treturn p.X\n\tcase 1:\n\t\treturn p.Y\n\tcase 2:\n\t\treturn p.Z\n\tdefault:\n\t\tpanic(\"invalid index\")\n\t}\n}\n\nfunc (p *point3d) ScanNull() error {\n\treturn fmt.Errorf(\"cannot scan NULL into point3d\")\n}\n\nfunc (p *point3d) ScanIndex(i int) any {\n\tswitch i {\n\tcase 0:\n\t\treturn &p.X\n\tcase 1:\n\t\treturn &p.Y\n\tcase 2:\n\t\treturn &p.Z\n\tdefault:\n\t\tpanic(\"invalid index\")\n\t}\n}\n\nfunc TestCompositeCodecTranscodeStruct(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop type if exists point3d;\n\ncreate type point3d as (\n\tx float8,\n\ty float8,\n\tz float8\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type point3d\")\n\n\t\tdt, err := conn.LoadType(ctx, \"point3d\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tinput := point3d{X: 1, Y: 2, Z: 3}\n\t\t\tvar output point3d\n\t\t\terr := conn.QueryRow(ctx, \"select $1::point3d\", pgx.QueryResultFormats{format.code}, input).Scan(&output)\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.Equalf(t, input, output, \"%v\", format.name)\n\t\t}\n\t})\n}\n\nfunc TestCompositeCodecTranscodeStructWrapper(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop type if exists point3d;\n\ncreate type point3d as (\n\tx float8,\n\ty float8,\n\tz float8\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type point3d\")\n\n\t\tdt, err := conn.LoadType(ctx, \"point3d\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\ttype anotherPoint struct {\n\t\t\tX, Y, Z float64\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tinput := anotherPoint{X: 1, Y: 2, Z: 3}\n\t\t\tvar output anotherPoint\n\t\t\terr := conn.QueryRow(ctx, \"select $1::point3d\", pgx.QueryResultFormats{format.code}, input).Scan(&output)\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.Equalf(t, input, output, \"%v\", format.name)\n\t\t}\n\t})\n}\n\ntype parent struct {\n\tName     string\n\tLocation *point3d\n}\n\nfunc (p parent) IsNull() bool {\n\treturn false\n}\n\nfunc (p parent) Index(i int) any {\n\tswitch i {\n\tcase 0:\n\t\treturn p.Name\n\tcase 1:\n\t\treturn p.Location\n\tdefault:\n\t\tpanic(\"invalid index\")\n\t}\n}\n\nfunc (p *parent) ScanNull() error {\n\treturn fmt.Errorf(\"cannot scan NULL into parent\")\n}\n\nfunc (p *parent) ScanIndex(i int) any {\n\tswitch i {\n\tcase 0:\n\t\treturn &p.Name\n\tcase 1:\n\t\treturn &p.Location\n\tdefault:\n\t\tpanic(\"invalid index\")\n\t}\n}\n\n// https://github.com/jackc/pgx/issues/2453\nfunc TestCompositeCodecTranscodeStructWithNilPointer(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tskipCockroachDB(t, \"Server does not support nested composite types\")\n\n\t\t_, err := conn.Exec(ctx, `drop type if exists parent;\ndrop type if exists point3d;\n\ncreate type point3d as (\n\tx float8,\n\ty float8,\n\tz float8\n);\n\ncreate type parent as (\n\tname text,\n\tlocation point3d\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type parent\")\n\t\tdefer conn.Exec(ctx, \"drop type point3d\")\n\n\t\tdataTypes, err := conn.LoadTypes(ctx, []string{\"point3d\", \"parent\"})\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterTypes(dataTypes)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tinput := parent{Name: \"test\", Location: nil}\n\t\t\tvar output parent\n\t\t\terr := conn.QueryRow(ctx, \"select $1::parent\", pgx.QueryResultFormats{format.code}, input).Scan(&output)\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.Equalf(t, input, output, \"%v\", format.name)\n\t\t}\n\t})\n}\n\ntype parentWithSlice struct {\n\tName      string\n\tLocations []*point3d\n}\n\nfunc (p parentWithSlice) IsNull() bool {\n\treturn false\n}\n\nfunc (p parentWithSlice) Index(i int) any {\n\tswitch i {\n\tcase 0:\n\t\treturn p.Name\n\tcase 1:\n\t\treturn p.Locations\n\tdefault:\n\t\tpanic(\"invalid index\")\n\t}\n}\n\nfunc (p *parentWithSlice) ScanNull() error {\n\treturn fmt.Errorf(\"cannot scan NULL into parent\")\n}\n\nfunc (p *parentWithSlice) ScanIndex(i int) any {\n\tswitch i {\n\tcase 0:\n\t\treturn &p.Name\n\tcase 1:\n\t\treturn &p.Locations\n\tdefault:\n\t\tpanic(\"invalid index\")\n\t}\n}\n\n// https://github.com/jackc/pgx/issues/2453\nfunc TestCompositeCodecTranscodeStructWithSliceOfNilPointer(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tskipCockroachDB(t, \"Server does not support nested composite types\")\n\n\t\t_, err := conn.Exec(ctx, `drop type if exists parent;\ndrop type if exists point3d;\n\ncreate type point3d as (\n\tx float8,\n\ty float8,\n\tz float8\n);\n\ncreate type parent_with_slice as (\n\tname text,\n\tlocations point3d[]\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type parent_with_slice\")\n\t\tdefer conn.Exec(ctx, \"drop type point3d\")\n\n\t\tdataTypes, err := conn.LoadTypes(ctx, []string{\"point3d\", \"parent_with_slice\"})\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterTypes(dataTypes)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tinput := parentWithSlice{Name: \"test\", Locations: []*point3d{nil}}\n\t\t\tvar output parentWithSlice\n\t\t\terr := conn.QueryRow(ctx, \"select $1::parent_with_slice\", pgx.QueryResultFormats{format.code}, input).Scan(&output)\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.Equalf(t, input, output, \"%v\", format.name)\n\t\t}\n\t})\n}\n\nfunc TestCompositeCodecDecodeValue(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop type if exists point3d;\n\ncreate type point3d as (\n\tx float8,\n\ty float8,\n\tz float8\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type point3d\")\n\n\t\tdt, err := conn.LoadType(ctx, \"point3d\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\trows, err := conn.Query(ctx, \"select '(1,2,3)'::point3d\", pgx.QueryResultFormats{format.code})\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.True(t, rows.Next())\n\t\t\tvalues, err := rows.Values()\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.Lenf(t, values, 1, \"%v\", format.name)\n\t\t\trequire.Equalf(t, map[string]any{\"x\": 1.0, \"y\": 2.0, \"z\": 3.0}, values[0], \"%v\", format.name)\n\t\t\trequire.False(t, rows.Next())\n\t\t\trequire.NoErrorf(t, rows.Err(), \"%v\", format.name)\n\t\t}\n\t})\n}\n\n// Test for composite type from table instead of create type. Table types have system / hidden columns like tableoid,\n// cmax, xmax, etc. These are not included when sending or receiving composite types.\n//\n// https://github.com/jackc/pgx/issues/1576\nfunc TestCompositeCodecTranscodeStructWrapperForTable(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support composite types from table definitions\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop table if exists point3d;\n\ncreate table point3d (\n\tx float8,\n\ty float8,\n\tz float8\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop table point3d\")\n\n\t\tdt, err := conn.LoadType(ctx, \"point3d\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\ttype anotherPoint struct {\n\t\t\tX, Y, Z float64\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tinput := anotherPoint{X: 1, Y: 2, Z: 3}\n\t\t\tvar output anotherPoint\n\t\t\terr := conn.QueryRow(ctx, \"select $1::point3d\", pgx.QueryResultFormats{format.code}, input).Scan(&output)\n\t\t\trequire.NoErrorf(t, err, \"%v\", format.name)\n\t\t\trequire.Equalf(t, input, output, \"%v\", format.name)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/convert.go",
    "content": "package pgtype\n\nimport (\n\t\"reflect\"\n)\n\nfunc NullAssignTo(dst any) error {\n\tdstPtr := reflect.ValueOf(dst)\n\n\t// AssignTo dst must always be a pointer\n\tif dstPtr.Kind() != reflect.Ptr {\n\t\treturn &nullAssignmentError{dst: dst}\n\t}\n\n\tdstVal := dstPtr.Elem()\n\n\tswitch dstVal.Kind() {\n\tcase reflect.Ptr, reflect.Slice, reflect.Map:\n\t\tdstVal.Set(reflect.Zero(dstVal.Type()))\n\t\treturn nil\n\t}\n\n\treturn &nullAssignmentError{dst: dst}\n}\n\nvar kindTypes map[reflect.Kind]reflect.Type\n\nfunc toInterface(dst reflect.Value, t reflect.Type) (any, bool) {\n\tnextDst := dst.Convert(t)\n\treturn nextDst.Interface(), dst.Type() != nextDst.Type()\n}\n\n// GetAssignToDstType attempts to convert dst to something AssignTo can assign\n// to. If dst is a pointer to pointer it allocates a value and returns the\n// dereferences pointer. If dst is a named type such as *Foo where Foo is type\n// Foo int16, it converts dst to *int16.\n//\n// GetAssignToDstType returns the converted dst and a bool representing if any\n// change was made.\nfunc GetAssignToDstType(dst any) (any, bool) {\n\tdstPtr := reflect.ValueOf(dst)\n\n\t// AssignTo dst must always be a pointer\n\tif dstPtr.Kind() != reflect.Ptr {\n\t\treturn nil, false\n\t}\n\n\tdstVal := dstPtr.Elem()\n\n\t// if dst is a pointer to pointer, allocate space try again with the dereferenced pointer\n\tif dstVal.Kind() == reflect.Ptr {\n\t\tdstVal.Set(reflect.New(dstVal.Type().Elem()))\n\t\treturn dstVal.Interface(), true\n\t}\n\n\t// if dst is pointer to a base type that has been renamed\n\tif baseValType, ok := kindTypes[dstVal.Kind()]; ok {\n\t\treturn toInterface(dstPtr, reflect.PtrTo(baseValType))\n\t}\n\n\tif dstVal.Kind() == reflect.Slice {\n\t\tif baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {\n\t\t\treturn toInterface(dstPtr, reflect.PtrTo(reflect.SliceOf(baseElemType)))\n\t\t}\n\t}\n\n\tif dstVal.Kind() == reflect.Array {\n\t\tif baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {\n\t\t\treturn toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(dstVal.Len(), baseElemType)))\n\t\t}\n\t}\n\n\tif dstVal.Kind() == reflect.Struct {\n\t\tif dstVal.Type().NumField() == 1 && dstVal.Type().Field(0).Anonymous {\n\t\t\tdstPtr = dstVal.Field(0).Addr()\n\t\t\tnested := dstVal.Type().Field(0).Type\n\t\t\tif nested.Kind() == reflect.Array {\n\t\t\t\tif baseElemType, ok := kindTypes[nested.Elem().Kind()]; ok {\n\t\t\t\t\treturn toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(nested.Len(), baseElemType)))\n\t\t\t\t}\n\t\t\t}\n\t\t\tif _, ok := kindTypes[nested.Kind()]; ok && dstPtr.CanInterface() {\n\t\t\t\treturn dstPtr.Interface(), true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\nfunc init() {\n\tkindTypes = map[reflect.Kind]reflect.Type{\n\t\treflect.Bool:    reflect.TypeOf(false),\n\t\treflect.Float32: reflect.TypeOf(float32(0)),\n\t\treflect.Float64: reflect.TypeOf(float64(0)),\n\t\treflect.Int:     reflect.TypeOf(int(0)),\n\t\treflect.Int8:    reflect.TypeOf(int8(0)),\n\t\treflect.Int16:   reflect.TypeOf(int16(0)),\n\t\treflect.Int32:   reflect.TypeOf(int32(0)),\n\t\treflect.Int64:   reflect.TypeOf(int64(0)),\n\t\treflect.Uint:    reflect.TypeOf(uint(0)),\n\t\treflect.Uint8:   reflect.TypeOf(uint8(0)),\n\t\treflect.Uint16:  reflect.TypeOf(uint16(0)),\n\t\treflect.Uint32:  reflect.TypeOf(uint32(0)),\n\t\treflect.Uint64:  reflect.TypeOf(uint64(0)),\n\t\treflect.String:  reflect.TypeOf(\"\"),\n\t}\n}\n"
  },
  {
    "path": "pgtype/date.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype DateScanner interface {\n\tScanDate(v Date) error\n}\n\ntype DateValuer interface {\n\tDateValue() (Date, error)\n}\n\ntype Date struct {\n\tTime             time.Time\n\tInfinityModifier InfinityModifier\n\tValid            bool\n}\n\n// ScanDate implements the [DateScanner] interface.\nfunc (d *Date) ScanDate(v Date) error {\n\t*d = v\n\treturn nil\n}\n\n// DateValue implements the [DateValuer] interface.\nfunc (d Date) DateValue() (Date, error) {\n\treturn d, nil\n}\n\nconst (\n\tnegativeInfinityDayOffset = -2147483648\n\tinfinityDayOffset         = 2147483647\n)\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Date) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Date{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToDateScanner{}.Scan([]byte(src), dst)\n\tcase time.Time:\n\t\t*dst = Date{Time: src, Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Date) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif src.InfinityModifier != Finite {\n\t\treturn src.InfinityModifier.String(), nil\n\t}\n\treturn src.Time, nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Date) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tvar s string\n\n\tswitch src.InfinityModifier {\n\tcase Finite:\n\t\ts = src.Time.Format(\"2006-01-02\")\n\tcase Infinity:\n\t\ts = \"infinity\"\n\tcase NegativeInfinity:\n\t\ts = \"-infinity\"\n\t}\n\n\treturn json.Marshal(s)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Date) UnmarshalJSON(b []byte) error {\n\tvar s *string\n\terr := json.Unmarshal(b, &s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif s == nil {\n\t\t*dst = Date{}\n\t\treturn nil\n\t}\n\n\tswitch *s {\n\tcase \"infinity\":\n\t\t*dst = Date{Valid: true, InfinityModifier: Infinity}\n\tcase \"-infinity\":\n\t\t*dst = Date{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\tt, err := time.ParseInLocation(\"2006-01-02\", *s, time.UTC)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t*dst = Date{Time: t, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype DateCodec struct{}\n\nfunc (DateCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (DateCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (DateCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(DateValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanDateCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanDateCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanDateCodecBinary struct{}\n\nfunc (encodePlanDateCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tdate, err := value.(DateValuer).DateValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !date.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar daysSinceDateEpoch int32\n\tswitch date.InfinityModifier {\n\tcase Finite:\n\t\ttUnix := time.Date(date.Time.Year(), date.Time.Month(), date.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()\n\t\tdateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()\n\n\t\tsecSinceDateEpoch := tUnix - dateEpoch\n\t\tdaysSinceDateEpoch = int32(secSinceDateEpoch / 86400)\n\tcase Infinity:\n\t\tdaysSinceDateEpoch = infinityDayOffset\n\tcase NegativeInfinity:\n\t\tdaysSinceDateEpoch = negativeInfinityDayOffset\n\t}\n\n\treturn pgio.AppendInt32(buf, daysSinceDateEpoch), nil\n}\n\ntype encodePlanDateCodecText struct{}\n\nfunc (encodePlanDateCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tdate, err := value.(DateValuer).DateValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !date.Valid {\n\t\treturn nil, nil\n\t}\n\n\tswitch date.InfinityModifier {\n\tcase Finite:\n\t\t// Year 0000 is 1 BC\n\t\tbc := false\n\t\tyear := date.Time.Year()\n\t\tif year <= 0 {\n\t\t\tyear = -year + 1\n\t\t\tbc = true\n\t\t}\n\n\t\tyearBytes := strconv.AppendInt(make([]byte, 0, 6), int64(year), 10)\n\t\tfor i := len(yearBytes); i < 4; i++ {\n\t\t\tbuf = append(buf, '0')\n\t\t}\n\t\tbuf = append(buf, yearBytes...)\n\t\tbuf = append(buf, '-')\n\t\tif date.Time.Month() < 10 {\n\t\t\tbuf = append(buf, '0')\n\t\t}\n\t\tbuf = strconv.AppendInt(buf, int64(date.Time.Month()), 10)\n\t\tbuf = append(buf, '-')\n\t\tif date.Time.Day() < 10 {\n\t\t\tbuf = append(buf, '0')\n\t\t}\n\t\tbuf = strconv.AppendInt(buf, int64(date.Time.Day()), 10)\n\n\t\tif bc {\n\t\t\tbuf = append(buf, \" BC\"...)\n\t\t}\n\tcase Infinity:\n\t\tbuf = append(buf, \"infinity\"...)\n\tcase NegativeInfinity:\n\t\tbuf = append(buf, \"-infinity\"...)\n\t}\n\n\treturn buf, nil\n}\n\nfunc (DateCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase DateScanner:\n\t\t\treturn scanPlanBinaryDateToDateScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase DateScanner:\n\t\t\treturn scanPlanTextAnyToDateScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryDateToDateScanner struct{}\n\nfunc (scanPlanBinaryDateToDateScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(DateScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanDate(Date{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for date: %v\", len(src))\n\t}\n\n\tdayOffset := int32(binary.BigEndian.Uint32(src))\n\n\tswitch dayOffset {\n\tcase infinityDayOffset:\n\t\treturn scanner.ScanDate(Date{InfinityModifier: Infinity, Valid: true})\n\tcase negativeInfinityDayOffset:\n\t\treturn scanner.ScanDate(Date{InfinityModifier: -Infinity, Valid: true})\n\tdefault:\n\t\tt := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)\n\t\treturn scanner.ScanDate(Date{Time: t, Valid: true})\n\t}\n}\n\ntype scanPlanTextAnyToDateScanner struct{}\n\nfunc (scanPlanTextAnyToDateScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(DateScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanDate(Date{})\n\t}\n\n\t// Check infinity cases first\n\tif len(src) == 8 && string(src) == \"infinity\" {\n\t\treturn scanner.ScanDate(Date{InfinityModifier: Infinity, Valid: true})\n\t}\n\tif len(src) == 9 && string(src) == \"-infinity\" {\n\t\treturn scanner.ScanDate(Date{InfinityModifier: -Infinity, Valid: true})\n\t}\n\n\t// Format: YYYY-MM-DD or YYYY...-MM-DD BC\n\t// Minimum: 10 chars (2000-01-01), with BC: 13 chars\n\tif len(src) < 10 {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\t// Check for BC suffix\n\tbc := false\n\tdatePart := src\n\tif len(src) >= 13 && string(src[len(src)-3:]) == \" BC\" {\n\t\tbc = true\n\t\tdatePart = src[:len(src)-3]\n\t}\n\n\t// Find year-month separator (first dash after at least 4 digits)\n\tyearEnd := -1\n\tfor i := 4; i < len(datePart); i++ {\n\t\tif datePart[i] == '-' {\n\t\t\tyearEnd = i\n\t\t\tbreak\n\t\t}\n\t\tif datePart[i] < '0' || datePart[i] > '9' {\n\t\t\treturn fmt.Errorf(\"invalid date format\")\n\t\t}\n\t}\n\tif yearEnd == -1 || yearEnd+6 > len(datePart) {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\t// Validate: -MM-DD structure after year\n\tif datePart[yearEnd+3] != '-' {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\t// Parse year\n\tyear, err := parseDigits(datePart[:yearEnd])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\t// Parse month (2 digits)\n\tmonth, err := parse2Digits(datePart[yearEnd+1 : yearEnd+3])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\t// Parse day (2 digits)\n\tday, err := parse2Digits(datePart[yearEnd+4 : yearEnd+6])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\t// Ensure nothing extra after day\n\tif yearEnd+6 != len(datePart) {\n\t\treturn fmt.Errorf(\"invalid date format\")\n\t}\n\n\tif bc {\n\t\tyear = -year + 1\n\t}\n\n\tt := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, time.UTC)\n\treturn scanner.ScanDate(Date{Time: t, Valid: true})\n}\n\n// parse2Digits parses exactly 2 ASCII digits.\nfunc parse2Digits(b []byte) (int64, error) {\n\tif len(b) != 2 {\n\t\treturn 0, fmt.Errorf(\"expected 2 digits\")\n\t}\n\td1, d2 := b[0], b[1]\n\tif d1 < '0' || d1 > '9' || d2 < '0' || d2 > '9' {\n\t\treturn 0, fmt.Errorf(\"expected digits\")\n\t}\n\treturn int64(d1-'0')*10 + int64(d2-'0'), nil\n}\n\n// parseDigits parses a sequence of ASCII digits.\nfunc parseDigits(b []byte) (int64, error) {\n\tif len(b) == 0 {\n\t\treturn 0, fmt.Errorf(\"empty\")\n\t}\n\tvar n int64\n\tfor _, c := range b {\n\t\tif c < '0' || c > '9' {\n\t\t\treturn 0, fmt.Errorf(\"non-digit\")\n\t\t}\n\t\tn = n*10 + int64(c-'0')\n\t}\n\treturn n, nil\n}\n\nfunc (c DateCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar date Date\n\terr := codecScan(c, m, oid, format, src, &date)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif date.InfinityModifier != Finite {\n\t\treturn date.InfinityModifier.String(), nil\n\t}\n\n\treturn date.Time, nil\n}\n\nfunc (c DateCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar date Date\n\terr := codecScan(c, m, oid, format, src, &date)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif date.InfinityModifier != Finite {\n\t\treturn date.InfinityModifier, nil\n\t}\n\n\treturn date.Time, nil\n}\n"
  },
  {
    "path": "pgtype/date_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc isExpectedEqTime(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tat := a.(time.Time)\n\t\tvt := v.(time.Time)\n\n\t\treturn at.Equal(vt)\n\t}\n}\n\nfunc TestDateCodec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"date\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(12200, 1, 2, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(12200, 1, 2, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true}, Result: new(pgtype.Date), Test: isExpectedEq(pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true})},\n\t\t{Param: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, Result: new(pgtype.Date), Test: isExpectedEq(pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true})},\n\t\t{Param: pgtype.Date{}, Result: new(pgtype.Date), Test: isExpectedEq(pgtype.Date{})},\n\t\t{Param: nil, Result: new(*time.Time), Test: isExpectedEq((*time.Time)(nil))},\n\t})\n}\n\nfunc TestDateCodecTextEncode(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Date\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Valid: true}, result: \"2012-03-29\"},\n\t\t{source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone(\"\", -6*60*60)), Valid: true}, result: \"2012-03-29\"},\n\t\t{source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone(\"\", -6*60*60)), Valid: true}, result: \"2012-03-29\"},\n\t\t{source: pgtype.Date{Time: time.Date(789, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, result: \"0789-01-02\"},\n\t\t{source: pgtype.Date{Time: time.Date(89, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, result: \"0089-01-02\"},\n\t\t{source: pgtype.Date{Time: time.Date(9, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, result: \"0009-01-02\"},\n\t\t{source: pgtype.Date{Time: time.Date(12200, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, result: \"12200-01-02\"},\n\t\t{source: pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true}, result: \"infinity\"},\n\t\t{source: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: \"-infinity\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tbuf, err := m.Encode(pgtype.DateOID, pgtype.TextFormatCode, tt.source, nil)\n\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\tassert.Equalf(t, tt.result, string(buf), \"%d\", i)\n\t}\n}\n\nfunc TestDateMarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Date\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Date{}, result: \"null\"},\n\t\t{source: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Valid: true}, result: \"\\\"2012-03-29\\\"\"},\n\t\t{source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone(\"\", -6*60*60)), Valid: true}, result: \"\\\"2012-03-29\\\"\"},\n\t\t{source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone(\"\", -6*60*60)), Valid: true}, result: \"\\\"2012-03-29\\\"\"},\n\t\t{source: pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true}, result: \"\\\"infinity\\\"\"},\n\t\t{source: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: \"\\\"-infinity\\\"\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestDateUnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Date\n\t}{\n\t\t{source: \"null\", result: pgtype.Date{}},\n\t\t{source: \"\\\"2012-03-29\\\"\", result: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Valid: true}},\n\t\t{source: \"\\\"2012-03-29\\\"\", result: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone(\"\", -6*60*60)), Valid: true}},\n\t\t{source: \"\\\"2012-03-29\\\"\", result: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone(\"\", -6*60*60)), Valid: true}},\n\t\t{source: \"\\\"infinity\\\"\", result: pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true}},\n\t\t{source: \"\\\"-infinity\\\"\", result: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Date\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r.Time.Year() != tt.result.Time.Year() || r.Time.Month() != tt.result.Time.Month() || r.Time.Day() != tt.result.Time.Day() || r.Valid != tt.result.Valid || r.InfinityModifier != tt.result.InfinityModifier {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n\nfunc TestDateScanTextFormat(t *testing.T) {\n\t// Tests for scanPlanTextAnyToDateScanner\n\n\tt.Run(\"StandardDates\", func(t *testing.T) {\n\t\ttests := []struct {\n\t\t\tinput string\n\t\t\tyear  int\n\t\t\tmonth time.Month\n\t\t\tday   int\n\t\t}{\n\t\t\t// Typical dates\n\t\t\t{\"2024-01-15\", 2024, 1, 15},\n\t\t\t{\"2024-12-31\", 2024, 12, 31},\n\t\t\t{\"1999-06-15\", 1999, 6, 15},\n\t\t\t{\"2000-01-01\", 2000, 1, 1},\n\t\t\t{\"2000-01-02\", 2000, 1, 2},\n\n\t\t\t// Epoch boundaries\n\t\t\t{\"1970-01-01\", 1970, 1, 1},\n\t\t\t{\"1969-12-31\", 1969, 12, 31},\n\n\t\t\t// Y2K boundaries\n\t\t\t{\"1999-12-31\", 1999, 12, 31},\n\n\t\t\t// Leap year dates\n\t\t\t{\"2000-02-29\", 2000, 2, 29},\n\t\t\t{\"2024-02-29\", 2024, 2, 29},\n\t\t\t{\"1900-02-28\", 1900, 2, 28},\n\n\t\t\t// Month boundaries\n\t\t\t{\"2024-01-31\", 2024, 1, 31},\n\t\t\t{\"2024-04-30\", 2024, 4, 30},\n\t\t\t{\"2024-06-30\", 2024, 6, 30},\n\n\t\t\t// Old dates\n\t\t\t{\"1900-01-01\", 1900, 1, 1},\n\t\t\t{\"1800-06-15\", 1800, 6, 15},\n\t\t\t{\"1000-01-01\", 1000, 1, 1},\n\n\t\t\t// Future dates\n\t\t\t{\"2100-01-01\", 2100, 1, 1},\n\t\t\t{\"2200-12-31\", 2200, 12, 31},\n\t\t\t{\"3000-06-15\", 3000, 6, 15},\n\n\t\t\t// 5+ digit years\n\t\t\t{\"12200-01-02\", 12200, 1, 2},\n\t\t\t{\"99999-12-31\", 99999, 12, 31},\n\t\t\t{\"100000-01-01\", 100000, 1, 1},\n\n\t\t\t// Zero-padded years\n\t\t\t{\"0001-01-01\", 1, 1, 1},\n\t\t\t{\"0009-01-02\", 9, 1, 2},\n\t\t\t{\"0089-01-02\", 89, 1, 2},\n\t\t\t{\"0789-01-02\", 789, 1, 2},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\t\tvar d pgtype.Date\n\t\t\t\terr := d.Scan(tt.input)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t\t\t}\n\t\t\t\tif !d.Valid {\n\t\t\t\t\tt.Error(\"expected Valid=true\")\n\t\t\t\t}\n\t\t\t\tif d.Time.Year() != tt.year {\n\t\t\t\t\tt.Errorf(\"year: got %d, want %d\", d.Time.Year(), tt.year)\n\t\t\t\t}\n\t\t\t\tif d.Time.Month() != tt.month {\n\t\t\t\t\tt.Errorf(\"month: got %d, want %d\", d.Time.Month(), tt.month)\n\t\t\t\t}\n\t\t\t\tif d.Time.Day() != tt.day {\n\t\t\t\t\tt.Errorf(\"day: got %d, want %d\", d.Time.Day(), tt.day)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"BCDates\", func(t *testing.T) {\n\t\ttests := []struct {\n\t\t\tinput string\n\t\t\tyear  int\n\t\t\tmonth time.Month\n\t\t\tday   int\n\t\t}{\n\t\t\t// BC date handling: year X BC = Go year (-X + 1)\n\t\t\t// 1 BC = year 0, 2 BC = year -1, etc.\n\t\t\t{\"0001-01-01 BC\", 0, 1, 1},\n\t\t\t{\"0002-01-01 BC\", -1, 1, 1},\n\t\t\t{\"0100-06-15 BC\", -99, 6, 15},\n\t\t\t{\"0500-12-31 BC\", -499, 12, 31},\n\t\t\t{\"1000-01-01 BC\", -999, 1, 1},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\t\tvar d pgtype.Date\n\t\t\t\terr := d.Scan(tt.input)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t\t\t}\n\t\t\t\tif !d.Valid {\n\t\t\t\t\tt.Error(\"expected Valid=true\")\n\t\t\t\t}\n\t\t\t\tif d.Time.Year() != tt.year {\n\t\t\t\t\tt.Errorf(\"year: got %d, want %d\", d.Time.Year(), tt.year)\n\t\t\t\t}\n\t\t\t\tif d.Time.Month() != tt.month {\n\t\t\t\t\tt.Errorf(\"month: got %d, want %d\", d.Time.Month(), tt.month)\n\t\t\t\t}\n\t\t\t\tif d.Time.Day() != tt.day {\n\t\t\t\t\tt.Errorf(\"day: got %d, want %d\", d.Time.Day(), tt.day)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"Infinity\", func(t *testing.T) {\n\t\ttests := []struct {\n\t\t\tinput   string\n\t\t\twantMod pgtype.InfinityModifier\n\t\t}{\n\t\t\t{\"infinity\", pgtype.Infinity},\n\t\t\t{\"-infinity\", pgtype.NegativeInfinity},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt.input, func(t *testing.T) {\n\t\t\t\tvar d pgtype.Date\n\t\t\t\terr := d.Scan(tt.input)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t\t\t}\n\t\t\t\tif !d.Valid {\n\t\t\t\t\tt.Error(\"expected Valid=true\")\n\t\t\t\t}\n\t\t\t\tif d.InfinityModifier != tt.wantMod {\n\t\t\t\t\tt.Errorf(\"got InfinityModifier=%d, want %d\", d.InfinityModifier, tt.wantMod)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"Invalid\", func(t *testing.T) {\n\t\ttests := []string{\n\t\t\t// Too short\n\t\t\t\"\",\n\t\t\t\"2024\",\n\t\t\t\"2024-01\",\n\t\t\t\"2024-1-1\",\n\n\t\t\t// Wrong separators\n\t\t\t\"2024/01/15\",\n\t\t\t\"2024.01.15\",\n\t\t\t\"20240115\",\n\n\t\t\t// Invalid characters\n\t\t\t\"2024-0a-15\",\n\t\t\t\"2024-01-1b\",\n\t\t\t\"abcd-01-15\",\n\n\t\t\t// Wrong format\n\t\t\t\"24-01-15\",\n\t\t\t\"01-15-2024\",\n\t\t\t\"15-01-2024\",\n\n\t\t\t// Trailing garbage\n\t\t\t\"2024-01-15x\",\n\t\t\t\"2024-01-15 \",\n\t\t\t\"2024-01-15\\n\",\n\n\t\t\t// Leading garbage\n\t\t\t\" 2024-01-15\",\n\t\t\t\"x2024-01-15\",\n\n\t\t\t// Wrong month/day digits\n\t\t\t\"2024-1-15\",\n\t\t\t\"2024-01-5\",\n\t\t\t\"2024-001-15\",\n\t\t\t\"2024-01-015\",\n\n\t\t\t// Partial infinity\n\t\t\t\"infinit\",\n\t\t\t\"infinityy\",\n\t\t\t\"-infinit\",\n\t\t\t\"--infinity\",\n\t\t\t\"Infinity\",\n\t\t\t\"-Infinity\",\n\t\t\t\"INFINITY\",\n\n\t\t\t// Malformed BC\n\t\t\t\"2024-01-15BC\",\n\t\t\t\"2024-01-15 bc\",\n\t\t\t\"2024-01-15  BC\",\n\t\t\t\"2024-01-15 B\",\n\n\t\t\t// Only 3 digit year\n\t\t\t\"123-01-15\",\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\tt.Run(tt, func(t *testing.T) {\n\t\t\t\tvar d pgtype.Date\n\t\t\t\terr := d.Scan(tt)\n\t\t\t\tif err == nil {\n\t\t\t\t\tt.Errorf(\"expected error for input %q, got date %+v\", tt, d)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"Nil\", func(t *testing.T) {\n\t\tvar d pgtype.Date\n\t\terr := d.Scan(nil)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t}\n\t\tif d.Valid {\n\t\t\tt.Error(\"expected Valid=false for nil input\")\n\t\t}\n\t})\n}\n\nfunc TestDateScanRoundTrip(t *testing.T) {\n\t// Test that dates from TestDateCodec roundtrip correctly through text format\n\tdates := []time.Time{\n\t\ttime.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(0, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(12200, 1, 2, 0, 0, 0, 0, time.UTC),\n\t}\n\n\tm := pgtype.NewMap()\n\n\tfor _, d := range dates {\n\t\tt.Run(d.Format(\"2006-01-02\"), func(t *testing.T) {\n\t\t\tsrc := pgtype.Date{Time: d, Valid: true}\n\n\t\t\t// Encode to text\n\t\t\tbuf, err := m.Encode(pgtype.DateOID, pgtype.TextFormatCode, src, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"encode failed: %v\", err)\n\t\t\t}\n\n\t\t\t// Decode back\n\t\t\tvar dst pgtype.Date\n\t\t\terr = dst.Scan(string(buf))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"scan failed for %q: %v\", string(buf), err)\n\t\t\t}\n\n\t\t\tif !dst.Valid {\n\t\t\t\tt.Error(\"expected Valid=true\")\n\t\t\t}\n\t\t\tif dst.Time.Year() != d.Year() || dst.Time.Month() != d.Month() || dst.Time.Day() != d.Day() {\n\t\t\t\tt.Errorf(\"roundtrip failed: input=%v encoded=%q parsed=%v\", d, string(buf), dst.Time)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDateScanInfinityRoundTrip(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\ttests := []pgtype.Date{\n\t\t{InfinityModifier: pgtype.Infinity, Valid: true},\n\t\t{InfinityModifier: pgtype.NegativeInfinity, Valid: true},\n\t}\n\n\tfor _, src := range tests {\n\t\tbuf, err := m.Encode(pgtype.DateOID, pgtype.TextFormatCode, src, nil)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"encode failed: %v\", err)\n\t\t}\n\n\t\tvar dst pgtype.Date\n\t\terr = dst.Scan(string(buf))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"scan failed for %q: %v\", string(buf), err)\n\t\t}\n\n\t\tif dst != src {\n\t\t\tt.Errorf(\"roundtrip failed: src=%+v dst=%+v\", src, dst)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/derived_types_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDerivedTypes(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `\ndrop type if exists dt_test;\ndrop domain if exists dt_uint64;\n\ncreate domain dt_uint64 as numeric(20,0);\ncreate type dt_test as (\n\ta text,\n    b dt_uint64,\n    c dt_uint64[]\n);`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop domain dt_uint64\")\n\t\tdefer conn.Exec(ctx, \"drop type dt_test\")\n\n\t\tdtypes, err := conn.LoadTypes(ctx, []string{\"dt_test\"})\n\t\trequire.Len(t, dtypes, 6)\n\t\trequire.Equal(t, dtypes[0].Name, \"public.dt_uint64\")\n\t\trequire.Equal(t, dtypes[1].Name, \"dt_uint64\")\n\t\trequire.Equal(t, dtypes[2].Name, \"public._dt_uint64\")\n\t\trequire.Equal(t, dtypes[3].Name, \"_dt_uint64\")\n\t\trequire.Equal(t, dtypes[4].Name, \"public.dt_test\")\n\t\trequire.Equal(t, dtypes[5].Name, \"dt_test\")\n\t\trequire.NoError(t, err)\n\t\tconn.TypeMap().RegisterTypes(dtypes)\n\n\t\tformats := []struct {\n\t\t\tname string\n\t\t\tcode int16\n\t\t}{\n\t\t\t{name: \"TextFormat\", code: pgx.TextFormatCode},\n\t\t\t{name: \"BinaryFormat\", code: pgx.BinaryFormatCode},\n\t\t}\n\n\t\tfor _, format := range formats {\n\t\t\tvar a string\n\t\t\tvar b uint64\n\t\t\tvar c *[]uint64\n\n\t\t\trow := conn.QueryRow(ctx, \"select $1::dt_test\", pgx.QueryResultFormats{format.code}, pgtype.CompositeFields{\"hi\", uint64(42), []uint64{10, 20, 30}})\n\t\t\terr := row.Scan(pgtype.CompositeFields{&a, &b, &c})\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.EqualValuesf(t, \"hi\", a, \"%v\", format.name)\n\t\t\trequire.EqualValuesf(t, 42, b, \"%v\", format.name)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/doc.go",
    "content": "// Package pgtype converts between Go and PostgreSQL values.\n/*\nThe primary type is the Map type. It is a map of PostgreSQL types identified by OID (object ID) to a Codec. A Codec is\nresponsible for converting between Go and PostgreSQL values. NewMap creates a Map with all supported standard PostgreSQL\ntypes already registered. Additional types can be registered with Map.RegisterType.\n\nUse Map.Scan and Map.Encode to decode PostgreSQL values to Go and encode Go values to PostgreSQL respectively.\n\nBase Type Mapping\n\npgtype maps between all common base types directly between Go and PostgreSQL. In particular:\n\n    Go           PostgreSQL\n    -----------------------\n    string        varchar\n                  text\n\n    // Integers are automatically be converted to any other integer type if\n    // it can be done without overflow or underflow.\n    int8\n    int16         smallint\n    int32         int\n    int64         bigint\n    int\n    uint8\n    uint16\n    uint32\n    uint64\n    uint\n\n    // Floats are strict and do not automatically convert like integers.\n    float32       float4\n    float64       float8\n\n    time.Time     date\n                  timestamp\n                  timestamptz\n\n    netip.Addr    inet\n    netip.Prefix  cidr\n\n    []byte        bytea\n\nNull Values\n\npgtype can map NULLs in two ways. The first is types that can directly represent NULL such as Int4. They work in a\nsimilar fashion to database/sql. The second is to use a pointer to a pointer.\n\n    var foo pgtype.Text\n    var bar *string\n    err := conn.QueryRow(\"select foo, bar from widgets where id=$1\", 42).Scan(&foo, &bar)\n    if err != nil {\n        return err\n    }\n\nWhen using nullable pgtype types as parameters for queries, one has to remember to explicitly set their Valid field to\ntrue, otherwise the parameter's value will be NULL.\n\nJSON Support\n\npgtype automatically marshals and unmarshals data from json and jsonb PostgreSQL types.\n\nExtending Existing PostgreSQL Type Support\n\nGenerally, all Codecs will support interfaces that can be implemented to enable scanning and encoding. For example,\nPointCodec can use any Go type that implements the PointScanner and PointValuer interfaces. So rather than use\npgtype.Point and application can directly use its own point type with pgtype as long as it implements those interfaces.\n\nSee example_custom_type_test.go for an example of a custom type for the PostgreSQL point type.\n\nSometimes pgx supports a PostgreSQL type such as numeric but the Go type is in an external package that does not have\npgx support such as github.com/shopspring/decimal. These types can be registered with pgtype with custom conversion\nlogic. See https://github.com/jackc/pgx-shopspring-decimal and https://github.com/jackc/pgx-gofrs-uuid for example\nintegrations.\n\nNew PostgreSQL Type Support\n\npgtype uses the PostgreSQL OID to determine how to encode or decode a value. pgtype supports array, composite, domain,\nand enum types. However, any type created in PostgreSQL with CREATE TYPE will receive a new OID. This means that the OID\nof each new PostgreSQL type must be registered for pgtype to handle values of that type with the correct Codec.\n\nThe pgx.Conn LoadType method can return a *Type for array, composite, domain, and enum types by inspecting the database\nmetadata. This *Type can then be registered with Map.RegisterType.\n\nFor example, the following function could be called after a connection is established:\n\n    func RegisterDataTypes(ctx context.Context, conn *pgx.Conn) error {\n      dataTypeNames := []string{\n        \"foo\",\n        \"_foo\",\n        \"bar\",\n        \"_bar\",\n      }\n\n      for _, typeName := range dataTypeNames {\n        dataType, err := conn.LoadType(ctx, typeName)\n        if err != nil {\n          return err\n        }\n        conn.TypeMap().RegisterType(dataType)\n      }\n\n      return nil\n    }\n\nA type cannot be registered unless all types it depends on are already registered. e.g. An array type cannot be\nregistered until its element type is registered.\n\nArrayCodec implements support for arrays. If pgtype supports type T then it can easily support []T by registering an\nArrayCodec for the appropriate PostgreSQL OID. In addition, Array[T] type can support multi-dimensional arrays.\n\nCompositeCodec implements support for PostgreSQL composite types. Go structs can be scanned into if the public fields of\nthe struct are in the exact order and type of the PostgreSQL type or by implementing CompositeIndexScanner and\nCompositeIndexGetter.\n\nDomain types are treated as their underlying type if the underlying type and the domain type are registered.\n\nPostgreSQL enums can usually be treated as text. However, EnumCodec implements support for interning strings which can\nreduce memory usage.\n\nWhile pgtype will often still work with unregistered types it is highly recommended that all types be registered due to\nan improvement in performance and the elimination of certain edge cases.\n\nIf an entirely new PostgreSQL type (e.g. PostGIS types) is used then the application or a library can create a new\nCodec. Then the OID / Codec mapping can be registered with Map.RegisterType. There is no difference between a Codec\ndefined and registered by the application and a Codec built in to pgtype. See any of the Codecs in pgtype for Codec\nexamples and for examples of type registration.\n\nEncoding Unknown Types\n\npgtype works best when the OID of the PostgreSQL type is known. But in some cases such as using the simple protocol the\nOID is unknown. In this case Map.RegisterDefaultPgType can be used to register an assumed OID for a particular Go type.\n\nRenamed Types\n\nIf pgtype does not recognize a type and that type is a renamed simple type simple (e.g. type MyInt32 int32) pgtype acts\nas if it is the underlying type. It currently cannot automatically detect the underlying type of renamed structs (eg.g.\ntype MyTime time.Time).\n\nCompatibility with database/sql\n\npgtype also includes support for custom types implementing the database/sql.Scanner and database/sql/driver.Valuer\ninterfaces.\n\nEncoding Typed Nils\n\npgtype encodes untyped and typed nils (e.g. nil and []byte(nil)) to the SQL NULL value without going through the Codec\nsystem. This means that Codecs and other encoding logic do not have to handle nil or *T(nil).\n\nHowever, database/sql compatibility requires Value to be called on T(nil) when T implements driver.Valuer. Therefore,\ndriver.Valuer values are only considered NULL when *T(nil) where driver.Valuer is implemented on T not on *T. See\nhttps://github.com/golang/go/issues/8415 and\nhttps://github.com/golang/go/commit/0ce1d79a6a771f7449ec493b993ed2a720917870.\n\nChild Records\n\npgtype's support for arrays and composite records can be used to load records and their children in a single query.  See\nexample_child_records_test.go for an example.\n\nOverview of Scanning Implementation\n\nThe first step is to use the OID to lookup the correct Codec. The Map will call the Codec's PlanScan method to get a\nplan for scanning into the Go value. A Codec will support scanning into one or more Go types. Oftentime these Go types\nare interfaces rather than explicit types. For example, PointCodec can use any Go type that implements the PointScanner\nand PointValuer interfaces.\n\nIf a Go value is not supported directly by a Codec then Map will try see if it is a sql.Scanner. If is then that\ninterface will be used to scan the value. Most sql.Scanners require the input to be in the text format (e.g. UUIDs and\nnumeric). However, pgx will typically have received the value in the binary format. In this case the binary value will be\nparsed, reencoded as text, and then passed to the sql.Scanner. This may incur additional overhead for query results with\na large number of affected values.\n\nIf a Go value is not supported directly by a Codec then Map will try wrapping it with additional logic and try again.\nFor example, Int8Codec does not support scanning into a renamed type (e.g. type myInt64 int64). But Map will detect that\nmyInt64 is a renamed type and create a plan that converts the value to the underlying int64 type and then passes that to\nthe Codec (see TryFindUnderlyingTypeScanPlan).\n\nThese plan wrappers are contained in Map.TryWrapScanPlanFuncs. By default these contain shared logic to handle renamed\ntypes, pointers to pointers, slices, composite types, etc. Additional plan wrappers can be added to seamlessly integrate\ntypes that do not support pgx directly. For example, the before mentioned\nhttps://github.com/jackc/pgx-shopspring-decimal package detects decimal.Decimal values, wraps them in something\nimplementing NumericScanner and passes that to the Codec.\n\nMap.Scan and Map.Encode are convenience methods that wrap Map.PlanScan and Map.PlanEncode.  Determining how to scan or\nencode a particular type may be a time consuming operation. Hence the planning and execution steps of a conversion are\ninternally separated.\n\nReducing Compiled Binary Size\n\npgx.QueryExecModeExec and pgx.QueryExecModeSimpleProtocol require the default PostgreSQL type to be registered for each\nGo type used as a query parameter. By default pgx does this for all supported types and their array variants. If an\napplication does not use those query execution modes or manually registers the default PostgreSQL type for the types it\nuses as query parameters it can use the build tag nopgxregisterdefaulttypes. This omits the default type registration\nand reduces the compiled binary size by ~2MB.\n*/\npackage pgtype\n"
  },
  {
    "path": "pgtype/enum_codec.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n)\n\n// EnumCodec is a codec that caches the strings it decodes. If the same string is read multiple times only one copy is\n// allocated. These strings are only garbage collected when the EnumCodec is garbage collected. EnumCodec can be used\n// for any text type not only enums, but it should only be used when there are a small number of possible values.\ntype EnumCodec struct {\n\tmembersMap map[string]string // map to quickly lookup member and reuse string instead of allocating\n}\n\nfunc (EnumCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (EnumCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n\nfunc (EnumCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase TextFormatCode, BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase string:\n\t\t\treturn encodePlanTextCodecString{}\n\t\tcase []byte:\n\t\t\treturn encodePlanTextCodecByteSlice{}\n\t\tcase TextValuer:\n\t\t\treturn encodePlanTextCodecTextValuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *EnumCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase TextFormatCode, BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *string:\n\t\t\treturn &scanPlanTextAnyToEnumString{codec: c}\n\t\tcase *[]byte:\n\t\t\treturn scanPlanAnyToNewByteSlice{}\n\t\tcase TextScanner:\n\t\t\treturn &scanPlanTextAnyToEnumTextScanner{codec: c}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *EnumCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn c.DecodeValue(m, oid, format, src)\n}\n\nfunc (c *EnumCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn c.lookupAndCacheString(src), nil\n}\n\n// lookupAndCacheString looks for src in the members map. If it is not found it is added to the map.\nfunc (c *EnumCodec) lookupAndCacheString(src []byte) string {\n\tif c.membersMap == nil {\n\t\tc.membersMap = make(map[string]string)\n\t}\n\n\tif s, found := c.membersMap[string(src)]; found {\n\t\treturn s\n\t}\n\n\ts := string(src)\n\tc.membersMap[s] = s\n\treturn s\n}\n\ntype scanPlanTextAnyToEnumString struct {\n\tcodec *EnumCodec\n}\n\nfunc (plan *scanPlanTextAnyToEnumString) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp := (dst).(*string)\n\t*p = plan.codec.lookupAndCacheString(src)\n\n\treturn nil\n}\n\ntype scanPlanTextAnyToEnumTextScanner struct {\n\tcodec *EnumCodec\n}\n\nfunc (plan *scanPlanTextAnyToEnumTextScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanText(Text{})\n\t}\n\n\treturn scanner.ScanText(Text{String: plan.codec.lookupAndCacheString(src), Valid: true})\n}\n"
  },
  {
    "path": "pgtype/enum_codec_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestEnumCodec(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop type if exists enum_test;\n\ncreate type enum_test as enum ('foo', 'bar', 'baz');`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type enum_test\")\n\n\t\tdt, err := conn.LoadType(ctx, \"enum_test\")\n\t\trequire.NoError(t, err)\n\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\tvar s string\n\t\terr = conn.QueryRow(ctx, `select 'foo'::enum_test`).Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"foo\", s)\n\n\t\terr = conn.QueryRow(ctx, `select $1::enum_test`, \"bar\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"bar\", s)\n\n\t\terr = conn.QueryRow(ctx, `select 'foo'::enum_test`).Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"foo\", s)\n\n\t\terr = conn.QueryRow(ctx, `select $1::enum_test`, \"bar\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"bar\", s)\n\n\t\terr = conn.QueryRow(ctx, `select 'baz'::enum_test`).Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"baz\", s)\n\t})\n}\n\nfunc TestEnumCodecValues(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `drop type if exists enum_test;\n\ncreate type enum_test as enum ('foo', 'bar', 'baz');`)\n\t\trequire.NoError(t, err)\n\t\tdefer conn.Exec(ctx, \"drop type enum_test\")\n\n\t\tdt, err := conn.LoadType(ctx, \"enum_test\")\n\t\trequire.NoError(t, err)\n\n\t\tconn.TypeMap().RegisterType(dt)\n\n\t\trows, err := conn.Query(ctx, `select 'foo'::enum_test`)\n\t\trequire.NoError(t, err)\n\t\trequire.True(t, rows.Next())\n\t\tvalues, err := rows.Values()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, []any{\"foo\"}, values)\n\t})\n}\n"
  },
  {
    "path": "pgtype/example_child_records_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\ntype Player struct {\n\tName     string\n\tPosition string\n}\n\ntype Team struct {\n\tName    string\n\tPlayers []Player\n}\n\n// This example uses a single query to return parent and child records.\nfunc Example_childRecords() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// Skip test / example when running on CockroachDB. Since an example can't be skipped fake success instead.\n\t\tfmt.Println(`Alpha\n  Adam: wing\n  Bill: halfback\n  Charlie: fullback\nBeta\n  Don: halfback\n  Edgar: halfback\n  Frank: fullback`)\n\t\treturn\n\t}\n\n\t// Setup example schema and data.\n\t_, err = conn.Exec(ctx, `\ncreate temporary table teams (\n\tname text primary key\n);\n\ncreate temporary table players (\n\tname text primary key,\n\tteam_name text,\n\tposition text\n);\n\ninsert into teams (name) values\n\t('Alpha'),\n\t('Beta');\n\ninsert into players (name, team_name, position) values\n\t('Adam', 'Alpha', 'wing'),\n\t('Bill', 'Alpha', 'halfback'),\n\t('Charlie', 'Alpha', 'fullback'),\n\t('Don', 'Beta', 'halfback'),\n\t('Edgar', 'Beta', 'halfback'),\n\t('Frank', 'Beta', 'fullback')\n`)\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to setup example schema and data: %v\", err)\n\t\treturn\n\t}\n\n\trows, _ := conn.Query(ctx, `\nselect t.name,\n\t(select array_agg(row(p.name, position) order by p.name) from players p where p.team_name = t.name)\nfrom teams t\norder by t.name\n`)\n\tteams, err := pgx.CollectRows(rows, pgx.RowToStructByPos[Team])\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfor _, team := range teams {\n\t\tfmt.Println(team.Name)\n\t\tfor _, player := range team.Players {\n\t\t\tfmt.Printf(\"  %s: %s\\n\", player.Name, player.Position)\n\t\t}\n\t}\n\n\t// Output:\n\t// Alpha\n\t//   Adam: wing\n\t//   Bill: halfback\n\t//   Charlie: fullback\n\t// Beta\n\t//   Don: halfback\n\t//   Edgar: halfback\n\t//   Frank: fullback\n}\n"
  },
  {
    "path": "pgtype/example_custom_type_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// Point represents a point that may be null.\ntype Point struct {\n\tX, Y  float32 // Coordinates of point\n\tValid bool\n}\n\nfunc (p *Point) ScanPoint(v pgtype.Point) error {\n\t*p = Point{\n\t\tX:     float32(v.P.X),\n\t\tY:     float32(v.P.Y),\n\t\tValid: v.Valid,\n\t}\n\treturn nil\n}\n\nfunc (p Point) PointValue() (pgtype.Point, error) {\n\treturn pgtype.Point{\n\t\tP:     pgtype.Vec2{X: float64(p.X), Y: float64(p.Y)},\n\t\tValid: true,\n\t}, nil\n}\n\nfunc (src *Point) String() string {\n\tif !src.Valid {\n\t\treturn \"null point\"\n\t}\n\n\treturn fmt.Sprintf(\"%.1f, %.1f\", src.X, src.Y)\n}\n\nfunc Example_customType() {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\tdefer conn.Close(context.Background())\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// Skip test / example when running on CockroachDB which doesn't support the point type. Since an example can't be\n\t\t// skipped fake success instead.\n\t\tfmt.Println(\"null point\")\n\t\tfmt.Println(\"1.5, 2.5\")\n\t\treturn\n\t}\n\n\tp := &Point{}\n\terr = conn.QueryRow(context.Background(), \"select null::point\").Scan(p)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Println(p)\n\n\terr = conn.QueryRow(context.Background(), \"select point(1.5,2.5)\").Scan(p)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tfmt.Println(p)\n\t// Output:\n\t// null point\n\t// 1.5, 2.5\n}\n"
  },
  {
    "path": "pgtype/example_json_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\nfunc Example_json() {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\ttype person struct {\n\t\tName string `json:\"name\"`\n\t\tAge  int    `json:\"age\"`\n\t}\n\n\tinput := person{\n\t\tName: \"John\",\n\t\tAge:  42,\n\t}\n\n\tvar output person\n\n\terr = conn.QueryRow(context.Background(), \"select $1::json\", input).Scan(&output)\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\n\tfmt.Println(output.Name, output.Age)\n\t// Output:\n\t// John 42\n}\n"
  },
  {
    "path": "pgtype/float4.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Float4 struct {\n\tFloat32 float32\n\tValid   bool\n}\n\n// ScanFloat64 implements the [Float64Scanner] interface.\nfunc (f *Float4) ScanFloat64(n Float8) error {\n\t*f = Float4{Float32: float32(n.Float64), Valid: n.Valid}\n\treturn nil\n}\n\n// Float64Value implements the [Float64Valuer] interface.\nfunc (f Float4) Float64Value() (Float8, error) {\n\treturn Float8{Float64: float64(f.Float32), Valid: f.Valid}, nil\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (f *Float4) ScanInt64(n Int8) error {\n\t*f = Float4{Float32: float32(n.Int64), Valid: n.Valid}\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (f Float4) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(f.Float32), Valid: f.Valid}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (f *Float4) Scan(src any) error {\n\tif src == nil {\n\t\t*f = Float4{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase float64:\n\t\t*f = Float4{Float32: float32(src), Valid: true}\n\t\treturn nil\n\tcase string:\n\t\tn, err := strconv.ParseFloat(string(src), 32)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*f = Float4{Float32: float32(n), Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (f Float4) Value() (driver.Value, error) {\n\tif !f.Valid {\n\t\treturn nil, nil\n\t}\n\treturn float64(f.Float32), nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (f Float4) MarshalJSON() ([]byte, error) {\n\tif !f.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn json.Marshal(f.Float32)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (f *Float4) UnmarshalJSON(b []byte) error {\n\tvar n *float32\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*f = Float4{}\n\t} else {\n\t\t*f = Float4{Float32: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Float4Codec struct{}\n\nfunc (Float4Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Float4Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Float4Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase float32:\n\t\t\treturn encodePlanFloat4CodecBinaryFloat32{}\n\t\tcase Float64Valuer:\n\t\t\treturn encodePlanFloat4CodecBinaryFloat64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanFloat4CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase float32:\n\t\t\treturn encodePlanTextFloat32{}\n\t\tcase Float64Valuer:\n\t\t\treturn encodePlanTextFloat64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanFloat4CodecBinaryFloat32 struct{}\n\nfunc (encodePlanFloat4CodecBinaryFloat32) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(float32)\n\treturn pgio.AppendUint32(buf, math.Float32bits(n)), nil\n}\n\ntype encodePlanTextFloat32 struct{}\n\nfunc (encodePlanTextFloat32) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(float32)\n\treturn append(buf, strconv.FormatFloat(float64(n), 'f', -1, 32)...), nil\n}\n\ntype encodePlanFloat4CodecBinaryFloat64Valuer struct{}\n\nfunc (encodePlanFloat4CodecBinaryFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Float64Valuer).Float64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn pgio.AppendUint32(buf, math.Float32bits(float32(n.Float64))), nil\n}\n\ntype encodePlanFloat4CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanFloat4CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tf := float32(n.Int64)\n\treturn pgio.AppendUint32(buf, math.Float32bits(f)), nil\n}\n\nfunc (Float4Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *float32:\n\t\t\treturn scanPlanBinaryFloat4ToFloat32{}\n\t\tcase Float64Scanner:\n\t\t\treturn scanPlanBinaryFloat4ToFloat64Scanner{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryFloat4ToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryFloat4ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *float32:\n\t\t\treturn scanPlanTextAnyToFloat32{}\n\t\tcase Float64Scanner:\n\t\t\treturn scanPlanTextAnyToFloat64Scanner{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryFloat4ToFloat32 struct{}\n\nfunc (scanPlanBinaryFloat4ToFloat32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for float4: %v\", len(src))\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tf := (dst).(*float32)\n\t*f = math.Float32frombits(uint32(n))\n\n\treturn nil\n}\n\ntype scanPlanBinaryFloat4ToFloat64Scanner struct{}\n\nfunc (scanPlanBinaryFloat4ToFloat64Scanner) Scan(src []byte, dst any) error {\n\ts := (dst).(Float64Scanner)\n\n\tif src == nil {\n\t\treturn s.ScanFloat64(Float8{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for float4: %v\", len(src))\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\treturn s.ScanFloat64(Float8{Float64: float64(math.Float32frombits(uint32(n))), Valid: true})\n}\n\ntype scanPlanBinaryFloat4ToInt64Scanner struct{}\n\nfunc (scanPlanBinaryFloat4ToInt64Scanner) Scan(src []byte, dst any) error {\n\ts := (dst).(Int64Scanner)\n\n\tif src == nil {\n\t\treturn s.ScanInt64(Int8{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for float4: %v\", len(src))\n\t}\n\n\tui32 := int32(binary.BigEndian.Uint32(src))\n\tf32 := math.Float32frombits(uint32(ui32))\n\ti64 := int64(f32)\n\tif f32 != float32(i64) {\n\t\treturn fmt.Errorf(\"cannot losslessly convert %v to int64\", f32)\n\t}\n\n\treturn s.ScanInt64(Int8{Int64: i64, Valid: true})\n}\n\ntype scanPlanBinaryFloat4ToTextScanner struct{}\n\nfunc (scanPlanBinaryFloat4ToTextScanner) Scan(src []byte, dst any) error {\n\ts := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for float4: %v\", len(src))\n\t}\n\n\tui32 := int32(binary.BigEndian.Uint32(src))\n\tf32 := math.Float32frombits(uint32(ui32))\n\n\treturn s.ScanText(Text{String: strconv.FormatFloat(float64(f32), 'f', -1, 32), Valid: true})\n}\n\ntype scanPlanTextAnyToFloat32 struct{}\n\nfunc (scanPlanTextAnyToFloat32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tn, err := strconv.ParseFloat(string(src), 32)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf := (dst).(*float32)\n\t*f = float32(n)\n\n\treturn nil\n}\n\nfunc (c Float4Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n float32\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn float64(n), nil\n}\n\nfunc (c Float4Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n float32\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n"
  },
  {
    "path": "pgtype/float4_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestFloat4Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"float4\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: pgtype.Float4{Float32: -1, Valid: true}, Result: new(pgtype.Float4), Test: isExpectedEq(pgtype.Float4{Float32: -1, Valid: true})},\n\t\t{Param: pgtype.Float4{Float32: 0, Valid: true}, Result: new(pgtype.Float4), Test: isExpectedEq(pgtype.Float4{Float32: 0, Valid: true})},\n\t\t{Param: pgtype.Float4{Float32: 1, Valid: true}, Result: new(pgtype.Float4), Test: isExpectedEq(pgtype.Float4{Float32: 1, Valid: true})},\n\t\t{Param: float32(0.00001), Result: new(float32), Test: isExpectedEq(float32(0.00001))},\n\t\t{Param: float32(9999.99), Result: new(float32), Test: isExpectedEq(float32(9999.99))},\n\t\t{Param: pgtype.Float4{}, Result: new(pgtype.Float4), Test: isExpectedEq(pgtype.Float4{})},\n\t\t{Param: int64(1), Result: new(int64), Test: isExpectedEq(int64(1))},\n\t\t{Param: \"1.23\", Result: new(string), Test: isExpectedEq(\"1.23\")},\n\t\t{Param: nil, Result: new(*float32), Test: isExpectedEq((*float32)(nil))},\n\t})\n}\n\nfunc TestFloat4MarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Float4\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Float4{Float32: 0}, result: \"null\"},\n\t\t{source: pgtype.Float4{Float32: 1.23, Valid: true}, result: \"1.23\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestFloat4UnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Float4\n\t}{\n\t\t{source: \"null\", result: pgtype.Float4{Float32: 0}},\n\t\t{source: \"1.23\", result: pgtype.Float4{Float32: 1.23, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Float4\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/float8.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Float64Scanner interface {\n\tScanFloat64(Float8) error\n}\n\ntype Float64Valuer interface {\n\tFloat64Value() (Float8, error)\n}\n\ntype Float8 struct {\n\tFloat64 float64\n\tValid   bool\n}\n\n// ScanFloat64 implements the [Float64Scanner] interface.\nfunc (f *Float8) ScanFloat64(n Float8) error {\n\t*f = n\n\treturn nil\n}\n\n// Float64Value implements the [Float64Valuer] interface.\nfunc (f Float8) Float64Value() (Float8, error) {\n\treturn f, nil\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (f *Float8) ScanInt64(n Int8) error {\n\t*f = Float8{Float64: float64(n.Int64), Valid: n.Valid}\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (f Float8) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(f.Float64), Valid: f.Valid}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (f *Float8) Scan(src any) error {\n\tif src == nil {\n\t\t*f = Float8{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase float64:\n\t\t*f = Float8{Float64: src, Valid: true}\n\t\treturn nil\n\tcase string:\n\t\tn, err := strconv.ParseFloat(string(src), 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*f = Float8{Float64: n, Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (f Float8) Value() (driver.Value, error) {\n\tif !f.Valid {\n\t\treturn nil, nil\n\t}\n\treturn f.Float64, nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (f Float8) MarshalJSON() ([]byte, error) {\n\tif !f.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn json.Marshal(f.Float64)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (f *Float8) UnmarshalJSON(b []byte) error {\n\tvar n *float64\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*f = Float8{}\n\t} else {\n\t\t*f = Float8{Float64: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Float8Codec struct{}\n\nfunc (Float8Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Float8Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Float8Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase float64:\n\t\t\treturn encodePlanFloat8CodecBinaryFloat64{}\n\t\tcase Float64Valuer:\n\t\t\treturn encodePlanFloat8CodecBinaryFloat64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanFloat8CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase float64:\n\t\t\treturn encodePlanTextFloat64{}\n\t\tcase Float64Valuer:\n\t\t\treturn encodePlanTextFloat64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanFloat8CodecBinaryFloat64 struct{}\n\nfunc (encodePlanFloat8CodecBinaryFloat64) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(float64)\n\treturn pgio.AppendUint64(buf, math.Float64bits(n)), nil\n}\n\ntype encodePlanTextFloat64 struct{}\n\nfunc (encodePlanTextFloat64) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(float64)\n\treturn append(buf, strconv.FormatFloat(n, 'f', -1, 64)...), nil\n}\n\ntype encodePlanFloat8CodecBinaryFloat64Valuer struct{}\n\nfunc (encodePlanFloat8CodecBinaryFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Float64Valuer).Float64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn pgio.AppendUint64(buf, math.Float64bits(n.Float64)), nil\n}\n\ntype encodePlanTextFloat64Valuer struct{}\n\nfunc (encodePlanTextFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Float64Valuer).Float64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, strconv.FormatFloat(n.Float64, 'f', -1, 64)...), nil\n}\n\ntype encodePlanFloat8CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanFloat8CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tf := float64(n.Int64)\n\treturn pgio.AppendUint64(buf, math.Float64bits(f)), nil\n}\n\ntype encodePlanTextInt64Valuer struct{}\n\nfunc (encodePlanTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, strconv.FormatInt(n.Int64, 10)...), nil\n}\n\nfunc (Float8Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *float64:\n\t\t\treturn scanPlanBinaryFloat8ToFloat64{}\n\t\tcase Float64Scanner:\n\t\t\treturn scanPlanBinaryFloat8ToFloat64Scanner{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryFloat8ToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryFloat8ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *float64:\n\t\t\treturn scanPlanTextAnyToFloat64{}\n\t\tcase Float64Scanner:\n\t\t\treturn scanPlanTextAnyToFloat64Scanner{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryFloat8ToFloat64 struct{}\n\nfunc (scanPlanBinaryFloat8ToFloat64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for float8: %v\", len(src))\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tf := (dst).(*float64)\n\t*f = math.Float64frombits(uint64(n))\n\n\treturn nil\n}\n\ntype scanPlanBinaryFloat8ToFloat64Scanner struct{}\n\nfunc (scanPlanBinaryFloat8ToFloat64Scanner) Scan(src []byte, dst any) error {\n\ts := (dst).(Float64Scanner)\n\n\tif src == nil {\n\t\treturn s.ScanFloat64(Float8{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for float8: %v\", len(src))\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\treturn s.ScanFloat64(Float8{Float64: math.Float64frombits(uint64(n)), Valid: true})\n}\n\ntype scanPlanBinaryFloat8ToInt64Scanner struct{}\n\nfunc (scanPlanBinaryFloat8ToInt64Scanner) Scan(src []byte, dst any) error {\n\ts := (dst).(Int64Scanner)\n\n\tif src == nil {\n\t\treturn s.ScanInt64(Int8{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for float8: %v\", len(src))\n\t}\n\n\tui64 := int64(binary.BigEndian.Uint64(src))\n\tf64 := math.Float64frombits(uint64(ui64))\n\ti64 := int64(f64)\n\tif f64 != float64(i64) {\n\t\treturn fmt.Errorf(\"cannot losslessly convert %v to int64\", f64)\n\t}\n\n\treturn s.ScanInt64(Int8{Int64: i64, Valid: true})\n}\n\ntype scanPlanBinaryFloat8ToTextScanner struct{}\n\nfunc (scanPlanBinaryFloat8ToTextScanner) Scan(src []byte, dst any) error {\n\ts := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for float8: %v\", len(src))\n\t}\n\n\tui64 := int64(binary.BigEndian.Uint64(src))\n\tf64 := math.Float64frombits(uint64(ui64))\n\n\treturn s.ScanText(Text{String: strconv.FormatFloat(f64, 'f', -1, 64), Valid: true})\n}\n\ntype scanPlanTextAnyToFloat64 struct{}\n\nfunc (scanPlanTextAnyToFloat64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tn, err := strconv.ParseFloat(string(src), 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf := (dst).(*float64)\n\t*f = n\n\n\treturn nil\n}\n\ntype scanPlanTextAnyToFloat64Scanner struct{}\n\nfunc (scanPlanTextAnyToFloat64Scanner) Scan(src []byte, dst any) error {\n\ts := (dst).(Float64Scanner)\n\n\tif src == nil {\n\t\treturn s.ScanFloat64(Float8{})\n\t}\n\n\tn, err := strconv.ParseFloat(string(src), 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.ScanFloat64(Float8{Float64: n, Valid: true})\n}\n\nfunc (c Float8Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn c.DecodeValue(m, oid, format, src)\n}\n\nfunc (c Float8Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n float64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n"
  },
  {
    "path": "pgtype/float8_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestFloat8Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"float8\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: pgtype.Float8{Float64: -1, Valid: true}, Result: new(pgtype.Float8), Test: isExpectedEq(pgtype.Float8{Float64: -1, Valid: true})},\n\t\t{Param: pgtype.Float8{Float64: 0, Valid: true}, Result: new(pgtype.Float8), Test: isExpectedEq(pgtype.Float8{Float64: 0, Valid: true})},\n\t\t{Param: pgtype.Float8{Float64: 1, Valid: true}, Result: new(pgtype.Float8), Test: isExpectedEq(pgtype.Float8{Float64: 1, Valid: true})},\n\t\t{Param: float64(0.00001), Result: new(float64), Test: isExpectedEq(float64(0.00001))},\n\t\t{Param: float64(9999.99), Result: new(float64), Test: isExpectedEq(float64(9999.99))},\n\t\t{Param: pgtype.Float8{}, Result: new(pgtype.Float8), Test: isExpectedEq(pgtype.Float8{})},\n\t\t{Param: int64(1), Result: new(int64), Test: isExpectedEq(int64(1))},\n\t\t{Param: \"1.23\", Result: new(string), Test: isExpectedEq(\"1.23\")},\n\t\t{Param: nil, Result: new(*float64), Test: isExpectedEq((*float64)(nil))},\n\t})\n}\n\nfunc TestFloat8MarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Float8\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Float8{Float64: 0}, result: \"null\"},\n\t\t{source: pgtype.Float8{Float64: 1.23, Valid: true}, result: \"1.23\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestFloat8UnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Float8\n\t}{\n\t\t{source: \"null\", result: pgtype.Float8{Float64: 0}},\n\t\t{source: \"1.23\", result: pgtype.Float8{Float64: 1.23, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Float8\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/hstore.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype HstoreScanner interface {\n\tScanHstore(v Hstore) error\n}\n\ntype HstoreValuer interface {\n\tHstoreValue() (Hstore, error)\n}\n\n// Hstore represents an hstore column that can be null or have null values\n// associated with its keys.\ntype Hstore map[string]*string\n\n// ScanHstore implements the [HstoreScanner] interface.\nfunc (h *Hstore) ScanHstore(v Hstore) error {\n\t*h = v\n\treturn nil\n}\n\n// HstoreValue implements the [HstoreValuer] interface.\nfunc (h Hstore) HstoreValue() (Hstore, error) {\n\treturn h, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (h *Hstore) Scan(src any) error {\n\tif src == nil {\n\t\t*h = nil\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToHstoreScanner{}.scanString(src, h)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (h Hstore) Value() (driver.Value, error) {\n\tif h == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := HstoreCodec{}.PlanEncode(nil, 0, TextFormatCode, h).Encode(h, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype HstoreCodec struct{}\n\nfunc (HstoreCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (HstoreCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (HstoreCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(HstoreValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanHstoreCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanHstoreCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanHstoreCodecBinary struct{}\n\nfunc (encodePlanHstoreCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\thstore, err := value.(HstoreValuer).HstoreValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif hstore == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendInt32(buf, int32(len(hstore)))\n\n\tfor k, v := range hstore {\n\t\tbuf = pgio.AppendInt32(buf, int32(len(k)))\n\t\tbuf = append(buf, k...)\n\n\t\tif v == nil {\n\t\t\tbuf = pgio.AppendInt32(buf, -1)\n\t\t} else {\n\t\t\tbuf = pgio.AppendInt32(buf, int32(len(*v)))\n\t\t\tbuf = append(buf, (*v)...)\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanHstoreCodecText struct{}\n\nfunc (encodePlanHstoreCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\thstore, err := value.(HstoreValuer).HstoreValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(hstore) == 0 {\n\t\t// distinguish between empty and nil: Not strictly required by Postgres, since its protocol\n\t\t// explicitly marks NULL column values separately. However, the Binary codec does this, and\n\t\t// this means we can \"round trip\" Encode and Scan without data loss.\n\t\t// nil: []byte(nil); empty: []byte{}\n\t\tif hstore == nil {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn []byte{}, nil\n\t}\n\n\tfirstPair := true\n\n\tfor k, v := range hstore {\n\t\tif firstPair {\n\t\t\tfirstPair = false\n\t\t} else {\n\t\t\tbuf = append(buf, ',', ' ')\n\t\t}\n\n\t\t// unconditionally quote hstore keys/values like Postgres does\n\t\t// this avoids a Mac OS X Postgres hstore parsing bug:\n\t\t// https://www.postgresql.org/message-id/CA%2BHWA9awUW0%2BRV_gO9r1ABZwGoZxPztcJxPy8vMFSTbTfi4jig%40mail.gmail.com\n\t\tbuf = append(buf, '\"')\n\t\tbuf = append(buf, quoteArrayReplacer.Replace(k)...)\n\t\tbuf = append(buf, '\"')\n\t\tbuf = append(buf, \"=>\"...)\n\n\t\tif v == nil {\n\t\t\tbuf = append(buf, \"NULL\"...)\n\t\t} else {\n\t\t\tbuf = append(buf, '\"')\n\t\t\tbuf = append(buf, quoteArrayReplacer.Replace(*v)...)\n\t\t\tbuf = append(buf, '\"')\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (HstoreCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase HstoreScanner:\n\t\t\treturn scanPlanBinaryHstoreToHstoreScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase HstoreScanner:\n\t\t\treturn scanPlanTextAnyToHstoreScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryHstoreToHstoreScanner struct{}\n\nfunc (scanPlanBinaryHstoreToHstoreScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(HstoreScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanHstore(Hstore(nil))\n\t}\n\n\trp := 0\n\n\tconst uint32Len = 4\n\tif len(src[rp:]) < uint32Len {\n\t\treturn fmt.Errorf(\"hstore incomplete %v\", src)\n\t}\n\tpairCount := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\trp += uint32Len\n\n\thstore := make(Hstore, pairCount)\n\t// one allocation for all *string, rather than one per string, just like text parsing\n\tvalueStrings := make([]string, pairCount)\n\n\tfor i := range pairCount {\n\t\tif len(src[rp:]) < uint32Len {\n\t\t\treturn fmt.Errorf(\"hstore incomplete %v\", src)\n\t\t}\n\t\tkeyLen := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\trp += uint32Len\n\n\t\tif len(src[rp:]) < keyLen {\n\t\t\treturn fmt.Errorf(\"hstore incomplete %v\", src)\n\t\t}\n\t\tkey := string(src[rp : rp+keyLen])\n\t\trp += keyLen\n\n\t\tif len(src[rp:]) < uint32Len {\n\t\t\treturn fmt.Errorf(\"hstore incomplete %v\", src)\n\t\t}\n\t\tvalueLen := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\trp += 4\n\n\t\tif valueLen >= 0 {\n\t\t\tvalueStrings[i] = string(src[rp : rp+valueLen])\n\t\t\trp += valueLen\n\n\t\t\thstore[key] = &valueStrings[i]\n\t\t} else {\n\t\t\thstore[key] = nil\n\t\t}\n\t}\n\n\treturn scanner.ScanHstore(hstore)\n}\n\ntype scanPlanTextAnyToHstoreScanner struct{}\n\nfunc (s scanPlanTextAnyToHstoreScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(HstoreScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanHstore(Hstore(nil))\n\t}\n\treturn s.scanString(string(src), scanner)\n}\n\n// scanString does not return nil hstore values because string cannot be nil.\nfunc (scanPlanTextAnyToHstoreScanner) scanString(src string, scanner HstoreScanner) error {\n\thstore, err := parseHstore(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn scanner.ScanHstore(hstore)\n}\n\nfunc (c HstoreCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c HstoreCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar hstore Hstore\n\terr := codecScan(c, m, oid, format, src, &hstore)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn hstore, nil\n}\n\ntype hstoreParser struct {\n\tstr           string\n\tpos           int\n\tnextBackslash int\n}\n\nfunc newHSP(in string) *hstoreParser {\n\treturn &hstoreParser{\n\t\tpos:           0,\n\t\tstr:           in,\n\t\tnextBackslash: strings.IndexByte(in, '\\\\'),\n\t}\n}\n\nfunc (p *hstoreParser) atEnd() bool {\n\treturn p.pos >= len(p.str)\n}\n\n// consume returns the next byte of the string, or end if the string is done.\nfunc (p *hstoreParser) consume() (b byte, end bool) {\n\tif p.pos >= len(p.str) {\n\t\treturn 0, true\n\t}\n\tb = p.str[p.pos]\n\tp.pos++\n\treturn b, false\n}\n\nfunc unexpectedByteErr(actualB, expectedB byte) error {\n\treturn fmt.Errorf(\"expected '%c' ('%#v'); found '%c' ('%#v')\", expectedB, expectedB, actualB, actualB)\n}\n\n// consumeExpectedByte consumes expectedB from the string, or returns an error.\nfunc (p *hstoreParser) consumeExpectedByte(expectedB byte) error {\n\tnextB, end := p.consume()\n\tif end {\n\t\treturn fmt.Errorf(\"expected '%c' ('%#v'); found end\", expectedB, expectedB)\n\t}\n\tif nextB != expectedB {\n\t\treturn unexpectedByteErr(nextB, expectedB)\n\t}\n\treturn nil\n}\n\n// consumeExpected2 consumes two expected bytes or returns an error.\n// This was a bit faster than using a string argument (better inlining? Not sure).\nfunc (p *hstoreParser) consumeExpected2(one, two byte) error {\n\tif p.pos+2 > len(p.str) {\n\t\treturn errors.New(\"unexpected end of string\")\n\t}\n\tif p.str[p.pos] != one {\n\t\treturn unexpectedByteErr(p.str[p.pos], one)\n\t}\n\tif p.str[p.pos+1] != two {\n\t\treturn unexpectedByteErr(p.str[p.pos+1], two)\n\t}\n\tp.pos += 2\n\treturn nil\n}\n\nvar errEOSInQuoted = errors.New(`found end before closing double-quote ('\"')`)\n\n// consumeDoubleQuoted consumes a double-quoted string from p. The double quote must have been\n// parsed already. This copies the string from the backing string so it can be garbage collected.\nfunc (p *hstoreParser) consumeDoubleQuoted() (string, error) {\n\t// fast path: assume most keys/values do not contain escapes\n\tnextDoubleQuote := strings.IndexByte(p.str[p.pos:], '\"')\n\tif nextDoubleQuote == -1 {\n\t\treturn \"\", errEOSInQuoted\n\t}\n\tnextDoubleQuote += p.pos\n\tif p.nextBackslash == -1 || p.nextBackslash > nextDoubleQuote {\n\t\t// clone the string from the source string to ensure it can be garbage collected separately\n\t\t// TODO: use strings.Clone on Go 1.20; this could get optimized away\n\t\ts := strings.Clone(p.str[p.pos:nextDoubleQuote])\n\t\tp.pos = nextDoubleQuote + 1\n\t\treturn s, nil\n\t}\n\n\t// slow path: string contains escapes\n\ts, err := p.consumeDoubleQuotedWithEscapes(p.nextBackslash)\n\tp.nextBackslash = strings.IndexByte(p.str[p.pos:], '\\\\')\n\tif p.nextBackslash != -1 {\n\t\tp.nextBackslash += p.pos\n\t}\n\treturn s, err\n}\n\n// consumeDoubleQuotedWithEscapes consumes a double-quoted string containing escapes, starting\n// at p.pos, and with the first backslash at firstBackslash. This copies the string so it can be\n// garbage collected separately.\nfunc (p *hstoreParser) consumeDoubleQuotedWithEscapes(firstBackslash int) (string, error) {\n\t// copy the prefix that does not contain backslashes\n\tvar builder strings.Builder\n\tbuilder.WriteString(p.str[p.pos:firstBackslash])\n\n\t// skip to the backslash\n\tp.pos = firstBackslash\n\n\t// copy bytes until the end, unescaping backslashes\n\tfor {\n\t\tnextB, end := p.consume()\n\t\tif end {\n\t\t\treturn \"\", errEOSInQuoted\n\t\t} else if nextB == '\"' {\n\t\t\tbreak\n\t\t} else if nextB == '\\\\' {\n\t\t\t// escape: skip the backslash and copy the char\n\t\t\tnextB, end = p.consume()\n\t\t\tif end {\n\t\t\t\treturn \"\", errEOSInQuoted\n\t\t\t}\n\t\t\tif !(nextB == '\\\\' || nextB == '\"') {\n\t\t\t\treturn \"\", fmt.Errorf(\"unexpected escape in quoted string: found '%#v'\", nextB)\n\t\t\t}\n\t\t\tbuilder.WriteByte(nextB)\n\t\t} else {\n\t\t\t// normal byte: copy it\n\t\t\tbuilder.WriteByte(nextB)\n\t\t}\n\t}\n\treturn builder.String(), nil\n}\n\n// consumePairSeparator consumes the Hstore pair separator \", \" or returns an error.\nfunc (p *hstoreParser) consumePairSeparator() error {\n\treturn p.consumeExpected2(',', ' ')\n}\n\n// consumeKVSeparator consumes the Hstore key/value separator \"=>\" or returns an error.\nfunc (p *hstoreParser) consumeKVSeparator() error {\n\treturn p.consumeExpected2('=', '>')\n}\n\n// consumeDoubleQuotedOrNull consumes the Hstore key/value separator \"=>\" or returns an error.\nfunc (p *hstoreParser) consumeDoubleQuotedOrNull() (Text, error) {\n\t// peek at the next byte\n\tif p.atEnd() {\n\t\treturn Text{}, errors.New(\"found end instead of value\")\n\t}\n\tnext := p.str[p.pos]\n\tif next == 'N' {\n\t\t// must be the exact string NULL: use consumeExpected2 twice\n\t\terr := p.consumeExpected2('N', 'U')\n\t\tif err != nil {\n\t\t\treturn Text{}, err\n\t\t}\n\t\terr = p.consumeExpected2('L', 'L')\n\t\tif err != nil {\n\t\t\treturn Text{}, err\n\t\t}\n\t\treturn Text{String: \"\", Valid: false}, nil\n\t} else if next != '\"' {\n\t\treturn Text{}, unexpectedByteErr(next, '\"')\n\t}\n\n\t// skip the double quote\n\tp.pos += 1\n\ts, err := p.consumeDoubleQuoted()\n\tif err != nil {\n\t\treturn Text{}, err\n\t}\n\treturn Text{String: s, Valid: true}, nil\n}\n\nfunc parseHstore(s string) (Hstore, error) {\n\tp := newHSP(s)\n\n\t// This is an over-estimate of the number of key/value pairs. Use '>' because I am guessing it\n\t// is less likely to occur in keys/values than '=' or ','.\n\tnumPairsEstimate := strings.Count(s, \">\")\n\t// makes one allocation of strings for the entire Hstore, rather than one allocation per value.\n\tvalueStrings := make([]string, 0, numPairsEstimate)\n\tresult := make(Hstore, numPairsEstimate)\n\tfirst := true\n\tfor !p.atEnd() {\n\t\tif !first {\n\t\t\terr := p.consumePairSeparator()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else {\n\t\t\tfirst = false\n\t\t}\n\n\t\terr := p.consumeExpectedByte('\"')\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tkey, err := p.consumeDoubleQuoted()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\terr = p.consumeKVSeparator()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvalue, err := p.consumeDoubleQuotedOrNull()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif value.Valid {\n\t\t\tvalueStrings = append(valueStrings, value.String)\n\t\t\tresult[key] = &valueStrings[len(valueStrings)-1]\n\t\t} else {\n\t\t\tresult[key] = nil\n\t\t}\n\t}\n\n\treturn result, nil\n}\n"
  },
  {
    "path": "pgtype/hstore_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqMapStringString(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tam := a.(map[string]string)\n\t\tvm := v.(map[string]string)\n\n\t\tif len(am) != len(vm) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor k, v := range am {\n\t\t\tif vm[k] != v {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n}\n\nfunc isExpectedEqMapStringPointerString(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tam := a.(map[string]*string)\n\t\tvm := v.(map[string]*string)\n\n\t\tif len(am) != len(vm) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor k, v := range am {\n\t\t\tif (vm[k] == nil) != (v == nil) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif v != nil && *vm[k] != *v {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n}\n\n// stringPtr returns a pointer to s.\nfunc stringPtr(s string) *string {\n\treturn &s\n}\n\nfunc TestHstoreCodec(t *testing.T) {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar hstoreOID uint32\n\t\terr := conn.QueryRow(context.Background(), `select oid from pg_type where typname = 'hstore'`).Scan(&hstoreOID)\n\t\tif err != nil {\n\t\t\tt.Skipf(\"Skipping: cannot find hstore OID\")\n\t\t}\n\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{Name: \"hstore\", OID: hstoreOID, Codec: pgtype.HstoreCodec{}})\n\t}\n\n\ttests := []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  map[string]string{},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]string{\"foo\": \"\", \"bar\": \"\", \"baz\": \"123\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\": \"\", \"bar\": \"\", \"baz\": \"123\"}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]string{\"NULL\": \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"NULL\": \"bar\"}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]string{\"bar\": \"NULL\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"bar\": \"NULL\"}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]string{\"\": \"foo\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"\": \"foo\"}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]*string{},\n\t\t\tResult: new(map[string]*string),\n\t\t\tTest:   isExpectedEqMapStringPointerString(map[string]*string{}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]*string{\"foo\": stringPtr(\"bar\"), \"baq\": stringPtr(\"quz\")},\n\t\t\tResult: new(map[string]*string),\n\t\t\tTest:   isExpectedEqMapStringPointerString(map[string]*string{\"foo\": stringPtr(\"bar\"), \"baq\": stringPtr(\"quz\")}),\n\t\t},\n\t\t{\n\t\t\tParam:  map[string]*string{\"foo\": nil, \"baq\": stringPtr(\"quz\")},\n\t\t\tResult: new(map[string]*string),\n\t\t\tTest:   isExpectedEqMapStringPointerString(map[string]*string{\"foo\": nil, \"baq\": stringPtr(\"quz\")}),\n\t\t},\n\t\t{Param: nil, Result: new(*map[string]string), Test: isExpectedEq((*map[string]string)(nil))},\n\t\t{Param: nil, Result: new(*map[string]*string), Test: isExpectedEq((*map[string]*string)(nil))},\n\t\t{Param: nil, Result: new(*pgtype.Hstore), Test: isExpectedEq((*pgtype.Hstore)(nil))},\n\t}\n\n\tspecialStrings := []string{\n\t\t`\"`,\n\t\t`'`,\n\t\t`\\`,\n\t\t`\\\\`,\n\t\t`=>`,\n\t\t` `,\n\t\t`\\ / / \\\\ => \" ' \" '`,\n\t\t\"line1\\nline2\",\n\t\t\"tab\\tafter\",\n\t\t\"vtab\\vafter\",\n\t\t\"form\\\\ffeed\",\n\t\t\"carriage\\rreturn\",\n\t\t\"curly{}braces\",\n\t\t// Postgres on Mac OS X hstore parsing bug:\n\t\t// ą = \"\\xc4\\x85\" in UTF-8; isspace(0x85) on Mac OS X returns true instead of false\n\t\t\"mac_bugą\",\n\t}\n\tfor _, s := range specialStrings {\n\t\t// Special key values\n\n\t\t// at beginning\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{s + \"foo\": \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{s + \"foo\": \"bar\"}),\n\t\t})\n\t\t// in middle\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{\"foo\" + s + \"bar\": \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\" + s + \"bar\": \"bar\"}),\n\t\t})\n\t\t// at end\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{\"foo\" + s: \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\" + s: \"bar\"}),\n\t\t})\n\t\t// is key\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{s: \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{s: \"bar\"}),\n\t\t})\n\n\t\t// Special value values\n\n\t\t// at beginning\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{\"foo\": s + \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\": s + \"bar\"}),\n\t\t})\n\t\t// in middle\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{\"foo\": \"foo\" + s + \"bar\"},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\": \"foo\" + s + \"bar\"}),\n\t\t})\n\t\t// at end\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{\"foo\": \"foo\" + s},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\": \"foo\" + s}),\n\t\t})\n\t\t// is key\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{\n\t\t\tParam:  map[string]string{\"foo\": s},\n\t\t\tResult: new(map[string]string),\n\t\t\tTest:   isExpectedEqMapStringString(map[string]string{\"foo\": s}),\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctr, pgxtest.KnownOIDQueryExecModes, \"hstore\", tests)\n\n\t// run the tests using pgtype.Hstore as input and output types, and test all query modes\n\tfor i := range tests {\n\t\tvar h pgtype.Hstore\n\t\tswitch typedParam := tests[i].Param.(type) {\n\t\tcase map[string]*string:\n\t\t\th = pgtype.Hstore(typedParam)\n\t\tcase map[string]string:\n\t\t\tif typedParam != nil {\n\t\t\t\th = pgtype.Hstore{}\n\t\t\t\tfor k, v := range typedParam {\n\t\t\t\t\th[k] = stringPtr(v)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttests[i].Param = h\n\t\ttests[i].Result = &pgtype.Hstore{}\n\t\ttests[i].Test = func(input any) bool {\n\t\t\treturn reflect.DeepEqual(input, h)\n\t\t}\n\t}\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctr, pgxtest.AllQueryExecModes, \"hstore\", tests)\n\n\t// run the tests again without the codec registered: uses the text protocol\n\tctrWithoutCodec := defaultConnTestRunner\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctrWithoutCodec, pgxtest.AllQueryExecModes, \"hstore\", tests)\n\n\t// scan empty and NULL: should be different in all query modes\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, pgxtest.AllQueryExecModes, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\th := pgtype.Hstore{\"should_be_erased\": nil}\n\t\terr := conn.QueryRow(ctx, `select cast(null as hstore)`).Scan(&h)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\texpectedNil := pgtype.Hstore(nil)\n\t\tif !reflect.DeepEqual(h, expectedNil) {\n\t\t\tt.Errorf(\"plain conn.Scan failed expectedNil=%#v actual=%#v\", expectedNil, h)\n\t\t}\n\n\t\terr = conn.QueryRow(ctx, `select cast('' as hstore)`).Scan(&h)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\texpectedEmpty := pgtype.Hstore{}\n\t\tif !reflect.DeepEqual(h, expectedEmpty) {\n\t\t\tt.Errorf(\"plain conn.Scan failed expectedEmpty=%#v actual=%#v\", expectedEmpty, h)\n\t\t}\n\t})\n}\n\nfunc TestParseInvalidInputs(t *testing.T) {\n\t// these inputs should be invalid, but previously were considered correct\n\tinvalidInputs := []string{\n\t\t// extra comma between values\n\t\t`\"a\"=>\"1\", ,b\"=>\"2\"`,\n\t\t// missing doublequote before second value\n\t\t`\"\"=>\"\", 0\"=>\"\"`,\n\t}\n\tfor i, input := range invalidInputs {\n\t\tvar hstore pgtype.Hstore\n\t\terr := hstore.Scan(input)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"test %d: input=%s (%#v) should fail; parsed correctly\", i, input, input)\n\t\t}\n\t}\n}\n\nfunc TestRoundTrip(t *testing.T) {\n\tcodecs := []struct {\n\t\tname       string\n\t\tencodePlan pgtype.EncodePlan\n\t\tscanPlan   pgtype.ScanPlan\n\t}{\n\t\t{\n\t\t\t\"text\",\n\t\t\tpgtype.HstoreCodec{}.PlanEncode(nil, 0, pgtype.TextFormatCode, pgtype.Hstore(nil)),\n\t\t\tpgtype.HstoreCodec{}.PlanScan(nil, 0, pgtype.TextFormatCode, (*pgtype.Hstore)(nil)),\n\t\t},\n\t\t{\n\t\t\t\"binary\",\n\t\t\tpgtype.HstoreCodec{}.PlanEncode(nil, 0, pgtype.BinaryFormatCode, pgtype.Hstore(nil)),\n\t\t\tpgtype.HstoreCodec{}.PlanScan(nil, 0, pgtype.BinaryFormatCode, (*pgtype.Hstore)(nil)),\n\t\t},\n\t}\n\n\tinputs := []pgtype.Hstore{\n\t\tnil,\n\t\t{},\n\t\t{\"\": stringPtr(\"\")},\n\t\t{\"k1\": stringPtr(\"v1\")},\n\t\t{\"k1\": stringPtr(\"v1\"), \"k2\": stringPtr(\"v2\")},\n\t}\n\tfor _, codec := range codecs {\n\t\tfor i, input := range inputs {\n\t\t\tt.Run(fmt.Sprintf(\"%s/%d\", codec.name, i), func(t *testing.T) {\n\t\t\t\tserialized, err := codec.encodePlan.Encode(input, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tvar output pgtype.Hstore\n\t\t\t\terr = codec.scanPlan.Scan(serialized, &output)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(output, input) {\n\t\t\t\t\tt.Errorf(\"output=%#v does not match input=%#v\", output, input)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc BenchmarkHstoreEncode(b *testing.B) {\n\th := pgtype.Hstore{\n\t\t\"a x\": stringPtr(\"100\"), \"b\": stringPtr(\"200\"), \"c\": stringPtr(\"300\"),\n\t\t\"d\": stringPtr(\"400\"), \"e\": stringPtr(\"500\"),\n\t}\n\n\tserializeConfigs := []struct {\n\t\tname       string\n\t\tencodePlan pgtype.EncodePlan\n\t}{\n\t\t{\"text\", pgtype.HstoreCodec{}.PlanEncode(nil, 0, pgtype.TextFormatCode, h)},\n\t\t{\"binary\", pgtype.HstoreCodec{}.PlanEncode(nil, 0, pgtype.BinaryFormatCode, h)},\n\t}\n\n\tfor _, serializeConfig := range serializeConfigs {\n\t\tvar buf []byte\n\t\tb.Run(serializeConfig.name, func(b *testing.B) {\n\t\t\tfor b.Loop() {\n\t\t\t\tvar err error\n\t\t\t\tbuf, err = serializeConfig.encodePlan.Encode(h, buf)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tbuf = buf[:0]\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkHstoreScan(b *testing.B) {\n\t// empty, NULL, escapes, and based on some real data\n\tbenchStrings := []string{\n\t\t\"\",\n\t\t`\"a\"=>\"b\"`,\n\t\t`\"a\"=>\"100\", \"b\"=>\"200\", \"c\"=>\"300\", \"d\"=>\"400\", \"e\"=>\"500\"`,\n\t\t`\"a\"=>\"100\", \"b\"=>NULL, \"c\"=>\"300\", \"d\"=>NULL, \"e\"=>\"500\"`,\n\t\t`\"pmd\"=>\"piokifjzxdy:mhvvmotns:sf1-dttudcp-orx-fuwzw-j8o-tl-jcg-1fb5d6dp50ke3l24\", \"ausz\"=>\"aorc-iosdby_tbxsjihj-kss64-32r128y-i2\", \"mgjo\"=>\"hxcp-ciag\", \"hkbee\"=>\"bokihheb\", \"gpcvhc\"=>\"ne-ywik-1\", \"olzjegk\"=>\"rxbkzba\", \"iy_quthhf\"=>\"sryizraxx\", \"bwpdpplfz\"=>\"gbdh-jikmnp_jwugdvjs-drh64-32k128h-p2\", \"njy_veipyyl\"=>\"727006795293\", \"vsgvqlrnqadzvk\"=>\"1_7_43\", \"mfdncuqvxp_gqlkytj\"=>\"fuyin\", \"cnuiswkwavoupqebov\"=>\"x32n128w\", \"mol_lcabioescln_ulstxauvi\"=>\"qm1-adbcand-tzi-fpnbv-s8j-vi-gqs-1om5b6lx50zk3u24\", \"arlyhgdxux.fc/bezucmz/mmfed\"=>\"vihsk\", \"jtkf.czddftrhr.ici/qbq_ftaz\"=>\"sse64\", \"notxkfqmpq.whxmykhtc.bcu/zmxz\"=>\"zauaklqp-uwo64-32q128a-g2\", \"ww_affdwqa_o8o_ilskcucq_urzltnf\"=>\"i6-9-0\", \"f8d.eq/bbqxwru-vsznvxerae/wsszbjw\"=>\"dgd\", \"ygpghkljze.dkrlrrieo.iur/xfqdqreft\"=>\"pfby-bhqlmm\", \"pmho-dqxuezyuu.ppslmznja.eam/ikehtxg\"=>\"wbku\", \"ckqeavtcqk.jiqdipgji.hjl/luzgqb-agm-wb\"=>\"ikpq\", \"akcn-yobdpxkyl.gktsjdo-xqwmivixku.p8y.vq/axqdw\"=>\"\", \"r8u.at/fbqrrss-ihxjmygoyc/ztqe-pqqqewnz/nepdj/njjv\"=>\"txtlffpp:ebwdksxkej\", \"q8x.wu/wenlhkz-govetdoibn/rcwg-ticalfjq/mgipy/awmjl\"=>\"dyzvbzvi\", \"p8l.wx/vadrnki-yfqhzlwcnt/hvun-geqhjsik/eqediipfr/vlc\"=>\"31900z\", \"t8z.be/qbtsmci-jqnqphssdg/ejma-slvywzry/txpnybwvn/kxdl\"=>\"210\", \"o8b.nb/bijgpwm-axvvqgujax/fjli-mxqwulfe/revyfoyty/oojpsd\"=>\"123421925786\", \"p8q.sk/ccpgzee-ufjempgvty/afwh-qvwzjvog/hsyhr/bklplujbfydtfw\"=>\"1_7_43\", \"k8y.jp/hqoymrw-flwqwvbntf/dlli-uggxkdqv/mtutu/qotjmacjitwtvcnblr\"=>\"m32x128f\", \"r8z.hj/eczodcw-lxzmeeqqii/fjba-psyoidht/gfjjcdbqs/apkqxiznu-muzubvl\"=>\"106068512341\", \"u8v.nf/ocnahkw-prhuwrrbjg/gxms-isohcouc/txfle/zfzw.neyygeeur.ejv/rnd_vdyo\"=>\"ibx64\", \"i8c.zz/dtiulqn-mmbskzjcib/fxuj-ejxbrnqi/optyp/wbbrancspv.pnkizgxcj.dbm/bldn\"=>\"znppnwzg-oxp64-32r128h-d2\", \"d8t.dg/jqtodoh-sokunyljow/svdf-ghplxxcx/wqkwl/dolljeqv.jcn.dxp.jmh.uyf/lyfv\"=>\"kc-lmpu-1i\", \"t8i.dy/imltbpr-atmthzarmk/fbbw-uaovyvdj/mmuwq/kseu-snmt.xtlgkstzph.mg/ehjdpgc\"=>\"\", \"o8c.yc/wximcpf-wmffadvnxx/tdim-szbqedqp/ztrui/puhx-kcwp.zziulqvvmb.ik/khfaxajj\"=>\"\", \"j8i.zc/sajavzi-kemnitliml/nloy-riqothpw/yxmnp/ttrnynffzy.lswpezbdq.wor/xkvqeexio\"=>\"ltmp-zajsxt\", \"a8f.xd/tfrrawy-ymihugugaa/ouzi-xdyecmqx/cwvgjvcrh/trgbxgbumo.uh/xmnqbds-nqxxeuqpq\"=>\"3123748065\", \"x8n.vx/juiqxkj-swvwogmncw/hvad-pojmevog/ytxit/auvo-duchssbth.uickilmnz.lja/hbeiakj\"=>\"hwhd\", \"z8j.bn/iplhrhv-wjdcwdclos/qndu-qvotchss/spvfx/brqotjnytw.aaemsoxor.ign/uwebjm-vzl-kb\"=>\"zwdg\", \"t8j.vx/iekvskm-xhikarvbty/czlm-xtipxwok/eeeow/uvtpuzmlqg.jgtpgiujc.wrs/mcofa-qxjjwak\"=>\"sovxb\", \"t8g.ab/wuncjdz-vsozsekgxz/aaea-hmgdjylm/qimwsoecgud-grgoowb/zveahbidvwcaebhlzigytiermehxy\"=>\"0.95\", \"n8k.ei/ohovibm-obkaatwlyw/bcow-gndyzpyt/aehyf/dpgifsorjx.ehsqntrka.jrr/meakdzy-ckxgnfavwm\"=>\"nlgw\", \"u8e.yi/qavbjew-qnmtzbeyce/rmwa-hcqlvadn/bhpml/taoj-wjnh.qqvkjmccfn.ja/nudbtwme-buc64-32j128i-k2\"=>\"\"`,\n\t\t`\"mbgs\"=>\"eqjillclhkxz\", \"bxci\"=>\"etksm.rudiu\", \"jijqqm\"=>\"kj-ryxhwqtco-2\", \"yivvcxy\"=>\"fwbujcu\", \"ybk_ztlajai\"=>\"601427279990\"`,\n\t\t`\"wte\"=>\"nrhw\", \"lqjm\"=>\"ifsbygchn\", \"wbmf\"=>\"amjsoykkwq\\\\ghvwbsmz-qeiv-iekd-ukcwbipzy\"`,\n\t\t`\"otx\"=>\"fcreomqbwtk:gqhxzhxuh:wrqo-rf1-avhdpfy-nqi-dldof-i8p-mw-jll-l5r9741753c3\", \"vbjy\"=>\"akzfspigip_muzyxzwuso-zvoifh-uw\", \"fmkb\"=>\"pkoe-lezf\", \"wfbq\"=>\"qoviagajeg\", \"zvxbiv\"=>\"db-bcngmoq-1\", \"olictqnpx\"=>\"taqcnrcwcj_ticfxydekq-fafbkg-ot\", \"wkt_jtzzqpt\"=>\"727006795293\", \"bsdncvmbvj_xivgkws\"=>\"zczag\", \"muzq.oyrphhtne.fqm/itc\"=>\"ihilzgx\", \"pfsd.xphmjdohu.hrm/yeimpfm\"=>\"lrrqxrwyud-uvcljo\", \"qukdxappwo.or/xgcsmdo/dodoj\"=>\"onflq\", \"ktqrsqtllo.xxxpkizlg.tnf/unrt\"=>\"jrveutvddu-loihei-ww\", \"tr_qmarsis_s8v_skzbuuvy_cnyuxyk\"=>\"g6-16-0\", \"z8q.yc/xistcyy-tftbikuuhg/zvhemmi\"=>\"knv\", \"zrgwpjnvzq.twkcxxuyk.qwc/nirbacaom\"=>\"okfdlcpbdg\", \"suvk-wwwjqdytq.wdjmzxl-nduettmnmf.e8e.ec/qhkan\"=>\"\", \"u8m.xa/uvbhlmw-rqrcyyaiju/otsg-bqjfitoq/zqfuq/fifo\"=>\"brarmrogdb\", \"b8o.ci/znwkyby-nzuxiguqus/nwou-cxxnqxrr/rtdsp/yawv\"=>\"juedpptnbt-khocdt-vg:vfxpdswxnc\", \"u8h.vl/kgmvysr-xhykrjcssj/jfjv-gzalgika/yhrjfytwz/kbm\"=>\"3900f\", \"y8b.cm/ttijscl-rznjossaqw/kvto-gvnavnep/bwdqyuzgo/ozoi\"=>\"40\", \"p8j.pd/bnucngv-vnqufgvfqw/qshw-obnkmlfx/obczheyis/zzbsos\"=>\"7009zf\", \"p8y.fc/ejbndrq-aariupaovi/mrah-hmrhjcsv/lvrmfwwiz/uskogxfuw-zamygae\"=>\"18747532246\", \"y8m.oh/xzuhilr-wqmqqzcznb/pcox-idpxmhfj/yzsoj/qebkjaeymc.abqznnelq.gyd/osvb\"=>\"hsgxlccalq-eeybug-mx\", \"p8f.ay/tyntrss-nljxedfihd/grvy-znfykhlf/fjsqd/ffxaixyv.jie.bkg.zpd.kim/mgtc\"=>\"or-vrkdcxm-1i\", \"i8m.ms/jtykfbi-jdrqsqjdwt/ibaq-zmeuyznf/uczny/ufmj-zklt.omodkgubqw.ip/xztdevd\"=>\"\", \"k8m.ui/ymxurqo-kuhofnewjj/twex-iuwljutj/warlx/zptkdgqdpr.uhvqtrclx.ohj/bdkgsozkk\"=>\"zlgisdikac\", \"g8b.wk/vecudfr-pljllpgzxi/lbwd-zsracrgq/fucssaowj/syizbmlfqt.si/swpbend-gxrhddxad\"=>\"156213905\", \"z8y.ah/azeasta-gffxfwklrn/hukw-hphwntwy/lfswv/tmaeaxekya.vgkxjhtvg.mht/bzolt-koioxpf\"=>\"wzkra\", \"f8l.sy/ouekhco-rlhsclfzwx/erfz-uuejogrs/bgvia/zpohrhmrmu.sbdxzlaxo.wii/jbnwfvz-shekbewool\"=>\"aiey\", \"j8w.pz/fjtkxhn-zxxizfldde/wsik-uiodldga/ljdtl/gswz-cjmt.ffkelhxcsd.lw/ftcqgdnnho-ibbfql-ww\"=>\"\"`,\n\t\t`\"uvd\"=>\"oneotg\", \"wsm\"=>\"djjgmwqyple:jtxtfvtjv:du1-nfxzmra-idl-ikxbx-t8n-id-nbo-6d08opx70381\", \"orq\"=>\"bkdvjw-xydgbd1\", \"gblm\"=>\"jtkcfd-unxbag1_xagyfw-nvachf1\", \"mfer\"=>\"jclz-yaim\", \"jvgvas\"=>\"jf-vhxh-1\", \"wwardeuqu\"=>\"ufimeb-bscfdy1_bfuagy-dhdqra1\", \"szs_rfgpqmc\"=>\"727006795293\", \"ckfxcgrnqc_rloxzxu\"=>\"qffbw\", \"yaigdvscju.ba/krpgzji/wvxyg\"=>\"srgtu\", \"gtxfjsigdv.pxujnffnp.aza/ycco\"=>\"ntranp-ahgeem1\", \"xj_lhdpvsl_i8i_qzrtlpjr_nroujqh\"=>\"q6-1-8\", \"czxy-sfym.enlohvvjmp.wb/huvcuhy\"=>\"\", \"x8a.of/sqpdqiq-vijrlgkkyl/oncckls\"=>\"mij\", \"oomgvfopmc.trnzktrtz.gza/rpeqqyqmm\"=>\"rgwnma-bwcbxe1\", \"gaud-giar.xuablvwkbo.wy/wvhmsk-uaycqn1\"=>\"\", \"oarbmcqzzw.qkfbtmltz.plh/aqssj-tlrhsof\"=>\"wxfd\", \"zepirccplb.qanvqnxlo.eld/emulnov-vgddsefeqv\"=>\"jnvh\", \"acby-kywxjuczc.suosfcy-drsgroeqvy.o8m.og/vyuxt\"=>\"\", \"q8j.by/lrwxbjt-yzrenlniog/gbmw-mnokcndu/etbcy/ibwr\"=>\"qpttug-jnxhwe1:grmslxhyky\", \"i8y.uy/awavkxk-nztmqujxys/pocu-sqjdqvzd/tfdjeflpn/xsj\"=>\"7900c\", \"z8g.ia/yzfdvta-ffkciorpfl/kmjc-fgcdomlv/snvhhbjil/nhvn\"=>\"45\", \"s8l.ky/dtvxoqu-lzfdnykmdh/wtdg-aktximmy/hofzkpzel/wtghso\"=>\"14837zg\", \"v8e.rq/uosznaz-drypoapgpe/vxss-mbxmvkjj/oglvxhxcz/whutvtjmr-tewtidr\"=>\"18747532246\", \"m8p.sz/hrgniti-aufhjdsdcc/whcp-cfuwjsnl/exugj/evphviokhl.ashpndixr.jvx/vgtt\"=>\"zdsacy-ppfuxf1\", \"w8t.fm/kljwjgc-fijbwsrvxa/dbzl-fhxvlrwk/yidyk/orrt-kgpr.wuzmpnxvtb.lc/dmbqfvt\"=>\"\", \"m8j.sv/takylmm-ywnolaflnl/ueih-fdcpfcpv/dslbc/dsspusnhtu.vgkihqtpb.fto/qmyksglfx\"=>\"wpwuih-deuiej1\", \"m8x.wi/jwobkio-mwupghbqbi/krqn-hqyfgwuw/mcbyi/yzkt-wtdy.pjxevrogab.tj/qlttbz-ppyzkd1\"=>\"\", \"c8j.tr/tzcbhid-lggaiypnny/wyms-zcjgxmwp/eaohd/bcwkheknsr.fqvtgecsf.qbf/uaqzj-jburpix\"=>\"ckkk\", \"w8h.wk/msbqvqy-nsmvbojwns/edpo-nsivbrmx/qifaf/sopuabsuvq.foyniwomd.zvj/lhvfwvv-zuufhhspso\"=>\"fghx\"`,\n\t\t`\"xxtlvd\"=>\"ba-zrzy-1\", \"hlebkcl\"=>\"entrcad\", \"ytn_toivqso\"=>\"601427279990\", \"czdllqyvkcfemhubpwvxakepubup\"=>\"jzhpff-vn2-sgiupfiii-qmuuz-ndex-vin-kmfm\", \"mefjcnjmcspgviisjalxmwdbksmge\"=>\"2022-11-20\"`,\n\t\t`\"ukq\"=>\"uhkbdj\", \"bmj\"=>\"mcoknsnhqcb:vmexvsccu:yt1-nscwdfr-zcp-ajfhr-z8i-ta-jhv-58yl03459t86\", \"cuq\"=>\"sqphbh-xkxbcgwdx\", \"dnac\"=>\"khzjpq-hljdvlbsw_azdisd-nshizhinc\", \"flgj\"=>\"zeem-pggu\", \"ksnn\"=>\"vpittgnl-xeojllby-toq\", \"wwxepg\"=>\"ki-cwee-1\", \"vigdnntxw\"=>\"sydsls-zidlsgugi_wviqvl-umwzyztab\", \"osz_utmlghi\"=>\"727006795293\", \"wacdaefqhc_buqmsci\"=>\"djtcv\", \"ljdbotgrsi.xn/gvtjfeg/iiyek\"=>\"lnfgg\", \"sohcclfodf.wkwiitult.ppm/hhsf\"=>\"ecpftm-ecmsibfjy\", \"dz_cgfnddq_o8j_cowdxlfz_rmjunpm\"=>\"v5-13-1\", \"niwk-fozq.tbamcxrhez.kl/zuxnisw\"=>\"\", \"h8k.xu/nbsezqz-fopcyqlnwt/lfcmgag\"=>\"dmm\", \"zebgpskksd.daigyeicb.dlj/dwmcpkohh\"=>\"hegecl-bnqmkunkl\", \"irjreiuove.qpmjixctw.mzv/xizjv-bpecdmy\"=>\"rkfl\", \"fupz-eiim.hwaqzvpzgv.yg/zhrqmr-qcydocyak\"=>\"\", \"djuscbflju.fmhnephvc.cmo/wzcisia-kqmrrhnkiv\"=>\"vchu\", \"hauo-olkeyvbrz.qzpaocu-wdbyfrzjkx.c8a.rn/bwhfe\"=>\"\", \"l8d.fj/jzojrmv-mbnxftbdzg/qvgo-oayrldze/tqmoa/oizo\"=>\"buwgyd-bjlrzrlci:ywosrfsnts\", \"q8l.sj/vifqvao-ynvfejvleb/ourc-jzridgtt/fgxnueuvm/wsg\"=>\"7900p\", \"d8b.mi/steijrv-bgajdbugff/kxkj-jhvctoxw/seyrafhni/xxrc\"=>\"45\", \"x8k.bn/dnnkttb-ywqrwwxirk/ngvt-eqyaeqsd/qesxmjfos/nlolbe\"=>\"14837xp\", \"v8o.az/vtbyyyo-rjuadsmwyb/gszv-ytnisfau/kfunvihsr/famkeacyo-skpueao\"=>\"18747532246\", \"f8w.ip/sjzrxbw-idgsgucprq/ster-zxiilwcf/luwzw/tavccuqfph.mcubdrtcr.ibw/dxnj\"=>\"ntyjnf-zwlyjqbfq\", \"y8f.mh/qykpkfr-fsnlckrhpe/hvyu-vstwrxkq/dmesn/kuor-acub.fqwqxcpiet.jf/zaxtdyb\"=>\"\", \"c8m.et/ekavnnp-gvpmldvoou/jzva-zzzpiecc/dvckb/qqxrfpoaiy.ssfqerwmb.cnz/odsfndorh\"=>\"liilkb-aekfuqzss\", \"e8n.gp/sybrxvz-mghjbpphpc/wcuo-naanbtcj/agtov/dztlgdacuz.fpbhhiybg.ncm/otgfu-hnezrwu\"=>\"ccez\", \"t8h.cy/bqsdiil-lxmioonwjt/drsw-qevzljvt/rvzjl/btbz-npvi.ypyxowgmfp.gf/jcfbyh-khpgbaayw\"=>\"\", \"y8b.df/anmudfn-gahfengbqw/fhdi-ozqtddmu/lvviu/kndwvowlby.jxkizwkac.hbq/fjkqyna-jijxahivma\"=>\"wxqg\"`,\n\t}\n\n\t// convert benchStrings into text and binary bytes\n\ttextBytes := make([][]byte, len(benchStrings))\n\tbinaryBytes := make([][]byte, len(benchStrings))\n\tcodec := pgtype.HstoreCodec{}.PlanEncode(nil, 0, pgtype.BinaryFormatCode, pgtype.Hstore(nil))\n\tfor i, s := range benchStrings {\n\t\ttextBytes[i] = []byte(s)\n\n\t\tvar tempH pgtype.Hstore\n\t\terr := tempH.Scan(s)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tbinaryBytes[i], err = codec.Encode(tempH, nil)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n\n\t// benchmark the database/sql.Scan API\n\tvar h pgtype.Hstore\n\tb.Run(\"databasesql.Scan\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\t\tfor b.Loop() {\n\t\t\tfor _, str := range benchStrings {\n\t\t\t\terr := h.Scan(str)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\n\t// benchmark the []byte scan API used by pgconn\n\tscanConfigs := []struct {\n\t\tname       string\n\t\tscanPlan   pgtype.ScanPlan\n\t\tinputBytes [][]byte\n\t}{\n\t\t{\"text\", pgtype.HstoreCodec{}.PlanScan(nil, 0, pgtype.TextFormatCode, &h), textBytes},\n\t\t{\"binary\", pgtype.HstoreCodec{}.PlanScan(nil, 0, pgtype.BinaryFormatCode, &h), binaryBytes},\n\t}\n\tfor _, scanConfig := range scanConfigs {\n\t\tb.Run(scanConfig.name, func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tfor b.Loop() {\n\t\t\t\tfor _, input := range scanConfig.inputBytes {\n\t\t\t\t\terr := scanConfig.scanPlan.Scan(input, &h)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Fatalf(\"input=%#v err=%s\", string(input), err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgtype/inet.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/netip\"\n)\n\n// Network address family is dependent on server socket.h value for AF_INET.\n// In practice, all platforms appear to have the same value. See\n// src/include/utils/inet.h for more information.\nconst (\n\tdefaultAFInet  = 2\n\tdefaultAFInet6 = 3\n)\n\ntype NetipPrefixScanner interface {\n\tScanNetipPrefix(v netip.Prefix) error\n}\n\ntype NetipPrefixValuer interface {\n\tNetipPrefixValue() (netip.Prefix, error)\n}\n\n// InetCodec handles both inet and cidr PostgreSQL types. The preferred Go types are [netip.Prefix] and [netip.Addr]. If\n// IsValid() is false then they are treated as SQL NULL.\ntype InetCodec struct{}\n\nfunc (InetCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (InetCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (InetCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(NetipPrefixValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanInetCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanInetCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanInetCodecBinary struct{}\n\nfunc (encodePlanInetCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tprefix, err := value.(NetipPrefixValuer).NetipPrefixValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !prefix.IsValid() {\n\t\treturn nil, nil\n\t}\n\n\tvar family byte\n\tif prefix.Addr().Is4() {\n\t\tfamily = defaultAFInet\n\t} else {\n\t\tfamily = defaultAFInet6\n\t}\n\n\tbuf = append(buf, family)\n\n\tones := prefix.Bits()\n\tbuf = append(buf, byte(ones))\n\n\t// is_cidr is ignored on server\n\tbuf = append(buf, 0)\n\n\tif family == defaultAFInet {\n\t\tbuf = append(buf, byte(4))\n\t\tb := prefix.Addr().As4()\n\t\tbuf = append(buf, b[:]...)\n\t} else {\n\t\tbuf = append(buf, byte(16))\n\t\tb := prefix.Addr().As16()\n\t\tbuf = append(buf, b[:]...)\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanInetCodecText struct{}\n\nfunc (encodePlanInetCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tprefix, err := value.(NetipPrefixValuer).NetipPrefixValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !prefix.IsValid() {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, prefix.String()...), nil\n}\n\nfunc (InetCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase NetipPrefixScanner:\n\t\t\treturn scanPlanBinaryInetToNetipPrefixScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase NetipPrefixScanner:\n\t\t\treturn scanPlanTextAnyToNetipPrefixScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c InetCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c InetCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar prefix netip.Prefix\n\terr := codecScan(c, m, oid, format, src, (*netipPrefixWrapper)(&prefix))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !prefix.IsValid() {\n\t\treturn nil, nil\n\t}\n\n\treturn prefix, nil\n}\n\ntype scanPlanBinaryInetToNetipPrefixScanner struct{}\n\nfunc (scanPlanBinaryInetToNetipPrefixScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(NetipPrefixScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanNetipPrefix(netip.Prefix{})\n\t}\n\n\tif len(src) != 8 && len(src) != 20 {\n\t\treturn fmt.Errorf(\"Received an invalid size for an inet: %d\", len(src))\n\t}\n\n\t// ignore family\n\tbits := src[1]\n\t// ignore is_cidr\n\t// ignore addressLength - implicit in length of message\n\n\taddr, ok := netip.AddrFromSlice(src[4:])\n\tif !ok {\n\t\treturn errors.New(\"netip.AddrFromSlice failed\")\n\t}\n\n\treturn scanner.ScanNetipPrefix(netip.PrefixFrom(addr, int(bits)))\n}\n\ntype scanPlanTextAnyToNetipPrefixScanner struct{}\n\nfunc (scanPlanTextAnyToNetipPrefixScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(NetipPrefixScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanNetipPrefix(netip.Prefix{})\n\t}\n\n\tvar prefix netip.Prefix\n\tif bytes.IndexByte(src, '/') == -1 {\n\t\taddr, err := netip.ParseAddr(string(src))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tprefix = netip.PrefixFrom(addr, addr.BitLen())\n\t} else {\n\t\tvar err error\n\t\tprefix, err = netip.ParsePrefix(string(src))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn scanner.ScanNetipPrefix(prefix)\n}\n"
  },
  {
    "path": "pgtype/inet_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqIPNet(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tap := a.(*net.IPNet)\n\t\tvp := v.(net.IPNet)\n\n\t\treturn ap.IP.Equal(vp.IP) && ap.Mask.String() == vp.Mask.String()\n\t}\n}\n\nfunc TestInetTranscode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"inet\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: mustParseInet(t, \"0.0.0.0/32\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"0.0.0.0/32\"))},\n\t\t{Param: mustParseInet(t, \"127.0.0.1/8\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"127.0.0.1/8\"))},\n\t\t{Param: mustParseInet(t, \"12.34.56.65/32\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"12.34.56.65/32\"))},\n\t\t{Param: mustParseInet(t, \"192.168.1.16/24\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"192.168.1.16/24\"))},\n\t\t{Param: mustParseInet(t, \"255.0.0.0/8\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"255.0.0.0/8\"))},\n\t\t{Param: mustParseInet(t, \"255.255.255.255/32\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"255.255.255.255/32\"))},\n\t\t{Param: mustParseInet(t, \"2607:f8b0:4009:80b::200e\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"2607:f8b0:4009:80b::200e\"))},\n\t\t{Param: mustParseInet(t, \"::1/64\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"::1/64\"))},\n\t\t{Param: mustParseInet(t, \"::/0\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"::/0\"))},\n\t\t{Param: mustParseInet(t, \"::1/128\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"::1/128\"))},\n\t\t{Param: mustParseInet(t, \"2607:f8b0:4009:80b::200e/64\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"2607:f8b0:4009:80b::200e/64\"))},\n\n\t\t{Param: mustParseInet(t, \"0.0.0.0/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"0.0.0.0/32\"))},\n\t\t{Param: mustParseInet(t, \"127.0.0.1/8\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"127.0.0.1/8\"))},\n\t\t{Param: mustParseInet(t, \"12.34.56.65/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"12.34.56.65/32\"))},\n\t\t{Param: mustParseInet(t, \"192.168.1.16/24\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"192.168.1.16/24\"))},\n\t\t{Param: mustParseInet(t, \"255.0.0.0/8\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"255.0.0.0/8\"))},\n\t\t{Param: mustParseInet(t, \"255.255.255.255/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"255.255.255.255/32\"))},\n\t\t{Param: mustParseInet(t, \"2607:f8b0:4009:80b::200e\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"2607:f8b0:4009:80b::200e/128\"))},\n\t\t{Param: mustParseInet(t, \"::1/64\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::1/64\"))},\n\t\t{Param: mustParseInet(t, \"::/0\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::/0\"))},\n\t\t{Param: mustParseInet(t, \"::1/128\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::1/128\"))},\n\t\t{Param: mustParseInet(t, \"2607:f8b0:4009:80b::200e/64\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"2607:f8b0:4009:80b::200e/64\"))},\n\n\t\t{Param: netip.MustParsePrefix(\"0.0.0.0/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"0.0.0.0/32\"))},\n\t\t{Param: netip.MustParsePrefix(\"127.0.0.1/8\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"127.0.0.1/8\"))},\n\t\t{Param: netip.MustParsePrefix(\"12.34.56.65/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"12.34.56.65/32\"))},\n\t\t{Param: netip.MustParsePrefix(\"192.168.1.16/24\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"192.168.1.16/24\"))},\n\t\t{Param: netip.MustParsePrefix(\"255.0.0.0/8\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"255.0.0.0/8\"))},\n\t\t{Param: netip.MustParsePrefix(\"255.255.255.255/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"255.255.255.255/32\"))},\n\t\t{Param: netip.MustParsePrefix(\"::1/64\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::1/64\"))},\n\t\t{Param: netip.MustParsePrefix(\"::/0\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::/0\"))},\n\t\t{Param: netip.MustParsePrefix(\"::1/128\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::1/128\"))},\n\t\t{Param: netip.MustParsePrefix(\"2607:f8b0:4009:80b::200e/64\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"2607:f8b0:4009:80b::200e/64\"))},\n\n\t\t{Param: netip.MustParseAddr(\"0.0.0.0\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"0.0.0.0\"))},\n\t\t{Param: netip.MustParseAddr(\"127.0.0.1\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"127.0.0.1\"))},\n\t\t{Param: netip.MustParseAddr(\"12.34.56.65\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"12.34.56.65\"))},\n\t\t{Param: netip.MustParseAddr(\"192.168.1.16\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"192.168.1.16\"))},\n\t\t{Param: netip.MustParseAddr(\"255.0.0.0\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"255.0.0.0\"))},\n\t\t{Param: netip.MustParseAddr(\"255.255.255.255\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"255.255.255.255\"))},\n\t\t{Param: netip.MustParseAddr(\"2607:f8b0:4009:80b::200e\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"2607:f8b0:4009:80b::200e\"))},\n\t\t{Param: netip.MustParseAddr(\"::1\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"::1\"))},\n\t\t{Param: netip.MustParseAddr(\"::\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"::\"))},\n\t\t{Param: netip.MustParseAddr(\"2607:f8b0:4009:80b::200e\"), Result: new(netip.Addr), Test: isExpectedEq(netip.MustParseAddr(\"2607:f8b0:4009:80b::200e\"))},\n\n\t\t{Param: nil, Result: new(netip.Prefix), Test: isExpectedEq(netip.Prefix{})},\n\t})\n}\n\nfunc TestCidrTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support cidr type (see https://github.com/cockroachdb/cockroach/issues/18846)\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"cidr\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: mustParseInet(t, \"0.0.0.0/32\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"0.0.0.0/32\"))},\n\t\t{Param: mustParseInet(t, \"127.0.0.1/32\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"127.0.0.1/32\"))},\n\t\t{Param: mustParseInet(t, \"12.34.56.0/32\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"12.34.56.0/32\"))},\n\t\t{Param: mustParseInet(t, \"192.168.1.0/24\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"192.168.1.0/24\"))},\n\t\t{Param: mustParseInet(t, \"255.0.0.0/8\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"255.0.0.0/8\"))},\n\t\t{Param: mustParseInet(t, \"::/128\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"::/128\"))},\n\t\t{Param: mustParseInet(t, \"::/0\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"::/0\"))},\n\t\t{Param: mustParseInet(t, \"::1/128\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"::1/128\"))},\n\t\t{Param: mustParseInet(t, \"2607:f8b0:4009:80b::200e/128\"), Result: new(net.IPNet), Test: isExpectedEqIPNet(mustParseInet(t, \"2607:f8b0:4009:80b::200e/128\"))},\n\n\t\t{Param: netip.MustParsePrefix(\"0.0.0.0/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"0.0.0.0/32\"))},\n\t\t{Param: netip.MustParsePrefix(\"127.0.0.1/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"127.0.0.1/32\"))},\n\t\t{Param: netip.MustParsePrefix(\"12.34.56.0/32\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"12.34.56.0/32\"))},\n\t\t{Param: netip.MustParsePrefix(\"192.168.1.0/24\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"192.168.1.0/24\"))},\n\t\t{Param: netip.MustParsePrefix(\"255.0.0.0/8\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"255.0.0.0/8\"))},\n\t\t{Param: netip.MustParsePrefix(\"::/128\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::/128\"))},\n\t\t{Param: netip.MustParsePrefix(\"::/0\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::/0\"))},\n\t\t{Param: netip.MustParsePrefix(\"::1/128\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"::1/128\"))},\n\t\t{Param: netip.MustParsePrefix(\"2607:f8b0:4009:80b::200e/128\"), Result: new(netip.Prefix), Test: isExpectedEq(netip.MustParsePrefix(\"2607:f8b0:4009:80b::200e/128\"))},\n\n\t\t{Param: nil, Result: new(netip.Prefix), Test: isExpectedEq(netip.Prefix{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/int.go",
    "content": "// Code generated from pgtype/int.go.erb. DO NOT EDIT.\n\npackage pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Int64Scanner interface {\n\tScanInt64(Int8) error\n}\n\ntype Int64Valuer interface {\n\tInt64Value() (Int8, error)\n}\n\ntype Int2 struct {\n\tInt16 int16\n\tValid bool\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (dst *Int2) ScanInt64(n Int8) error {\n\tif !n.Valid {\n\t\t*dst = Int2{}\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt16 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int2\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int2\", n.Int64)\n\t}\n\t*dst = Int2{Int16: int16(n.Int64), Valid: true}\n\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (n Int2) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(n.Int16), Valid: n.Valid}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int2) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Int2{}\n\t\treturn nil\n\t}\n\n\tvar n int64\n\n\tswitch src := src.(type) {\n\tcase int64:\n\t\tn = src\n\tcase string:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(src, 10, 16)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase []byte:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(string(src), 10, 16)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", src)\n\t}\n\n\tif n < math.MinInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int2\", n)\n\t}\n\tif n > math.MaxInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int2\", n)\n\t}\n\t*dst = Int2{Int16: int16(n), Valid: true}\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int2) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\treturn int64(src.Int16), nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Int2) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn []byte(strconv.FormatInt(int64(src.Int16), 10)), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Int2) UnmarshalJSON(b []byte) error {\n\tvar n *int16\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*dst = Int2{}\n\t} else {\n\t\t*dst = Int2{Int16: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Int2Codec struct{}\n\nfunc (Int2Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Int2Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Int2Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int16:\n\t\t\treturn encodePlanInt2CodecBinaryInt16{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt2CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int16:\n\t\t\treturn encodePlanInt2CodecTextInt16{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt2CodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanInt2CodecBinaryInt16 struct{}\n\nfunc (encodePlanInt2CodecBinaryInt16) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(int16)\n\treturn pgio.AppendInt16(buf, int16(n)), nil\n}\n\ntype encodePlanInt2CodecTextInt16 struct{}\n\nfunc (encodePlanInt2CodecTextInt16) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(int16)\n\treturn append(buf, strconv.FormatInt(int64(n), 10)...), nil\n}\n\ntype encodePlanInt2CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanInt2CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt16 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int2\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt16 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int2\", n.Int64)\n\t}\n\n\treturn pgio.AppendInt16(buf, int16(n.Int64)), nil\n}\n\ntype encodePlanInt2CodecTextInt64Valuer struct{}\n\nfunc (encodePlanInt2CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt16 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int2\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt16 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int2\", n.Int64)\n\t}\n\n\treturn append(buf, strconv.FormatInt(n.Int64, 10)...), nil\n}\n\nfunc (Int2Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanBinaryInt2ToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanBinaryInt2ToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanBinaryInt2ToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanBinaryInt2ToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanBinaryInt2ToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanBinaryInt2ToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanBinaryInt2ToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanBinaryInt2ToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanBinaryInt2ToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanBinaryInt2ToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryInt2ToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryInt2ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanTextAnyToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanTextAnyToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanTextAnyToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanTextAnyToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanTextAnyToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanTextAnyToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanTextAnyToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanTextAnyToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanTextAnyToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanTextAnyToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c Int2Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\nfunc (c Int2Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int16\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\ntype scanPlanBinaryInt2ToInt8 struct{}\n\nfunc (scanPlanBinaryInt2ToInt8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int16(binary.BigEndian.Uint16(src))\n\tif n < math.MinInt8 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int8\", n)\n\t} else if n > math.MaxInt8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int8\", n)\n\t}\n\n\t*p = int8(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToUint8 struct{}\n\nfunc (scanPlanBinaryInt2ToUint8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for uint2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int16(binary.BigEndian.Uint16(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint8\", n)\n\t}\n\n\tif n > math.MaxUint8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint8\", n)\n\t}\n\n\t*p = uint8(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToInt16 struct{}\n\nfunc (scanPlanBinaryInt2ToInt16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int16(binary.BigEndian.Uint16(src))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToUint16 struct{}\n\nfunc (scanPlanBinaryInt2ToUint16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for uint2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int16(binary.BigEndian.Uint16(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint16\", n)\n\t}\n\n\t*p = uint16(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToInt32 struct{}\n\nfunc (scanPlanBinaryInt2ToInt32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int32(int16(binary.BigEndian.Uint16(src)))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToUint32 struct{}\n\nfunc (scanPlanBinaryInt2ToUint32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for uint2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int16(binary.BigEndian.Uint16(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint32\", n)\n\t}\n\n\t*p = uint32(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToInt64 struct{}\n\nfunc (scanPlanBinaryInt2ToInt64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int64(int16(binary.BigEndian.Uint16(src)))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToUint64 struct{}\n\nfunc (scanPlanBinaryInt2ToUint64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for uint2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int16(binary.BigEndian.Uint16(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint64\", n)\n\t}\n\n\t*p = uint64(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToInt struct{}\n\nfunc (scanPlanBinaryInt2ToInt) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int(int16(binary.BigEndian.Uint16(src)))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToUint struct{}\n\nfunc (scanPlanBinaryInt2ToUint) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for uint2: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(int16(binary.BigEndian.Uint16(src)))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint\", n)\n\t}\n\n\t*p = uint(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt2ToInt64Scanner struct{}\n\nfunc (scanPlanBinaryInt2ToInt64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Int64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanInt64(Int8{})\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tn := int64(int16(binary.BigEndian.Uint16(src)))\n\n\treturn s.ScanInt64(Int8{Int64: n, Valid: true})\n}\n\ntype scanPlanBinaryInt2ToTextScanner struct{}\n\nfunc (scanPlanBinaryInt2ToTextScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 2 {\n\t\treturn fmt.Errorf(\"invalid length for int2: %v\", len(src))\n\t}\n\n\tn := int64(int16(binary.BigEndian.Uint16(src)))\n\n\treturn s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})\n}\n\ntype Int4 struct {\n\tInt32 int32\n\tValid bool\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (dst *Int4) ScanInt64(n Int8) error {\n\tif !n.Valid {\n\t\t*dst = Int4{}\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt32 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int4\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int4\", n.Int64)\n\t}\n\t*dst = Int4{Int32: int32(n.Int64), Valid: true}\n\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (n Int4) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(n.Int32), Valid: n.Valid}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int4) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Int4{}\n\t\treturn nil\n\t}\n\n\tvar n int64\n\n\tswitch src := src.(type) {\n\tcase int64:\n\t\tn = src\n\tcase string:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(src, 10, 32)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase []byte:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(string(src), 10, 32)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", src)\n\t}\n\n\tif n < math.MinInt32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int4\", n)\n\t}\n\tif n > math.MaxInt32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int4\", n)\n\t}\n\t*dst = Int4{Int32: int32(n), Valid: true}\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int4) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\treturn int64(src.Int32), nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Int4) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn []byte(strconv.FormatInt(int64(src.Int32), 10)), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Int4) UnmarshalJSON(b []byte) error {\n\tvar n *int32\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*dst = Int4{}\n\t} else {\n\t\t*dst = Int4{Int32: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Int4Codec struct{}\n\nfunc (Int4Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Int4Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Int4Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int32:\n\t\t\treturn encodePlanInt4CodecBinaryInt32{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt4CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int32:\n\t\t\treturn encodePlanInt4CodecTextInt32{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt4CodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanInt4CodecBinaryInt32 struct{}\n\nfunc (encodePlanInt4CodecBinaryInt32) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(int32)\n\treturn pgio.AppendInt32(buf, int32(n)), nil\n}\n\ntype encodePlanInt4CodecTextInt32 struct{}\n\nfunc (encodePlanInt4CodecTextInt32) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(int32)\n\treturn append(buf, strconv.FormatInt(int64(n), 10)...), nil\n}\n\ntype encodePlanInt4CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanInt4CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt32 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int4\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt32 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int4\", n.Int64)\n\t}\n\n\treturn pgio.AppendInt32(buf, int32(n.Int64)), nil\n}\n\ntype encodePlanInt4CodecTextInt64Valuer struct{}\n\nfunc (encodePlanInt4CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt32 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int4\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt32 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int4\", n.Int64)\n\t}\n\n\treturn append(buf, strconv.FormatInt(n.Int64, 10)...), nil\n}\n\nfunc (Int4Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanBinaryInt4ToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanBinaryInt4ToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanBinaryInt4ToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanBinaryInt4ToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanBinaryInt4ToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanBinaryInt4ToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanBinaryInt4ToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanBinaryInt4ToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanBinaryInt4ToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanBinaryInt4ToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryInt4ToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryInt4ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanTextAnyToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanTextAnyToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanTextAnyToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanTextAnyToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanTextAnyToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanTextAnyToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanTextAnyToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanTextAnyToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanTextAnyToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanTextAnyToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c Int4Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\nfunc (c Int4Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int32\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\ntype scanPlanBinaryInt4ToInt8 struct{}\n\nfunc (scanPlanBinaryInt4ToInt8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tif n < math.MinInt8 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int8\", n)\n\t} else if n > math.MaxInt8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int8\", n)\n\t}\n\n\t*p = int8(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToUint8 struct{}\n\nfunc (scanPlanBinaryInt4ToUint8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint8\", n)\n\t}\n\n\tif n > math.MaxUint8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint8\", n)\n\t}\n\n\t*p = uint8(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToInt16 struct{}\n\nfunc (scanPlanBinaryInt4ToInt16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tif n < math.MinInt16 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int16\", n)\n\t} else if n > math.MaxInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int16\", n)\n\t}\n\n\t*p = int16(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToUint16 struct{}\n\nfunc (scanPlanBinaryInt4ToUint16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint16\", n)\n\t}\n\n\tif n > math.MaxUint16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint16\", n)\n\t}\n\n\t*p = uint16(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToInt32 struct{}\n\nfunc (scanPlanBinaryInt4ToInt32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int32(binary.BigEndian.Uint32(src))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToUint32 struct{}\n\nfunc (scanPlanBinaryInt4ToUint32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint32\", n)\n\t}\n\n\t*p = uint32(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToInt64 struct{}\n\nfunc (scanPlanBinaryInt4ToInt64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int64(int32(binary.BigEndian.Uint32(src)))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToUint64 struct{}\n\nfunc (scanPlanBinaryInt4ToUint64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int32(binary.BigEndian.Uint32(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint64\", n)\n\t}\n\n\t*p = uint64(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToInt struct{}\n\nfunc (scanPlanBinaryInt4ToInt) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int(int32(binary.BigEndian.Uint32(src)))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToUint struct{}\n\nfunc (scanPlanBinaryInt4ToUint) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint4: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(int32(binary.BigEndian.Uint32(src)))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint\", n)\n\t}\n\n\t*p = uint(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt4ToInt64Scanner struct{}\n\nfunc (scanPlanBinaryInt4ToInt64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Int64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanInt64(Int8{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tn := int64(int32(binary.BigEndian.Uint32(src)))\n\n\treturn s.ScanInt64(Int8{Int64: n, Valid: true})\n}\n\ntype scanPlanBinaryInt4ToTextScanner struct{}\n\nfunc (scanPlanBinaryInt4ToTextScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for int4: %v\", len(src))\n\t}\n\n\tn := int64(int32(binary.BigEndian.Uint32(src)))\n\n\treturn s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})\n}\n\ntype Int8 struct {\n\tInt64 int64\n\tValid bool\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (dst *Int8) ScanInt64(n Int8) error {\n\tif !n.Valid {\n\t\t*dst = Int8{}\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt64 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int8\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt64 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int8\", n.Int64)\n\t}\n\t*dst = Int8{Int64: int64(n.Int64), Valid: true}\n\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (n Int8) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(n.Int64), Valid: n.Valid}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int8) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Int8{}\n\t\treturn nil\n\t}\n\n\tvar n int64\n\n\tswitch src := src.(type) {\n\tcase int64:\n\t\tn = src\n\tcase string:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(src, 10, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase []byte:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(string(src), 10, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", src)\n\t}\n\n\tif n < math.MinInt64 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int8\", n)\n\t}\n\tif n > math.MaxInt64 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int8\", n)\n\t}\n\t*dst = Int8{Int64: int64(n), Valid: true}\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int8) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\treturn int64(src.Int64), nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Int8) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn []byte(strconv.FormatInt(int64(src.Int64), 10)), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Int8) UnmarshalJSON(b []byte) error {\n\tvar n *int64\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*dst = Int8{}\n\t} else {\n\t\t*dst = Int8{Int64: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Int8Codec struct{}\n\nfunc (Int8Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Int8Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Int8Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int64:\n\t\t\treturn encodePlanInt8CodecBinaryInt64{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt8CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int64:\n\t\t\treturn encodePlanInt8CodecTextInt64{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt8CodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanInt8CodecBinaryInt64 struct{}\n\nfunc (encodePlanInt8CodecBinaryInt64) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(int64)\n\treturn pgio.AppendInt64(buf, int64(n)), nil\n}\n\ntype encodePlanInt8CodecTextInt64 struct{}\n\nfunc (encodePlanInt8CodecTextInt64) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn := value.(int64)\n\treturn append(buf, strconv.FormatInt(int64(n), 10)...), nil\n}\n\ntype encodePlanInt8CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanInt8CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt64 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int8\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt64 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int8\", n.Int64)\n\t}\n\n\treturn pgio.AppendInt64(buf, int64(n.Int64)), nil\n}\n\ntype encodePlanInt8CodecTextInt64Valuer struct{}\n\nfunc (encodePlanInt8CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt64 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int8\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt64 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int8\", n.Int64)\n\t}\n\n\treturn append(buf, strconv.FormatInt(n.Int64, 10)...), nil\n}\n\nfunc (Int8Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanBinaryInt8ToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanBinaryInt8ToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanBinaryInt8ToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanBinaryInt8ToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanBinaryInt8ToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanBinaryInt8ToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanBinaryInt8ToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanBinaryInt8ToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanBinaryInt8ToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanBinaryInt8ToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryInt8ToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryInt8ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanTextAnyToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanTextAnyToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanTextAnyToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanTextAnyToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanTextAnyToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanTextAnyToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanTextAnyToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanTextAnyToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanTextAnyToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanTextAnyToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c Int8Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\nfunc (c Int8Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\ntype scanPlanBinaryInt8ToInt8 struct{}\n\nfunc (scanPlanBinaryInt8ToInt8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < math.MinInt8 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int8\", n)\n\t} else if n > math.MaxInt8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int8\", n)\n\t}\n\n\t*p = int8(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToUint8 struct{}\n\nfunc (scanPlanBinaryInt8ToUint8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint8\", n)\n\t}\n\n\tif n > math.MaxUint8 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint8\", n)\n\t}\n\n\t*p = uint8(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToInt16 struct{}\n\nfunc (scanPlanBinaryInt8ToInt16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < math.MinInt16 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int16\", n)\n\t} else if n > math.MaxInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int16\", n)\n\t}\n\n\t*p = int16(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToUint16 struct{}\n\nfunc (scanPlanBinaryInt8ToUint16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint16\", n)\n\t}\n\n\tif n > math.MaxUint16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint16\", n)\n\t}\n\n\t*p = uint16(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToInt32 struct{}\n\nfunc (scanPlanBinaryInt8ToInt32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < math.MinInt32 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int32\", n)\n\t} else if n > math.MaxInt32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int32\", n)\n\t}\n\n\t*p = int32(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToUint32 struct{}\n\nfunc (scanPlanBinaryInt8ToUint32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint32\", n)\n\t}\n\n\tif n > math.MaxUint32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint32\", n)\n\t}\n\n\t*p = uint32(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToInt64 struct{}\n\nfunc (scanPlanBinaryInt8ToInt64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\t*p = int64(binary.BigEndian.Uint64(src))\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToUint64 struct{}\n\nfunc (scanPlanBinaryInt8ToUint64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint64\", n)\n\t}\n\n\t*p = uint64(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToInt struct{}\n\nfunc (scanPlanBinaryInt8ToInt) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(binary.BigEndian.Uint64(src))\n\tif n < math.MinInt {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int\", n)\n\t} else if n > math.MaxInt {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int\", n)\n\t}\n\n\t*p = int(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToUint struct{}\n\nfunc (scanPlanBinaryInt8ToUint) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint8: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(int64(binary.BigEndian.Uint64(src)))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint\", n)\n\t}\n\n\tif uint64(n) > math.MaxUint {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint\", n)\n\t}\n\n\t*p = uint(n)\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt8ToInt64Scanner struct{}\n\nfunc (scanPlanBinaryInt8ToInt64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Int64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanInt64(Int8{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tn := int64(int64(binary.BigEndian.Uint64(src)))\n\n\treturn s.ScanInt64(Int8{Int64: n, Valid: true})\n}\n\ntype scanPlanBinaryInt8ToTextScanner struct{}\n\nfunc (scanPlanBinaryInt8ToTextScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for int8: %v\", len(src))\n\t}\n\n\tn := int64(int64(binary.BigEndian.Uint64(src)))\n\n\treturn s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})\n}\n\ntype scanPlanTextAnyToInt8 struct{}\n\nfunc (scanPlanTextAnyToInt8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*int8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 8)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = int8(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToUint8 struct{}\n\nfunc (scanPlanTextAnyToUint8) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*uint8)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 8)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = uint8(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToInt16 struct{}\n\nfunc (scanPlanTextAnyToInt16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*int16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 16)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = int16(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToUint16 struct{}\n\nfunc (scanPlanTextAnyToUint16) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*uint16)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 16)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = uint16(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToInt32 struct{}\n\nfunc (scanPlanTextAnyToInt32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*int32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 32)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = int32(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToUint32 struct{}\n\nfunc (scanPlanTextAnyToUint32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*uint32)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 32)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = uint32(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToInt64 struct{}\n\nfunc (scanPlanTextAnyToInt64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*int64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = int64(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToUint64 struct{}\n\nfunc (scanPlanTextAnyToUint64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*uint64)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = uint64(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToInt struct{}\n\nfunc (scanPlanTextAnyToInt) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*int)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = int(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToUint struct{}\n\nfunc (scanPlanTextAnyToUint) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*uint)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = uint(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToInt64Scanner struct{}\n\nfunc (scanPlanTextAnyToInt64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Int64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanInt64(Int8{})\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = s.ScanInt64(Int8{Int64: n, Valid: true})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pgtype/int.go.erb",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Int64Scanner interface {\n\tScanInt64(Int8) error\n}\n\ntype Int64Valuer interface {\n\tInt64Value() (Int8, error)\n}\n\n\n<% [2, 4, 8].each do |pg_byte_size| %>\n<% pg_bit_size = pg_byte_size * 8 %>\ntype Int<%= pg_byte_size %> struct {\n\tInt<%= pg_bit_size %>   int<%= pg_bit_size %>\n\tValid bool\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (dst *Int<%= pg_byte_size %>) ScanInt64(n Int8) error {\n\tif !n.Valid {\n\t\t*dst = Int<%= pg_byte_size %>{}\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt<%= pg_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int<%= pg_byte_size %>\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt<%= pg_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int<%= pg_byte_size %>\", n.Int64)\n\t}\n\t*dst = Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: int<%= pg_bit_size %>(n.Int64), Valid: true}\n\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (n Int<%= pg_byte_size %>) Int64Value() (Int8, error) {\n\treturn Int8{Int64: int64(n.Int<%= pg_bit_size %>), Valid: n.Valid}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int<%= pg_byte_size %>) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Int<%= pg_byte_size %>{}\n\t\treturn nil\n\t}\n\n\tvar n int64\n\n\tswitch src := src.(type) {\n\tcase int64:\n\t\tn = src\n\tcase string:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(src, 10, <%= pg_bit_size %>)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase []byte:\n\t\tvar err error\n\t\tn, err = strconv.ParseInt(string(src), 10, <%= pg_bit_size %>)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", src)\n\t}\n\n\tif n < math.MinInt<%= pg_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int<%= pg_byte_size %>\", n)\n\t}\n\tif n > math.MaxInt<%= pg_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int<%= pg_byte_size %>\", n)\n\t}\n\t*dst = Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: int<%= pg_bit_size %>(n), Valid: true}\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int<%= pg_byte_size %>) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\treturn int64(src.Int<%= pg_bit_size %>), nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Int<%= pg_byte_size %>) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn []byte(strconv.FormatInt(int64(src.Int<%= pg_bit_size %>), 10)), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Int<%= pg_byte_size %>) UnmarshalJSON(b []byte) error {\n\tvar n *int<%= pg_bit_size %>\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*dst = Int<%= pg_byte_size %>{}\n\t} else {\n\t\t*dst = Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Int<%= pg_byte_size %>Codec struct{}\n\nfunc (Int<%= pg_byte_size %>Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Int<%= pg_byte_size %>Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Int<%= pg_byte_size %>Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int<%= pg_bit_size %>:\n\t\t\treturn encodePlanInt<%= pg_byte_size %>CodecBinaryInt<%= pg_bit_size %>{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt<%= pg_byte_size %>CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase int<%= pg_bit_size %>:\n\t\t\treturn encodePlanInt<%= pg_byte_size %>CodecTextInt<%= pg_bit_size %>{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanInt<%= pg_byte_size %>CodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanInt<%= pg_byte_size %>CodecBinaryInt<%= pg_bit_size %> struct{}\n\nfunc (encodePlanInt<%= pg_byte_size %>CodecBinaryInt<%= pg_bit_size %>) Encode(value any, buf []byte) (newBuf []byte, err error) {\n  n := value.(int<%= pg_bit_size %>)\n  return pgio.AppendInt<%= pg_bit_size %>(buf, int<%= pg_bit_size %>(n)), nil\n}\n\ntype encodePlanInt<%= pg_byte_size %>CodecTextInt<%= pg_bit_size %> struct{}\n\nfunc (encodePlanInt<%= pg_byte_size %>CodecTextInt<%= pg_bit_size %>) Encode(value any, buf []byte) (newBuf []byte, err error) {\n  n := value.(int<%= pg_bit_size %>)\n  return append(buf, strconv.FormatInt(int64(n), 10)...), nil\n}\n\ntype encodePlanInt<%= pg_byte_size %>CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanInt<%= pg_byte_size %>CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n  n, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt<%= pg_bit_size %> {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int<%= pg_byte_size %>\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt<%= pg_bit_size %> {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int<%= pg_byte_size %>\", n.Int64)\n\t}\n\n  return pgio.AppendInt<%= pg_bit_size %>(buf, int<%= pg_bit_size %>(n.Int64)), nil\n}\n\ntype encodePlanInt<%= pg_byte_size %>CodecTextInt64Valuer struct{}\n\nfunc (encodePlanInt<%= pg_byte_size %>CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n  n, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.Int64 > math.MaxInt<%= pg_bit_size %> {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for int<%= pg_byte_size %>\", n.Int64)\n\t}\n\tif n.Int64 < math.MinInt<%= pg_bit_size %> {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for int<%= pg_byte_size %>\", n.Int64)\n\t}\n\n  return append(buf, strconv.FormatInt(n.Int64, 10)...), nil\n}\n\nfunc (Int<%= pg_byte_size %>Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *int8:\n\t\t\treturn scanPlanTextAnyToInt8{}\n\t\tcase *int16:\n\t\t\treturn scanPlanTextAnyToInt16{}\n\t\tcase *int32:\n\t\t\treturn scanPlanTextAnyToInt32{}\n\t\tcase *int64:\n\t\t\treturn scanPlanTextAnyToInt64{}\n\t\tcase *int:\n\t\t\treturn scanPlanTextAnyToInt{}\n\t\tcase *uint8:\n\t\t\treturn scanPlanTextAnyToUint8{}\n\t\tcase *uint16:\n\t\t\treturn scanPlanTextAnyToUint16{}\n\t\tcase *uint32:\n\t\t\treturn scanPlanTextAnyToUint32{}\n\t\tcase *uint64:\n\t\t\treturn scanPlanTextAnyToUint64{}\n\t\tcase *uint:\n\t\t\treturn scanPlanTextAnyToUint{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c Int<%= pg_byte_size %>Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\nfunc (c Int<%= pg_byte_size %>Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n int<%= pg_bit_size %>\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\n<%# PostgreSQL binary format integer to fixed size Go integers %>\n<% [8, 16, 32, 64].each do |dst_bit_size| %>\ntype scanPlanBinaryInt<%= pg_byte_size %>ToInt<%= dst_bit_size %> struct{}\n\nfunc (scanPlanBinaryInt<%= pg_byte_size %>ToInt<%= dst_bit_size %>) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != <%= pg_byte_size %> {\n\t\treturn fmt.Errorf(\"invalid length for int<%= pg_byte_size %>: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int<%= dst_bit_size %>)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n  <% if dst_bit_size < pg_bit_size %>\n\tn := int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))\n\tif n < math.MinInt<%= dst_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int<%= dst_bit_size %>\", n)\n\t} else if n > math.MaxInt<%= dst_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int<%= dst_bit_size %>\", n)\n\t}\n\n\t*p = int<%= dst_bit_size %>(n)\n  <% elsif dst_bit_size == pg_bit_size %>\n\t*p = int<%= dst_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))\n  <% else %>\n\t*p = int<%= dst_bit_size %>(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)))\n  <% end %>\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt<%= pg_byte_size %>ToUint<%= dst_bit_size %> struct{}\n\nfunc (scanPlanBinaryInt<%= pg_byte_size %>ToUint<%= dst_bit_size %>) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != <%= pg_byte_size %> {\n\t\treturn fmt.Errorf(\"invalid length for uint<%= pg_byte_size %>: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint<%= dst_bit_size %>)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint<%= dst_bit_size %>\", n)\n\t}\n  <% if dst_bit_size < pg_bit_size %>\n\tif n > math.MaxUint<%= dst_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint<%= dst_bit_size %>\", n)\n\t}\n  <% end %>\n\t*p = uint<%= dst_bit_size %>(n)\n\n\treturn nil\n}\n<% end %>\n\n<%# PostgreSQL binary format integer to Go machine integers %>\ntype scanPlanBinaryInt<%= pg_byte_size %>ToInt struct{}\n\nfunc (scanPlanBinaryInt<%= pg_byte_size %>ToInt) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != <%= pg_byte_size %> {\n\t\treturn fmt.Errorf(\"invalid length for int<%= pg_byte_size %>: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*int)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n  <% if 32 < pg_bit_size %>\n\tn := int64(binary.BigEndian.Uint<%= pg_bit_size %>(src))\n\tif n < math.MinInt {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for int\", n)\n\t} else if n > math.MaxInt {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for int\", n)\n\t}\n\n\t*p = int(n)\n  <% else %>\n\t*p = int(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)))\n  <% end %>\n\n\treturn nil\n}\n\ntype scanPlanBinaryInt<%= pg_byte_size %>ToUint struct{}\n\nfunc (scanPlanBinaryInt<%= pg_byte_size %>ToUint) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != <%= pg_byte_size %> {\n\t\treturn fmt.Errorf(\"invalid length for uint<%= pg_byte_size %>: %v\", len(src))\n\t}\n\n\tp, ok := (dst).(*uint)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)))\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for uint\", n)\n\t}\n  <% if 32 < pg_bit_size %>\n\tif uint64(n) > math.MaxUint {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for uint\", n)\n\t}\n  <% end %>\n\t*p = uint(n)\n\n\treturn nil\n}\n\n<%# PostgreSQL binary format integer to Go Int64Scanner %>\ntype scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner struct{}\n\nfunc (scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Int64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n    return s.ScanInt64(Int8{})\n\t}\n\n\tif len(src) != <%= pg_byte_size %> {\n\t\treturn fmt.Errorf(\"invalid length for int<%= pg_byte_size %>: %v\", len(src))\n\t}\n\n\n\tn := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)))\n\n  return s.ScanInt64(Int8{Int64: n, Valid: true})\n}\n\n<%# PostgreSQL binary format integer to Go TextScanner %>\ntype scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner struct{}\n\nfunc (scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n    return s.ScanText(Text{})\n\t}\n\n\tif len(src) != <%= pg_byte_size %> {\n\t\treturn fmt.Errorf(\"invalid length for int<%= pg_byte_size %>: %v\", len(src))\n\t}\n\n\n\tn := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)))\n\n  return s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})\n}\n<% end %>\n\n<%# Any text to all integer types %>\n<% [\n  [\"8\", 8],\n  [\"16\", 16],\n  [\"32\", 32],\n  [\"64\", 64],\n  [\"\", 0]\n].each do |type_suffix, bit_size| %>\ntype scanPlanTextAnyToInt<%= type_suffix %> struct{}\n\nfunc (scanPlanTextAnyToInt<%= type_suffix %>) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*int<%= type_suffix %>)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, <%= bit_size %>)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = int<%= type_suffix %>(n)\n\treturn nil\n}\n\ntype scanPlanTextAnyToUint<%= type_suffix %> struct{}\n\nfunc (scanPlanTextAnyToUint<%= type_suffix %>) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp, ok := (dst).(*uint<%= type_suffix %>)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, <%= bit_size %>)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = uint<%= type_suffix %>(n)\n\treturn nil\n}\n<% end %>\n\ntype scanPlanTextAnyToInt64Scanner struct{}\n\nfunc (scanPlanTextAnyToInt64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Int64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n    return s.ScanInt64(Int8{})\n\t}\n\n\tn, err := strconv.ParseInt(string(src), 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n  err = s.ScanInt64(Int8{Int64: n, Valid: true})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pgtype/int_test.go",
    "content": "// Code generated from pgtype/int_test.go.erb. DO NOT EDIT.\n\npackage pgtype_test\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestInt2Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int2\", []pgxtest.ValueRoundTripTest{\n\t\t{int8(1), new(int16), isExpectedEq(int16(1))},\n\t\t{int16(1), new(int16), isExpectedEq(int16(1))},\n\t\t{int32(1), new(int16), isExpectedEq(int16(1))},\n\t\t{int64(1), new(int16), isExpectedEq(int16(1))},\n\t\t{uint8(1), new(int16), isExpectedEq(int16(1))},\n\t\t{uint16(1), new(int16), isExpectedEq(int16(1))},\n\t\t{uint32(1), new(int16), isExpectedEq(int16(1))},\n\t\t{uint64(1), new(int16), isExpectedEq(int16(1))},\n\t\t{int(1), new(int16), isExpectedEq(int16(1))},\n\t\t{uint(1), new(int16), isExpectedEq(int16(1))},\n\t\t{pgtype.Int2{Int16: 1, Valid: true}, new(int16), isExpectedEq(int16(1))},\n\t\t{int32(-1), new(pgtype.Int2), isExpectedEq(pgtype.Int2{Int16: -1, Valid: true})},\n\t\t{1, new(int8), isExpectedEq(int8(1))},\n\t\t{1, new(int16), isExpectedEq(int16(1))},\n\t\t{1, new(int32), isExpectedEq(int32(1))},\n\t\t{1, new(int64), isExpectedEq(int64(1))},\n\t\t{1, new(uint8), isExpectedEq(uint8(1))},\n\t\t{1, new(uint16), isExpectedEq(uint16(1))},\n\t\t{1, new(uint32), isExpectedEq(uint32(1))},\n\t\t{1, new(uint64), isExpectedEq(uint64(1))},\n\t\t{1, new(int), isExpectedEq(int(1))},\n\t\t{1, new(uint), isExpectedEq(uint(1))},\n\t\t{-1, new(int8), isExpectedEq(int8(-1))},\n\t\t{-1, new(int16), isExpectedEq(int16(-1))},\n\t\t{-1, new(int32), isExpectedEq(int32(-1))},\n\t\t{-1, new(int64), isExpectedEq(int64(-1))},\n\t\t{-1, new(int), isExpectedEq(int(-1))},\n\t\t{math.MinInt16, new(int16), isExpectedEq(int16(math.MinInt16))},\n\t\t{-1, new(int16), isExpectedEq(int16(-1))},\n\t\t{0, new(int16), isExpectedEq(int16(0))},\n\t\t{1, new(int16), isExpectedEq(int16(1))},\n\t\t{math.MaxInt16, new(int16), isExpectedEq(int16(math.MaxInt16))},\n\t\t{1, new(pgtype.Int2), isExpectedEq(pgtype.Int2{Int16: 1, Valid: true})},\n\t\t{\"1\", new(string), isExpectedEq(\"1\")},\n\t\t{pgtype.Int2{}, new(pgtype.Int2), isExpectedEq(pgtype.Int2{})},\n\t\t{nil, new(*int16), isExpectedEq((*int16)(nil))},\n\t})\n}\n\nfunc TestInt2MarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Int2\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Int2{Int16: 0}, result: \"null\"},\n\t\t{source: pgtype.Int2{Int16: 1, Valid: true}, result: \"1\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestInt2UnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Int2\n\t}{\n\t\t{source: \"null\", result: pgtype.Int2{Int16: 0}},\n\t\t{source: \"1\", result: pgtype.Int2{Int16: 1, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Int2\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n\nfunc TestInt4Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int4\", []pgxtest.ValueRoundTripTest{\n\t\t{int8(1), new(int32), isExpectedEq(int32(1))},\n\t\t{int16(1), new(int32), isExpectedEq(int32(1))},\n\t\t{int32(1), new(int32), isExpectedEq(int32(1))},\n\t\t{int64(1), new(int32), isExpectedEq(int32(1))},\n\t\t{uint8(1), new(int32), isExpectedEq(int32(1))},\n\t\t{uint16(1), new(int32), isExpectedEq(int32(1))},\n\t\t{uint32(1), new(int32), isExpectedEq(int32(1))},\n\t\t{uint64(1), new(int32), isExpectedEq(int32(1))},\n\t\t{int(1), new(int32), isExpectedEq(int32(1))},\n\t\t{uint(1), new(int32), isExpectedEq(int32(1))},\n\t\t{pgtype.Int4{Int32: 1, Valid: true}, new(int32), isExpectedEq(int32(1))},\n\t\t{int32(-1), new(pgtype.Int4), isExpectedEq(pgtype.Int4{Int32: -1, Valid: true})},\n\t\t{1, new(int8), isExpectedEq(int8(1))},\n\t\t{1, new(int16), isExpectedEq(int16(1))},\n\t\t{1, new(int32), isExpectedEq(int32(1))},\n\t\t{1, new(int64), isExpectedEq(int64(1))},\n\t\t{1, new(uint8), isExpectedEq(uint8(1))},\n\t\t{1, new(uint16), isExpectedEq(uint16(1))},\n\t\t{1, new(uint32), isExpectedEq(uint32(1))},\n\t\t{1, new(uint64), isExpectedEq(uint64(1))},\n\t\t{1, new(int), isExpectedEq(int(1))},\n\t\t{1, new(uint), isExpectedEq(uint(1))},\n\t\t{-1, new(int8), isExpectedEq(int8(-1))},\n\t\t{-1, new(int16), isExpectedEq(int16(-1))},\n\t\t{-1, new(int32), isExpectedEq(int32(-1))},\n\t\t{-1, new(int64), isExpectedEq(int64(-1))},\n\t\t{-1, new(int), isExpectedEq(int(-1))},\n\t\t{math.MinInt32, new(int32), isExpectedEq(int32(math.MinInt32))},\n\t\t{-1, new(int32), isExpectedEq(int32(-1))},\n\t\t{0, new(int32), isExpectedEq(int32(0))},\n\t\t{1, new(int32), isExpectedEq(int32(1))},\n\t\t{math.MaxInt32, new(int32), isExpectedEq(int32(math.MaxInt32))},\n\t\t{1, new(pgtype.Int4), isExpectedEq(pgtype.Int4{Int32: 1, Valid: true})},\n\t\t{\"1\", new(string), isExpectedEq(\"1\")},\n\t\t{pgtype.Int4{}, new(pgtype.Int4), isExpectedEq(pgtype.Int4{})},\n\t\t{nil, new(*int32), isExpectedEq((*int32)(nil))},\n\t})\n}\n\nfunc TestInt4MarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Int4\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Int4{Int32: 0}, result: \"null\"},\n\t\t{source: pgtype.Int4{Int32: 1, Valid: true}, result: \"1\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestInt4UnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Int4\n\t}{\n\t\t{source: \"null\", result: pgtype.Int4{Int32: 0}},\n\t\t{source: \"1\", result: pgtype.Int4{Int32: 1, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Int4\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n\nfunc TestInt8Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int8\", []pgxtest.ValueRoundTripTest{\n\t\t{int8(1), new(int64), isExpectedEq(int64(1))},\n\t\t{int16(1), new(int64), isExpectedEq(int64(1))},\n\t\t{int32(1), new(int64), isExpectedEq(int64(1))},\n\t\t{int64(1), new(int64), isExpectedEq(int64(1))},\n\t\t{uint8(1), new(int64), isExpectedEq(int64(1))},\n\t\t{uint16(1), new(int64), isExpectedEq(int64(1))},\n\t\t{uint32(1), new(int64), isExpectedEq(int64(1))},\n\t\t{uint64(1), new(int64), isExpectedEq(int64(1))},\n\t\t{int(1), new(int64), isExpectedEq(int64(1))},\n\t\t{uint(1), new(int64), isExpectedEq(int64(1))},\n\t\t{pgtype.Int8{Int64: 1, Valid: true}, new(int64), isExpectedEq(int64(1))},\n\t\t{int32(-1), new(pgtype.Int8), isExpectedEq(pgtype.Int8{Int64: -1, Valid: true})},\n\t\t{1, new(int8), isExpectedEq(int8(1))},\n\t\t{1, new(int16), isExpectedEq(int16(1))},\n\t\t{1, new(int32), isExpectedEq(int32(1))},\n\t\t{1, new(int64), isExpectedEq(int64(1))},\n\t\t{1, new(uint8), isExpectedEq(uint8(1))},\n\t\t{1, new(uint16), isExpectedEq(uint16(1))},\n\t\t{1, new(uint32), isExpectedEq(uint32(1))},\n\t\t{1, new(uint64), isExpectedEq(uint64(1))},\n\t\t{1, new(int), isExpectedEq(int(1))},\n\t\t{1, new(uint), isExpectedEq(uint(1))},\n\t\t{-1, new(int8), isExpectedEq(int8(-1))},\n\t\t{-1, new(int16), isExpectedEq(int16(-1))},\n\t\t{-1, new(int32), isExpectedEq(int32(-1))},\n\t\t{-1, new(int64), isExpectedEq(int64(-1))},\n\t\t{-1, new(int), isExpectedEq(int(-1))},\n\t\t{math.MinInt64, new(int64), isExpectedEq(int64(math.MinInt64))},\n\t\t{-1, new(int64), isExpectedEq(int64(-1))},\n\t\t{0, new(int64), isExpectedEq(int64(0))},\n\t\t{1, new(int64), isExpectedEq(int64(1))},\n\t\t{math.MaxInt64, new(int64), isExpectedEq(int64(math.MaxInt64))},\n\t\t{1, new(pgtype.Int8), isExpectedEq(pgtype.Int8{Int64: 1, Valid: true})},\n\t\t{\"1\", new(string), isExpectedEq(\"1\")},\n\t\t{pgtype.Int8{}, new(pgtype.Int8), isExpectedEq(pgtype.Int8{})},\n\t\t{nil, new(*int64), isExpectedEq((*int64)(nil))},\n\t})\n}\n\nfunc TestInt8MarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Int8\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Int8{Int64: 0}, result: \"null\"},\n\t\t{source: pgtype.Int8{Int64: 1, Valid: true}, result: \"1\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestInt8UnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Int8\n\t}{\n\t\t{source: \"null\", result: pgtype.Int8{Int64: 0}},\n\t\t{source: \"1\", result: pgtype.Int8{Int64: 1, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Int8\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/int_test.go.erb",
    "content": "package pgtype_test\n\nimport (\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n<% [2, 4, 8].each do |pg_byte_size| %>\n<% pg_bit_size = pg_byte_size * 8 %>\nfunc TestInt<%= pg_byte_size %>Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int<%= pg_byte_size %>\", []pgxtest.ValueRoundTripTest{\n\t\t{int8(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{int16(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{int32(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{int64(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{uint8(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{uint16(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{uint32(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{uint64(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{int(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{uint(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 1, Valid: true}, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{int32(-1), new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: -1, Valid: true})},\n\t\t{1, new(int8), isExpectedEq(int8(1))},\n\t\t{1, new(int16), isExpectedEq(int16(1))},\n\t\t{1, new(int32), isExpectedEq(int32(1))},\n\t\t{1, new(int64), isExpectedEq(int64(1))},\n\t\t{1, new(uint8), isExpectedEq(uint8(1))},\n\t\t{1, new(uint16), isExpectedEq(uint16(1))},\n\t\t{1, new(uint32), isExpectedEq(uint32(1))},\n\t\t{1, new(uint64), isExpectedEq(uint64(1))},\n\t\t{1, new(int), isExpectedEq(int(1))},\n\t\t{1, new(uint), isExpectedEq(uint(1))},\n\t\t{-1, new(int8), isExpectedEq(int8(-1))},\n\t\t{-1, new(int16), isExpectedEq(int16(-1))},\n\t\t{-1, new(int32), isExpectedEq(int32(-1))},\n\t\t{-1, new(int64), isExpectedEq(int64(-1))},\n\t\t{-1, new(int), isExpectedEq(int(-1))},\n\t\t{math.MinInt<%= pg_bit_size %>, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(math.MinInt<%= pg_bit_size %>))},\n\t\t{-1, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(-1))},\n\t\t{0, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(0))},\n\t\t{1, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},\n\t\t{math.MaxInt<%= pg_bit_size %>, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(math.MaxInt<%= pg_bit_size %>))},\n\t\t{1, new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 1, Valid: true})},\n\t\t{\"1\", new(string), isExpectedEq(\"1\")},\n\t\t{pgtype.Int<%= pg_byte_size %>{}, new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{})},\n\t\t{nil, new(*int<%= pg_bit_size %>), isExpectedEq((*int<%= pg_bit_size %>)(nil))},\n\t})\n}\n\nfunc TestInt<%= pg_byte_size %>MarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Int<%= pg_byte_size %>\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 0}, result: \"null\"},\n\t\t{source: pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 1, Valid: true}, result: \"1\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestInt<%= pg_byte_size %>UnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Int<%= pg_byte_size %>\n\t}{\n\t\t{source: \"null\", result: pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 0}},\n\t\t{source: \"1\", result: pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 1, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Int<%= pg_byte_size %>\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n<% end %>\n"
  },
  {
    "path": "pgtype/integration_benchmark_test.go",
    "content": "// Code generated from pgtype/integration_benchmark_test.go.erb. DO NOT EDIT.\n\npackage pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int16_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int16_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int16_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int16_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int16_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int16_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int16_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int16_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int16\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int32_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int32_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int32_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int32_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int32_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int32_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int32_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int32_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_int64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_int64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_uint64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_uint64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_uint64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_uint64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_uint64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_uint64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_uint64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_uint64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]uint64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_pgtype_Int4_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_pgtype_Int4_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_pgtype_Int4_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_pgtype_Int4_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_pgtype_Int4_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_pgtype_Int4_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_int4_to_Go_pgtype_Int4_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_int4_to_Go_pgtype_Int4_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Int4\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::int4 + 0, n::int4 + 1, n::int4 + 2, n::int4 + 3, n::int4 + 4, n::int4 + 5, n::int4 + 6, n::int4 + 7, n::int4 + 8, n::int4 + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_int64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_int64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_int64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_int64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_int64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_int64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_int64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_int64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]int64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_float64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_float64_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_float64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_float64_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_float64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_float64_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_float64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_float64_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]float64\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_pgtype_Numeric_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_pgtype_Numeric_1_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_pgtype_Numeric_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_pgtype_Numeric_1_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 1) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_pgtype_Numeric_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_pgtype_Numeric_10_rows_1_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [1]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0 from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_numeric_to_Go_pgtype_Numeric_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_numeric_to_Go_pgtype_Numeric_100_rows_10_columns(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v [10]pgtype.Numeric\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select n::numeric + 0, n::numeric + 1, n::numeric + 2, n::numeric + 3, n::numeric + 4, n::numeric + 5, n::numeric + 6, n::numeric + 7, n::numeric + 8, n::numeric + 9 from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8], &v[9]}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_Int4Array_With_Go_Int4Array_10(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v []int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select array_agg(n) from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_Int4Array_With_Go_Int4Array_10(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v []int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select array_agg(n) from generate_series(1, 10) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_Int4Array_With_Go_Int4Array_100(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v []int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select array_agg(n) from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_Int4Array_With_Go_Int4Array_100(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v []int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select array_agg(n) from generate_series(1, 100) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryTextFormatDecode_PG_Int4Array_With_Go_Int4Array_1000(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v []int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select array_agg(n) from generate_series(1, 1000) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.TextFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkQueryBinaryFormatDecode_PG_Int4Array_With_Go_Int4Array_1000(b *testing.B) {\n\tdefaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tb.ResetTimer()\n\t\tvar v []int32\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\trows, _ := conn.Query(\n\t\t\t\tctx,\n\t\t\t\t`select array_agg(n) from generate_series(1, 1000) n`,\n\t\t\t\tpgx.QueryResultFormats{pgx.BinaryFormatCode},\n\t\t\t)\n\t\t\t_, err := pgx.ForEachRow(rows, []any{&v}, func() error { return nil })\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/integration_benchmark_test.go.erb",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/testutil\"\n\t\"github.com/jackc/pgx/v5\"\n)\n\n<%\n  [\n    [\"int4\", [\"int16\", \"int32\", \"int64\", \"uint64\", \"pgtype.Int4\"], [[1, 1], [1, 10], [10, 1], [100, 10]]],\n    [\"numeric\", [\"int64\", \"float64\", \"pgtype.Numeric\"], [[1, 1], [1, 10], [10, 1], [100, 10]]],\n  ].each do |pg_type, go_types, rows_columns|\n%>\n<% go_types.each do |go_type| %>\n<% rows_columns.each do |rows, columns| %>\n<% [[\"Text\", \"pgx.TextFormatCode\"], [\"Binary\", \"pgx.BinaryFormatCode\"]].each do |format_name, format_code| %>\nfunc BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go_type.gsub(/\\W/, \"_\") %>_<%= rows %>_rows_<%= columns %>_columns(b *testing.B) {\n  defaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n    b.ResetTimer()\n    var v [<%= columns %>]<%= go_type %>\n    for i := 0; i < b.N; i++ {\n      rows, _ := conn.Query(\n        ctx,\n        `select <% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>n::<%= pg_type %> + <%= col_idx%><% end %> from generate_series(1, <%= rows %>) n`,\n        pgx.QueryResultFormats{<%= format_code %>},\n      )\n      _, err := pgx.ForEachRow(rows, []any{<% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>&v[<%= col_idx%>]<% end %>},  func() error { return nil })\n      if err != nil {\n        b.Fatal(err)\n      }\n    }\n  })\n}\n<% end %>\n<% end %>\n<% end %>\n<% end %>\n\n<% [10, 100, 1000].each do |array_size| %>\n<% [[\"Text\", \"pgx.TextFormatCode\"], [\"Binary\", \"pgx.BinaryFormatCode\"]].each do |format_name, format_code| %>\nfunc BenchmarkQuery<%= format_name %>FormatDecode_PG_Int4Array_With_Go_Int4Array_<%= array_size %>(b *testing.B) {\n  defaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n    b.ResetTimer()\n    var v []int32\n    for i := 0; i < b.N; i++ {\n      rows, _ := conn.Query(\n        ctx,\n        `select array_agg(n) from generate_series(1, <%= array_size %>) n`,\n        pgx.QueryResultFormats{<%= format_code %>},\n      )\n      _, err := pgx.ForEachRow(rows, []any{&v},  func() error { return nil })\n      if err != nil {\n        b.Fatal(err)\n      }\n    }\n  })\n}\n<% end %>\n<% end %>\n"
  },
  {
    "path": "pgtype/integration_benchmark_test_gen.sh",
    "content": "erb integration_benchmark_test.go.erb > integration_benchmark_test.go\ngoimports -w integration_benchmark_test.go\n"
  },
  {
    "path": "pgtype/interval.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst (\n\tmicrosecondsPerSecond = 1_000_000\n\tmicrosecondsPerMinute = 60 * microsecondsPerSecond\n\tmicrosecondsPerHour   = 60 * microsecondsPerMinute\n\tmicrosecondsPerDay    = 24 * microsecondsPerHour\n\tmicrosecondsPerMonth  = 30 * microsecondsPerDay\n)\n\ntype IntervalScanner interface {\n\tScanInterval(v Interval) error\n}\n\ntype IntervalValuer interface {\n\tIntervalValue() (Interval, error)\n}\n\ntype Interval struct {\n\tMicroseconds int64\n\tDays         int32\n\tMonths       int32\n\tValid        bool\n}\n\n// ScanInterval implements the [IntervalScanner] interface.\nfunc (interval *Interval) ScanInterval(v Interval) error {\n\t*interval = v\n\treturn nil\n}\n\n// IntervalValue implements the [IntervalValuer] interface.\nfunc (interval Interval) IntervalValue() (Interval, error) {\n\treturn interval, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (interval *Interval) Scan(src any) error {\n\tif src == nil {\n\t\t*interval = Interval{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToIntervalScanner{}.Scan([]byte(src), interval)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (interval Interval) Value() (driver.Value, error) {\n\tif !interval.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := IntervalCodec{}.PlanEncode(nil, 0, TextFormatCode, interval).Encode(interval, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype IntervalCodec struct{}\n\nfunc (IntervalCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (IntervalCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (IntervalCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(IntervalValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanIntervalCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanIntervalCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanIntervalCodecBinary struct{}\n\nfunc (encodePlanIntervalCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tinterval, err := value.(IntervalValuer).IntervalValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !interval.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendInt64(buf, interval.Microseconds)\n\tbuf = pgio.AppendInt32(buf, interval.Days)\n\tbuf = pgio.AppendInt32(buf, interval.Months)\n\treturn buf, nil\n}\n\ntype encodePlanIntervalCodecText struct{}\n\nfunc (encodePlanIntervalCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tinterval, err := value.(IntervalValuer).IntervalValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !interval.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif interval.Months != 0 {\n\t\tbuf = append(buf, strconv.FormatInt(int64(interval.Months), 10)...)\n\t\tbuf = append(buf, \" mon \"...)\n\t}\n\n\tif interval.Days != 0 {\n\t\tbuf = append(buf, strconv.FormatInt(int64(interval.Days), 10)...)\n\t\tbuf = append(buf, \" day \"...)\n\t}\n\n\tabsMicroseconds := interval.Microseconds\n\tif absMicroseconds < 0 {\n\t\tabsMicroseconds = -absMicroseconds\n\t\tbuf = append(buf, '-')\n\t}\n\n\thours := absMicroseconds / microsecondsPerHour\n\tminutes := (absMicroseconds % microsecondsPerHour) / microsecondsPerMinute\n\tseconds := (absMicroseconds % microsecondsPerMinute) / microsecondsPerSecond\n\n\ttimeStr := fmt.Sprintf(\"%02d:%02d:%02d\", hours, minutes, seconds)\n\tbuf = append(buf, timeStr...)\n\n\tmicroseconds := absMicroseconds % microsecondsPerSecond\n\tif microseconds != 0 {\n\t\tbuf = append(buf, fmt.Sprintf(\".%06d\", microseconds)...)\n\t}\n\n\treturn buf, nil\n}\n\nfunc (IntervalCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase IntervalScanner:\n\t\t\treturn scanPlanBinaryIntervalToIntervalScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase IntervalScanner:\n\t\t\treturn scanPlanTextAnyToIntervalScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryIntervalToIntervalScanner struct{}\n\nfunc (scanPlanBinaryIntervalToIntervalScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(IntervalScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanInterval(Interval{})\n\t}\n\n\tif len(src) != 16 {\n\t\treturn fmt.Errorf(\"Received an invalid size for an interval: %d\", len(src))\n\t}\n\n\tmicroseconds := int64(binary.BigEndian.Uint64(src))\n\tdays := int32(binary.BigEndian.Uint32(src[8:]))\n\tmonths := int32(binary.BigEndian.Uint32(src[12:]))\n\n\treturn scanner.ScanInterval(Interval{Microseconds: microseconds, Days: days, Months: months, Valid: true})\n}\n\ntype scanPlanTextAnyToIntervalScanner struct{}\n\nfunc (scanPlanTextAnyToIntervalScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(IntervalScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanInterval(Interval{})\n\t}\n\n\tvar microseconds int64\n\tvar days int32\n\tvar months int32\n\n\tparts := strings.Split(string(src), \" \")\n\n\tfor i := 0; i < len(parts)-1; i += 2 {\n\t\tscalar, err := strconv.ParseInt(parts[i], 10, 64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"bad interval format\")\n\t\t}\n\n\t\tswitch parts[i+1] {\n\t\tcase \"year\", \"years\":\n\t\t\tmonths += int32(scalar * 12)\n\t\tcase \"mon\", \"mons\":\n\t\t\tmonths += int32(scalar)\n\t\tcase \"day\", \"days\":\n\t\t\tdays = int32(scalar)\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"bad interval format: %q\", parts[i+1])\n\t\t}\n\t}\n\n\tif len(parts)%2 == 1 {\n\t\ttimeParts := strings.SplitN(parts[len(parts)-1], \":\", 3)\n\t\tif len(timeParts) != 3 {\n\t\t\treturn fmt.Errorf(\"bad interval format\")\n\t\t}\n\n\t\tvar negative bool\n\t\tif timeParts[0][0] == '-' {\n\t\t\tnegative = true\n\t\t\ttimeParts[0] = timeParts[0][1:]\n\t\t}\n\n\t\thours, err := strconv.ParseInt(timeParts[0], 10, 64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"bad interval hour format: %s\", timeParts[0])\n\t\t}\n\n\t\tminutes, err := strconv.ParseInt(timeParts[1], 10, 64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"bad interval minute format: %s\", timeParts[1])\n\t\t}\n\n\t\tsec, secFrac, secFracFound := strings.Cut(timeParts[2], \".\")\n\n\t\tseconds, err := strconv.ParseInt(sec, 10, 64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"bad interval second format: %s\", sec)\n\t\t}\n\n\t\tvar uSeconds int64\n\t\tif secFracFound {\n\t\t\tuSeconds, err = strconv.ParseInt(secFrac, 10, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"bad interval decimal format: %s\", secFrac)\n\t\t\t}\n\n\t\t\tfor i := 0; i < 6-len(secFrac); i++ {\n\t\t\t\tuSeconds *= 10\n\t\t\t}\n\t\t}\n\n\t\tmicroseconds = hours * microsecondsPerHour\n\t\tmicroseconds += minutes * microsecondsPerMinute\n\t\tmicroseconds += seconds * microsecondsPerSecond\n\t\tmicroseconds += uSeconds\n\n\t\tif negative {\n\t\t\tmicroseconds = -microseconds\n\t\t}\n\t}\n\n\treturn scanner.ScanInterval(Interval{Months: months, Days: days, Microseconds: microseconds, Valid: true})\n}\n\nfunc (c IntervalCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c IntervalCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar interval Interval\n\terr := codecScan(c, m, oid, format, src, &interval)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn interval, nil\n}\n"
  },
  {
    "path": "pgtype/interval_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIntervalCodec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"interval\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: 1, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: 1_000_000, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 1_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: 1_000_001, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 1_000_001, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: 123_202_800_000_000, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 123_202_800_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Days: 1, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Days: 1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: 1, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: 1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: 12, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: 12, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: 13, Days: 15, Microseconds: 1_000_001, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: 13, Days: 15, Microseconds: 1_000_001, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: -1, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: -1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: -1_000_000, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: -1_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: -1_000_001, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: -1_000_001, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Microseconds: -123_202_800_000_000, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: -123_202_800_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Days: -1, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Days: -1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: -1, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: -1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: -12, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: -12, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: -13, Days: -15, Microseconds: -1_000_001, Valid: true},\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: -13, Days: -15, Microseconds: -1_000_001, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"1 second\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 1_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"1.000001 second\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 1_000_001, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"34223 hours\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Microseconds: 123_202_800_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"1 day\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Days: 1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"1 month\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: 1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"1 year\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: 12, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"-13 mon\",\n\t\t\tResult: new(pgtype.Interval),\n\t\t\tTest:   isExpectedEq(pgtype.Interval{Months: -13, Valid: true}),\n\t\t},\n\t\t{Param: time.Hour, Result: new(time.Duration), Test: isExpectedEq(time.Hour)},\n\t\t{\n\t\t\tParam:  pgtype.Interval{Months: 1, Days: 1, Valid: true},\n\t\t\tResult: new(time.Duration),\n\t\t\tTest:   isExpectedEq(time.Duration(2_678_400_000_000_000)),\n\t\t},\n\t\t{Param: pgtype.Interval{}, Result: new(pgtype.Interval), Test: isExpectedEq(pgtype.Interval{})},\n\t\t{Param: nil, Result: new(pgtype.Interval), Test: isExpectedEq(pgtype.Interval{})},\n\t})\n}\n\nfunc TestIntervalTextEncode(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Interval\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Interval{Months: 2, Days: 1, Microseconds: 0, Valid: true}, result: \"2 mon 1 day 00:00:00\"},\n\t\t{source: pgtype.Interval{Months: 0, Days: 0, Microseconds: 0, Valid: true}, result: \"00:00:00\"},\n\t\t{source: pgtype.Interval{Months: 0, Days: 0, Microseconds: 6 * 60 * 1_000_000, Valid: true}, result: \"00:06:00\"},\n\t\t{source: pgtype.Interval{Months: 0, Days: 1, Microseconds: 6*60*1_000_000 + 30, Valid: true}, result: \"1 day 00:06:00.000030\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tbuf, err := m.Encode(pgtype.DateOID, pgtype.TextFormatCode, tt.source, nil)\n\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\tassert.Equalf(t, tt.result, string(buf), \"%d\", i)\n\t}\n}\n"
  },
  {
    "path": "pgtype/json.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype JSONCodec struct {\n\tMarshal   func(v any) ([]byte, error)\n\tUnmarshal func(data []byte, v any) error\n}\n\nfunc (*JSONCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (*JSONCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n\nfunc (c *JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch value.(type) {\n\tcase string:\n\t\treturn encodePlanJSONCodecEitherFormatString{}\n\tcase []byte:\n\t\treturn encodePlanJSONCodecEitherFormatByteSlice{}\n\n\t// Handle json.RawMessage specifically because if it is run through json.Marshal it may be mutated.\n\t// e.g. `{\"foo\": \"bar\"}` -> `{\"foo\":\"bar\"}`.\n\tcase json.RawMessage:\n\t\treturn encodePlanJSONCodecEitherFormatJSONRawMessage{}\n\n\t// Cannot rely on driver.Valuer being handled later because anything can be marshalled.\n\t//\n\t// https://github.com/jackc/pgx/issues/1430\n\t//\n\t// Check for driver.Valuer must come before json.Marshaler so that it is guaranteed to be used\n\t// when both are implemented https://github.com/jackc/pgx/issues/1805\n\tcase driver.Valuer:\n\t\treturn &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}\n\n\t// Must come before trying wrap encode plans because a pointer to a struct may be unwrapped to a struct that can be\n\t// marshalled.\n\t//\n\t// https://github.com/jackc/pgx/issues/1681\n\tcase json.Marshaler:\n\t\treturn &encodePlanJSONCodecEitherFormatMarshal{\n\t\t\tmarshal: c.Marshal,\n\t\t}\n\t}\n\n\t// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the\n\t// appropriate wrappers here.\n\tfor _, f := range []TryWrapEncodePlanFunc{\n\t\tTryWrapDerefPointerEncodePlan,\n\t\tTryWrapFindUnderlyingTypeEncodePlan,\n\t} {\n\t\tif wrapperPlan, nextValue, ok := f(value); ok {\n\t\t\tif nextPlan := c.PlanEncode(m, oid, format, nextValue); nextPlan != nil {\n\t\t\t\twrapperPlan.SetNext(nextPlan)\n\t\t\t\treturn wrapperPlan\n\t\t\t}\n\t\t}\n\t}\n\n\treturn &encodePlanJSONCodecEitherFormatMarshal{\n\t\tmarshal: c.Marshal,\n\t}\n}\n\n// JSON needs its on scan plan for pointers to handle 'null'::json(b).\n// Consider making pointerPointerScanPlan more flexible in the future.\ntype jsonPointerScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (p jsonPointerScanPlan) Scan(src []byte, dst any) error {\n\tel := reflect.ValueOf(dst).Elem()\n\tif src == nil || string(src) == \"null\" {\n\t\tel.SetZero()\n\t\treturn nil\n\t}\n\n\tel.Set(reflect.New(el.Type().Elem()))\n\tif p.next != nil {\n\t\treturn p.next.Scan(src, el.Interface())\n\t}\n\n\treturn nil\n}\n\ntype encodePlanJSONCodecEitherFormatString struct{}\n\nfunc (encodePlanJSONCodecEitherFormatString) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tjsonString := value.(string)\n\tbuf = append(buf, jsonString...)\n\treturn buf, nil\n}\n\ntype encodePlanJSONCodecEitherFormatByteSlice struct{}\n\nfunc (encodePlanJSONCodecEitherFormatByteSlice) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tjsonBytes := value.([]byte)\n\tif jsonBytes == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, jsonBytes...)\n\treturn buf, nil\n}\n\ntype encodePlanJSONCodecEitherFormatJSONRawMessage struct{}\n\nfunc (encodePlanJSONCodecEitherFormatJSONRawMessage) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tjsonBytes := value.(json.RawMessage)\n\tif jsonBytes == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, jsonBytes...)\n\treturn buf, nil\n}\n\ntype encodePlanJSONCodecEitherFormatMarshal struct {\n\tmarshal func(v any) ([]byte, error)\n}\n\nfunc (e *encodePlanJSONCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tjsonBytes, err := e.marshal(value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbuf = append(buf, jsonBytes...)\n\treturn buf, nil\n}\n\nfunc (c *JSONCodec) PlanScan(m *Map, oid uint32, formatCode int16, target any) ScanPlan {\n\treturn c.planScan(m, oid, formatCode, target, 0)\n}\n\n// JSON cannot fallback to pointerPointerScanPlan because of 'null'::json(b),\n// so we need to duplicate the logic here.\nfunc (c *JSONCodec) planScan(m *Map, oid uint32, formatCode int16, target any, depth int) ScanPlan {\n\tif depth > 8 {\n\t\treturn &scanPlanFail{m: m, oid: oid, formatCode: formatCode}\n\t}\n\n\tswitch target.(type) {\n\tcase *string:\n\t\treturn &scanPlanAnyToString{}\n\tcase *[]byte:\n\t\treturn &scanPlanJSONToByteSlice{}\n\tcase BytesScanner:\n\t\treturn &scanPlanBinaryBytesToBytesScanner{}\n\tcase sql.Scanner:\n\t\treturn &scanPlanCodecSQLScanner{c: c, m: m, oid: oid, formatCode: formatCode}\n\t}\n\n\trv := reflect.ValueOf(target)\n\tif rv.Kind() == reflect.Pointer && rv.Elem().Kind() == reflect.Pointer {\n\t\tvar plan jsonPointerScanPlan\n\t\tplan.next = c.planScan(m, oid, formatCode, rv.Elem().Interface(), depth+1)\n\t\treturn plan\n\t} else {\n\t\treturn &scanPlanJSONToJSONUnmarshal{unmarshal: c.Unmarshal}\n\t}\n}\n\ntype scanPlanAnyToString struct{}\n\nfunc (scanPlanAnyToString) Scan(src []byte, dst any) error {\n\tp := dst.(*string)\n\t*p = string(src)\n\treturn nil\n}\n\ntype scanPlanJSONToByteSlice struct{}\n\nfunc (scanPlanJSONToByteSlice) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*[]byte)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\t*dstBuf = make([]byte, len(src))\n\tcopy(*dstBuf, src)\n\treturn nil\n}\n\ntype scanPlanJSONToJSONUnmarshal struct {\n\tunmarshal func(data []byte, v any) error\n}\n\nfunc (s *scanPlanJSONToJSONUnmarshal) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\tdstValue := reflect.ValueOf(dst)\n\t\tif dstValue.Kind() == reflect.Ptr {\n\t\t\tel := dstValue.Elem()\n\t\t\tswitch el.Kind() {\n\t\t\tcase reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface:\n\t\t\t\tel.Set(reflect.Zero(el.Type()))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tv := reflect.ValueOf(dst)\n\tif v.Kind() != reflect.Pointer || v.IsNil() {\n\t\treturn fmt.Errorf(\"cannot scan into non-pointer or nil destinations %T\", dst)\n\t}\n\n\telem := v.Elem()\n\telem.Set(reflect.Zero(elem.Type()))\n\n\treturn s.unmarshal(src, dst)\n}\n\nfunc (c *JSONCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tdstBuf := make([]byte, len(src))\n\tcopy(dstBuf, src)\n\treturn dstBuf, nil\n}\n\nfunc (c *JSONCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar dst any\n\terr := c.Unmarshal(src, &dst)\n\treturn dst, err\n}\n"
  },
  {
    "path": "pgtype/json_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc isExpectedEqMap(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\taa := a.(map[string]any)\n\t\tbb := v.(map[string]any)\n\n\t\tif (aa == nil) != (bb == nil) {\n\t\t\treturn false\n\t\t}\n\n\t\tif aa == nil {\n\t\t\treturn true\n\t\t}\n\n\t\tif len(aa) != len(bb) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor k := range aa {\n\t\t\tif aa[k] != bb[k] {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n}\n\nfunc TestJSONCodec(t *testing.T) {\n\ttype jsonStruct struct {\n\t\tName string `json:\"name\"`\n\t\tAge  int    `json:\"age\"`\n\t}\n\n\tvar str string\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"json\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: nil, Result: new(*jsonStruct), Test: isExpectedEq((*jsonStruct)(nil))},\n\t\t{Param: map[string]any(nil), Result: new(*string), Test: isExpectedEq((*string)(nil))},\n\t\t{Param: map[string]any(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: []byte(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: nil, Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\n\t\t// Test sql.Scanner. (https://github.com/jackc/pgx/issues/1418)\n\t\t{Param: \"42\", Result: new(sql.NullInt64), Test: isExpectedEq(sql.NullInt64{Int64: 42, Valid: true})},\n\n\t\t// Test driver.Valuer. (https://github.com/jackc/pgx/issues/1430)\n\t\t{Param: sql.NullInt64{Int64: 42, Valid: true}, Result: new(sql.NullInt64), Test: isExpectedEq(sql.NullInt64{Int64: 42, Valid: true})},\n\n\t\t// Test driver.Valuer is used before json.Marshaler (https://github.com/jackc/pgx/issues/1805)\n\t\t{Param: Issue1805(7), Result: new(Issue1805), Test: isExpectedEq(Issue1805(7))},\n\t\t// Test driver.Scanner is used before json.Unmarshaler (https://github.com/jackc/pgx/issues/2146)\n\t\t{Param: Issue2146(7), Result: new(*Issue2146), Test: isPtrExpectedEq(Issue2146(7))},\n\n\t\t// Test driver.Scanner without pointer receiver (https://github.com/jackc/pgx/issues/2204)\n\t\t{Param: NonPointerJSONScanner{V: stringPtr(\"{}\")}, Result: NonPointerJSONScanner{V: &str}, Test: func(a any) bool { return str == \"{}\" }},\n\t})\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"json\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: []byte(\"{}\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"{}\"))},\n\t\t{Param: []byte(\"null\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"null\"))},\n\t\t{Param: []byte(\"42\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"42\"))},\n\t\t{Param: []byte(`\"hello\"`), Result: new([]byte), Test: isExpectedEqBytes([]byte(`\"hello\"`))},\n\t\t{Param: []byte(`\"hello\"`), Result: new(string), Test: isExpectedEq(`\"hello\"`)},\n\t\t{Param: map[string]any{\"foo\": \"bar\"}, Result: new(map[string]any), Test: isExpectedEqMap(map[string]any{\"foo\": \"bar\"})},\n\t\t{Param: jsonStruct{Name: \"Adam\", Age: 10}, Result: new(jsonStruct), Test: isExpectedEq(jsonStruct{Name: \"Adam\", Age: 10})},\n\t})\n}\n\ntype Issue1805 int\n\nfunc (i *Issue1805) Scan(src any) error {\n\tvar source []byte\n\tswitch src.(type) {\n\tcase string:\n\t\tsource = []byte(src.(string))\n\tcase []byte:\n\t\tsource = src.([]byte)\n\tdefault:\n\t\treturn errors.New(\"unknown source type\")\n\t}\n\tvar newI int\n\tif err := json.Unmarshal(source, &newI); err != nil {\n\t\treturn err\n\t}\n\t*i = Issue1805(newI)\n\treturn nil\n}\n\nfunc (i Issue1805) Value() (driver.Value, error) {\n\tb, err := json.Marshal(int(i))\n\treturn string(b), err\n}\n\nfunc (i Issue1805) UnmarshalJSON(bytes []byte) error {\n\treturn errors.New(\"UnmarshalJSON called\")\n}\n\nfunc (i Issue1805) MarshalJSON() ([]byte, error) {\n\treturn nil, errors.New(\"MarshalJSON called\")\n}\n\ntype Issue2146 int\n\nfunc (i *Issue2146) Scan(src any) error {\n\tvar source []byte\n\tswitch src.(type) {\n\tcase string:\n\t\tsource = []byte(src.(string))\n\tcase []byte:\n\t\tsource = src.([]byte)\n\tdefault:\n\t\treturn errors.New(\"unknown source type\")\n\t}\n\tvar newI int\n\tif err := json.Unmarshal(source, &newI); err != nil {\n\t\treturn err\n\t}\n\t*i = Issue2146(newI + 1)\n\treturn nil\n}\n\nfunc (i Issue2146) Value() (driver.Value, error) {\n\tb, err := json.Marshal(int(i - 1))\n\treturn string(b), err\n}\n\ntype NonPointerJSONScanner struct {\n\tV *string\n}\n\nfunc (i NonPointerJSONScanner) Scan(src any) error {\n\tswitch c := src.(type) {\n\tcase string:\n\t\t*i.V = c\n\tcase []byte:\n\t\t*i.V = string(c)\n\tdefault:\n\t\treturn errors.New(\"unknown source type\")\n\t}\n\n\treturn nil\n}\n\nfunc (i NonPointerJSONScanner) Value() (driver.Value, error) {\n\treturn i.V, nil\n}\n\n// https://github.com/jackc/pgx/issues/1273#issuecomment-1221414648\nfunc TestJSONCodecUnmarshalSQLNull(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t// Slices are nilified\n\t\tslice := []string{\"foo\", \"bar\", \"baz\"}\n\t\terr := conn.QueryRow(ctx, \"select null::json\").Scan(&slice)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, slice)\n\n\t\t// Maps are nilified\n\t\tm := map[string]any{\"foo\": \"bar\"}\n\t\terr = conn.QueryRow(ctx, \"select null::json\").Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, m)\n\n\t\tm = map[string]any{\"foo\": \"bar\"}\n\t\terr = conn.QueryRow(ctx, \"select null::json\").Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, m)\n\n\t\t// Pointer to pointer are nilified\n\t\tn := 42\n\t\tp := &n\n\t\terr = conn.QueryRow(ctx, \"select null::json\").Scan(&p)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, p)\n\n\t\t// A string cannot scan a NULL.\n\t\tstr := \"foobar\"\n\t\terr = conn.QueryRow(ctx, \"select null::json\").Scan(&str)\n\t\tfieldName := \"json\"\n\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t\tfieldName = \"jsonb\" // Seems like CockroachDB treats json as jsonb.\n\t\t}\n\t\trequire.EqualError(t, err, fmt.Sprintf(\"can't scan into dest[0] (col: %s): cannot scan NULL into *string\", fieldName))\n\n\t\t// A non-string cannot scan a NULL.\n\t\terr = conn.QueryRow(ctx, \"select null::json\").Scan(&n)\n\t\trequire.EqualError(t, err, fmt.Sprintf(\"can't scan into dest[0] (col: %s): cannot scan NULL into *int\", fieldName))\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1470\nfunc TestJSONCodecPointerToPointerToString(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar s *string\n\t\terr := conn.QueryRow(ctx, \"select '{}'::json\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, s)\n\t\trequire.Equal(t, \"{}\", *s)\n\n\t\terr = conn.QueryRow(ctx, \"select null::json\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, s)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1691\nfunc TestJSONCodecPointerToPointerToInt(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tn := 44\n\t\tp := &n\n\t\terr := conn.QueryRow(ctx, \"select 'null'::jsonb\").Scan(&p)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, p)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1691\nfunc TestJSONCodecPointerToPointerToStruct(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttype ImageSize struct {\n\t\t\tHeight int    `json:\"height\"`\n\t\t\tWidth  int    `json:\"width\"`\n\t\t\tStr    string `json:\"str\"`\n\t\t}\n\t\tis := &ImageSize{Height: 100, Width: 100, Str: \"str\"}\n\t\terr := conn.QueryRow(ctx, `select 'null'::jsonb`).Scan(&is)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, is)\n\t})\n}\n\nfunc TestJSONCodecClearExistingValueBeforeUnmarshal(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tm := map[string]any{}\n\t\terr := conn.QueryRow(ctx, `select '{\"foo\": \"bar\"}'::json`).Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, map[string]any{\"foo\": \"bar\"}, m)\n\n\t\terr = conn.QueryRow(ctx, `select '{\"baz\": \"quz\"}'::json`).Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, map[string]any{\"baz\": \"quz\"}, m)\n\t})\n}\n\ntype ParentIssue1681 struct {\n\tChild ChildIssue1681\n}\n\nfunc (t *ParentIssue1681) MarshalJSON() ([]byte, error) {\n\treturn []byte(`{\"custom\":\"thing\"}`), nil\n}\n\ntype ChildIssue1681 struct{}\n\nfunc (t ChildIssue1681) MarshalJSON() ([]byte, error) {\n\treturn []byte(`{\"someVal\": false}`), nil\n}\n\n// https://github.com/jackc/pgx/issues/1681\nfunc TestJSONCodecEncodeJSONMarshalerThatCanBeWrapped(t *testing.T) {\n\tskipCockroachDB(t, \"CockroachDB treats json as jsonb. This causes it to format differently than PostgreSQL.\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar jsonStr string\n\t\terr := conn.QueryRow(context.Background(), \"select $1::json\", &ParentIssue1681{}).Scan(&jsonStr)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, `{\"custom\":\"thing\"}`, jsonStr)\n\t})\n}\n\nfunc TestJSONCodecCustomMarshal(t *testing.T) {\n\tskipCockroachDB(t, \"CockroachDB treats json as jsonb. This causes it to format differently than PostgreSQL.\")\n\n\tconnTestRunner := defaultConnTestRunner\n\tconnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{\n\t\t\tName: \"json\", OID: pgtype.JSONOID, Codec: &pgtype.JSONCodec{\n\t\t\t\tMarshal: func(v any) ([]byte, error) {\n\t\t\t\t\treturn []byte(`{\"custom\":\"value\"}`), nil\n\t\t\t\t},\n\t\t\t\tUnmarshal: func(data []byte, v any) error {\n\t\t\t\t\treturn json.Unmarshal([]byte(`{\"custom\":\"value\"}`), v)\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, pgxtest.KnownOIDQueryExecModes, \"json\", []pgxtest.ValueRoundTripTest{\n\t\t// There is no space between \"custom\" and \"value\" in json type.\n\t\t{Param: map[string]any{\"something\": \"else\"}, Result: new(string), Test: isExpectedEq(`{\"custom\":\"value\"}`)},\n\t\t{Param: []byte(`{\"something\":\"else\"}`), Result: new(map[string]any), Test: func(v any) bool {\n\t\t\treturn reflect.DeepEqual(v, map[string]any{\"custom\": \"value\"})\n\t\t}},\n\t})\n}\n\nfunc TestJSONCodecScanToNonPointerValues(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tn := 44\n\t\terr := conn.QueryRow(ctx, \"select '42'::jsonb\").Scan(n)\n\t\trequire.Error(t, err)\n\n\t\tvar i *int\n\t\terr = conn.QueryRow(ctx, \"select '42'::jsonb\").Scan(i)\n\t\trequire.Error(t, err)\n\n\t\tm := 0\n\t\terr = conn.QueryRow(ctx, \"select '42'::jsonb\").Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, 42, m)\n\t})\n}\n\nfunc TestJSONCodecScanNull(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar dest struct{}\n\t\terr := conn.QueryRow(ctx, \"select null::jsonb\").Scan(&dest)\n\t\trequire.Error(t, err)\n\t\trequire.Contains(t, err.Error(), \"cannot scan NULL into *struct {}\")\n\n\t\terr = conn.QueryRow(ctx, \"select 'null'::jsonb\").Scan(&dest)\n\t\trequire.NoError(t, err)\n\n\t\tvar destPointer *struct{}\n\t\terr = conn.QueryRow(ctx, \"select null::jsonb\").Scan(&destPointer)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, destPointer)\n\n\t\terr = conn.QueryRow(ctx, \"select 'null'::jsonb\").Scan(&destPointer)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, destPointer)\n\n\t\tvar raw json.RawMessage\n\t\trequire.NoError(t, conn.QueryRow(ctx, \"select 'null'::jsonb\").Scan(&raw))\n\t\trequire.Equal(t, json.RawMessage(\"null\"), raw)\n\t})\n}\n\nfunc TestJSONCodecScanNullToPointerToSQLScanner(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar dest *Issue2146\n\t\terr := conn.QueryRow(ctx, \"select null::jsonb\").Scan(&dest)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, dest)\n\t})\n}\n\ntype SQLScannerFunc func(src any) error\n\nfunc (f SQLScannerFunc) Scan(src any) error {\n\treturn f(src)\n}\n\n// https://github.com/jackc/pgx/issues/2403#issuecomment-3480454865\n//\n// Previously, when scanning a json/jsonb value into a sql.Scanner via database/sql the source value was a []byte.\n// However, when scanning into a sql.Scanner via pgx the source value was a string. This test ensures that the source\n// value is a []byte.\nfunc TestJSONCodecSQLScannerReceivesByteSlice(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tf := SQLScannerFunc(func(src any) error {\n\t\t\trequire.IsType(t, []byte{}, src)\n\t\t\treturn nil\n\t\t})\n\t\terr := conn.QueryRow(ctx, \"select '{}'::json\").Scan(&f)\n\t\trequire.NoError(t, err)\n\t})\n}\n"
  },
  {
    "path": "pgtype/jsonb.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n)\n\ntype JSONBCodec struct {\n\tMarshal   func(v any) ([]byte, error)\n\tUnmarshal func(data []byte, v any) error\n}\n\nfunc (*JSONBCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (*JSONBCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n\nfunc (c *JSONBCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tplan := (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanEncode(m, oid, TextFormatCode, value)\n\t\tif plan != nil {\n\t\t\treturn &encodePlanJSONBCodecBinaryWrapper{textPlan: plan}\n\t\t}\n\tcase TextFormatCode:\n\t\treturn (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanEncode(m, oid, format, value)\n\t}\n\n\treturn nil\n}\n\ntype encodePlanJSONBCodecBinaryWrapper struct {\n\ttextPlan EncodePlan\n}\n\nfunc (plan *encodePlanJSONBCodecBinaryWrapper) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tbuf = append(buf, 1)\n\treturn plan.textPlan.Encode(value, buf)\n}\n\nfunc (c *JSONBCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tplan := (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanScan(m, oid, TextFormatCode, target)\n\t\tif plan != nil {\n\t\t\treturn &scanPlanJSONBCodecBinaryUnwrapper{textPlan: plan}\n\t\t}\n\tcase TextFormatCode:\n\t\treturn (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanScan(m, oid, format, target)\n\t}\n\n\treturn nil\n}\n\ntype scanPlanJSONBCodecBinaryUnwrapper struct {\n\ttextPlan ScanPlan\n}\n\nfunc (plan *scanPlanJSONBCodecBinaryUnwrapper) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn plan.textPlan.Scan(src, dst)\n\t}\n\n\tif len(src) == 0 {\n\t\treturn fmt.Errorf(\"jsonb too short\")\n\t}\n\n\tif src[0] != 1 {\n\t\treturn fmt.Errorf(\"unknown jsonb version number %d\", src[0])\n\t}\n\n\treturn plan.textPlan.Scan(src[1:], dst)\n}\n\nfunc (c *JSONBCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tif len(src) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"jsonb too short\")\n\t\t}\n\n\t\tif src[0] != 1 {\n\t\t\treturn nil, fmt.Errorf(\"unknown jsonb version number %d\", src[0])\n\t\t}\n\n\t\tdstBuf := make([]byte, len(src)-1)\n\t\tcopy(dstBuf, src[1:])\n\t\treturn dstBuf, nil\n\tcase TextFormatCode:\n\t\tdstBuf := make([]byte, len(src))\n\t\tcopy(dstBuf, src)\n\t\treturn dstBuf, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code: %v\", format)\n\t}\n}\n\nfunc (c *JSONBCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tif len(src) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"jsonb too short\")\n\t\t}\n\n\t\tif src[0] != 1 {\n\t\t\treturn nil, fmt.Errorf(\"unknown jsonb version number %d\", src[0])\n\t\t}\n\n\t\tsrc = src[1:]\n\tcase TextFormatCode:\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code: %v\", format)\n\t}\n\n\tvar dst any\n\terr := c.Unmarshal(src, &dst)\n\treturn dst, err\n}\n"
  },
  {
    "path": "pgtype/jsonb_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"reflect\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestJSONBTranscode(t *testing.T) {\n\ttype jsonStruct struct {\n\t\tName string `json:\"name\"`\n\t\tAge  int    `json:\"age\"`\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"jsonb\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: nil, Result: new(*jsonStruct), Test: isExpectedEq((*jsonStruct)(nil))},\n\t\t{Param: map[string]any(nil), Result: new(*string), Test: isExpectedEq((*string)(nil))},\n\t\t{Param: map[string]any(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: []byte(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: nil, Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t})\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"jsonb\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: []byte(\"{}\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"{}\"))},\n\t\t{Param: []byte(\"null\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"null\"))},\n\t\t{Param: []byte(\"42\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"42\"))},\n\t\t{Param: []byte(`\"hello\"`), Result: new([]byte), Test: isExpectedEqBytes([]byte(`\"hello\"`))},\n\t\t{Param: []byte(`\"hello\"`), Result: new(string), Test: isExpectedEq(`\"hello\"`)},\n\t\t{Param: map[string]any{\"foo\": \"bar\"}, Result: new(map[string]any), Test: isExpectedEqMap(map[string]any{\"foo\": \"bar\"})},\n\t\t{Param: jsonStruct{Name: \"Adam\", Age: 10}, Result: new(jsonStruct), Test: isExpectedEq(jsonStruct{Name: \"Adam\", Age: 10})},\n\t})\n}\n\nfunc TestJSONBCodecUnmarshalSQLNull(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t// Slices are nilified\n\t\tslice := []string{\"foo\", \"bar\", \"baz\"}\n\t\terr := conn.QueryRow(ctx, \"select null::jsonb\").Scan(&slice)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, slice)\n\n\t\t// Maps are nilified\n\t\tm := map[string]any{\"foo\": \"bar\"}\n\t\terr = conn.QueryRow(ctx, \"select null::jsonb\").Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, m)\n\n\t\tm = map[string]any{\"foo\": \"bar\"}\n\t\terr = conn.QueryRow(ctx, \"select null::jsonb\").Scan(&m)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, m)\n\n\t\t// Pointer to pointer are nilified\n\t\tn := 42\n\t\tp := &n\n\t\terr = conn.QueryRow(ctx, \"select null::jsonb\").Scan(&p)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, p)\n\n\t\t// A string cannot scan a NULL.\n\t\tstr := \"foobar\"\n\t\terr = conn.QueryRow(ctx, \"select null::jsonb\").Scan(&str)\n\t\trequire.EqualError(t, err, \"can't scan into dest[0] (col: jsonb): cannot scan NULL into *string\")\n\n\t\t// A non-string cannot scan a NULL.\n\t\terr = conn.QueryRow(ctx, \"select null::jsonb\").Scan(&n)\n\t\trequire.EqualError(t, err, \"can't scan into dest[0] (col: jsonb): cannot scan NULL into *int\")\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1681\nfunc TestJSONBCodecEncodeJSONMarshalerThatCanBeWrapped(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar jsonStr string\n\t\terr := conn.QueryRow(context.Background(), \"select $1::jsonb\", &ParentIssue1681{}).Scan(&jsonStr)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, `{\"custom\": \"thing\"}`, jsonStr) // Note that unlike json, jsonb reformats the JSON string.\n\t})\n}\n\nfunc TestJSONBCodecCustomMarshal(t *testing.T) {\n\tconnTestRunner := defaultConnTestRunner\n\tconnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{\n\t\t\tName: \"jsonb\", OID: pgtype.JSONBOID, Codec: &pgtype.JSONBCodec{\n\t\t\t\tMarshal: func(v any) ([]byte, error) {\n\t\t\t\t\treturn []byte(`{\"custom\":\"value\"}`), nil\n\t\t\t\t},\n\t\t\t\tUnmarshal: func(data []byte, v any) error {\n\t\t\t\t\treturn json.Unmarshal([]byte(`{\"custom\":\"value\"}`), v)\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, pgxtest.KnownOIDQueryExecModes, \"jsonb\", []pgxtest.ValueRoundTripTest{\n\t\t// There is space between \"custom\" and \"value\" in jsonb type.\n\t\t{Param: map[string]any{\"something\": \"else\"}, Result: new(string), Test: isExpectedEq(`{\"custom\": \"value\"}`)},\n\t\t{Param: []byte(`{\"something\":\"else\"}`), Result: new(map[string]any), Test: func(v any) bool {\n\t\t\treturn reflect.DeepEqual(v, map[string]any{\"custom\": \"value\"})\n\t\t}},\n\t})\n}\n"
  },
  {
    "path": "pgtype/line.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype LineScanner interface {\n\tScanLine(v Line) error\n}\n\ntype LineValuer interface {\n\tLineValue() (Line, error)\n}\n\ntype Line struct {\n\tA, B, C float64\n\tValid   bool\n}\n\n// ScanLine implements the [LineScanner] interface.\nfunc (line *Line) ScanLine(v Line) error {\n\t*line = v\n\treturn nil\n}\n\n// LineValue implements the [LineValuer] interface.\nfunc (line Line) LineValue() (Line, error) {\n\treturn line, nil\n}\n\nfunc (line *Line) Set(src any) error {\n\treturn fmt.Errorf(\"cannot convert %v to Line\", src)\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (line *Line) Scan(src any) error {\n\tif src == nil {\n\t\t*line = Line{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToLineScanner{}.Scan([]byte(src), line)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (line Line) Value() (driver.Value, error) {\n\tif !line.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := LineCodec{}.PlanEncode(nil, 0, TextFormatCode, line).Encode(line, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype LineCodec struct{}\n\nfunc (LineCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (LineCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (LineCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(LineValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanLineCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanLineCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanLineCodecBinary struct{}\n\nfunc (encodePlanLineCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tline, err := value.(LineValuer).LineValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !line.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(line.A))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(line.B))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(line.C))\n\treturn buf, nil\n}\n\ntype encodePlanLineCodecText struct{}\n\nfunc (encodePlanLineCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tline, err := value.(LineValuer).LineValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !line.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, fmt.Sprintf(`{%s,%s,%s}`,\n\t\tstrconv.FormatFloat(line.A, 'f', -1, 64),\n\t\tstrconv.FormatFloat(line.B, 'f', -1, 64),\n\t\tstrconv.FormatFloat(line.C, 'f', -1, 64),\n\t)...)\n\treturn buf, nil\n}\n\nfunc (LineCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase LineScanner:\n\t\t\treturn scanPlanBinaryLineToLineScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase LineScanner:\n\t\t\treturn scanPlanTextAnyToLineScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryLineToLineScanner struct{}\n\nfunc (scanPlanBinaryLineToLineScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(LineScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanLine(Line{})\n\t}\n\n\tif len(src) != 24 {\n\t\treturn fmt.Errorf(\"invalid length for line: %v\", len(src))\n\t}\n\n\ta := binary.BigEndian.Uint64(src)\n\tb := binary.BigEndian.Uint64(src[8:])\n\tc := binary.BigEndian.Uint64(src[16:])\n\n\treturn scanner.ScanLine(Line{\n\t\tA:     math.Float64frombits(a),\n\t\tB:     math.Float64frombits(b),\n\t\tC:     math.Float64frombits(c),\n\t\tValid: true,\n\t})\n}\n\ntype scanPlanTextAnyToLineScanner struct{}\n\nfunc (scanPlanTextAnyToLineScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(LineScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanLine(Line{})\n\t}\n\n\tif len(src) < 7 {\n\t\treturn fmt.Errorf(\"invalid length for line: %v\", len(src))\n\t}\n\n\tparts := strings.SplitN(string(src[1:len(src)-1]), \",\", 3)\n\tif len(parts) < 3 {\n\t\treturn fmt.Errorf(\"invalid format for line\")\n\t}\n\n\ta, err := strconv.ParseFloat(parts[0], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tb, err := strconv.ParseFloat(parts[1], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc, err := strconv.ParseFloat(parts[2], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanLine(Line{A: a, B: b, C: c, Valid: true})\n}\n\nfunc (c LineCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c LineCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar line Line\n\terr := codecScan(c, m, oid, format, src, &line)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn line, nil\n}\n"
  },
  {
    "path": "pgtype/line_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestLineTranscode(t *testing.T) {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support type line\")\n\n\t\tif _, ok := conn.TypeMap().TypeForName(\"line\"); !ok {\n\t\t\tt.Skip(\"Skipping due to no line type\")\n\t\t}\n\n\t\t// line may exist but not be usable on 9.3 :(\n\t\tvar isPG93 bool\n\t\terr := conn.QueryRow(context.Background(), \"select version() ~ '9.3'\").Scan(&isPG93)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif isPG93 {\n\t\t\tt.Skip(\"Skipping due to unimplemented line type in PG 9.3\")\n\t\t}\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, \"line\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam: pgtype.Line{\n\t\t\t\tA: 1.23, B: 4.56, C: 7.89012345,\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Line),\n\t\t\tTest: isExpectedEq(pgtype.Line{\n\t\t\t\tA: 1.23, B: 4.56, C: 7.89012345,\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Line{\n\t\t\t\tA: -1.23, B: -4.56, C: -7.89,\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Line),\n\t\t\tTest: isExpectedEq(pgtype.Line{\n\t\t\t\tA: -1.23, B: -4.56, C: -7.89,\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Line{}, Result: new(pgtype.Line), Test: isExpectedEq(pgtype.Line{})},\n\t\t{Param: nil, Result: new(pgtype.Line), Test: isExpectedEq(pgtype.Line{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/lseg.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype LsegScanner interface {\n\tScanLseg(v Lseg) error\n}\n\ntype LsegValuer interface {\n\tLsegValue() (Lseg, error)\n}\n\ntype Lseg struct {\n\tP     [2]Vec2\n\tValid bool\n}\n\n// ScanLseg implements the [LsegScanner] interface.\nfunc (lseg *Lseg) ScanLseg(v Lseg) error {\n\t*lseg = v\n\treturn nil\n}\n\n// LsegValue implements the [LsegValuer] interface.\nfunc (lseg Lseg) LsegValue() (Lseg, error) {\n\treturn lseg, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (lseg *Lseg) Scan(src any) error {\n\tif src == nil {\n\t\t*lseg = Lseg{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToLsegScanner{}.Scan([]byte(src), lseg)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (lseg Lseg) Value() (driver.Value, error) {\n\tif !lseg.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := LsegCodec{}.PlanEncode(nil, 0, TextFormatCode, lseg).Encode(lseg, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype LsegCodec struct{}\n\nfunc (LsegCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (LsegCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (LsegCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(LsegValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanLsegCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanLsegCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanLsegCodecBinary struct{}\n\nfunc (encodePlanLsegCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tlseg, err := value.(LsegValuer).LsegValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !lseg.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(lseg.P[0].X))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(lseg.P[0].Y))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(lseg.P[1].X))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(lseg.P[1].Y))\n\treturn buf, nil\n}\n\ntype encodePlanLsegCodecText struct{}\n\nfunc (encodePlanLsegCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tlseg, err := value.(LsegValuer).LsegValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !lseg.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, fmt.Sprintf(`[(%s,%s),(%s,%s)]`,\n\t\tstrconv.FormatFloat(lseg.P[0].X, 'f', -1, 64),\n\t\tstrconv.FormatFloat(lseg.P[0].Y, 'f', -1, 64),\n\t\tstrconv.FormatFloat(lseg.P[1].X, 'f', -1, 64),\n\t\tstrconv.FormatFloat(lseg.P[1].Y, 'f', -1, 64),\n\t)...)\n\treturn buf, nil\n}\n\nfunc (LsegCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase LsegScanner:\n\t\t\treturn scanPlanBinaryLsegToLsegScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase LsegScanner:\n\t\t\treturn scanPlanTextAnyToLsegScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryLsegToLsegScanner struct{}\n\nfunc (scanPlanBinaryLsegToLsegScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(LsegScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanLseg(Lseg{})\n\t}\n\n\tif len(src) != 32 {\n\t\treturn fmt.Errorf(\"invalid length for lseg: %v\", len(src))\n\t}\n\n\tx1 := binary.BigEndian.Uint64(src)\n\ty1 := binary.BigEndian.Uint64(src[8:])\n\tx2 := binary.BigEndian.Uint64(src[16:])\n\ty2 := binary.BigEndian.Uint64(src[24:])\n\n\treturn scanner.ScanLseg(Lseg{\n\t\tP: [2]Vec2{\n\t\t\t{math.Float64frombits(x1), math.Float64frombits(y1)},\n\t\t\t{math.Float64frombits(x2), math.Float64frombits(y2)},\n\t\t},\n\t\tValid: true,\n\t})\n}\n\ntype scanPlanTextAnyToLsegScanner struct{}\n\nfunc (scanPlanTextAnyToLsegScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(LsegScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanLseg(Lseg{})\n\t}\n\n\tif len(src) < 11 {\n\t\treturn fmt.Errorf(\"invalid length for lseg: %v\", len(src))\n\t}\n\n\tstr := string(src[2:])\n\n\tvar end int\n\tend = strings.IndexByte(str, ',')\n\n\tx1, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+1:]\n\tend = strings.IndexByte(str, ')')\n\n\ty1, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+3:]\n\tend = strings.IndexByte(str, ',')\n\n\tx2, err := strconv.ParseFloat(str[:end], 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstr = str[end+1 : len(str)-2]\n\n\ty2, err := strconv.ParseFloat(str, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanLseg(Lseg{P: [2]Vec2{{x1, y1}, {x2, y2}}, Valid: true})\n}\n\nfunc (c LsegCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c LsegCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar lseg Lseg\n\terr := codecScan(c, m, oid, format, src, &lseg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn lseg, nil\n}\n"
  },
  {
    "path": "pgtype/lseg_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestLsegTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type lseg\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"lseg\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam: pgtype.Lseg{\n\t\t\t\tP:     [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Lseg),\n\t\t\tTest: isExpectedEq(pgtype.Lseg{\n\t\t\t\tP:     [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}},\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Lseg{\n\t\t\t\tP:     [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Lseg),\n\t\t\tTest: isExpectedEq(pgtype.Lseg{\n\t\t\t\tP:     [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}},\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Lseg{}, Result: new(pgtype.Lseg), Test: isExpectedEq(pgtype.Lseg{})},\n\t\t{Param: nil, Result: new(pgtype.Lseg), Test: isExpectedEq(pgtype.Lseg{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/ltree.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n)\n\ntype LtreeCodec struct{}\n\nfunc (l LtreeCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\n// PreferredFormat returns the preferred format.\nfunc (l LtreeCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n\n// PlanEncode returns an EncodePlan for encoding value into PostgreSQL format for oid and format. If no plan can be\n// found then nil is returned.\nfunc (l LtreeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn (TextCodec)(l).PlanEncode(m, oid, format, value)\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase string:\n\t\t\treturn encodeLtreeCodecBinaryString{}\n\t\tcase []byte:\n\t\t\treturn encodeLtreeCodecBinaryByteSlice{}\n\t\tcase TextValuer:\n\t\t\treturn encodeLtreeCodecBinaryTextValuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodeLtreeCodecBinaryString struct{}\n\nfunc (encodeLtreeCodecBinaryString) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tltree := value.(string)\n\tbuf = append(buf, 1)\n\treturn append(buf, ltree...), nil\n}\n\ntype encodeLtreeCodecBinaryByteSlice struct{}\n\nfunc (encodeLtreeCodecBinaryByteSlice) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tltree := value.([]byte)\n\tbuf = append(buf, 1)\n\treturn append(buf, ltree...), nil\n}\n\ntype encodeLtreeCodecBinaryTextValuer struct{}\n\nfunc (encodeLtreeCodecBinaryTextValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tt, err := value.(TextValuer).TextValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, 1)\n\treturn append(buf, t.String...), nil\n}\n\n// PlanScan returns a ScanPlan for scanning a PostgreSQL value into a destination with the same type as target. If\n// no plan can be found then nil is returned.\nfunc (l LtreeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn (TextCodec)(l).PlanScan(m, oid, format, target)\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *string:\n\t\t\treturn scanPlanBinaryLtreeToString{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryLtreeToTextScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryLtreeToString struct{}\n\nfunc (scanPlanBinaryLtreeToString) Scan(src []byte, target any) error {\n\tversion := src[0]\n\tif version != 1 {\n\t\treturn fmt.Errorf(\"unsupported ltree version %d\", version)\n\t}\n\n\tp := (target).(*string)\n\t*p = string(src[1:])\n\n\treturn nil\n}\n\ntype scanPlanBinaryLtreeToTextScanner struct{}\n\nfunc (scanPlanBinaryLtreeToTextScanner) Scan(src []byte, target any) error {\n\tversion := src[0]\n\tif version != 1 {\n\t\treturn fmt.Errorf(\"unsupported ltree version %d\", version)\n\t}\n\n\tscanner := (target).(TextScanner)\n\treturn scanner.ScanText(Text{String: string(src[1:]), Valid: true})\n}\n\n// DecodeDatabaseSQLValue returns src decoded into a value compatible with the sql.Scanner interface.\nfunc (l LtreeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn (TextCodec)(l).DecodeDatabaseSQLValue(m, oid, format, src)\n}\n\n// DecodeValue returns src decoded into its default format.\nfunc (l LtreeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\treturn (TextCodec)(l).DecodeValue(m, oid, format, src)\n}\n"
  },
  {
    "path": "pgtype/ltree_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestLtreeCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type ltree\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"ltree\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  \"A.B.C\",\n\t\t\tResult: new(string),\n\t\t\tTest:   isExpectedEq(\"A.B.C\"),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Text{String: \"\", Valid: true},\n\t\t\tResult: new(pgtype.Text),\n\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"\", Valid: true}),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/macaddr.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"net\"\n)\n\ntype MacaddrCodec struct{}\n\nfunc (MacaddrCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (MacaddrCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (MacaddrCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase net.HardwareAddr:\n\t\t\treturn encodePlanMacaddrCodecBinaryHardwareAddr{}\n\t\tcase TextValuer:\n\t\t\treturn encodePlanMacAddrCodecTextValuer{}\n\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase net.HardwareAddr:\n\t\t\treturn encodePlanMacaddrCodecTextHardwareAddr{}\n\t\tcase TextValuer:\n\t\t\treturn encodePlanTextCodecTextValuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanMacaddrCodecBinaryHardwareAddr struct{}\n\nfunc (encodePlanMacaddrCodecBinaryHardwareAddr) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\taddr := value.(net.HardwareAddr)\n\tif addr == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, addr...), nil\n}\n\ntype encodePlanMacAddrCodecTextValuer struct{}\n\nfunc (encodePlanMacAddrCodecTextValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tt, err := value.(TextValuer).TextValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\taddr, err := net.ParseMAC(t.String)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn append(buf, addr...), nil\n}\n\ntype encodePlanMacaddrCodecTextHardwareAddr struct{}\n\nfunc (encodePlanMacaddrCodecTextHardwareAddr) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\taddr := value.(net.HardwareAddr)\n\tif addr == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, addr.String()...), nil\n}\n\nfunc (MacaddrCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *net.HardwareAddr:\n\t\t\treturn scanPlanBinaryMacaddrToHardwareAddr{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryMacaddrToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *net.HardwareAddr:\n\t\t\treturn scanPlanTextMacaddrToHardwareAddr{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanTextAnyToTextScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryMacaddrToHardwareAddr struct{}\n\nfunc (scanPlanBinaryMacaddrToHardwareAddr) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*net.HardwareAddr)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\t*dstBuf = make([]byte, len(src))\n\tcopy(*dstBuf, src)\n\treturn nil\n}\n\ntype scanPlanBinaryMacaddrToTextScanner struct{}\n\nfunc (scanPlanBinaryMacaddrToTextScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TextScanner)\n\tif src == nil {\n\t\treturn scanner.ScanText(Text{})\n\t}\n\n\treturn scanner.ScanText(Text{String: net.HardwareAddr(src).String(), Valid: true})\n}\n\ntype scanPlanTextMacaddrToHardwareAddr struct{}\n\nfunc (scanPlanTextMacaddrToHardwareAddr) Scan(src []byte, dst any) error {\n\tp := dst.(*net.HardwareAddr)\n\n\tif src == nil {\n\t\t*p = nil\n\t\treturn nil\n\t}\n\n\taddr, err := net.ParseMAC(string(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*p = addr\n\n\treturn nil\n}\n\nfunc (c MacaddrCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c MacaddrCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar addr net.HardwareAddr\n\terr := codecScan(c, m, oid, format, src, &addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn addr, nil\n}\n"
  },
  {
    "path": "pgtype/macaddr_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqHardwareAddr(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\taa := a.(net.HardwareAddr)\n\t\tvv := v.(net.HardwareAddr)\n\n\t\tif (aa == nil) != (vv == nil) {\n\t\t\treturn false\n\t\t}\n\n\t\tif aa == nil {\n\t\t\treturn true\n\t\t}\n\n\t\treturn bytes.Equal(aa, vv)\n\t}\n}\n\nfunc TestMacaddrCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type macaddr\")\n\n\t// Only testing known OID query exec modes as net.HardwareAddr could map to macaddr or macaddr8.\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"macaddr\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  mustParseMacaddr(t, \"01:23:45:67:89:ab\"),\n\t\t\tResult: new(net.HardwareAddr),\n\t\t\tTest:   isExpectedEqHardwareAddr(mustParseMacaddr(t, \"01:23:45:67:89:ab\")),\n\t\t},\n\t\t{\n\t\t\tParam:  \"01:23:45:67:89:ab\",\n\t\t\tResult: new(net.HardwareAddr),\n\t\t\tTest:   isExpectedEqHardwareAddr(mustParseMacaddr(t, \"01:23:45:67:89:ab\")),\n\t\t},\n\t\t{\n\t\t\tParam:  mustParseMacaddr(t, \"01:23:45:67:89:ab\"),\n\t\t\tResult: new(string),\n\t\t\tTest:   isExpectedEq(\"01:23:45:67:89:ab\"),\n\t\t},\n\t\t{Param: nil, Result: new(*net.HardwareAddr), Test: isExpectedEq((*net.HardwareAddr)(nil))},\n\t})\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"macaddr8\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  mustParseMacaddr(t, \"01:23:45:67:89:ab:01:08\"),\n\t\t\tResult: new(net.HardwareAddr),\n\t\t\tTest:   isExpectedEqHardwareAddr(mustParseMacaddr(t, \"01:23:45:67:89:ab:01:08\")),\n\t\t},\n\t\t{\n\t\t\tParam:  \"01:23:45:67:89:ab:01:08\",\n\t\t\tResult: new(net.HardwareAddr),\n\t\t\tTest:   isExpectedEqHardwareAddr(mustParseMacaddr(t, \"01:23:45:67:89:ab:01:08\")),\n\t\t},\n\t\t{\n\t\t\tParam:  mustParseMacaddr(t, \"01:23:45:67:89:ab:01:08\"),\n\t\t\tResult: new(string),\n\t\t\tTest:   isExpectedEq(\"01:23:45:67:89:ab:01:08\"),\n\t\t},\n\t\t{Param: nil, Result: new(*net.HardwareAddr), Test: isExpectedEq((*net.HardwareAddr)(nil))},\n\t})\n}\n"
  },
  {
    "path": "pgtype/multirange.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// MultirangeGetter is a type that can be converted into a PostgreSQL multirange.\ntype MultirangeGetter interface {\n\t// IsNull returns true if the value is SQL NULL.\n\tIsNull() bool\n\n\t// Len returns the number of elements in the multirange.\n\tLen() int\n\n\t// Index returns the element at i.\n\tIndex(i int) any\n\n\t// IndexType returns a non-nil scan target of the type Index will return. This is used by MultirangeCodec.PlanEncode.\n\tIndexType() any\n}\n\n// MultirangeSetter is a type can be set from a PostgreSQL multirange.\ntype MultirangeSetter interface {\n\t// ScanNull sets the value to SQL NULL.\n\tScanNull() error\n\n\t// SetLen prepares the value such that ScanIndex can be called for each element. This will remove any existing\n\t// elements.\n\tSetLen(n int) error\n\n\t// ScanIndex returns a value usable as a scan target for i. SetLen must be called before ScanIndex.\n\tScanIndex(i int) any\n\n\t// ScanIndexType returns a non-nil scan target of the type ScanIndex will return. This is used by\n\t// MultirangeCodec.PlanScan.\n\tScanIndexType() any\n}\n\n// MultirangeCodec is a codec for any multirange type.\ntype MultirangeCodec struct {\n\tElementType *Type\n}\n\nfunc (c *MultirangeCodec) FormatSupported(format int16) bool {\n\treturn c.ElementType.Codec.FormatSupported(format)\n}\n\nfunc (c *MultirangeCodec) PreferredFormat() int16 {\n\treturn c.ElementType.Codec.PreferredFormat()\n}\n\nfunc (c *MultirangeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tmultirangeValuer, ok := value.(MultirangeGetter)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\telementType := multirangeValuer.IndexType()\n\n\telementEncodePlan := m.PlanEncode(c.ElementType.OID, format, elementType)\n\tif elementEncodePlan == nil {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn &encodePlanMultirangeCodecBinary{ac: c, m: m, oid: oid}\n\tcase TextFormatCode:\n\t\treturn &encodePlanMultirangeCodecText{ac: c, m: m, oid: oid}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanMultirangeCodecText struct {\n\tac  *MultirangeCodec\n\tm   *Map\n\toid uint32\n}\n\nfunc (p *encodePlanMultirangeCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tmultirange := value.(MultirangeGetter)\n\n\tif multirange.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\telementCount := multirange.Len()\n\n\tbuf = append(buf, '{')\n\n\tvar encodePlan EncodePlan\n\tvar lastElemType reflect.Type\n\tinElemBuf := make([]byte, 0, 32)\n\tfor i := range elementCount {\n\t\tif i > 0 {\n\t\t\tbuf = append(buf, ',')\n\t\t}\n\n\t\telem := multirange.Index(i)\n\t\tvar elemBuf []byte\n\t\tif elem != nil {\n\t\t\telemType := reflect.TypeOf(elem)\n\t\t\tif lastElemType != elemType {\n\t\t\t\tlastElemType = elemType\n\t\t\t\tencodePlan = p.m.PlanEncode(p.ac.ElementType.OID, TextFormatCode, elem)\n\t\t\t\tif encodePlan == nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unable to encode %v\", multirange.Index(i))\n\t\t\t\t}\n\t\t\t}\n\t\t\telemBuf, err = encodePlan.Encode(elem, inElemBuf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif elemBuf == nil {\n\t\t\treturn nil, fmt.Errorf(\"multirange cannot contain NULL element\")\n\t\t} else {\n\t\t\tbuf = append(buf, elemBuf...)\n\t\t}\n\t}\n\n\tbuf = append(buf, '}')\n\n\treturn buf, nil\n}\n\ntype encodePlanMultirangeCodecBinary struct {\n\tac  *MultirangeCodec\n\tm   *Map\n\toid uint32\n}\n\nfunc (p *encodePlanMultirangeCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tmultirange := value.(MultirangeGetter)\n\n\tif multirange.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\telementCount := multirange.Len()\n\n\tbuf = pgio.AppendInt32(buf, int32(elementCount))\n\n\tvar encodePlan EncodePlan\n\tvar lastElemType reflect.Type\n\tfor i := range elementCount {\n\t\tsp := len(buf)\n\t\tbuf = pgio.AppendInt32(buf, -1)\n\n\t\telem := multirange.Index(i)\n\t\tvar elemBuf []byte\n\t\tif elem != nil {\n\t\t\telemType := reflect.TypeOf(elem)\n\t\t\tif lastElemType != elemType {\n\t\t\t\tlastElemType = elemType\n\t\t\t\tencodePlan = p.m.PlanEncode(p.ac.ElementType.OID, BinaryFormatCode, elem)\n\t\t\t\tif encodePlan == nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unable to encode %v\", multirange.Index(i))\n\t\t\t\t}\n\t\t\t}\n\t\t\telemBuf, err = encodePlan.Encode(elem, buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif elemBuf == nil {\n\t\t\treturn nil, fmt.Errorf(\"multirange cannot contain NULL element\")\n\t\t} else {\n\t\t\tbuf = elemBuf\n\t\t\tpgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (c *MultirangeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tmultirangeScanner, ok := target.(MultirangeSetter)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\telementType := multirangeScanner.ScanIndexType()\n\n\telementScanPlan := m.PlanScan(c.ElementType.OID, format, elementType)\n\tif _, ok := elementScanPlan.(*scanPlanFail); ok {\n\t\treturn nil\n\t}\n\n\treturn &scanPlanMultirangeCodec{\n\t\tmultirangeCodec: c,\n\t\tm:               m,\n\t\toid:             oid,\n\t\tformatCode:      format,\n\t}\n}\n\nfunc (c *MultirangeCodec) decodeBinary(m *Map, multirangeOID uint32, src []byte, multirange MultirangeSetter) error {\n\trp := 0\n\n\telementCount := int(binary.BigEndian.Uint32(src[rp:]))\n\trp += 4\n\n\terr := multirange.SetLen(elementCount)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif elementCount == 0 {\n\t\treturn nil\n\t}\n\n\telementScanPlan := c.ElementType.Codec.PlanScan(m, c.ElementType.OID, BinaryFormatCode, multirange.ScanIndex(0))\n\tif elementScanPlan == nil {\n\t\telementScanPlan = m.PlanScan(c.ElementType.OID, BinaryFormatCode, multirange.ScanIndex(0))\n\t}\n\n\tfor i := range elementCount {\n\t\telem := multirange.ScanIndex(i)\n\t\telemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\t\trp += 4\n\t\tvar elemSrc []byte\n\t\tif elemLen >= 0 {\n\t\t\telemSrc = src[rp : rp+elemLen]\n\t\t\trp += elemLen\n\t\t}\n\t\terr = elementScanPlan.Scan(elemSrc, elem)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to scan multirange element %d: %w\", i, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *MultirangeCodec) decodeText(m *Map, multirangeOID uint32, src []byte, multirange MultirangeSetter) error {\n\telements, err := parseUntypedTextMultirange(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = multirange.SetLen(len(elements))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(elements) == 0 {\n\t\treturn nil\n\t}\n\n\telementScanPlan := c.ElementType.Codec.PlanScan(m, c.ElementType.OID, TextFormatCode, multirange.ScanIndex(0))\n\tif elementScanPlan == nil {\n\t\telementScanPlan = m.PlanScan(c.ElementType.OID, TextFormatCode, multirange.ScanIndex(0))\n\t}\n\n\tfor i, s := range elements {\n\t\telem := multirange.ScanIndex(i)\n\t\terr = elementScanPlan.Scan([]byte(s), elem)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanMultirangeCodec struct {\n\tmultirangeCodec *MultirangeCodec\n\tm               *Map\n\toid             uint32\n\tformatCode      int16\n\telementScanPlan ScanPlan\n}\n\nfunc (spac *scanPlanMultirangeCodec) Scan(src []byte, dst any) error {\n\tc := spac.multirangeCodec\n\tm := spac.m\n\toid := spac.oid\n\tformatCode := spac.formatCode\n\n\tmultirange := dst.(MultirangeSetter)\n\n\tif src == nil {\n\t\treturn multirange.ScanNull()\n\t}\n\n\tswitch formatCode {\n\tcase BinaryFormatCode:\n\t\treturn c.decodeBinary(m, oid, src, multirange)\n\tcase TextFormatCode:\n\t\treturn c.decodeText(m, oid, src, multirange)\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown format code %d\", formatCode)\n\t}\n}\n\nfunc (c *MultirangeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn string(src), nil\n\tcase BinaryFormatCode:\n\t\tbuf := make([]byte, len(src))\n\t\tcopy(buf, src)\n\t\treturn buf, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n\nfunc (c *MultirangeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar multirange Multirange[Range[any]]\n\terr := m.PlanScan(oid, format, &multirange).Scan(src, &multirange)\n\treturn multirange, err\n}\n\nfunc parseUntypedTextMultirange(src []byte) ([]string, error) {\n\telements := make([]string, 0)\n\n\tbuf := bytes.NewBuffer(src)\n\n\tskipWhitespace(buf)\n\n\tr, _, err := buf.ReadRune()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid array: %w\", err)\n\t}\n\n\tif r != '{' {\n\t\treturn nil, fmt.Errorf(\"invalid multirange, expected '{' got %v\", r)\n\t}\n\nparseValueLoop:\n\tfor {\n\t\tr, _, err = buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid multirange: %w\", err)\n\t\t}\n\n\t\tswitch r {\n\t\tcase ',': // skip range separator\n\t\tcase '}':\n\t\t\tbreak parseValueLoop\n\t\tdefault:\n\t\t\tbuf.UnreadRune()\n\t\t\tvalue, err := parseRange(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid multirange value: %w\", err)\n\t\t\t}\n\t\t\telements = append(elements, value)\n\t\t}\n\t}\n\n\tskipWhitespace(buf)\n\n\tif buf.Len() > 0 {\n\t\treturn nil, fmt.Errorf(\"unexpected trailing data: %v\", buf.String())\n\t}\n\n\treturn elements, nil\n}\n\nfunc parseRange(buf *bytes.Buffer) (string, error) {\n\ts := &bytes.Buffer{}\n\n\tboundSepRead := false\n\tfor {\n\t\tr, _, err := buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tswitch r {\n\t\tcase ',', '}':\n\t\t\tif r == ',' && !boundSepRead {\n\t\t\t\tboundSepRead = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tbuf.UnreadRune()\n\t\t\treturn s.String(), nil\n\t\t}\n\n\t\ts.WriteRune(r)\n\t}\n}\n\n// Multirange is a generic multirange type.\n//\n// T should implement [RangeValuer] and *T should implement [RangeScanner]. However, there does not appear to be a way to\n// enforce the [RangeScanner] constraint.\ntype Multirange[T RangeValuer] []T\n\nfunc (r Multirange[T]) IsNull() bool {\n\treturn r == nil\n}\n\nfunc (r Multirange[T]) Len() int {\n\treturn len(r)\n}\n\nfunc (r Multirange[T]) Index(i int) any {\n\treturn r[i]\n}\n\nfunc (r Multirange[T]) IndexType() any {\n\tvar zero T\n\treturn zero\n}\n\nfunc (r *Multirange[T]) ScanNull() error {\n\t*r = nil\n\treturn nil\n}\n\nfunc (r *Multirange[T]) SetLen(n int) error {\n\t*r = make([]T, n)\n\treturn nil\n}\n\nfunc (r Multirange[T]) ScanIndex(i int) any {\n\treturn &r[i]\n}\n\nfunc (r Multirange[T]) ScanIndexType() any {\n\treturn new(T)\n}\n"
  },
  {
    "path": "pgtype/multirange_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMultirangeCodecTranscode(t *testing.T) {\n\tskipPostgreSQLVersionLessThan(t, 14)\n\tskipCockroachDB(t, \"Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int4multirange\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Multirange[pgtype.Range[pgtype.Int4]](nil),\n\t\t\tResult: new(pgtype.Multirange[pgtype.Range[pgtype.Int4]]),\n\t\t\tTest:   func(a any) bool { return reflect.DeepEqual(pgtype.Multirange[pgtype.Range[pgtype.Int4]](nil), a) },\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Multirange[pgtype.Range[pgtype.Int4]]{},\n\t\t\tResult: new(pgtype.Multirange[pgtype.Range[pgtype.Int4]]),\n\t\t\tTest:   func(a any) bool { return reflect.DeepEqual(pgtype.Multirange[pgtype.Range[pgtype.Int4]]{}, a) },\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Multirange[pgtype.Range[pgtype.Int4]]{\n\t\t\t\t{\n\t\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\t\tUpper:     pgtype.Int4{Int32: 5, Valid: true},\n\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\tValid:     true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tLower:     pgtype.Int4{Int32: 7, Valid: true},\n\t\t\t\t\tUpper:     pgtype.Int4{Int32: 9, Valid: true},\n\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\tValid:     true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tResult: new(pgtype.Multirange[pgtype.Range[pgtype.Int4]]),\n\t\t\tTest: func(a any) bool {\n\t\t\t\treturn reflect.DeepEqual(pgtype.Multirange[pgtype.Range[pgtype.Int4]]{\n\t\t\t\t\t{\n\t\t\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\t\t\tUpper:     pgtype.Int4{Int32: 5, Valid: true},\n\t\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\t\tValid:     true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tLower:     pgtype.Int4{Int32: 7, Valid: true},\n\t\t\t\t\t\tUpper:     pgtype.Int4{Int32: 9, Valid: true},\n\t\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\t\tValid:     true,\n\t\t\t\t\t},\n\t\t\t\t}, a)\n\t\t\t},\n\t\t},\n\t})\n}\n\nfunc TestMultirangeCodecDecodeValue(t *testing.T) {\n\tskipPostgreSQLVersionLessThan(t, 14)\n\tskipCockroachDB(t, \"Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tfor _, tt := range []struct {\n\t\t\tsql      string\n\t\t\texpected any\n\t\t}{\n\t\t\t{\n\t\t\t\tsql: `select int4multirange(int4range(1, 5), int4range(7,9))`,\n\t\t\t\texpected: pgtype.Multirange[pgtype.Range[any]]{\n\t\t\t\t\t{\n\t\t\t\t\t\tLower:     int32(1),\n\t\t\t\t\t\tUpper:     int32(5),\n\t\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\t\tValid:     true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tLower:     int32(7),\n\t\t\t\t\t\tUpper:     int32(9),\n\t\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\t\tValid:     true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(tt.sql, func(t *testing.T) {\n\t\t\t\trows, err := conn.Query(ctx, tt.sql)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\tvalues, err := rows.Values()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, values, 1)\n\t\t\t\t\trequire.Equal(t, tt.expected, values[0])\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, rows.Err())\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/numeric.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// PostgreSQL internal numeric storage uses 16-bit \"digits\" with base of 10,000\nconst nbase = 10_000\n\nconst (\n\tpgNumericNaN     = 0x00000000c0000000\n\tpgNumericNaNSign = 0xc000\n\n\tpgNumericPosInf     = 0x00000000d0000000\n\tpgNumericPosInfSign = 0xd000\n\n\tpgNumericNegInf     = 0x00000000f0000000\n\tpgNumericNegInfSign = 0xf000\n)\n\nvar (\n\tbig1    *big.Int = big.NewInt(1)\n\tbig10   *big.Int = big.NewInt(10)\n\tbig100  *big.Int = big.NewInt(100)\n\tbig1000 *big.Int = big.NewInt(1000)\n)\n\nvar (\n\tbigNBase   *big.Int = big.NewInt(nbase)\n\tbigNBaseX2 *big.Int = big.NewInt(nbase * nbase)\n\tbigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase)\n\tbigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase)\n)\n\ntype NumericScanner interface {\n\tScanNumeric(v Numeric) error\n}\n\ntype NumericValuer interface {\n\tNumericValue() (Numeric, error)\n}\n\ntype Numeric struct {\n\tInt              *big.Int\n\tExp              int32\n\tNaN              bool\n\tInfinityModifier InfinityModifier\n\tValid            bool\n}\n\n// ScanNumeric implements the [NumericScanner] interface.\nfunc (n *Numeric) ScanNumeric(v Numeric) error {\n\t*n = v\n\treturn nil\n}\n\n// NumericValue implements the [NumericValuer] interface.\nfunc (n Numeric) NumericValue() (Numeric, error) {\n\treturn n, nil\n}\n\n// Float64Value implements the [Float64Valuer] interface.\nfunc (n Numeric) Float64Value() (Float8, error) {\n\tif !n.Valid {\n\t\treturn Float8{}, nil\n\t} else if n.NaN {\n\t\treturn Float8{Float64: math.NaN(), Valid: true}, nil\n\t} else if n.InfinityModifier == Infinity {\n\t\treturn Float8{Float64: math.Inf(1), Valid: true}, nil\n\t} else if n.InfinityModifier == NegativeInfinity {\n\t\treturn Float8{Float64: math.Inf(-1), Valid: true}, nil\n\t}\n\n\tbuf := make([]byte, 0, 32)\n\n\tif n.Int == nil {\n\t\tbuf = append(buf, '0')\n\t} else {\n\t\tbuf = append(buf, n.Int.String()...)\n\t}\n\tbuf = append(buf, 'e')\n\tbuf = append(buf, strconv.FormatInt(int64(n.Exp), 10)...)\n\n\tf, err := strconv.ParseFloat(string(buf), 64)\n\tif err != nil {\n\t\treturn Float8{}, err\n\t}\n\n\treturn Float8{Float64: f, Valid: true}, nil\n}\n\n// ScanInt64 implements the [Int64Scanner] interface.\nfunc (n *Numeric) ScanInt64(v Int8) error {\n\tif !v.Valid {\n\t\t*n = Numeric{}\n\t\treturn nil\n\t}\n\n\t*n = Numeric{Int: big.NewInt(v.Int64), Valid: true}\n\treturn nil\n}\n\n// Int64Value implements the [Int64Valuer] interface.\nfunc (n Numeric) Int64Value() (Int8, error) {\n\tif !n.Valid {\n\t\treturn Int8{}, nil\n\t}\n\n\tbi, err := n.toBigInt()\n\tif err != nil {\n\t\treturn Int8{}, err\n\t}\n\n\tif !bi.IsInt64() {\n\t\treturn Int8{}, fmt.Errorf(\"cannot convert %v to int64\", n)\n\t}\n\n\treturn Int8{Int64: bi.Int64(), Valid: true}, nil\n}\n\nfunc (n *Numeric) ScanScientific(src string) error {\n\tif !strings.ContainsAny(\"eE\", src) {\n\t\treturn scanPlanTextAnyToNumericScanner{}.Scan([]byte(src), n)\n\t}\n\n\tif bigF, ok := new(big.Float).SetString(string(src)); ok {\n\t\tsmallF, _ := bigF.Float64()\n\t\tsrc = strconv.FormatFloat(smallF, 'f', -1, 64)\n\t}\n\n\tnum, exp, err := parseNumericString(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*n = Numeric{Int: num, Exp: exp, Valid: true}\n\n\treturn nil\n}\n\nfunc (n *Numeric) toBigInt() (*big.Int, error) {\n\tif n.Exp == 0 {\n\t\treturn n.Int, nil\n\t}\n\n\tnum := &big.Int{}\n\tnum.Set(n.Int)\n\tif n.Exp > 0 {\n\t\tmul := &big.Int{}\n\t\tmul.Exp(big10, big.NewInt(int64(n.Exp)), nil)\n\t\tnum.Mul(num, mul)\n\t\treturn num, nil\n\t}\n\n\tdiv := &big.Int{}\n\tdiv.Exp(big10, big.NewInt(int64(-n.Exp)), nil)\n\tremainder := &big.Int{}\n\tnum.DivMod(num, div, remainder)\n\tif remainder.Sign() != 0 {\n\t\treturn nil, fmt.Errorf(\"cannot convert %v to integer\", n)\n\t}\n\treturn num, nil\n}\n\nfunc parseNumericString(str string) (n *big.Int, exp int32, err error) {\n\tidx := strings.IndexByte(str, '.')\n\n\tif idx == -1 {\n\t\tfor len(str) > 1 && str[len(str)-1] == '0' && str[len(str)-2] != '-' {\n\t\t\tstr = str[:len(str)-1]\n\t\t\texp++\n\t\t}\n\t} else {\n\t\texp = int32(-(len(str) - idx - 1))\n\t\tstr = str[:idx] + str[idx+1:]\n\t}\n\n\taccum := &big.Int{}\n\tif _, ok := accum.SetString(str, 10); !ok {\n\t\treturn nil, 0, fmt.Errorf(\"%s is not a number\", str)\n\t}\n\n\treturn accum, exp, nil\n}\n\nfunc nbaseDigitsToInt64(src []byte) (accum int64, bytesRead, digitsRead int) {\n\tdigits := min(len(src)/2, 4)\n\n\trp := 0\n\n\tfor i := range digits {\n\t\tif i > 0 {\n\t\t\taccum *= nbase\n\t\t}\n\t\taccum += int64(binary.BigEndian.Uint16(src[rp:]))\n\t\trp += 2\n\t}\n\n\treturn accum, rp, digits\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (n *Numeric) Scan(src any) error {\n\tif src == nil {\n\t\t*n = Numeric{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToNumericScanner{}.Scan([]byte(src), n)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (n Numeric) Value() (driver.Value, error) {\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := NumericCodec{}.PlanEncode(nil, 0, TextFormatCode, n).Encode(n, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (n Numeric) MarshalJSON() ([]byte, error) {\n\tif !n.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tif n.NaN {\n\t\treturn []byte(`\"NaN\"`), nil\n\t}\n\n\treturn n.numberTextBytes(), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (n *Numeric) UnmarshalJSON(src []byte) error {\n\tif bytes.Equal(src, []byte(`null`)) {\n\t\t*n = Numeric{}\n\t\treturn nil\n\t}\n\tif bytes.Equal(src, []byte(`\"NaN\"`)) {\n\t\t*n = Numeric{NaN: true, Valid: true}\n\t\treturn nil\n\t}\n\treturn scanPlanTextAnyToNumericScanner{}.Scan(src, n)\n}\n\n// numberString returns a string of the number. undefined if NaN, infinite, or NULL\nfunc (n Numeric) numberTextBytes() []byte {\n\tintStr := n.Int.String()\n\n\tbuf := &bytes.Buffer{}\n\n\tif len(intStr) > 0 && intStr[:1] == \"-\" {\n\t\tintStr = intStr[1:]\n\t\tbuf.WriteByte('-')\n\t}\n\n\texp := int(n.Exp)\n\tif exp > 0 {\n\t\tbuf.WriteString(intStr)\n\t\tfor range exp {\n\t\t\tbuf.WriteByte('0')\n\t\t}\n\t} else if exp < 0 {\n\t\tif len(intStr) <= -exp {\n\t\t\tbuf.WriteString(\"0.\")\n\t\t\tleadingZeros := -exp - len(intStr)\n\t\t\tfor range leadingZeros {\n\t\t\t\tbuf.WriteByte('0')\n\t\t\t}\n\t\t\tbuf.WriteString(intStr)\n\t\t} else if len(intStr) > -exp {\n\t\t\tdpPos := len(intStr) + exp\n\t\t\tbuf.WriteString(intStr[:dpPos])\n\t\t\tbuf.WriteByte('.')\n\t\t\tbuf.WriteString(intStr[dpPos:])\n\t\t}\n\t} else {\n\t\tbuf.WriteString(intStr)\n\t}\n\n\treturn buf.Bytes()\n}\n\ntype NumericCodec struct{}\n\nfunc (NumericCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (NumericCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (NumericCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase NumericValuer:\n\t\t\treturn encodePlanNumericCodecBinaryNumericValuer{}\n\t\tcase Float64Valuer:\n\t\t\treturn encodePlanNumericCodecBinaryFloat64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanNumericCodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase NumericValuer:\n\t\t\treturn encodePlanNumericCodecTextNumericValuer{}\n\t\tcase Float64Valuer:\n\t\t\treturn encodePlanNumericCodecTextFloat64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanNumericCodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanNumericCodecBinaryNumericValuer struct{}\n\nfunc (encodePlanNumericCodecBinaryNumericValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(NumericValuer).NumericValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn encodeNumericBinary(n, buf)\n}\n\ntype encodePlanNumericCodecBinaryFloat64Valuer struct{}\n\nfunc (encodePlanNumericCodecBinaryFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Float64Valuer).Float64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif math.IsNaN(n.Float64) {\n\t\treturn encodeNumericBinary(Numeric{NaN: true, Valid: true}, buf)\n\t} else if math.IsInf(n.Float64, 1) {\n\t\treturn encodeNumericBinary(Numeric{InfinityModifier: Infinity, Valid: true}, buf)\n\t} else if math.IsInf(n.Float64, -1) {\n\t\treturn encodeNumericBinary(Numeric{InfinityModifier: NegativeInfinity, Valid: true}, buf)\n\t}\n\tnum, exp, err := parseNumericString(strconv.FormatFloat(n.Float64, 'f', -1, 64))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn encodeNumericBinary(Numeric{Int: num, Exp: exp, Valid: true}, buf)\n}\n\ntype encodePlanNumericCodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanNumericCodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn encodeNumericBinary(Numeric{Int: big.NewInt(n.Int64), Valid: true}, buf)\n}\n\nfunc encodeNumericBinary(n Numeric, buf []byte) (newBuf []byte, err error) {\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.NaN {\n\t\tbuf = pgio.AppendUint64(buf, pgNumericNaN)\n\t\treturn buf, nil\n\t} else if n.InfinityModifier == Infinity {\n\t\tbuf = pgio.AppendUint64(buf, pgNumericPosInf)\n\t\treturn buf, nil\n\t} else if n.InfinityModifier == NegativeInfinity {\n\t\tbuf = pgio.AppendUint64(buf, pgNumericNegInf)\n\t\treturn buf, nil\n\t}\n\n\tvar sign int16\n\tif n.Int.Sign() < 0 {\n\t\tsign = 16384\n\t}\n\n\tabsInt := &big.Int{}\n\twholePart := &big.Int{}\n\tfracPart := &big.Int{}\n\tremainder := &big.Int{}\n\tabsInt.Abs(n.Int)\n\n\t// Normalize absInt and exp to where exp is always a multiple of 4. This makes\n\t// converting to 16-bit base 10,000 digits easier.\n\tvar exp int32\n\tswitch n.Exp % 4 {\n\tcase 1, -3:\n\t\texp = n.Exp - 1\n\t\tabsInt.Mul(absInt, big10)\n\tcase 2, -2:\n\t\texp = n.Exp - 2\n\t\tabsInt.Mul(absInt, big100)\n\tcase 3, -1:\n\t\texp = n.Exp - 3\n\t\tabsInt.Mul(absInt, big1000)\n\tdefault:\n\t\texp = n.Exp\n\t}\n\n\tif exp < 0 {\n\t\tdivisor := &big.Int{}\n\t\tdivisor.Exp(big10, big.NewInt(int64(-exp)), nil)\n\t\twholePart.DivMod(absInt, divisor, fracPart)\n\t\tfracPart.Add(fracPart, divisor)\n\t} else {\n\t\twholePart = absInt\n\t}\n\n\tvar wholeDigits, fracDigits []int16\n\n\tfor wholePart.Sign() != 0 {\n\t\twholePart.DivMod(wholePart, bigNBase, remainder)\n\t\twholeDigits = append(wholeDigits, int16(remainder.Int64()))\n\t}\n\n\tif fracPart.Sign() != 0 {\n\t\tfor fracPart.Cmp(big1) != 0 {\n\t\t\tfracPart.DivMod(fracPart, bigNBase, remainder)\n\t\t\tfracDigits = append(fracDigits, int16(remainder.Int64()))\n\t\t}\n\t}\n\n\tbuf = pgio.AppendInt16(buf, int16(len(wholeDigits)+len(fracDigits)))\n\n\tvar weight int16\n\tif len(wholeDigits) > 0 {\n\t\tweight = int16(len(wholeDigits) - 1)\n\t\tif exp > 0 {\n\t\t\tweight += int16(exp / 4)\n\t\t}\n\t} else {\n\t\tweight = int16(exp/4) - 1 + int16(len(fracDigits))\n\t}\n\tbuf = pgio.AppendInt16(buf, weight)\n\n\tbuf = pgio.AppendInt16(buf, sign)\n\n\tvar dscale int16\n\tif n.Exp < 0 {\n\t\tdscale = int16(-n.Exp)\n\t}\n\tbuf = pgio.AppendInt16(buf, dscale)\n\n\tfor i := len(wholeDigits) - 1; i >= 0; i-- {\n\t\tbuf = pgio.AppendInt16(buf, wholeDigits[i])\n\t}\n\n\tfor i := len(fracDigits) - 1; i >= 0; i-- {\n\t\tbuf = pgio.AppendInt16(buf, fracDigits[i])\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanNumericCodecTextNumericValuer struct{}\n\nfunc (encodePlanNumericCodecTextNumericValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(NumericValuer).NumericValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn encodeNumericText(n, buf)\n}\n\ntype encodePlanNumericCodecTextFloat64Valuer struct{}\n\nfunc (encodePlanNumericCodecTextFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Float64Valuer).Float64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif math.IsNaN(n.Float64) {\n\t\tbuf = append(buf, \"NaN\"...)\n\t} else if math.IsInf(n.Float64, 1) {\n\t\tbuf = append(buf, \"Infinity\"...)\n\t} else if math.IsInf(n.Float64, -1) {\n\t\tbuf = append(buf, \"-Infinity\"...)\n\t} else {\n\t\tbuf = append(buf, strconv.FormatFloat(n.Float64, 'f', -1, 64)...)\n\t}\n\treturn buf, nil\n}\n\ntype encodePlanNumericCodecTextInt64Valuer struct{}\n\nfunc (encodePlanNumericCodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tn, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, strconv.FormatInt(n.Int64, 10)...)\n\treturn buf, nil\n}\n\nfunc encodeNumericText(n Numeric, buf []byte) (newBuf []byte, err error) {\n\tif !n.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif n.NaN {\n\t\tbuf = append(buf, \"NaN\"...)\n\t\treturn buf, nil\n\t} else if n.InfinityModifier == Infinity {\n\t\tbuf = append(buf, \"Infinity\"...)\n\t\treturn buf, nil\n\t} else if n.InfinityModifier == NegativeInfinity {\n\t\tbuf = append(buf, \"-Infinity\"...)\n\t\treturn buf, nil\n\t}\n\n\tbuf = append(buf, n.numberTextBytes()...)\n\n\treturn buf, nil\n}\n\nfunc (NumericCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase NumericScanner:\n\t\t\treturn scanPlanBinaryNumericToNumericScanner{}\n\t\tcase Float64Scanner:\n\t\t\treturn scanPlanBinaryNumericToFloat64Scanner{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanBinaryNumericToInt64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryNumericToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase NumericScanner:\n\t\t\treturn scanPlanTextAnyToNumericScanner{}\n\t\tcase Float64Scanner:\n\t\t\treturn scanPlanTextAnyToFloat64Scanner{}\n\t\tcase Int64Scanner:\n\t\t\treturn scanPlanTextAnyToInt64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryNumericToNumericScanner struct{}\n\nfunc (scanPlanBinaryNumericToNumericScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(NumericScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanNumeric(Numeric{})\n\t}\n\n\tif len(src) < 8 {\n\t\treturn fmt.Errorf(\"numeric incomplete %v\", src)\n\t}\n\n\trp := 0\n\tndigits := binary.BigEndian.Uint16(src[rp:])\n\trp += 2\n\tweight := int16(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\tsign := binary.BigEndian.Uint16(src[rp:])\n\trp += 2\n\tdscale := int16(binary.BigEndian.Uint16(src[rp:]))\n\trp += 2\n\n\tif sign == pgNumericNaNSign {\n\t\treturn scanner.ScanNumeric(Numeric{NaN: true, Valid: true})\n\t} else if sign == pgNumericPosInfSign {\n\t\treturn scanner.ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})\n\t} else if sign == pgNumericNegInfSign {\n\t\treturn scanner.ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})\n\t}\n\n\tif ndigits == 0 {\n\t\treturn scanner.ScanNumeric(Numeric{Int: big.NewInt(0), Valid: true})\n\t}\n\n\tif len(src[rp:]) < int(ndigits)*2 {\n\t\treturn fmt.Errorf(\"numeric incomplete %v\", src)\n\t}\n\n\taccum := &big.Int{}\n\n\tfor i := 0; i < int(ndigits+3)/4; i++ {\n\t\tint64accum, bytesRead, digitsRead := nbaseDigitsToInt64(src[rp:])\n\t\trp += bytesRead\n\n\t\tif i > 0 {\n\t\t\tvar mul *big.Int\n\t\t\tswitch digitsRead {\n\t\t\tcase 1:\n\t\t\t\tmul = bigNBase\n\t\t\tcase 2:\n\t\t\t\tmul = bigNBaseX2\n\t\t\tcase 3:\n\t\t\t\tmul = bigNBaseX3\n\t\t\tcase 4:\n\t\t\t\tmul = bigNBaseX4\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid digitsRead: %d (this can't happen)\", digitsRead)\n\t\t\t}\n\t\t\taccum.Mul(accum, mul)\n\t\t}\n\n\t\taccum.Add(accum, big.NewInt(int64accum))\n\t}\n\n\texp := (int32(weight) - int32(ndigits) + 1) * 4\n\n\tif dscale > 0 {\n\t\tfracNBaseDigits := int(ndigits) - int(weight) - 1\n\t\tfracDecimalDigits := fracNBaseDigits * 4\n\t\tdscaleInt := int(dscale)\n\n\t\tif dscaleInt > fracDecimalDigits {\n\t\t\tmultCount := dscaleInt - fracDecimalDigits\n\t\t\tfor range multCount {\n\t\t\t\taccum.Mul(accum, big10)\n\t\t\t\texp--\n\t\t\t}\n\t\t} else if dscaleInt < fracDecimalDigits {\n\t\t\tdivCount := fracDecimalDigits - dscaleInt\n\t\t\tfor range divCount {\n\t\t\t\taccum.Div(accum, big10)\n\t\t\t\texp++\n\t\t\t}\n\t\t}\n\t}\n\n\treduced := &big.Int{}\n\tremainder := &big.Int{}\n\tif exp >= 0 {\n\t\tfor {\n\t\t\treduced.DivMod(accum, big10, remainder)\n\t\t\tif remainder.Sign() != 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\taccum.Set(reduced)\n\t\t\texp++\n\t\t}\n\t}\n\n\tif sign != 0 {\n\t\taccum.Neg(accum)\n\t}\n\n\treturn scanner.ScanNumeric(Numeric{Int: accum, Exp: exp, Valid: true})\n}\n\ntype scanPlanBinaryNumericToFloat64Scanner struct{}\n\nfunc (scanPlanBinaryNumericToFloat64Scanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(Float64Scanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanFloat64(Float8{})\n\t}\n\n\tvar n Numeric\n\n\terr := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tf8, err := n.Float64Value()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanFloat64(f8)\n}\n\ntype scanPlanBinaryNumericToInt64Scanner struct{}\n\nfunc (scanPlanBinaryNumericToInt64Scanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(Int64Scanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanInt64(Int8{})\n\t}\n\n\tvar n Numeric\n\n\terr := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbigInt, err := n.toBigInt()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !bigInt.IsInt64() {\n\t\treturn fmt.Errorf(\"%v is out of range for int64\", bigInt)\n\t}\n\n\treturn scanner.ScanInt64(Int8{Int64: bigInt.Int64(), Valid: true})\n}\n\ntype scanPlanBinaryNumericToTextScanner struct{}\n\nfunc (scanPlanBinaryNumericToTextScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanText(Text{})\n\t}\n\n\tvar n Numeric\n\n\terr := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsbuf, err := encodeNumericText(n, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanText(Text{String: string(sbuf), Valid: true})\n}\n\ntype scanPlanTextAnyToNumericScanner struct{}\n\nfunc (scanPlanTextAnyToNumericScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(NumericScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanNumeric(Numeric{})\n\t}\n\n\tif string(src) == \"NaN\" {\n\t\treturn scanner.ScanNumeric(Numeric{NaN: true, Valid: true})\n\t} else if string(src) == \"Infinity\" {\n\t\treturn scanner.ScanNumeric(Numeric{InfinityModifier: Infinity, Valid: true})\n\t} else if string(src) == \"-Infinity\" {\n\t\treturn scanner.ScanNumeric(Numeric{InfinityModifier: NegativeInfinity, Valid: true})\n\t}\n\n\tnum, exp, err := parseNumericString(string(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanNumeric(Numeric{Int: num, Exp: exp, Valid: true})\n}\n\nfunc (c NumericCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tif format == TextFormatCode {\n\t\treturn string(src), nil\n\t}\n\n\tvar n Numeric\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbuf, err := m.Encode(oid, TextFormatCode, n, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), nil\n}\n\nfunc (c NumericCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n Numeric\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n"
  },
  {
    "path": "pgtype/numeric_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"math\"\n\t\"math/big\"\n\t\"math/rand/v2\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc mustParseBigInt(t *testing.T, src string) *big.Int {\n\ti := &big.Int{}\n\tif _, ok := i.SetString(src, 10); !ok {\n\t\tt.Fatalf(\"could not parse big.Int: %s\", src)\n\t}\n\treturn i\n}\n\nfunc isExpectedEqNumeric(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\taa := a.(pgtype.Numeric)\n\t\tvv := v.(pgtype.Numeric)\n\n\t\tif aa.Valid != vv.Valid {\n\t\t\treturn false\n\t\t}\n\n\t\t// If NULL doesn't matter what the rest of the values are.\n\t\tif !aa.Valid {\n\t\t\treturn true\n\t\t}\n\n\t\tif !(aa.NaN == vv.NaN && aa.InfinityModifier == vv.InfinityModifier) {\n\t\t\treturn false\n\t\t}\n\n\t\t// If NaN or InfinityModifier are set then Int and Exp don't matter.\n\t\tif aa.NaN || aa.InfinityModifier != pgtype.Finite {\n\t\t\treturn true\n\t\t}\n\n\t\taaInt := (&big.Int{}).Set(aa.Int)\n\t\tvvInt := (&big.Int{}).Set(vv.Int)\n\n\t\tif aa.Exp < vv.Exp {\n\t\t\tmul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(vv.Exp-aa.Exp)), nil)\n\t\t\tvvInt.Mul(vvInt, mul)\n\t\t} else if aa.Exp > vv.Exp {\n\t\t\tmul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(aa.Exp-vv.Exp)), nil)\n\t\t\taaInt.Mul(aaInt, mul)\n\t\t}\n\n\t\treturn aaInt.Cmp(vvInt) == 0\n\t}\n}\n\nfunc mustParseNumeric(t *testing.T, src string) pgtype.Numeric {\n\tvar n pgtype.Numeric\n\tplan := pgtype.NumericCodec{}.PlanScan(nil, pgtype.NumericOID, pgtype.TextFormatCode, &n)\n\trequire.NotNil(t, plan)\n\terr := plan.Scan([]byte(src), &n)\n\trequire.NoError(t, err)\n\treturn n\n}\n\nfunc TestNumericCodec(t *testing.T) {\n\tskipCockroachDB(t, \"server formats numeric text format differently\")\n\n\tmax := new(big.Int).Exp(big.NewInt(10), big.NewInt(147454), nil)\n\tmax.Add(max, big.NewInt(1))\n\tlongestNumeric := pgtype.Numeric{Int: max, Exp: -16383, Valid: true}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"numeric\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: mustParseNumeric(t, \"1\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"1\"))},\n\t\t{Param: mustParseNumeric(t, \"3.14159\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"3.14159\"))},\n\t\t{Param: mustParseNumeric(t, \"100010001\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"100010001\"))},\n\t\t{Param: mustParseNumeric(t, \"100010001.0001\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"100010001.0001\"))},\n\t\t{Param: mustParseNumeric(t, \"4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981\"))},\n\t\t{Param: mustParseNumeric(t, \"0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234\"))},\n\t\t{Param: mustParseNumeric(t, \"0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123\"))},\n\t\t{Param: mustParseNumeric(t, \"67\"+strings.Repeat(\"0\", 44535)+\".0\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"67\"+strings.Repeat(\"0\", 44535)+\".0\"))},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"243723409723490243842378942378901237502734019231380123\"), Exp: 23790, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"243723409723490243842378942378901237502734019231380123\"), Exp: 23790, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"2437\"), Exp: 23790, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"2437\"), Exp: 23790, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 80, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 80, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 81, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 81, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 82, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 82, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 83, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 83, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 84, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"43723409723490243842378942378901237502734019231380123\"), Exp: 84, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"913423409823409243892349028349023482934092340892390101\"), Exp: -14021, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"913423409823409243892349028349023482934092340892390101\"), Exp: -14021, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -90, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -90, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -91, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -91, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -92, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -92, Valid: true})},\n\t\t{Param: pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -93, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, \"13423409823409243892349028349023482934092340892390101\"), Exp: -93, Valid: true})},\n\t\t{Param: pgtype.Numeric{NaN: true, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{NaN: true, Valid: true})},\n\t\t{Param: longestNumeric, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(longestNumeric)},\n\t\t{Param: mustParseNumeric(t, \"1\"), Result: new(int64), Test: isExpectedEq(int64(1))},\n\t\t{Param: math.NaN(), Result: new(float64), Test: func(a any) bool { return math.IsNaN(a.(float64)) }},\n\t\t{Param: float32(math.NaN()), Result: new(float32), Test: func(a any) bool { return math.IsNaN(float64(a.(float32))) }},\n\t\t{Param: int64(-1), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"-1\"))},\n\t\t{Param: int64(0), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"0\"))},\n\t\t{Param: int64(1), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"1\"))},\n\t\t{Param: int64(math.MinInt64), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MinInt64, 10)))},\n\t\t{Param: int64(math.MinInt64 + 1), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MinInt64+1, 10)))},\n\t\t{Param: int64(math.MaxInt64), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64, 10)))},\n\t\t{Param: int64(math.MaxInt64 - 1), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64-1, 10)))},\n\t\t{Param: uint64(100), Result: new(uint64), Test: isExpectedEq(uint64(100))},\n\t\t{Param: uint64(math.MaxUint64), Result: new(uint64), Test: isExpectedEq(uint64(math.MaxUint64))},\n\t\t{Param: uint(math.MaxUint), Result: new(uint), Test: isExpectedEq(uint(math.MaxUint))},\n\t\t{Param: uint(100), Result: new(uint), Test: isExpectedEq(uint(100))},\n\t\t{Param: \"1.23\", Result: new(string), Test: isExpectedEq(\"1.23\")},\n\t\t{Param: pgtype.Numeric{}, Result: new(pgtype.Numeric), Test: isExpectedEq(pgtype.Numeric{})},\n\t\t{Param: nil, Result: new(pgtype.Numeric), Test: isExpectedEq(pgtype.Numeric{})},\n\t\t{Param: mustParseNumeric(t, \"1\"), Result: new(string), Test: isExpectedEq(\"1\")},\n\t\t{Param: pgtype.Numeric{NaN: true, Valid: true}, Result: new(string), Test: isExpectedEq(\"NaN\")},\n\t})\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int8\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: mustParseNumeric(t, \"-1\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"-1\"))},\n\t\t{Param: mustParseNumeric(t, \"0\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"0\"))},\n\t\t{Param: mustParseNumeric(t, \"1\"), Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(mustParseNumeric(t, \"1\"))},\n\t})\n}\n\nfunc TestNumericCodecInfinity(t *testing.T) {\n\tskipCockroachDB(t, \"server formats numeric text format differently\")\n\tskipPostgreSQLVersionLessThan(t, 14)\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"numeric\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: math.Inf(1), Result: new(float64), Test: isExpectedEq(math.Inf(1))},\n\t\t{Param: float32(math.Inf(1)), Result: new(float32), Test: isExpectedEq(float32(math.Inf(1)))},\n\t\t{Param: math.Inf(-1), Result: new(float64), Test: isExpectedEq(math.Inf(-1))},\n\t\t{Param: float32(math.Inf(-1)), Result: new(float32), Test: isExpectedEq(float32(math.Inf(-1)))},\n\t\t{Param: pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true})},\n\t\t{Param: pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true})},\n\t\t{Param: pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}, Result: new(string), Test: isExpectedEq(\"Infinity\")},\n\t\t{Param: pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, Result: new(string), Test: isExpectedEq(\"-Infinity\")},\n\t})\n}\n\nfunc TestNumericFloat64Valuer(t *testing.T) {\n\tfor i, tt := range []struct {\n\t\tn pgtype.Numeric\n\t\tf pgtype.Float8\n\t}{\n\t\t{mustParseNumeric(t, \"1\"), pgtype.Float8{Float64: 1, Valid: true}},\n\t\t{mustParseNumeric(t, \"0.0000000000000000001\"), pgtype.Float8{Float64: 0.0000000000000000001, Valid: true}},\n\t\t{mustParseNumeric(t, \"-99999999999\"), pgtype.Float8{Float64: -99999999999, Valid: true}},\n\t\t{pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}, pgtype.Float8{Float64: math.Inf(1), Valid: true}},\n\t\t{pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, pgtype.Float8{Float64: math.Inf(-1), Valid: true}},\n\t\t{pgtype.Numeric{Valid: true}, pgtype.Float8{Valid: true}},\n\t\t{pgtype.Numeric{}, pgtype.Float8{}},\n\t} {\n\t\tf, err := tt.n.Float64Value()\n\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\tassert.Equalf(t, tt.f, f, \"%d\", i)\n\t}\n\n\tf, err := pgtype.Numeric{NaN: true, Valid: true}.Float64Value()\n\tassert.NoError(t, err)\n\tassert.True(t, math.IsNaN(f.Float64))\n\tassert.True(t, f.Valid)\n}\n\nfunc TestNumericCodecFuzz(t *testing.T) {\n\tskipCockroachDB(t, \"server formats numeric text format differently\")\n\n\tr := rand.New(rand.NewPCG(0, 0))\n\tmax := &big.Int{}\n\tmax.SetString(\"9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999\", 10)\n\n\ttests := make([]pgxtest.ValueRoundTripTest, 0, 2000)\n\tfor range 10 {\n\t\tfor j := -50; j < 50; j++ {\n\t\t\tbyteLen := (max.BitLen() + 7) / 8\n\t\t\tbytes := make([]byte, byteLen)\n\t\t\tfor k := 0; k < byteLen; {\n\t\t\t\tval := r.Uint64()\n\t\t\t\tfor b := 0; b < 8 && k < byteLen; b++ {\n\t\t\t\t\tbytes[k] = byte(val >> (b * 8))\n\t\t\t\t\tk++\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum := new(big.Int).SetBytes(bytes)\n\t\t\tnum.Mod(num, max)\n\n\t\t\tn := pgtype.Numeric{Int: num, Exp: int32(j), Valid: true}\n\t\t\ttests = append(tests, pgxtest.ValueRoundTripTest{Param: n, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(n)})\n\n\t\t\tnegNum := &big.Int{}\n\t\t\tnegNum.Neg(num)\n\t\t\tn = pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true}\n\t\t\ttests = append(tests, pgxtest.ValueRoundTripTest{Param: n, Result: new(pgtype.Numeric), Test: isExpectedEqNumeric(n)})\n\t\t}\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"numeric\", tests)\n}\n\nfunc TestNumericMarshalJSON(t *testing.T) {\n\tskipCockroachDB(t, \"server formats numeric text format differently\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tfor i, tt := range []struct {\n\t\t\tdecString string\n\t\t}{\n\t\t\t{\"NaN\"},\n\t\t\t{\"0\"},\n\t\t\t{\"1\"},\n\t\t\t{\"-1\"},\n\t\t\t{\"1000000000000000000\"},\n\t\t\t{\"1234.56789\"},\n\t\t\t{\"1.56789\"},\n\t\t\t{\"0.00000000000056789\"},\n\t\t\t{\"0.00123000\"},\n\t\t\t{\"123e-3\"},\n\t\t\t{\"243723409723490243842378942378901237502734019231380123e23790\"},\n\t\t\t{\"3409823409243892349028349023482934092340892390101e-14021\"},\n\t\t\t{\"-1.1\"},\n\t\t\t{\"-1.0231\"},\n\t\t\t{\"-10.0231\"},\n\t\t\t{\"-0.1\"},   // failed with \"invalid character '.' in numeric literal\"\n\t\t\t{\"-0.01\"},  // failed with \"invalid character '-' after decimal point in numeric literal\"\n\t\t\t{\"-0.001\"}, // failed with \"invalid character '-' after top-level value\"\n\t\t} {\n\t\t\tvar num pgtype.Numeric\n\t\t\tvar pgJSON string\n\t\t\terr := conn.QueryRow(ctx, `select $1::numeric, to_json($1::numeric)`, tt.decString).Scan(&num, &pgJSON)\n\t\t\trequire.NoErrorf(t, err, \"%d\", i)\n\n\t\t\tgoJSON, err := json.Marshal(num)\n\t\t\trequire.NoErrorf(t, err, \"%d\", i)\n\n\t\t\trequire.Equal(t, pgJSON, string(goJSON))\n\t\t}\n\t})\n}\n\nfunc TestNumericUnmarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\twant    *pgtype.Numeric\n\t\tsrc     []byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"null\",\n\t\t\twant:    &pgtype.Numeric{},\n\t\t\tsrc:     []byte(`null`),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"NaN\",\n\t\t\twant:    &pgtype.Numeric{Valid: true, NaN: true},\n\t\t\tsrc:     []byte(`\"NaN\"`),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"0\",\n\t\t\twant:    &pgtype.Numeric{Valid: true, Int: big.NewInt(0)},\n\t\t\tsrc:     []byte(\"0\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"1\",\n\t\t\twant:    &pgtype.Numeric{Valid: true, Int: big.NewInt(1)},\n\t\t\tsrc:     []byte(\"1\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"-1\",\n\t\t\twant:    &pgtype.Numeric{Valid: true, Int: big.NewInt(-1)},\n\t\t\tsrc:     []byte(\"-1\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"bigInt\",\n\t\t\twant:    &pgtype.Numeric{Valid: true, Int: big.NewInt(1), Exp: 30},\n\t\t\tsrc:     []byte(\"1000000000000000000000000000000\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"float: 1234.56789\",\n\t\t\twant:    &pgtype.Numeric{Valid: true, Int: big.NewInt(123456789), Exp: -5},\n\t\t\tsrc:     []byte(\"1234.56789\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid value\",\n\t\t\twant:    &pgtype.Numeric{},\n\t\t\tsrc:     []byte(\"0xffff\"),\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := &pgtype.Numeric{}\n\t\t\tif err := got.UnmarshalJSON(tt.src); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"UnmarshalJSON() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"UnmarshalJSON() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgtype/path.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype PathScanner interface {\n\tScanPath(v Path) error\n}\n\ntype PathValuer interface {\n\tPathValue() (Path, error)\n}\n\ntype Path struct {\n\tP      []Vec2\n\tClosed bool\n\tValid  bool\n}\n\n// ScanPath implements the [PathScanner] interface.\nfunc (path *Path) ScanPath(v Path) error {\n\t*path = v\n\treturn nil\n}\n\n// PathValue implements the [PathValuer] interface.\nfunc (path Path) PathValue() (Path, error) {\n\treturn path, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (path *Path) Scan(src any) error {\n\tif src == nil {\n\t\t*path = Path{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToPathScanner{}.Scan([]byte(src), path)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (path Path) Value() (driver.Value, error) {\n\tif !path.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := PathCodec{}.PlanEncode(nil, 0, TextFormatCode, path).Encode(path, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn string(buf), err\n}\n\ntype PathCodec struct{}\n\nfunc (PathCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (PathCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (PathCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(PathValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanPathCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanPathCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanPathCodecBinary struct{}\n\nfunc (encodePlanPathCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tpath, err := value.(PathValuer).PathValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !path.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar closeByte byte\n\tif path.Closed {\n\t\tcloseByte = 1\n\t}\n\tbuf = append(buf, closeByte)\n\n\tbuf = pgio.AppendInt32(buf, int32(len(path.P)))\n\n\tfor _, p := range path.P {\n\t\tbuf = pgio.AppendUint64(buf, math.Float64bits(p.X))\n\t\tbuf = pgio.AppendUint64(buf, math.Float64bits(p.Y))\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanPathCodecText struct{}\n\nfunc (encodePlanPathCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tpath, err := value.(PathValuer).PathValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !path.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar startByte, endByte byte\n\tif path.Closed {\n\t\tstartByte = '('\n\t\tendByte = ')'\n\t} else {\n\t\tstartByte = '['\n\t\tendByte = ']'\n\t}\n\tbuf = append(buf, startByte)\n\n\tfor i, p := range path.P {\n\t\tif i > 0 {\n\t\t\tbuf = append(buf, ',')\n\t\t}\n\t\tbuf = append(buf, fmt.Sprintf(`(%s,%s)`,\n\t\t\tstrconv.FormatFloat(p.X, 'f', -1, 64),\n\t\t\tstrconv.FormatFloat(p.Y, 'f', -1, 64),\n\t\t)...)\n\t}\n\n\tbuf = append(buf, endByte)\n\n\treturn buf, nil\n}\n\nfunc (PathCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase PathScanner:\n\t\t\treturn scanPlanBinaryPathToPathScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase PathScanner:\n\t\t\treturn scanPlanTextAnyToPathScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryPathToPathScanner struct{}\n\nfunc (scanPlanBinaryPathToPathScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(PathScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanPath(Path{})\n\t}\n\n\tif len(src) < 5 {\n\t\treturn fmt.Errorf(\"invalid length for Path: %v\", len(src))\n\t}\n\n\tclosed := src[0] == 1\n\tpointCount := int(binary.BigEndian.Uint32(src[1:]))\n\n\trp := 5\n\n\tif 5+pointCount*16 != len(src) {\n\t\treturn fmt.Errorf(\"invalid length for Path with %d points: %v\", pointCount, len(src))\n\t}\n\n\tpoints := make([]Vec2, pointCount)\n\tfor i := range points {\n\t\tx := binary.BigEndian.Uint64(src[rp:])\n\t\trp += 8\n\t\ty := binary.BigEndian.Uint64(src[rp:])\n\t\trp += 8\n\t\tpoints[i] = Vec2{math.Float64frombits(x), math.Float64frombits(y)}\n\t}\n\n\treturn scanner.ScanPath(Path{\n\t\tP:      points,\n\t\tClosed: closed,\n\t\tValid:  true,\n\t})\n}\n\ntype scanPlanTextAnyToPathScanner struct{}\n\nfunc (scanPlanTextAnyToPathScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(PathScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanPath(Path{})\n\t}\n\n\tif len(src) < 7 {\n\t\treturn fmt.Errorf(\"invalid length for Path: %v\", len(src))\n\t}\n\n\tclosed := src[0] == '('\n\tpoints := make([]Vec2, 0)\n\n\tstr := string(src[2:])\n\n\tfor {\n\t\tend := strings.IndexByte(str, ',')\n\t\tx, err := strconv.ParseFloat(str[:end], 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstr = str[end+1:]\n\t\tend = strings.IndexByte(str, ')')\n\n\t\ty, err := strconv.ParseFloat(str[:end], 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tpoints = append(points, Vec2{x, y})\n\n\t\tif end+3 < len(str) {\n\t\t\tstr = str[end+3:]\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn scanner.ScanPath(Path{P: points, Closed: closed, Valid: true})\n}\n\nfunc (c PathCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c PathCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar path Path\n\terr := codecScan(c, m, oid, format, src, &path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn path, nil\n}\n"
  },
  {
    "path": "pgtype/path_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqPath(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tap := a.(pgtype.Path)\n\t\tvp := v.(pgtype.Path)\n\n\t\tif !(ap.Valid == vp.Valid && ap.Closed == vp.Closed && len(ap.P) == len(vp.P)) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor i := range ap.P {\n\t\t\tif ap.P[i] != vp.P[i] {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n}\n\nfunc TestPathTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type path\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"path\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam: pgtype.Path{\n\t\t\t\tP:      []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}},\n\t\t\t\tClosed: false,\n\t\t\t\tValid:  true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Path),\n\t\t\tTest: isExpectedEqPath(pgtype.Path{\n\t\t\t\tP:      []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}},\n\t\t\t\tClosed: false,\n\t\t\t\tValid:  true,\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Path{\n\t\t\t\tP:      []pgtype.Vec2{{3.14, 1.678}, {7.1, 5.234}, {23.1, 9.34}},\n\t\t\t\tClosed: true,\n\t\t\t\tValid:  true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Path),\n\t\t\tTest: isExpectedEqPath(pgtype.Path{\n\t\t\t\tP:      []pgtype.Vec2{{3.14, 1.678}, {7.1, 5.234}, {23.1, 9.34}},\n\t\t\t\tClosed: true,\n\t\t\t\tValid:  true,\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Path{\n\t\t\t\tP:      []pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}},\n\t\t\t\tClosed: true,\n\t\t\t\tValid:  true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Path),\n\t\t\tTest: isExpectedEqPath(pgtype.Path{\n\t\t\t\tP:      []pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}},\n\t\t\t\tClosed: true,\n\t\t\t\tValid:  true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Path{}, Result: new(pgtype.Path), Test: isExpectedEqPath(pgtype.Path{})},\n\t\t{Param: nil, Result: new(pgtype.Path), Test: isExpectedEqPath(pgtype.Path{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/pgtype.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// PostgreSQL oids for common types\nconst (\n\tBoolOID                = 16\n\tByteaOID               = 17\n\tQCharOID               = 18\n\tNameOID                = 19\n\tInt8OID                = 20\n\tInt2OID                = 21\n\tInt4OID                = 23\n\tTextOID                = 25\n\tOIDOID                 = 26\n\tTIDOID                 = 27\n\tXIDOID                 = 28\n\tCIDOID                 = 29\n\tJSONOID                = 114\n\tXMLOID                 = 142\n\tXMLArrayOID            = 143\n\tJSONArrayOID           = 199\n\tXID8ArrayOID           = 271\n\tPointOID               = 600\n\tLsegOID                = 601\n\tPathOID                = 602\n\tBoxOID                 = 603\n\tPolygonOID             = 604\n\tLineOID                = 628\n\tLineArrayOID           = 629\n\tCIDROID                = 650\n\tCIDRArrayOID           = 651\n\tFloat4OID              = 700\n\tFloat8OID              = 701\n\tCircleOID              = 718\n\tCircleArrayOID         = 719\n\tUnknownOID             = 705\n\tMacaddr8OID            = 774\n\tMacaddrOID             = 829\n\tInetOID                = 869\n\tBoolArrayOID           = 1000\n\tQCharArrayOID          = 1002\n\tNameArrayOID           = 1003\n\tInt2ArrayOID           = 1005\n\tInt4ArrayOID           = 1007\n\tTextArrayOID           = 1009\n\tTIDArrayOID            = 1010\n\tByteaArrayOID          = 1001\n\tXIDArrayOID            = 1011\n\tCIDArrayOID            = 1012\n\tBPCharArrayOID         = 1014\n\tVarcharArrayOID        = 1015\n\tInt8ArrayOID           = 1016\n\tPointArrayOID          = 1017\n\tLsegArrayOID           = 1018\n\tPathArrayOID           = 1019\n\tBoxArrayOID            = 1020\n\tFloat4ArrayOID         = 1021\n\tFloat8ArrayOID         = 1022\n\tPolygonArrayOID        = 1027\n\tOIDArrayOID            = 1028\n\tACLItemOID             = 1033\n\tACLItemArrayOID        = 1034\n\tMacaddrArrayOID        = 1040\n\tInetArrayOID           = 1041\n\tBPCharOID              = 1042\n\tVarcharOID             = 1043\n\tDateOID                = 1082\n\tTimeOID                = 1083\n\tTimestampOID           = 1114\n\tTimestampArrayOID      = 1115\n\tDateArrayOID           = 1182\n\tTimeArrayOID           = 1183\n\tTimestamptzOID         = 1184\n\tTimestamptzArrayOID    = 1185\n\tIntervalOID            = 1186\n\tIntervalArrayOID       = 1187\n\tNumericArrayOID        = 1231\n\tTimetzOID              = 1266\n\tTimetzArrayOID         = 1270\n\tBitOID                 = 1560\n\tBitArrayOID            = 1561\n\tVarbitOID              = 1562\n\tVarbitArrayOID         = 1563\n\tNumericOID             = 1700\n\tRecordOID              = 2249\n\tRecordArrayOID         = 2287\n\tUUIDOID                = 2950\n\tUUIDArrayOID           = 2951\n\tTSVectorOID            = 3614\n\tTSVectorArrayOID       = 3643\n\tJSONBOID               = 3802\n\tJSONBArrayOID          = 3807\n\tDaterangeOID           = 3912\n\tDaterangeArrayOID      = 3913\n\tInt4rangeOID           = 3904\n\tInt4rangeArrayOID      = 3905\n\tNumrangeOID            = 3906\n\tNumrangeArrayOID       = 3907\n\tTsrangeOID             = 3908\n\tTsrangeArrayOID        = 3909\n\tTstzrangeOID           = 3910\n\tTstzrangeArrayOID      = 3911\n\tInt8rangeOID           = 3926\n\tInt8rangeArrayOID      = 3927\n\tJSONPathOID            = 4072\n\tJSONPathArrayOID       = 4073\n\tInt4multirangeOID      = 4451\n\tNummultirangeOID       = 4532\n\tTsmultirangeOID        = 4533\n\tTstzmultirangeOID      = 4534\n\tDatemultirangeOID      = 4535\n\tInt8multirangeOID      = 4536\n\tXID8OID                = 5069\n\tInt4multirangeArrayOID = 6150\n\tNummultirangeArrayOID  = 6151\n\tTsmultirangeArrayOID   = 6152\n\tTstzmultirangeArrayOID = 6153\n\tDatemultirangeArrayOID = 6155\n\tInt8multirangeArrayOID = 6157\n)\n\ntype InfinityModifier int8\n\nconst (\n\tInfinity         InfinityModifier = 1\n\tFinite           InfinityModifier = 0\n\tNegativeInfinity InfinityModifier = -Infinity\n)\n\nfunc (im InfinityModifier) String() string {\n\tswitch im {\n\tcase Finite:\n\t\treturn \"finite\"\n\tcase Infinity:\n\t\treturn \"infinity\"\n\tcase NegativeInfinity:\n\t\treturn \"-infinity\"\n\tdefault:\n\t\treturn \"invalid\"\n\t}\n}\n\n// PostgreSQL format codes\nconst (\n\tTextFormatCode   = 0\n\tBinaryFormatCode = 1\n)\n\n// A Codec converts between Go and PostgreSQL values. A Codec must not be mutated after it is registered with a Map.\ntype Codec interface {\n\t// FormatSupported returns true if the format is supported.\n\tFormatSupported(int16) bool\n\n\t// PreferredFormat returns the preferred format.\n\tPreferredFormat() int16\n\n\t// PlanEncode returns an EncodePlan for encoding value into PostgreSQL format for oid and format. If no plan can be\n\t// found then nil is returned.\n\tPlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan\n\n\t// PlanScan returns a ScanPlan for scanning a PostgreSQL value into a destination with the same type as target. If\n\t// no plan can be found then nil is returned.\n\tPlanScan(m *Map, oid uint32, format int16, target any) ScanPlan\n\n\t// DecodeDatabaseSQLValue returns src decoded into a value compatible with the sql.Scanner interface.\n\tDecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error)\n\n\t// DecodeValue returns src decoded into its default format.\n\tDecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error)\n}\n\ntype nullAssignmentError struct {\n\tdst any\n}\n\nfunc (e *nullAssignmentError) Error() string {\n\treturn fmt.Sprintf(\"cannot assign NULL to %T\", e.dst)\n}\n\n// Type represents a PostgreSQL data type. It must not be mutated after it is registered with a Map.\ntype Type struct {\n\tCodec Codec\n\tName  string\n\tOID   uint32\n}\n\n// Map is the mapping between PostgreSQL server types and Go type handling logic. It can encode values for\n// transmission to a PostgreSQL server and scan received values.\ntype Map struct {\n\toidToType         map[uint32]*Type\n\tnameToType        map[string]*Type\n\treflectTypeToName map[reflect.Type]string\n\toidToFormatCode   map[uint32]int16\n\n\treflectTypeToType map[reflect.Type]*Type\n\n\tmemoizedEncodePlans map[uint32]map[reflect.Type][2]EncodePlan\n\n\t// TryWrapEncodePlanFuncs is a slice of functions that will wrap a value that cannot be encoded by the Codec. Every\n\t// time a wrapper is found the PlanEncode method will be recursively called with the new value. This allows several layers of wrappers\n\t// to be built up. There are default functions placed in this slice by NewMap(). In most cases these functions\n\t// should run last. i.e. Additional functions should typically be prepended not appended.\n\tTryWrapEncodePlanFuncs []TryWrapEncodePlanFunc\n\n\t// TryWrapScanPlanFuncs is a slice of functions that will wrap a target that cannot be scanned into by the Codec. Every\n\t// time a wrapper is found the PlanScan method will be recursively called with the new target. This allows several layers of wrappers\n\t// to be built up. There are default functions placed in this slice by NewMap(). In most cases these functions\n\t// should run last. i.e. Additional functions should typically be prepended not appended.\n\tTryWrapScanPlanFuncs []TryWrapScanPlanFunc\n}\n\n// Copy returns a new Map containing the same registered types.\nfunc (m *Map) Copy() *Map {\n\tnewMap := NewMap()\n\tfor _, type_ := range m.oidToType {\n\t\tnewMap.RegisterType(type_)\n\t}\n\treturn newMap\n}\n\nfunc NewMap() *Map {\n\tdefaultMapInitOnce.Do(initDefaultMap)\n\n\treturn &Map{\n\t\toidToType:         make(map[uint32]*Type),\n\t\tnameToType:        make(map[string]*Type),\n\t\treflectTypeToName: make(map[reflect.Type]string),\n\t\toidToFormatCode:   make(map[uint32]int16),\n\n\t\tmemoizedEncodePlans: make(map[uint32]map[reflect.Type][2]EncodePlan),\n\n\t\tTryWrapEncodePlanFuncs: []TryWrapEncodePlanFunc{\n\t\t\tTryWrapDerefPointerEncodePlan,\n\t\t\tTryWrapBuiltinTypeEncodePlan,\n\t\t\tTryWrapFindUnderlyingTypeEncodePlan,\n\t\t\tTryWrapStructEncodePlan,\n\t\t\tTryWrapSliceEncodePlan,\n\t\t\tTryWrapMultiDimSliceEncodePlan,\n\t\t\tTryWrapArrayEncodePlan,\n\t\t},\n\n\t\tTryWrapScanPlanFuncs: []TryWrapScanPlanFunc{\n\t\t\tTryPointerPointerScanPlan,\n\t\t\tTryWrapBuiltinTypeScanPlan,\n\t\t\tTryFindUnderlyingTypeScanPlan,\n\t\t\tTryWrapStructScanPlan,\n\t\t\tTryWrapPtrSliceScanPlan,\n\t\t\tTryWrapPtrMultiDimSliceScanPlan,\n\t\t\tTryWrapPtrArrayScanPlan,\n\t\t},\n\t}\n}\n\n// RegisterTypes registers multiple data types in the sequence they are provided.\nfunc (m *Map) RegisterTypes(types []*Type) {\n\tfor _, t := range types {\n\t\tm.RegisterType(t)\n\t}\n}\n\n// RegisterType registers a data type with the Map. t must not be mutated after it is registered.\nfunc (m *Map) RegisterType(t *Type) {\n\tm.oidToType[t.OID] = t\n\tm.nameToType[t.Name] = t\n\tm.oidToFormatCode[t.OID] = t.Codec.PreferredFormat()\n\n\t// Invalidated by type registration\n\tm.reflectTypeToType = nil\n\tfor k := range m.memoizedEncodePlans {\n\t\tdelete(m.memoizedEncodePlans, k)\n\t}\n}\n\n// RegisterDefaultPgType registers a mapping of a Go type to a PostgreSQL type name. Typically the data type to be\n// encoded or decoded is determined by the PostgreSQL OID. But if the OID of a value to be encoded or decoded is\n// unknown, this additional mapping will be used by TypeForValue to determine a suitable data type.\nfunc (m *Map) RegisterDefaultPgType(value any, name string) {\n\tm.reflectTypeToName[reflect.TypeOf(value)] = name\n\n\t// Invalidated by type registration\n\tm.reflectTypeToType = nil\n\tfor k := range m.memoizedEncodePlans {\n\t\tdelete(m.memoizedEncodePlans, k)\n\t}\n}\n\n// TypeForOID returns the Type registered for the given OID. The returned Type must not be mutated.\nfunc (m *Map) TypeForOID(oid uint32) (*Type, bool) {\n\tif dt, ok := m.oidToType[oid]; ok {\n\t\treturn dt, true\n\t}\n\n\tdt, ok := defaultMap.oidToType[oid]\n\treturn dt, ok\n}\n\n// TypeForName returns the Type registered for the given name. The returned Type must not be mutated.\nfunc (m *Map) TypeForName(name string) (*Type, bool) {\n\tif dt, ok := m.nameToType[name]; ok {\n\t\treturn dt, true\n\t}\n\tdt, ok := defaultMap.nameToType[name]\n\treturn dt, ok\n}\n\nfunc (m *Map) buildReflectTypeToType() {\n\tm.reflectTypeToType = make(map[reflect.Type]*Type)\n\n\tfor reflectType, name := range m.reflectTypeToName {\n\t\tif dt, ok := m.TypeForName(name); ok {\n\t\t\tm.reflectTypeToType[reflectType] = dt\n\t\t}\n\t}\n}\n\n// TypeForValue finds a data type suitable for v. Use RegisterType to register types that can encode and decode\n// themselves. Use RegisterDefaultPgType to register that can be handled by a registered data type.  The returned Type\n// must not be mutated.\nfunc (m *Map) TypeForValue(v any) (*Type, bool) {\n\tif m.reflectTypeToType == nil {\n\t\tm.buildReflectTypeToType()\n\t}\n\n\tif dt, ok := m.reflectTypeToType[reflect.TypeOf(v)]; ok {\n\t\treturn dt, true\n\t}\n\n\tdt, ok := defaultMap.reflectTypeToType[reflect.TypeOf(v)]\n\treturn dt, ok\n}\n\n// FormatCodeForOID returns the preferred format code for type oid. If the type is not registered it returns the text\n// format code.\nfunc (m *Map) FormatCodeForOID(oid uint32) int16 {\n\tif fc, ok := m.oidToFormatCode[oid]; ok {\n\t\treturn fc\n\t}\n\n\tif fc, ok := defaultMap.oidToFormatCode[oid]; ok {\n\t\treturn fc\n\t}\n\n\treturn TextFormatCode\n}\n\n// EncodePlan is a precompiled plan to encode a particular type into a particular OID and format.\ntype EncodePlan interface {\n\t// Encode appends the encoded bytes of value to buf. If value is the SQL value NULL then append nothing and return\n\t// (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data\n\t// written.\n\tEncode(value any, buf []byte) (newBuf []byte, err error)\n}\n\n// ScanPlan is a precompiled plan to scan into a type of destination.\ntype ScanPlan interface {\n\t// Scan scans src into target. src is only valid during the call to Scan. The ScanPlan must not retain a reference to\n\t// src.\n\tScan(src []byte, target any) error\n}\n\ntype scanPlanCodecSQLScanner struct {\n\tc          Codec\n\tm          *Map\n\toid        uint32\n\tformatCode int16\n}\n\nfunc (plan *scanPlanCodecSQLScanner) Scan(src []byte, dst any) error {\n\tvalue, err := plan.c.DecodeDatabaseSQLValue(plan.m, plan.oid, plan.formatCode, src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tscanner := dst.(sql.Scanner)\n\treturn scanner.Scan(value)\n}\n\ntype scanPlanSQLScanner struct {\n\tformatCode int16\n}\n\nfunc (plan *scanPlanSQLScanner) Scan(src []byte, dst any) error {\n\tscanner := dst.(sql.Scanner)\n\n\tif src == nil {\n\t\t// This is necessary because interface value []byte:nil does not equal nil:nil for the binary format path and the\n\t\t// text format path would be converted to empty string.\n\t\treturn scanner.Scan(nil)\n\t} else if plan.formatCode == BinaryFormatCode {\n\t\treturn scanner.Scan(src)\n\t} else {\n\t\treturn scanner.Scan(string(src))\n\t}\n}\n\ntype scanPlanString struct{}\n\nfunc (scanPlanString) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp := (dst).(*string)\n\t*p = string(src)\n\treturn nil\n}\n\ntype scanPlanAnyTextToBytes struct{}\n\nfunc (scanPlanAnyTextToBytes) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*[]byte)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\t*dstBuf = make([]byte, len(src))\n\tcopy(*dstBuf, src)\n\treturn nil\n}\n\ntype scanPlanFail struct {\n\tm          *Map\n\toid        uint32\n\tformatCode int16\n}\n\nfunc (plan *scanPlanFail) Scan(src []byte, dst any) error {\n\t// If src is NULL it might be possible to scan into dst even though it is the types are not compatible. While this\n\t// may seem to be a contrived case it can occur when selecting NULL directly. PostgreSQL assigns it the type of text.\n\t// It would be surprising to the caller to have to cast the NULL (e.g. `select null::int`). So try to figure out a\n\t// compatible data type for dst and scan with that.\n\t//\n\t// See https://github.com/jackc/pgx/issues/1326\n\tif src == nil {\n\t\t// As a horrible hack try all types to find anything that can scan into dst.\n\t\tfor oid := range plan.m.oidToType {\n\t\t\t// using planScan instead of Scan or PlanScan to avoid polluting the planned scan cache.\n\t\t\tplan := plan.m.planScan(oid, plan.formatCode, dst, 0)\n\t\t\tif _, ok := plan.(*scanPlanFail); !ok {\n\t\t\t\treturn plan.Scan(src, dst)\n\t\t\t}\n\t\t}\n\t\tfor oid := range defaultMap.oidToType {\n\t\t\tif _, ok := plan.m.oidToType[oid]; !ok {\n\t\t\t\tplan := plan.m.planScan(oid, plan.formatCode, dst, 0)\n\t\t\t\tif _, ok := plan.(*scanPlanFail); !ok {\n\t\t\t\t\treturn plan.Scan(src, dst)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar format string\n\tswitch plan.formatCode {\n\tcase TextFormatCode:\n\t\tformat = \"text\"\n\tcase BinaryFormatCode:\n\t\tformat = \"binary\"\n\tdefault:\n\t\tformat = fmt.Sprintf(\"unknown %d\", plan.formatCode)\n\t}\n\n\tvar dataTypeName string\n\tif t, ok := plan.m.TypeForOID(plan.oid); ok {\n\t\tdataTypeName = t.Name\n\t} else {\n\t\tdataTypeName = \"unknown type\"\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %s (OID %d) in %v format into %T\", dataTypeName, plan.oid, format, dst)\n}\n\n// TryWrapScanPlanFunc is a function that tries to create a wrapper plan for target. If successful it returns a plan\n// that will convert the target passed to Scan and then call the next plan. nextTarget is target as it will be converted\n// by plan. It must be used to find another suitable ScanPlan. When it is found SetNext must be called on plan for it\n// to be usabled. ok indicates if a suitable wrapper was found.\ntype TryWrapScanPlanFunc func(target any) (plan WrappedScanPlanNextSetter, nextTarget any, ok bool)\n\ntype pointerPointerScanPlan struct {\n\tdstType reflect.Type\n\tnext    ScanPlan\n}\n\nfunc (plan *pointerPointerScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *pointerPointerScanPlan) Scan(src []byte, dst any) error {\n\tel := reflect.ValueOf(dst).Elem()\n\tif src == nil {\n\t\tel.Set(reflect.Zero(el.Type()))\n\t\treturn nil\n\t}\n\n\tel.Set(reflect.New(el.Type().Elem()))\n\treturn plan.next.Scan(src, el.Interface())\n}\n\n// TryPointerPointerScanPlan handles a pointer to a pointer by setting the target to nil for SQL NULL and allocating and\n// scanning for non-NULL.\nfunc TryPointerPointerScanPlan(target any) (plan WrappedScanPlanNextSetter, nextTarget any, ok bool) {\n\tif dstValue := reflect.ValueOf(target); dstValue.Kind() == reflect.Ptr {\n\t\telemValue := dstValue.Elem()\n\t\tif elemValue.Kind() == reflect.Ptr {\n\t\t\tplan = &pointerPointerScanPlan{dstType: dstValue.Type()}\n\t\t\treturn plan, reflect.Zero(elemValue.Type()).Interface(), true\n\t\t}\n\t}\n\n\treturn nil, nil, false\n}\n\n// SkipUnderlyingTypePlanner prevents PlanScan and PlanDecode from trying to use the underlying type.\ntype SkipUnderlyingTypePlanner interface {\n\tSkipUnderlyingTypePlan()\n}\n\nvar elemKindToPointerTypes map[reflect.Kind]reflect.Type = map[reflect.Kind]reflect.Type{\n\treflect.Int:     reflect.TypeOf(new(int)),\n\treflect.Int8:    reflect.TypeOf(new(int8)),\n\treflect.Int16:   reflect.TypeOf(new(int16)),\n\treflect.Int32:   reflect.TypeOf(new(int32)),\n\treflect.Int64:   reflect.TypeOf(new(int64)),\n\treflect.Uint:    reflect.TypeOf(new(uint)),\n\treflect.Uint8:   reflect.TypeOf(new(uint8)),\n\treflect.Uint16:  reflect.TypeOf(new(uint16)),\n\treflect.Uint32:  reflect.TypeOf(new(uint32)),\n\treflect.Uint64:  reflect.TypeOf(new(uint64)),\n\treflect.Float32: reflect.TypeOf(new(float32)),\n\treflect.Float64: reflect.TypeOf(new(float64)),\n\treflect.String:  reflect.TypeOf(new(string)),\n\treflect.Bool:    reflect.TypeOf(new(bool)),\n}\n\ntype underlyingTypeScanPlan struct {\n\tdstType     reflect.Type\n\tnextDstType reflect.Type\n\tnext        ScanPlan\n}\n\nfunc (plan *underlyingTypeScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *underlyingTypeScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, reflect.ValueOf(dst).Convert(plan.nextDstType).Interface())\n}\n\n// TryFindUnderlyingTypeScanPlan tries to convert to a Go builtin type. e.g. If value was of type MyString and\n// MyString was defined as a string then a wrapper plan would be returned that converts MyString to string.\nfunc TryFindUnderlyingTypeScanPlan(dst any) (plan WrappedScanPlanNextSetter, nextDst any, ok bool) {\n\tif _, ok := dst.(SkipUnderlyingTypePlanner); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tdstValue := reflect.ValueOf(dst)\n\n\tif dstValue.Kind() == reflect.Ptr {\n\t\tvar elemValue reflect.Value\n\t\tif dstValue.IsNil() {\n\t\t\telemValue = reflect.New(dstValue.Type().Elem()).Elem()\n\t\t} else {\n\t\t\telemValue = dstValue.Elem()\n\t\t}\n\t\tnextDstType := elemKindToPointerTypes[elemValue.Kind()]\n\t\tif nextDstType == nil {\n\t\t\tif elemValue.Kind() == reflect.Slice {\n\t\t\t\tif elemValue.Type().Elem().Kind() == reflect.Uint8 {\n\t\t\t\t\tvar v *[]byte\n\t\t\t\t\tnextDstType = reflect.TypeOf(v)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Get underlying type of any array.\n\t\t\t// https://github.com/jackc/pgx/issues/2107\n\t\t\tif elemValue.Kind() == reflect.Array {\n\t\t\t\tnextDstType = reflect.PointerTo(reflect.ArrayOf(elemValue.Len(), elemValue.Type().Elem()))\n\t\t\t}\n\t\t}\n\n\t\tif nextDstType != nil && dstValue.Type() != nextDstType && dstValue.CanConvert(nextDstType) {\n\t\t\treturn &underlyingTypeScanPlan{dstType: dstValue.Type(), nextDstType: nextDstType}, dstValue.Convert(nextDstType).Interface(), true\n\t\t}\n\t}\n\n\treturn nil, nil, false\n}\n\ntype WrappedScanPlanNextSetter interface {\n\tSetNext(ScanPlan)\n\tScanPlan\n}\n\n// TryWrapBuiltinTypeScanPlan tries to wrap a builtin type with a wrapper that provides additional methods. e.g. If\n// value was of type int32 then a wrapper plan would be returned that converts target to a value that implements\n// Int64Scanner.\nfunc TryWrapBuiltinTypeScanPlan(target any) (plan WrappedScanPlanNextSetter, nextDst any, ok bool) {\n\tswitch target := target.(type) {\n\tcase *int8:\n\t\treturn &wrapInt8ScanPlan{}, (*int8Wrapper)(target), true\n\tcase *int16:\n\t\treturn &wrapInt16ScanPlan{}, (*int16Wrapper)(target), true\n\tcase *int32:\n\t\treturn &wrapInt32ScanPlan{}, (*int32Wrapper)(target), true\n\tcase *int64:\n\t\treturn &wrapInt64ScanPlan{}, (*int64Wrapper)(target), true\n\tcase *int:\n\t\treturn &wrapIntScanPlan{}, (*intWrapper)(target), true\n\tcase *uint8:\n\t\treturn &wrapUint8ScanPlan{}, (*uint8Wrapper)(target), true\n\tcase *uint16:\n\t\treturn &wrapUint16ScanPlan{}, (*uint16Wrapper)(target), true\n\tcase *uint32:\n\t\treturn &wrapUint32ScanPlan{}, (*uint32Wrapper)(target), true\n\tcase *uint64:\n\t\treturn &wrapUint64ScanPlan{}, (*uint64Wrapper)(target), true\n\tcase *uint:\n\t\treturn &wrapUintScanPlan{}, (*uintWrapper)(target), true\n\tcase *float32:\n\t\treturn &wrapFloat32ScanPlan{}, (*float32Wrapper)(target), true\n\tcase *float64:\n\t\treturn &wrapFloat64ScanPlan{}, (*float64Wrapper)(target), true\n\tcase *string:\n\t\treturn &wrapStringScanPlan{}, (*stringWrapper)(target), true\n\tcase *time.Time:\n\t\treturn &wrapTimeScanPlan{}, (*timeWrapper)(target), true\n\tcase *time.Duration:\n\t\treturn &wrapDurationScanPlan{}, (*durationWrapper)(target), true\n\tcase *net.IPNet:\n\t\treturn &wrapNetIPNetScanPlan{}, (*netIPNetWrapper)(target), true\n\tcase *net.IP:\n\t\treturn &wrapNetIPScanPlan{}, (*netIPWrapper)(target), true\n\tcase *netip.Prefix:\n\t\treturn &wrapNetipPrefixScanPlan{}, (*netipPrefixWrapper)(target), true\n\tcase *netip.Addr:\n\t\treturn &wrapNetipAddrScanPlan{}, (*netipAddrWrapper)(target), true\n\tcase *map[string]*string:\n\t\treturn &wrapMapStringToPointerStringScanPlan{}, (*mapStringToPointerStringWrapper)(target), true\n\tcase *map[string]string:\n\t\treturn &wrapMapStringToStringScanPlan{}, (*mapStringToStringWrapper)(target), true\n\tcase *[16]byte:\n\t\treturn &wrapByte16ScanPlan{}, (*byte16Wrapper)(target), true\n\tcase *[]byte:\n\t\treturn &wrapByteSliceScanPlan{}, (*byteSliceWrapper)(target), true\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapInt8ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapInt8ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapInt8ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*int8Wrapper)(dst.(*int8)))\n}\n\ntype wrapInt16ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapInt16ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapInt16ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*int16Wrapper)(dst.(*int16)))\n}\n\ntype wrapInt32ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapInt32ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapInt32ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*int32Wrapper)(dst.(*int32)))\n}\n\ntype wrapInt64ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapInt64ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapInt64ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*int64Wrapper)(dst.(*int64)))\n}\n\ntype wrapIntScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapIntScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapIntScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*intWrapper)(dst.(*int)))\n}\n\ntype wrapUint8ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapUint8ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapUint8ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*uint8Wrapper)(dst.(*uint8)))\n}\n\ntype wrapUint16ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapUint16ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapUint16ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*uint16Wrapper)(dst.(*uint16)))\n}\n\ntype wrapUint32ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapUint32ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapUint32ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*uint32Wrapper)(dst.(*uint32)))\n}\n\ntype wrapUint64ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapUint64ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapUint64ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*uint64Wrapper)(dst.(*uint64)))\n}\n\ntype wrapUintScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapUintScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapUintScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*uintWrapper)(dst.(*uint)))\n}\n\ntype wrapFloat32ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapFloat32ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapFloat32ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*float32Wrapper)(dst.(*float32)))\n}\n\ntype wrapFloat64ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapFloat64ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapFloat64ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*float64Wrapper)(dst.(*float64)))\n}\n\ntype wrapStringScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapStringScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapStringScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*stringWrapper)(dst.(*string)))\n}\n\ntype wrapTimeScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapTimeScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapTimeScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*timeWrapper)(dst.(*time.Time)))\n}\n\ntype wrapDurationScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapDurationScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapDurationScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*durationWrapper)(dst.(*time.Duration)))\n}\n\ntype wrapNetIPNetScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapNetIPNetScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapNetIPNetScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*netIPNetWrapper)(dst.(*net.IPNet)))\n}\n\ntype wrapNetIPScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapNetIPScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapNetIPScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*netIPWrapper)(dst.(*net.IP)))\n}\n\ntype wrapNetipPrefixScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapNetipPrefixScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapNetipPrefixScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*netipPrefixWrapper)(dst.(*netip.Prefix)))\n}\n\ntype wrapNetipAddrScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapNetipAddrScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapNetipAddrScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*netipAddrWrapper)(dst.(*netip.Addr)))\n}\n\ntype wrapMapStringToPointerStringScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapMapStringToPointerStringScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapMapStringToPointerStringScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*mapStringToPointerStringWrapper)(dst.(*map[string]*string)))\n}\n\ntype wrapMapStringToStringScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapMapStringToStringScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapMapStringToStringScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*mapStringToStringWrapper)(dst.(*map[string]string)))\n}\n\ntype wrapByte16ScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapByte16ScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapByte16ScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*byte16Wrapper)(dst.(*[16]byte)))\n}\n\ntype wrapByteSliceScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapByteSliceScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapByteSliceScanPlan) Scan(src []byte, dst any) error {\n\treturn plan.next.Scan(src, (*byteSliceWrapper)(dst.(*[]byte)))\n}\n\ntype pointerEmptyInterfaceScanPlan struct {\n\tcodec      Codec\n\tm          *Map\n\toid        uint32\n\tformatCode int16\n}\n\nfunc (plan *pointerEmptyInterfaceScanPlan) Scan(src []byte, dst any) error {\n\tvalue, err := plan.codec.DecodeValue(plan.m, plan.oid, plan.formatCode, src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tptrAny := dst.(*any)\n\t*ptrAny = value\n\n\treturn nil\n}\n\n// TryWrapStructScanPlan tries to wrap a struct with a wrapper that implements CompositeIndexGetter.\nfunc TryWrapStructScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {\n\ttargetValue := reflect.ValueOf(target)\n\tif targetValue.Kind() != reflect.Ptr {\n\t\treturn nil, nil, false\n\t}\n\n\tvar targetElemValue reflect.Value\n\tif targetValue.IsNil() {\n\t\ttargetElemValue = reflect.Zero(targetValue.Type().Elem())\n\t} else {\n\t\ttargetElemValue = targetValue.Elem()\n\t}\n\ttargetElemType := targetElemValue.Type()\n\n\tif targetElemType.Kind() == reflect.Struct {\n\t\texportedFields := getExportedFieldValues(targetElemValue)\n\t\tif len(exportedFields) == 0 {\n\t\t\treturn nil, nil, false\n\t\t}\n\n\t\tw := ptrStructWrapper{\n\t\t\ts:              target,\n\t\t\texportedFields: exportedFields,\n\t\t}\n\t\treturn &wrapAnyPtrStructScanPlan{}, &w, true\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapAnyPtrStructScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapAnyPtrStructScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapAnyPtrStructScanPlan) Scan(src []byte, target any) error {\n\tw := ptrStructWrapper{\n\t\ts:              target,\n\t\texportedFields: getExportedFieldValues(reflect.ValueOf(target).Elem()),\n\t}\n\n\treturn plan.next.Scan(src, &w)\n}\n\n// TryWrapPtrSliceScanPlan tries to wrap a pointer to a single dimension slice.\nfunc TryWrapPtrSliceScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {\n\t// Avoid using reflect path for common types.\n\tswitch target := target.(type) {\n\tcase *[]int16:\n\t\treturn &wrapPtrSliceScanPlan[int16]{}, (*FlatArray[int16])(target), true\n\tcase *[]int32:\n\t\treturn &wrapPtrSliceScanPlan[int32]{}, (*FlatArray[int32])(target), true\n\tcase *[]int64:\n\t\treturn &wrapPtrSliceScanPlan[int64]{}, (*FlatArray[int64])(target), true\n\tcase *[]float32:\n\t\treturn &wrapPtrSliceScanPlan[float32]{}, (*FlatArray[float32])(target), true\n\tcase *[]float64:\n\t\treturn &wrapPtrSliceScanPlan[float64]{}, (*FlatArray[float64])(target), true\n\tcase *[]string:\n\t\treturn &wrapPtrSliceScanPlan[string]{}, (*FlatArray[string])(target), true\n\tcase *[]time.Time:\n\t\treturn &wrapPtrSliceScanPlan[time.Time]{}, (*FlatArray[time.Time])(target), true\n\t}\n\n\ttargetType := reflect.TypeOf(target)\n\tif targetType.Kind() != reflect.Ptr {\n\t\treturn nil, nil, false\n\t}\n\n\ttargetElemType := targetType.Elem()\n\n\tif targetElemType.Kind() == reflect.Slice {\n\t\tslice := reflect.New(targetElemType).Elem()\n\t\treturn &wrapPtrSliceReflectScanPlan{}, &anySliceArrayReflect{slice: slice}, true\n\t}\n\treturn nil, nil, false\n}\n\ntype wrapPtrSliceScanPlan[T any] struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapPtrSliceScanPlan[T]) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapPtrSliceScanPlan[T]) Scan(src []byte, target any) error {\n\treturn plan.next.Scan(src, (*FlatArray[T])(target.(*[]T)))\n}\n\ntype wrapPtrSliceReflectScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapPtrSliceReflectScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapPtrSliceReflectScanPlan) Scan(src []byte, target any) error {\n\treturn plan.next.Scan(src, &anySliceArrayReflect{slice: reflect.ValueOf(target).Elem()})\n}\n\n// TryWrapPtrMultiDimSliceScanPlan tries to wrap a pointer to a multi-dimension slice.\nfunc TryWrapPtrMultiDimSliceScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {\n\ttargetValue := reflect.ValueOf(target)\n\tif targetValue.Kind() != reflect.Ptr {\n\t\treturn nil, nil, false\n\t}\n\n\ttargetElemValue := targetValue.Elem()\n\n\tif targetElemValue.Kind() == reflect.Slice {\n\t\telemElemKind := targetElemValue.Type().Elem().Kind()\n\t\tif elemElemKind == reflect.Slice {\n\t\t\tif !isRagged(targetElemValue) {\n\t\t\t\treturn &wrapPtrMultiDimSliceScanPlan{}, &anyMultiDimSliceArray{slice: targetValue.Elem()}, true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapPtrMultiDimSliceScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapPtrMultiDimSliceScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapPtrMultiDimSliceScanPlan) Scan(src []byte, target any) error {\n\treturn plan.next.Scan(src, &anyMultiDimSliceArray{slice: reflect.ValueOf(target).Elem()})\n}\n\n// TryWrapPtrArrayScanPlan tries to wrap a pointer to a single dimension array.\nfunc TryWrapPtrArrayScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {\n\ttargetValue := reflect.ValueOf(target)\n\tif targetValue.Kind() != reflect.Ptr {\n\t\treturn nil, nil, false\n\t}\n\n\ttargetElemValue := targetValue.Elem()\n\n\tif targetElemValue.Kind() == reflect.Array {\n\t\treturn &wrapPtrArrayReflectScanPlan{}, &anyArrayArrayReflect{array: targetElemValue}, true\n\t}\n\treturn nil, nil, false\n}\n\ntype wrapPtrArrayReflectScanPlan struct {\n\tnext ScanPlan\n}\n\nfunc (plan *wrapPtrArrayReflectScanPlan) SetNext(next ScanPlan) { plan.next = next }\n\nfunc (plan *wrapPtrArrayReflectScanPlan) Scan(src []byte, target any) error {\n\treturn plan.next.Scan(src, &anyArrayArrayReflect{array: reflect.ValueOf(target).Elem()})\n}\n\n// PlanScan prepares a plan to scan a value into target.\nfunc (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {\n\treturn m.planScan(oid, formatCode, target, 0)\n}\n\nfunc (m *Map) planScan(oid uint32, formatCode int16, target any, depth int) ScanPlan {\n\tif depth > 8 {\n\t\treturn &scanPlanFail{m: m, oid: oid, formatCode: formatCode}\n\t}\n\n\tif target == nil {\n\t\treturn &scanPlanFail{m: m, oid: oid, formatCode: formatCode}\n\t}\n\n\tif _, ok := target.(*UndecodedBytes); ok {\n\t\treturn scanPlanAnyToUndecodedBytes{}\n\t}\n\n\tswitch formatCode {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *string:\n\t\t\tswitch oid {\n\t\t\tcase TextOID, VarcharOID:\n\t\t\t\treturn scanPlanString{}\n\t\t\t}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *string:\n\t\t\treturn scanPlanString{}\n\t\tcase *[]byte:\n\t\t\tif oid != ByteaOID {\n\t\t\t\treturn scanPlanAnyTextToBytes{}\n\t\t\t}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanTextAnyToTextScanner{}\n\t\t}\n\t}\n\n\tvar dt *Type\n\n\tif dataType, ok := m.TypeForOID(oid); ok {\n\t\tdt = dataType\n\t} else if dataType, ok := m.TypeForValue(target); ok {\n\t\tdt = dataType\n\t\toid = dt.OID // Preserve assumed OID in case we are recursively called below.\n\t}\n\n\tif dt != nil {\n\t\tif plan := dt.Codec.PlanScan(m, oid, formatCode, target); plan != nil {\n\t\t\treturn plan\n\t\t}\n\t}\n\n\t// This needs to happen before trying m.TryWrapScanPlanFuncs. Otherwise, a sql.Scanner would not get called if it was\n\t// defined on a type that could be unwrapped such as `type myString string`.\n\t//\n\t//  https://github.com/jackc/pgtype/issues/197\n\tif _, ok := target.(sql.Scanner); ok {\n\t\tif dt == nil {\n\t\t\treturn &scanPlanSQLScanner{formatCode: formatCode}\n\t\t} else {\n\t\t\treturn &scanPlanCodecSQLScanner{c: dt.Codec, m: m, oid: oid, formatCode: formatCode}\n\t\t}\n\t}\n\n\tfor _, f := range m.TryWrapScanPlanFuncs {\n\t\tif wrapperPlan, nextDst, ok := f(target); ok {\n\t\t\tif nextPlan := m.planScan(oid, formatCode, nextDst, depth+1); nextPlan != nil {\n\t\t\t\tif _, failed := nextPlan.(*scanPlanFail); !failed {\n\t\t\t\t\twrapperPlan.SetNext(nextPlan)\n\t\t\t\t\treturn wrapperPlan\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif _, ok := target.(*any); ok {\n\t\tvar codec Codec\n\t\tif dt != nil {\n\t\t\tcodec = dt.Codec\n\t\t} else {\n\t\t\tif formatCode == TextFormatCode {\n\t\t\t\tcodec = TextCodec{}\n\t\t\t} else {\n\t\t\t\tcodec = ByteaCodec{}\n\t\t\t}\n\t\t}\n\t\treturn &pointerEmptyInterfaceScanPlan{codec: codec, m: m, oid: oid, formatCode: formatCode}\n\t}\n\n\treturn &scanPlanFail{m: m, oid: oid, formatCode: formatCode}\n}\n\nfunc (m *Map) Scan(oid uint32, formatCode int16, src []byte, dst any) error {\n\tif dst == nil {\n\t\treturn nil\n\t}\n\n\tplan := m.PlanScan(oid, formatCode, dst)\n\treturn plan.Scan(src, dst)\n}\n\nvar ErrScanTargetTypeChanged = errors.New(\"scan target type changed\")\n\nfunc codecScan(codec Codec, m *Map, oid uint32, format int16, src []byte, dst any) error {\n\tscanPlan := codec.PlanScan(m, oid, format, dst)\n\tif scanPlan == nil {\n\t\treturn fmt.Errorf(\"PlanScan did not find a plan\")\n\t}\n\treturn scanPlan.Scan(src, dst)\n}\n\nfunc codecDecodeToTextFormat(codec Codec, m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tif format == TextFormatCode {\n\t\treturn string(src), nil\n\t} else {\n\t\tvalue, err := codec.DecodeValue(m, oid, format, src)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbuf, err := m.Encode(oid, TextFormatCode, value, nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn string(buf), nil\n\t}\n}\n\n// PlanEncode returns an EncodePlan for encoding value into PostgreSQL format for oid and format. If no plan can be\n// found then nil is returned.\nfunc (m *Map) PlanEncode(oid uint32, format int16, value any) EncodePlan {\n\treturn m.planEncodeDepth(oid, format, value, 0)\n}\n\nfunc (m *Map) planEncodeDepth(oid uint32, format int16, value any, depth int) EncodePlan {\n\t// Guard against infinite recursion.\n\tif depth > 8 {\n\t\treturn nil\n\t}\n\n\toidMemo := m.memoizedEncodePlans[oid]\n\tif oidMemo == nil {\n\t\toidMemo = make(map[reflect.Type][2]EncodePlan)\n\t\tm.memoizedEncodePlans[oid] = oidMemo\n\t}\n\ttargetReflectType := reflect.TypeOf(value)\n\ttypeMemo := oidMemo[targetReflectType]\n\tplan := typeMemo[format]\n\tif plan == nil {\n\t\tplan = m.planEncode(oid, format, value, depth)\n\t\ttypeMemo[format] = plan\n\t\toidMemo[targetReflectType] = typeMemo\n\t}\n\n\treturn plan\n}\n\nfunc (m *Map) planEncode(oid uint32, format int16, value any, depth int) EncodePlan {\n\tif format == TextFormatCode {\n\t\tswitch value.(type) {\n\t\tcase string:\n\t\t\treturn encodePlanStringToAnyTextFormat{}\n\t\tcase TextValuer:\n\t\t\treturn encodePlanTextValuerToAnyTextFormat{}\n\t\t}\n\t}\n\n\tvar dt *Type\n\tif dataType, ok := m.TypeForOID(oid); ok {\n\t\tdt = dataType\n\t} else {\n\t\t// If no type for the OID was found, then either it is unknowable (e.g. the simple protocol) or it is an\n\t\t// unregistered type. In either case try to find the type and OID that matches the value (e.g. a []byte would be\n\t\t// registered to PostgreSQL bytea).\n\t\tif dataType, ok := m.TypeForValue(value); ok {\n\t\t\tdt = dataType\n\t\t\toid = dt.OID // Preserve assumed OID in case we are recursively called below.\n\t\t}\n\t}\n\n\tif dt != nil {\n\t\tif plan := dt.Codec.PlanEncode(m, oid, format, value); plan != nil {\n\t\t\treturn plan\n\t\t}\n\t}\n\n\tfor _, f := range m.TryWrapEncodePlanFuncs {\n\t\tif wrapperPlan, nextValue, ok := f(value); ok {\n\t\t\tif nextPlan := m.planEncodeDepth(oid, format, nextValue, depth+1); nextPlan != nil {\n\t\t\t\twrapperPlan.SetNext(nextPlan)\n\t\t\t\treturn wrapperPlan\n\t\t\t}\n\t\t}\n\t}\n\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanStringToAnyTextFormat struct{}\n\nfunc (encodePlanStringToAnyTextFormat) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ts := value.(string)\n\treturn append(buf, s...), nil\n}\n\ntype encodePlanTextValuerToAnyTextFormat struct{}\n\nfunc (encodePlanTextValuerToAnyTextFormat) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tt, err := value.(TextValuer).TextValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, t.String...), nil\n}\n\ntype encodePlanDriverValuer struct {\n\tm          *Map\n\toid        uint32\n\tformatCode int16\n}\n\nfunc (plan *encodePlanDriverValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tdv := value.(driver.Valuer)\n\tif dv == nil {\n\t\treturn nil, nil\n\t}\n\tv, err := dv.Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif v == nil {\n\t\treturn nil, nil\n\t}\n\n\tnewBuf, err = plan.m.Encode(plan.oid, plan.formatCode, v, buf)\n\tif err == nil {\n\t\treturn newBuf, nil\n\t}\n\n\ts, ok := v.(string)\n\tif !ok {\n\t\treturn nil, err\n\t}\n\n\tvar scannedValue any\n\tscanErr := plan.m.Scan(plan.oid, TextFormatCode, []byte(s), &scannedValue)\n\tif scanErr != nil {\n\t\treturn nil, err\n\t}\n\n\t// Prevent infinite loop. We can't encode this. See https://github.com/jackc/pgx/issues/1331.\n\tif reflect.TypeOf(value) == reflect.TypeOf(scannedValue) {\n\t\treturn nil, fmt.Errorf(\"tried to encode %v via encoding to text and scanning but failed due to receiving same type back\", value)\n\t}\n\n\tvar err2 error\n\tnewBuf, err2 = plan.m.Encode(plan.oid, BinaryFormatCode, scannedValue, buf)\n\tif err2 != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newBuf, nil\n}\n\n// TryWrapEncodePlanFunc is a function that tries to create a wrapper plan for value. If successful it returns a plan\n// that will convert the value passed to Encode and then call the next plan. nextValue is value as it will be converted\n// by plan. It must be used to find another suitable EncodePlan. When it is found SetNext must be called on plan for it\n// to be usabled. ok indicates if a suitable wrapper was found.\ntype TryWrapEncodePlanFunc func(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool)\n\ntype derefPointerEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *derefPointerEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *derefPointerEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tptr := reflect.ValueOf(value)\n\n\tif ptr.IsNil() {\n\t\treturn nil, nil\n\t}\n\n\treturn plan.next.Encode(ptr.Elem().Interface(), buf)\n}\n\n// TryWrapDerefPointerEncodePlan tries to dereference a pointer. e.g. If value was of type *string then a wrapper plan\n// would be returned that dereferences the value.\nfunc TryWrapDerefPointerEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tif valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Ptr {\n\t\treturn &derefPointerEncodePlan{}, reflect.New(valueType.Elem()).Elem().Interface(), true\n\t}\n\n\treturn nil, nil, false\n}\n\nvar kindToTypes map[reflect.Kind]reflect.Type = map[reflect.Kind]reflect.Type{\n\treflect.Int:     reflect.TypeOf(int(0)),\n\treflect.Int8:    reflect.TypeOf(int8(0)),\n\treflect.Int16:   reflect.TypeOf(int16(0)),\n\treflect.Int32:   reflect.TypeOf(int32(0)),\n\treflect.Int64:   reflect.TypeOf(int64(0)),\n\treflect.Uint:    reflect.TypeOf(uint(0)),\n\treflect.Uint8:   reflect.TypeOf(uint8(0)),\n\treflect.Uint16:  reflect.TypeOf(uint16(0)),\n\treflect.Uint32:  reflect.TypeOf(uint32(0)),\n\treflect.Uint64:  reflect.TypeOf(uint64(0)),\n\treflect.Float32: reflect.TypeOf(float32(0)),\n\treflect.Float64: reflect.TypeOf(float64(0)),\n\treflect.String:  reflect.TypeOf(\"\"),\n\treflect.Bool:    reflect.TypeOf(false),\n}\n\nvar byteSliceType = reflect.TypeOf([]byte{})\n\ntype underlyingTypeEncodePlan struct {\n\tnextValueType reflect.Type\n\tnext          EncodePlan\n}\n\nfunc (plan *underlyingTypeEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *underlyingTypeEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(reflect.ValueOf(value).Convert(plan.nextValueType).Interface(), buf)\n}\n\n// TryWrapFindUnderlyingTypeEncodePlan tries to convert to a Go builtin type. e.g. If value was of type MyString and\n// MyString was defined as a string then a wrapper plan would be returned that converts MyString to string.\nfunc TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif value == nil {\n\t\treturn nil, nil, false\n\t}\n\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tif _, ok := value.(SkipUnderlyingTypePlanner); ok {\n\t\treturn nil, nil, false\n\t}\n\n\trefValue := reflect.ValueOf(value)\n\n\tnextValueType := kindToTypes[refValue.Kind()]\n\tif nextValueType != nil && refValue.Type() != nextValueType {\n\t\treturn &underlyingTypeEncodePlan{nextValueType: nextValueType}, refValue.Convert(nextValueType).Interface(), true\n\t}\n\n\t// []byte is a special case. It is a slice but we treat it as a scalar type. In the case of a named type like\n\t// json.RawMessage which is defined as []byte the underlying type should be considered as []byte. But any other slice\n\t// does not have a special underlying type.\n\t//\n\t// https://github.com/jackc/pgx/issues/1763\n\tif refValue.Type() != byteSliceType && refValue.Type().AssignableTo(byteSliceType) {\n\t\treturn &underlyingTypeEncodePlan{nextValueType: byteSliceType}, refValue.Convert(byteSliceType).Interface(), true\n\t}\n\n\t// Get underlying type of any array.\n\t// https://github.com/jackc/pgx/issues/2107\n\tif refValue.Kind() == reflect.Array {\n\t\tunderlyingArrayType := reflect.ArrayOf(refValue.Len(), refValue.Type().Elem())\n\t\tif refValue.Type() != underlyingArrayType {\n\t\t\treturn &underlyingTypeEncodePlan{nextValueType: underlyingArrayType}, refValue.Convert(underlyingArrayType).Interface(), true\n\t\t}\n\t}\n\n\treturn nil, nil, false\n}\n\ntype WrappedEncodePlanNextSetter interface {\n\tSetNext(EncodePlan)\n\tEncodePlan\n}\n\n// TryWrapBuiltinTypeEncodePlan tries to wrap a builtin type with a wrapper that provides additional methods. e.g. If\n// value was of type int32 then a wrapper plan would be returned that converts value to a type that implements\n// Int64Valuer.\nfunc TryWrapBuiltinTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tswitch value := value.(type) {\n\tcase int8:\n\t\treturn &wrapInt8EncodePlan{}, int8Wrapper(value), true\n\tcase int16:\n\t\treturn &wrapInt16EncodePlan{}, int16Wrapper(value), true\n\tcase int32:\n\t\treturn &wrapInt32EncodePlan{}, int32Wrapper(value), true\n\tcase int64:\n\t\treturn &wrapInt64EncodePlan{}, int64Wrapper(value), true\n\tcase int:\n\t\treturn &wrapIntEncodePlan{}, intWrapper(value), true\n\tcase uint8:\n\t\treturn &wrapUint8EncodePlan{}, uint8Wrapper(value), true\n\tcase uint16:\n\t\treturn &wrapUint16EncodePlan{}, uint16Wrapper(value), true\n\tcase uint32:\n\t\treturn &wrapUint32EncodePlan{}, uint32Wrapper(value), true\n\tcase uint64:\n\t\treturn &wrapUint64EncodePlan{}, uint64Wrapper(value), true\n\tcase uint:\n\t\treturn &wrapUintEncodePlan{}, uintWrapper(value), true\n\tcase float32:\n\t\treturn &wrapFloat32EncodePlan{}, float32Wrapper(value), true\n\tcase float64:\n\t\treturn &wrapFloat64EncodePlan{}, float64Wrapper(value), true\n\tcase string:\n\t\treturn &wrapStringEncodePlan{}, stringWrapper(value), true\n\tcase time.Time:\n\t\treturn &wrapTimeEncodePlan{}, timeWrapper(value), true\n\tcase time.Duration:\n\t\treturn &wrapDurationEncodePlan{}, durationWrapper(value), true\n\tcase net.IPNet:\n\t\treturn &wrapNetIPNetEncodePlan{}, netIPNetWrapper(value), true\n\tcase net.IP:\n\t\treturn &wrapNetIPEncodePlan{}, netIPWrapper(value), true\n\tcase netip.Prefix:\n\t\treturn &wrapNetipPrefixEncodePlan{}, netipPrefixWrapper(value), true\n\tcase netip.Addr:\n\t\treturn &wrapNetipAddrEncodePlan{}, netipAddrWrapper(value), true\n\tcase map[string]*string:\n\t\treturn &wrapMapStringToPointerStringEncodePlan{}, mapStringToPointerStringWrapper(value), true\n\tcase map[string]string:\n\t\treturn &wrapMapStringToStringEncodePlan{}, mapStringToStringWrapper(value), true\n\tcase [16]byte:\n\t\treturn &wrapByte16EncodePlan{}, byte16Wrapper(value), true\n\tcase []byte:\n\t\treturn &wrapByteSliceEncodePlan{}, byteSliceWrapper(value), true\n\tcase fmt.Stringer:\n\t\treturn &wrapFmtStringerEncodePlan{}, fmtStringerWrapper{value}, true\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapInt8EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapInt8EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapInt8EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(int8Wrapper(value.(int8)), buf)\n}\n\ntype wrapInt16EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapInt16EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapInt16EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(int16Wrapper(value.(int16)), buf)\n}\n\ntype wrapInt32EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapInt32EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapInt32EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(int32Wrapper(value.(int32)), buf)\n}\n\ntype wrapInt64EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapInt64EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapInt64EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(int64Wrapper(value.(int64)), buf)\n}\n\ntype wrapIntEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapIntEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapIntEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(intWrapper(value.(int)), buf)\n}\n\ntype wrapUint8EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapUint8EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapUint8EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(uint8Wrapper(value.(uint8)), buf)\n}\n\ntype wrapUint16EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapUint16EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapUint16EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(uint16Wrapper(value.(uint16)), buf)\n}\n\ntype wrapUint32EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapUint32EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapUint32EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(uint32Wrapper(value.(uint32)), buf)\n}\n\ntype wrapUint64EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapUint64EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapUint64EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(uint64Wrapper(value.(uint64)), buf)\n}\n\ntype wrapUintEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapUintEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapUintEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(uintWrapper(value.(uint)), buf)\n}\n\ntype wrapFloat32EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapFloat32EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapFloat32EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(float32Wrapper(value.(float32)), buf)\n}\n\ntype wrapFloat64EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapFloat64EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapFloat64EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(float64Wrapper(value.(float64)), buf)\n}\n\ntype wrapStringEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapStringEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapStringEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(stringWrapper(value.(string)), buf)\n}\n\ntype wrapTimeEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapTimeEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapTimeEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(timeWrapper(value.(time.Time)), buf)\n}\n\ntype wrapDurationEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapDurationEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapDurationEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(durationWrapper(value.(time.Duration)), buf)\n}\n\ntype wrapNetIPNetEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapNetIPNetEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapNetIPNetEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(netIPNetWrapper(value.(net.IPNet)), buf)\n}\n\ntype wrapNetIPEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapNetIPEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapNetIPEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(netIPWrapper(value.(net.IP)), buf)\n}\n\ntype wrapNetipPrefixEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapNetipPrefixEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapNetipPrefixEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(netipPrefixWrapper(value.(netip.Prefix)), buf)\n}\n\ntype wrapNetipAddrEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapNetipAddrEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapNetipAddrEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(netipAddrWrapper(value.(netip.Addr)), buf)\n}\n\ntype wrapMapStringToPointerStringEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapMapStringToPointerStringEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapMapStringToPointerStringEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(mapStringToPointerStringWrapper(value.(map[string]*string)), buf)\n}\n\ntype wrapMapStringToStringEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapMapStringToStringEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapMapStringToStringEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(mapStringToStringWrapper(value.(map[string]string)), buf)\n}\n\ntype wrapByte16EncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapByte16EncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapByte16EncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(byte16Wrapper(value.([16]byte)), buf)\n}\n\ntype wrapByteSliceEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapByteSliceEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapByteSliceEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(byteSliceWrapper(value.([]byte)), buf)\n}\n\ntype wrapFmtStringerEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapFmtStringerEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapFmtStringerEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode(fmtStringerWrapper{value.(fmt.Stringer)}, buf)\n}\n\n// TryWrapStructEncodePlan tries to wrap a struct with a wrapper that implements CompositeIndexGetter.\nfunc TryWrapStructEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tif valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Struct {\n\t\texportedFields := getExportedFieldValues(reflect.ValueOf(value))\n\t\tif len(exportedFields) == 0 {\n\t\t\treturn nil, nil, false\n\t\t}\n\n\t\tw := structWrapper{\n\t\t\ts:              value,\n\t\t\texportedFields: exportedFields,\n\t\t}\n\t\treturn &wrapAnyStructEncodePlan{}, w, true\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapAnyStructEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapAnyStructEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapAnyStructEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tw := structWrapper{\n\t\ts:              value,\n\t\texportedFields: getExportedFieldValues(reflect.ValueOf(value)),\n\t}\n\n\treturn plan.next.Encode(w, buf)\n}\n\nfunc getExportedFieldValues(structValue reflect.Value) []reflect.Value {\n\tstructType := structValue.Type()\n\texportedFields := make([]reflect.Value, 0, structValue.NumField())\n\tfor i := 0; i < structType.NumField(); i++ {\n\t\tsf := structType.Field(i)\n\t\tif sf.IsExported() {\n\t\t\texportedFields = append(exportedFields, structValue.Field(i))\n\t\t}\n\t}\n\n\treturn exportedFields\n}\n\nfunc TryWrapSliceEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\t// Avoid using reflect path for common types.\n\tswitch value := value.(type) {\n\tcase []int16:\n\t\treturn &wrapSliceEncodePlan[int16]{}, (FlatArray[int16])(value), true\n\tcase []int32:\n\t\treturn &wrapSliceEncodePlan[int32]{}, (FlatArray[int32])(value), true\n\tcase []int64:\n\t\treturn &wrapSliceEncodePlan[int64]{}, (FlatArray[int64])(value), true\n\tcase []float32:\n\t\treturn &wrapSliceEncodePlan[float32]{}, (FlatArray[float32])(value), true\n\tcase []float64:\n\t\treturn &wrapSliceEncodePlan[float64]{}, (FlatArray[float64])(value), true\n\tcase []string:\n\t\treturn &wrapSliceEncodePlan[string]{}, (FlatArray[string])(value), true\n\tcase []time.Time:\n\t\treturn &wrapSliceEncodePlan[time.Time]{}, (FlatArray[time.Time])(value), true\n\t}\n\n\tif valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Slice {\n\t\tw := anySliceArrayReflect{\n\t\t\tslice: reflect.ValueOf(value),\n\t\t}\n\t\treturn &wrapSliceEncodeReflectPlan{}, w, true\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapSliceEncodePlan[T any] struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapSliceEncodePlan[T]) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapSliceEncodePlan[T]) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\treturn plan.next.Encode((FlatArray[T])(value.([]T)), buf)\n}\n\ntype wrapSliceEncodeReflectPlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapSliceEncodeReflectPlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapSliceEncodeReflectPlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tw := anySliceArrayReflect{\n\t\tslice: reflect.ValueOf(value),\n\t}\n\n\treturn plan.next.Encode(w, buf)\n}\n\nfunc TryWrapMultiDimSliceEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tsliceValue := reflect.ValueOf(value)\n\tif sliceValue.Kind() == reflect.Slice {\n\t\tvalueElemType := sliceValue.Type().Elem()\n\n\t\tif valueElemType.Kind() == reflect.Slice {\n\t\t\tif !isRagged(sliceValue) {\n\t\t\t\tw := anyMultiDimSliceArray{\n\t\t\t\t\tslice: reflect.ValueOf(value),\n\t\t\t\t}\n\t\t\t\treturn &wrapMultiDimSliceEncodePlan{}, &w, true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapMultiDimSliceEncodePlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapMultiDimSliceEncodePlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapMultiDimSliceEncodePlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tw := anyMultiDimSliceArray{\n\t\tslice: reflect.ValueOf(value),\n\t}\n\n\treturn plan.next.Encode(&w, buf)\n}\n\nfunc TryWrapArrayEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {\n\tif _, ok := value.(driver.Valuer); ok {\n\t\treturn nil, nil, false\n\t}\n\n\tif valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Array {\n\t\tw := anyArrayArrayReflect{\n\t\t\tarray: reflect.ValueOf(value),\n\t\t}\n\t\treturn &wrapArrayEncodeReflectPlan{}, w, true\n\t}\n\n\treturn nil, nil, false\n}\n\ntype wrapArrayEncodeReflectPlan struct {\n\tnext EncodePlan\n}\n\nfunc (plan *wrapArrayEncodeReflectPlan) SetNext(next EncodePlan) { plan.next = next }\n\nfunc (plan *wrapArrayEncodeReflectPlan) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tw := anyArrayArrayReflect{\n\t\tarray: reflect.ValueOf(value),\n\t}\n\n\treturn plan.next.Encode(w, buf)\n}\n\nfunc newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error) error {\n\tvar format string\n\tswitch formatCode {\n\tcase TextFormatCode:\n\t\tformat = \"text\"\n\tcase BinaryFormatCode:\n\t\tformat = \"binary\"\n\tdefault:\n\t\tformat = fmt.Sprintf(\"unknown (%d)\", formatCode)\n\t}\n\n\tvar dataTypeName string\n\tif t, ok := m.TypeForOID(oid); ok {\n\t\tdataTypeName = t.Name\n\t} else {\n\t\tdataTypeName = \"unknown type\"\n\t}\n\n\treturn fmt.Errorf(\"unable to encode %#v into %s format for %s (OID %d): %w\", value, format, dataTypeName, oid, err)\n}\n\n// Encode appends the encoded bytes of value to buf. If value is the SQL value NULL then append nothing and return\n// (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data\n// written.\nfunc (m *Map) Encode(oid uint32, formatCode int16, value any, buf []byte) (newBuf []byte, err error) {\n\tif isNil, callNilDriverValuer := isNilDriverValuer(value); isNil {\n\t\tif callNilDriverValuer {\n\t\t\tnewBuf, err = (&encodePlanDriverValuer{m: m, oid: oid, formatCode: formatCode}).Encode(value, buf)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, newEncodeError(value, m, oid, formatCode, err)\n\t\t\t}\n\n\t\t\treturn newBuf, nil\n\t\t} else {\n\t\t\treturn nil, nil\n\t\t}\n\t}\n\n\tplan := m.PlanEncode(oid, formatCode, value)\n\tif plan == nil {\n\t\treturn nil, newEncodeError(value, m, oid, formatCode, errors.New(\"cannot find encode plan\"))\n\t}\n\n\tnewBuf, err = plan.Encode(value, buf)\n\tif err != nil {\n\t\treturn nil, newEncodeError(value, m, oid, formatCode, err)\n\t}\n\n\treturn newBuf, nil\n}\n\n// SQLScanner returns a database/sql.Scanner for v. This is necessary for types like Array[T] and Range[T] where the\n// type needs assistance from Map to implement the sql.Scanner interface. It is not necessary for types like Box that\n// implement sql.Scanner directly.\n//\n// This uses the type of v to look up the PostgreSQL OID that v presumably came from. This means v must be registered\n// with m by calling RegisterDefaultPgType.\nfunc (m *Map) SQLScanner(v any) sql.Scanner {\n\tif s, ok := v.(sql.Scanner); ok {\n\t\treturn s\n\t}\n\n\treturn &sqlScannerWrapper{m: m, v: v}\n}\n\ntype sqlScannerWrapper struct {\n\tm *Map\n\tv any\n}\n\nfunc (w *sqlScannerWrapper) Scan(src any) error {\n\tt, ok := w.m.TypeForValue(w.v)\n\tif !ok {\n\t\treturn fmt.Errorf(\"cannot convert to sql.Scanner: cannot find registered type for %T\", w.v)\n\t}\n\n\tvar bufSrc []byte\n\tif src != nil {\n\t\tswitch src := src.(type) {\n\t\tcase string:\n\t\t\tbufSrc = []byte(src)\n\t\tcase []byte:\n\t\t\tbufSrc = src\n\t\tdefault:\n\t\t\tbufSrc = fmt.Append(nil, bufSrc)\n\t\t}\n\t}\n\n\treturn w.m.Scan(t.OID, TextFormatCode, bufSrc, w.v)\n}\n\nvar valuerReflectType = reflect.TypeFor[driver.Valuer]()\n\n// isNilDriverValuer returns true if value is any type of nil unless it implements driver.Valuer. *T is not considered to implement\n// driver.Valuer if it is only implemented by T.\nfunc isNilDriverValuer(value any) (isNil, callNilDriverValuer bool) {\n\tif value == nil {\n\t\treturn true, false\n\t}\n\n\trefVal := reflect.ValueOf(value)\n\tkind := refVal.Kind()\n\tswitch kind {\n\tcase reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:\n\t\tif !refVal.IsNil() {\n\t\t\treturn false, false\n\t\t}\n\n\t\tif _, ok := value.(driver.Valuer); ok {\n\t\t\tif kind == reflect.Ptr {\n\t\t\t\t// The type assertion will succeed if driver.Valuer is implemented on T or *T. Check if it is implemented on *T\n\t\t\t\t// by checking if it is not implemented on *T.\n\t\t\t\treturn true, !refVal.Type().Elem().Implements(valuerReflectType)\n\t\t\t} else {\n\t\t\t\treturn true, true\n\t\t\t}\n\t\t}\n\n\t\treturn true, false\n\tdefault:\n\t\treturn false, false\n\t}\n}\n"
  },
  {
    "path": "pgtype/pgtype_default.go",
    "content": "package pgtype\n\nimport (\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"net\"\n\t\"net/netip\"\n\t\"reflect\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\t// defaultMap contains default mappings between PostgreSQL server types and Go type handling logic.\n\tdefaultMap         *Map\n\tdefaultMapInitOnce = sync.Once{}\n)\n\nfunc initDefaultMap() {\n\tdefaultMap = &Map{\n\t\toidToType:         make(map[uint32]*Type),\n\t\tnameToType:        make(map[string]*Type),\n\t\treflectTypeToName: make(map[reflect.Type]string),\n\t\toidToFormatCode:   make(map[uint32]int16),\n\n\t\tmemoizedEncodePlans: make(map[uint32]map[reflect.Type][2]EncodePlan),\n\n\t\tTryWrapEncodePlanFuncs: []TryWrapEncodePlanFunc{\n\t\t\tTryWrapDerefPointerEncodePlan,\n\t\t\tTryWrapBuiltinTypeEncodePlan,\n\t\t\tTryWrapFindUnderlyingTypeEncodePlan,\n\t\t\tTryWrapStructEncodePlan,\n\t\t\tTryWrapSliceEncodePlan,\n\t\t\tTryWrapMultiDimSliceEncodePlan,\n\t\t\tTryWrapArrayEncodePlan,\n\t\t},\n\n\t\tTryWrapScanPlanFuncs: []TryWrapScanPlanFunc{\n\t\t\tTryPointerPointerScanPlan,\n\t\t\tTryWrapBuiltinTypeScanPlan,\n\t\t\tTryFindUnderlyingTypeScanPlan,\n\t\t\tTryWrapStructScanPlan,\n\t\t\tTryWrapPtrSliceScanPlan,\n\t\t\tTryWrapPtrMultiDimSliceScanPlan,\n\t\t\tTryWrapPtrArrayScanPlan,\n\t\t},\n\t}\n\n\t// Base types\n\tdefaultMap.RegisterType(&Type{Name: \"aclitem\", OID: ACLItemOID, Codec: &TextFormatOnlyCodec{TextCodec{}}})\n\tdefaultMap.RegisterType(&Type{Name: \"bit\", OID: BitOID, Codec: BitsCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"bool\", OID: BoolOID, Codec: BoolCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"box\", OID: BoxOID, Codec: BoxCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"bpchar\", OID: BPCharOID, Codec: TextCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"bytea\", OID: ByteaOID, Codec: ByteaCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"char\", OID: QCharOID, Codec: QCharCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"cid\", OID: CIDOID, Codec: Uint32Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"cidr\", OID: CIDROID, Codec: InetCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"circle\", OID: CircleOID, Codec: CircleCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"date\", OID: DateOID, Codec: DateCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"float4\", OID: Float4OID, Codec: Float4Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"float8\", OID: Float8OID, Codec: Float8Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"inet\", OID: InetOID, Codec: InetCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"int2\", OID: Int2OID, Codec: Int2Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"int4\", OID: Int4OID, Codec: Int4Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"int8\", OID: Int8OID, Codec: Int8Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"interval\", OID: IntervalOID, Codec: IntervalCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"json\", OID: JSONOID, Codec: &JSONCodec{Marshal: json.Marshal, Unmarshal: json.Unmarshal}})\n\tdefaultMap.RegisterType(&Type{Name: \"jsonb\", OID: JSONBOID, Codec: &JSONBCodec{Marshal: json.Marshal, Unmarshal: json.Unmarshal}})\n\tdefaultMap.RegisterType(&Type{Name: \"jsonpath\", OID: JSONPathOID, Codec: &TextFormatOnlyCodec{TextCodec{}}})\n\tdefaultMap.RegisterType(&Type{Name: \"line\", OID: LineOID, Codec: LineCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"lseg\", OID: LsegOID, Codec: LsegCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"macaddr8\", OID: Macaddr8OID, Codec: MacaddrCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"macaddr\", OID: MacaddrOID, Codec: MacaddrCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"name\", OID: NameOID, Codec: TextCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"numeric\", OID: NumericOID, Codec: NumericCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"oid\", OID: OIDOID, Codec: Uint32Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"path\", OID: PathOID, Codec: PathCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"point\", OID: PointOID, Codec: PointCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"polygon\", OID: PolygonOID, Codec: PolygonCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"record\", OID: RecordOID, Codec: RecordCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"text\", OID: TextOID, Codec: TextCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"tid\", OID: TIDOID, Codec: TIDCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"tsvector\", OID: TSVectorOID, Codec: TSVectorCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"time\", OID: TimeOID, Codec: TimeCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"timestamp\", OID: TimestampOID, Codec: &TimestampCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"timestamptz\", OID: TimestamptzOID, Codec: &TimestamptzCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"unknown\", OID: UnknownOID, Codec: TextCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"uuid\", OID: UUIDOID, Codec: UUIDCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"varbit\", OID: VarbitOID, Codec: BitsCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"varchar\", OID: VarcharOID, Codec: TextCodec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"xid\", OID: XIDOID, Codec: Uint32Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"xid8\", OID: XID8OID, Codec: Uint64Codec{}})\n\tdefaultMap.RegisterType(&Type{Name: \"xml\", OID: XMLOID, Codec: &XMLCodec{\n\t\tMarshal: xml.Marshal,\n\t\t// xml.Unmarshal does not support unmarshalling into *any. However, XMLCodec.DecodeValue calls Unmarshal with a\n\t\t// *any. Wrap xml.Marshal with a function that copies the data into a new byte slice in this case. Not implementing\n\t\t// directly in XMLCodec.DecodeValue to allow for the unlikely possibility that someone uses an alternative XML\n\t\t// unmarshaler that does support unmarshalling into *any.\n\t\t//\n\t\t// https://github.com/jackc/pgx/issues/2227\n\t\t// https://github.com/jackc/pgx/pull/2228\n\t\tUnmarshal: func(data []byte, v any) error {\n\t\t\tif v, ok := v.(*any); ok {\n\t\t\t\tdstBuf := make([]byte, len(data))\n\t\t\t\tcopy(dstBuf, data)\n\t\t\t\t*v = dstBuf\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn xml.Unmarshal(data, v)\n\t\t},\n\t}})\n\n\t// Range types\n\tdefaultMap.RegisterType(&Type{Name: \"daterange\", OID: DaterangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[DateOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"int4range\", OID: Int4rangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[Int4OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"int8range\", OID: Int8rangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[Int8OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"numrange\", OID: NumrangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[NumericOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"tsrange\", OID: TsrangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[TimestampOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"tstzrange\", OID: TstzrangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[TimestamptzOID]}})\n\n\t// Multirange types\n\tdefaultMap.RegisterType(&Type{Name: \"datemultirange\", OID: DatemultirangeOID, Codec: &MultirangeCodec{ElementType: defaultMap.oidToType[DaterangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"int4multirange\", OID: Int4multirangeOID, Codec: &MultirangeCodec{ElementType: defaultMap.oidToType[Int4rangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"int8multirange\", OID: Int8multirangeOID, Codec: &MultirangeCodec{ElementType: defaultMap.oidToType[Int8rangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"nummultirange\", OID: NummultirangeOID, Codec: &MultirangeCodec{ElementType: defaultMap.oidToType[NumrangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"tsmultirange\", OID: TsmultirangeOID, Codec: &MultirangeCodec{ElementType: defaultMap.oidToType[TsrangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"tstzmultirange\", OID: TstzmultirangeOID, Codec: &MultirangeCodec{ElementType: defaultMap.oidToType[TstzrangeOID]}})\n\n\t// Array types\n\tdefaultMap.RegisterType(&Type{Name: \"_aclitem\", OID: ACLItemArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[ACLItemOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_bit\", OID: BitArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[BitOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_bool\", OID: BoolArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[BoolOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_box\", OID: BoxArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[BoxOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_bpchar\", OID: BPCharArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[BPCharOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_bytea\", OID: ByteaArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[ByteaOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_char\", OID: QCharArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[QCharOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_cid\", OID: CIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[CIDOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_cidr\", OID: CIDRArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[CIDROID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_circle\", OID: CircleArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[CircleOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_date\", OID: DateArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[DateOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_daterange\", OID: DaterangeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[DaterangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_float4\", OID: Float4ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Float4OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_float8\", OID: Float8ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Float8OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_inet\", OID: InetArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[InetOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_int2\", OID: Int2ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Int2OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_int4\", OID: Int4ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Int4OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_int4range\", OID: Int4rangeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Int4rangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_int8\", OID: Int8ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Int8OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_int8range\", OID: Int8rangeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[Int8rangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_interval\", OID: IntervalArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[IntervalOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_json\", OID: JSONArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[JSONOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_jsonb\", OID: JSONBArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[JSONBOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_jsonpath\", OID: JSONPathArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[JSONPathOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_line\", OID: LineArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[LineOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_lseg\", OID: LsegArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[LsegOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_macaddr\", OID: MacaddrArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[MacaddrOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_name\", OID: NameArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[NameOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_numeric\", OID: NumericArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[NumericOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_numrange\", OID: NumrangeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[NumrangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_oid\", OID: OIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[OIDOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_path\", OID: PathArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[PathOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_point\", OID: PointArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[PointOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_polygon\", OID: PolygonArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[PolygonOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_record\", OID: RecordArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[RecordOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_text\", OID: TextArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TextOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_tid\", OID: TIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TIDOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_tsvector\", OID: TSVectorArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TSVectorOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_time\", OID: TimeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TimeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_timestamp\", OID: TimestampArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TimestampOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_timestamptz\", OID: TimestamptzArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TimestamptzOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_tsrange\", OID: TsrangeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TsrangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_tstzrange\", OID: TstzrangeArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[TstzrangeOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_uuid\", OID: UUIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[UUIDOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_varbit\", OID: VarbitArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[VarbitOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_varchar\", OID: VarcharArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[VarcharOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_xid\", OID: XIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XIDOID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_xid8\", OID: XID8ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XID8OID]}})\n\tdefaultMap.RegisterType(&Type{Name: \"_xml\", OID: XMLArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XMLOID]}})\n\n\t// Integer types that directly map to a PostgreSQL type\n\tregisterDefaultPgTypeVariants[int16](defaultMap, \"int2\")\n\tregisterDefaultPgTypeVariants[int32](defaultMap, \"int4\")\n\tregisterDefaultPgTypeVariants[int64](defaultMap, \"int8\")\n\n\t// Integer types that do not have a direct match to a PostgreSQL type\n\tregisterDefaultPgTypeVariants[int8](defaultMap, \"int8\")\n\tregisterDefaultPgTypeVariants[int](defaultMap, \"int8\")\n\tregisterDefaultPgTypeVariants[uint8](defaultMap, \"int8\")\n\tregisterDefaultPgTypeVariants[uint16](defaultMap, \"int8\")\n\tregisterDefaultPgTypeVariants[uint32](defaultMap, \"int8\")\n\tregisterDefaultPgTypeVariants[uint64](defaultMap, \"numeric\")\n\tregisterDefaultPgTypeVariants[uint](defaultMap, \"numeric\")\n\n\tregisterDefaultPgTypeVariants[float32](defaultMap, \"float4\")\n\tregisterDefaultPgTypeVariants[float64](defaultMap, \"float8\")\n\n\tregisterDefaultPgTypeVariants[bool](defaultMap, \"bool\")\n\tregisterDefaultPgTypeVariants[time.Time](defaultMap, \"timestamptz\")\n\tregisterDefaultPgTypeVariants[time.Duration](defaultMap, \"interval\")\n\tregisterDefaultPgTypeVariants[string](defaultMap, \"text\")\n\tregisterDefaultPgTypeVariants[json.RawMessage](defaultMap, \"json\")\n\tregisterDefaultPgTypeVariants[[]byte](defaultMap, \"bytea\")\n\n\tregisterDefaultPgTypeVariants[net.IP](defaultMap, \"inet\")\n\tregisterDefaultPgTypeVariants[net.IPNet](defaultMap, \"cidr\")\n\tregisterDefaultPgTypeVariants[netip.Addr](defaultMap, \"inet\")\n\tregisterDefaultPgTypeVariants[netip.Prefix](defaultMap, \"cidr\")\n\n\t// pgtype provided structs\n\tregisterDefaultPgTypeVariants[Bits](defaultMap, \"varbit\")\n\tregisterDefaultPgTypeVariants[Bool](defaultMap, \"bool\")\n\tregisterDefaultPgTypeVariants[Box](defaultMap, \"box\")\n\tregisterDefaultPgTypeVariants[Circle](defaultMap, \"circle\")\n\tregisterDefaultPgTypeVariants[Date](defaultMap, \"date\")\n\tregisterDefaultPgTypeVariants[Range[Date]](defaultMap, \"daterange\")\n\tregisterDefaultPgTypeVariants[Multirange[Range[Date]]](defaultMap, \"datemultirange\")\n\tregisterDefaultPgTypeVariants[Float4](defaultMap, \"float4\")\n\tregisterDefaultPgTypeVariants[Float8](defaultMap, \"float8\")\n\tregisterDefaultPgTypeVariants[Range[Float8]](defaultMap, \"numrange\")                  // There is no PostgreSQL builtin float8range so map it to numrange.\n\tregisterDefaultPgTypeVariants[Multirange[Range[Float8]]](defaultMap, \"nummultirange\") // There is no PostgreSQL builtin float8multirange so map it to nummultirange.\n\tregisterDefaultPgTypeVariants[Int2](defaultMap, \"int2\")\n\tregisterDefaultPgTypeVariants[Int4](defaultMap, \"int4\")\n\tregisterDefaultPgTypeVariants[Range[Int4]](defaultMap, \"int4range\")\n\tregisterDefaultPgTypeVariants[Multirange[Range[Int4]]](defaultMap, \"int4multirange\")\n\tregisterDefaultPgTypeVariants[Int8](defaultMap, \"int8\")\n\tregisterDefaultPgTypeVariants[Range[Int8]](defaultMap, \"int8range\")\n\tregisterDefaultPgTypeVariants[Multirange[Range[Int8]]](defaultMap, \"int8multirange\")\n\tregisterDefaultPgTypeVariants[Interval](defaultMap, \"interval\")\n\tregisterDefaultPgTypeVariants[Line](defaultMap, \"line\")\n\tregisterDefaultPgTypeVariants[Lseg](defaultMap, \"lseg\")\n\tregisterDefaultPgTypeVariants[Numeric](defaultMap, \"numeric\")\n\tregisterDefaultPgTypeVariants[Range[Numeric]](defaultMap, \"numrange\")\n\tregisterDefaultPgTypeVariants[Multirange[Range[Numeric]]](defaultMap, \"nummultirange\")\n\tregisterDefaultPgTypeVariants[Path](defaultMap, \"path\")\n\tregisterDefaultPgTypeVariants[Point](defaultMap, \"point\")\n\tregisterDefaultPgTypeVariants[Polygon](defaultMap, \"polygon\")\n\tregisterDefaultPgTypeVariants[TID](defaultMap, \"tid\")\n\tregisterDefaultPgTypeVariants[Text](defaultMap, \"text\")\n\tregisterDefaultPgTypeVariants[Time](defaultMap, \"time\")\n\tregisterDefaultPgTypeVariants[Timestamp](defaultMap, \"timestamp\")\n\tregisterDefaultPgTypeVariants[Timestamptz](defaultMap, \"timestamptz\")\n\tregisterDefaultPgTypeVariants[Range[Timestamp]](defaultMap, \"tsrange\")\n\tregisterDefaultPgTypeVariants[Multirange[Range[Timestamp]]](defaultMap, \"tsmultirange\")\n\tregisterDefaultPgTypeVariants[Range[Timestamptz]](defaultMap, \"tstzrange\")\n\tregisterDefaultPgTypeVariants[Multirange[Range[Timestamptz]]](defaultMap, \"tstzmultirange\")\n\tregisterDefaultPgTypeVariants[TSVector](defaultMap, \"tsvector\")\n\tregisterDefaultPgTypeVariants[UUID](defaultMap, \"uuid\")\n\n\tdefaultMap.buildReflectTypeToType()\n}\n"
  },
  {
    "path": "pgtype/pgtype_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar defaultConnTestRunner pgxtest.ConnTestRunner\n\nfunc init() {\n\tdefaultConnTestRunner = pgxtest.DefaultConnTestRunner()\n\tdefaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\treturn config\n\t}\n}\n\n// Test for renamed types\ntype (\n\t_string       string\n\t_bool         bool\n\t_uint8        uint8\n\t_int8         int8\n\t_int16        int16\n\t_int16Slice   []int16\n\t_int32Slice   []int32\n\t_int64Slice   []int64\n\t_float32Slice []float32\n\t_float64Slice []float64\n\t_byteSlice    []byte\n)\n\n// unregisteredOID represents an actual type that is not registered. Cannot use 0 because that represents that the type\n// is not known (e.g. when using the simple protocol).\nconst unregisteredOID = uint32(1)\n\nfunc mustParseInet(t testing.TB, s string) *net.IPNet {\n\tip, ipnet, err := net.ParseCIDR(s)\n\tif err == nil {\n\t\tif ipv4 := ip.To4(); ipv4 != nil {\n\t\t\tipnet.IP = ipv4\n\t\t} else {\n\t\t\tipnet.IP = ip\n\t\t}\n\t\treturn ipnet\n\t}\n\n\t// May be bare IP address.\n\t//\n\tip = net.ParseIP(s)\n\tif ip == nil {\n\t\tt.Fatal(errors.New(\"unable to parse inet address\"))\n\t}\n\tipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}\n\tif ipv4 := ip.To4(); ipv4 != nil {\n\t\tipnet.IP = ipv4\n\t\tipnet.Mask = net.CIDRMask(32, 32)\n\t}\n\treturn ipnet\n}\n\nfunc mustParseMacaddr(t testing.TB, s string) net.HardwareAddr {\n\taddr, err := net.ParseMAC(s)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn addr\n}\n\nfunc skipCockroachDB(t testing.TB, msg string) {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer conn.Close(context.Background())\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(msg)\n\t}\n}\n\nfunc skipPostgreSQLVersionLessThan(t testing.TB, minVersion int64) {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer conn.Close(context.Background())\n\n\tserverVersionStr := conn.PgConn().ParameterStatus(\"server_version\")\n\tserverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)\n\t// if not PostgreSQL do nothing\n\tif serverVersionStr == \"\" {\n\t\treturn\n\t}\n\n\tserverVersion, err := strconv.ParseInt(serverVersionStr, 10, 64)\n\trequire.NoError(t, err)\n\n\tif serverVersion < minVersion {\n\t\tt.Skipf(\"Test requires PostgreSQL v%d+\", minVersion)\n\t}\n}\n\n// sqlScannerFunc lets an arbitrary function be used as a sql.Scanner.\ntype sqlScannerFunc func(src any) error\n\nfunc (f sqlScannerFunc) Scan(src any) error {\n\treturn f(src)\n}\n\n// driverValuerFunc lets an arbitrary function be used as a driver.Valuer.\ntype driverValuerFunc func() (driver.Value, error)\n\nfunc (f driverValuerFunc) Value() (driver.Value, error) {\n\treturn f()\n}\n\nfunc TestMapScanNilIsNoOp(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\terr := m.Scan(pgtype.TextOID, pgx.TextFormatCode, []byte(\"foo\"), nil)\n\tassert.NoError(t, err)\n}\n\nfunc TestMapScanTextFormatInterfacePtr(t *testing.T) {\n\tm := pgtype.NewMap()\n\tvar got any\n\terr := m.Scan(pgtype.TextOID, pgx.TextFormatCode, []byte(\"foo\"), &got)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"foo\", got)\n}\n\nfunc TestMapScanTextFormatNonByteaIntoByteSlice(t *testing.T) {\n\tm := pgtype.NewMap()\n\tvar got []byte\n\terr := m.Scan(pgtype.JSONBOID, pgx.TextFormatCode, []byte(\"{}\"), &got)\n\trequire.NoError(t, err)\n\tassert.Equal(t, []byte(\"{}\"), got)\n}\n\nfunc TestMapScanBinaryFormatInterfacePtr(t *testing.T) {\n\tm := pgtype.NewMap()\n\tvar got any\n\terr := m.Scan(pgtype.TextOID, pgx.BinaryFormatCode, []byte(\"foo\"), &got)\n\trequire.NoError(t, err)\n\tassert.Equal(t, \"foo\", got)\n}\n\nfunc TestMapScanUnknownOIDToPtrToAny(t *testing.T) {\n\tunknownOID := uint32(999999)\n\tsrcBuf := []byte(\"foo\")\n\tm := pgtype.NewMap()\n\n\tvar a any\n\terr := m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &a)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"foo\", a)\n\n\terr = m.Scan(unknownOID, pgx.BinaryFormatCode, srcBuf, &a)\n\tassert.NoError(t, err)\n\tassert.Equal(t, []byte(\"foo\"), a)\n}\n\nfunc TestMapScanUnknownOIDToStringsAndBytes(t *testing.T) {\n\tunknownOID := uint32(999999)\n\tsrcBuf := []byte(\"foo\")\n\tm := pgtype.NewMap()\n\n\tvar s string\n\terr := m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &s)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"foo\", s)\n\n\tvar rs _string\n\terr = m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &rs)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"foo\", string(rs))\n\n\tvar b []byte\n\terr = m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &b)\n\tassert.NoError(t, err)\n\tassert.Equal(t, []byte(\"foo\"), b)\n\n\tvar rb _byteSlice\n\terr = m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &rb)\n\tassert.NoError(t, err)\n\tassert.Equal(t, []byte(\"foo\"), []byte(rb))\n}\n\nfunc TestMapScanPointerToNilStructDoesNotCrash(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\ttype myStruct struct{}\n\tvar p *myStruct\n\terr := m.Scan(0, pgx.TextFormatCode, []byte(\"(foo,bar)\"), &p)\n\trequire.NotNil(t, err)\n}\n\nfunc TestMapScanUnknownOIDTextFormat(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\tvar n int32\n\terr := m.Scan(0, pgx.TextFormatCode, []byte(\"123\"), &n)\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 123, n)\n}\n\nfunc TestMapScanUnknownOIDIntoSQLScanner(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\tvar s sql.NullString\n\terr := m.Scan(0, pgx.TextFormatCode, []byte(nil), &s)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"\", s.String)\n\tassert.False(t, s.Valid)\n}\n\ntype scannerString string\n\nfunc (ss *scannerString) Scan(v any) error {\n\t*ss = scannerString(\"scanned\")\n\treturn nil\n}\n\n// https://github.com/jackc/pgtype/issues/197\nfunc TestMapScanUnregisteredOIDIntoRenamedStringSQLScanner(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\tvar s scannerString\n\terr := m.Scan(unregisteredOID, pgx.TextFormatCode, []byte(nil), &s)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"scanned\", string(s))\n}\n\ntype pgCustomInt int64\n\nfunc (ci *pgCustomInt) Scan(src any) error {\n\t*ci = pgCustomInt(src.(int64))\n\treturn nil\n}\n\nfunc TestScanPlanBinaryInt32ScanScanner(t *testing.T) {\n\tm := pgtype.NewMap()\n\tsrc := []byte{0, 42}\n\tvar v pgCustomInt\n\n\tplan := m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &v)\n\terr := plan.Scan(src, &v)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 42, v)\n\n\tptr := new(pgCustomInt)\n\tplan = m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &ptr)\n\terr = plan.Scan(src, &ptr)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 42, *ptr)\n\n\tptr = new(pgCustomInt)\n\terr = plan.Scan(nil, &ptr)\n\trequire.NoError(t, err)\n\tassert.Nil(t, ptr)\n\n\tptr = nil\n\tplan = m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &ptr)\n\terr = plan.Scan(src, &ptr)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 42, *ptr)\n\n\tptr = nil\n\tplan = m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &ptr)\n\terr = plan.Scan(nil, &ptr)\n\trequire.NoError(t, err)\n\tassert.Nil(t, ptr)\n}\n\n// Test for https://github.com/jackc/pgtype/issues/164\nfunc TestScanPlanInterface(t *testing.T) {\n\tm := pgtype.NewMap()\n\tsrc := []byte{0, 42}\n\tvar v any\n\tplan := m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, v)\n\terr := plan.Scan(src, v)\n\tassert.Error(t, err)\n}\n\nfunc TestPointerPointerStructScan(t *testing.T) {\n\tm := pgtype.NewMap()\n\ttype composite struct {\n\t\tID int\n\t}\n\n\tint4Type, _ := m.TypeForOID(pgtype.Int4OID)\n\tpgt := &pgtype.Type{\n\t\tCodec: &pgtype.CompositeCodec{\n\t\t\tFields: []pgtype.CompositeCodecField{\n\t\t\t\t{\n\t\t\t\t\tName: \"id\",\n\t\t\t\t\tType: int4Type,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tName: \"composite\",\n\t\tOID:  215333,\n\t}\n\tm.RegisterType(pgt)\n\n\tvar c *composite\n\tplan := m.PlanScan(pgt.OID, pgtype.TextFormatCode, &c)\n\terr := plan.Scan([]byte(\"(1)\"), &c)\n\trequire.NoError(t, err)\n\trequire.Equal(t, 1, c.ID)\n}\n\n// https://github.com/jackc/pgx/issues/1263\nfunc TestMapScanPtrToPtrToSlice(t *testing.T) {\n\tm := pgtype.NewMap()\n\tsrc := []byte(\"{foo,bar}\")\n\tvar v *[]string\n\tplan := m.PlanScan(pgtype.TextArrayOID, pgtype.TextFormatCode, &v)\n\terr := plan.Scan(src, &v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []string{\"foo\", \"bar\"}, *v)\n}\n\nfunc TestMapScanPtrToPtrToSliceOfStruct(t *testing.T) {\n\ttype Team struct {\n\t\tTeamID int\n\t\tName   string\n\t}\n\n\t// Have to use binary format because text format doesn't include type information.\n\tm := pgtype.NewMap()\n\tsrc := []byte{0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc9, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x6, 0x74, 0x65, 0x61, 0x6d, 0x20, 0x31, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x6, 0x74, 0x65, 0x61, 0x6d, 0x20, 0x32}\n\tvar v *[]Team\n\tplan := m.PlanScan(pgtype.RecordArrayOID, pgtype.BinaryFormatCode, &v)\n\terr := plan.Scan(src, &v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []Team{{1, \"team 1\"}, {2, \"team 2\"}}, *v)\n}\n\ntype databaseValuerString string\n\nfunc (s databaseValuerString) Value() (driver.Value, error) {\n\treturn fmt.Sprintf(\"%d\", len(s)), nil\n}\n\n// https://github.com/jackc/pgx/issues/1319\nfunc TestMapEncodeTextFormatDatabaseValuerThatIsRenamedSimpleType(t *testing.T) {\n\tm := pgtype.NewMap()\n\tsrc := databaseValuerString(\"foo\")\n\tbuf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, src, nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"3\", string(buf))\n}\n\ntype databaseValuerFmtStringer string\n\nfunc (s databaseValuerFmtStringer) Value() (driver.Value, error) {\n\treturn nil, nil\n}\n\nfunc (s databaseValuerFmtStringer) String() string {\n\treturn \"foobar\"\n}\n\n// https://github.com/jackc/pgx/issues/1311\nfunc TestMapEncodeTextFormatDatabaseValuerThatIsFmtStringer(t *testing.T) {\n\tm := pgtype.NewMap()\n\tsrc := databaseValuerFmtStringer(\"\")\n\tbuf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, src, nil)\n\trequire.NoError(t, err)\n\trequire.Nil(t, buf)\n}\n\ntype databaseValuerStringFormat struct {\n\tn int32\n}\n\nfunc (v databaseValuerStringFormat) Value() (driver.Value, error) {\n\treturn fmt.Sprint(v.n), nil\n}\n\nfunc TestMapEncodeBinaryFormatDatabaseValuerThatReturnsString(t *testing.T) {\n\tm := pgtype.NewMap()\n\tsrc := databaseValuerStringFormat{n: 42}\n\tbuf, err := m.Encode(pgtype.Int4OID, pgtype.BinaryFormatCode, src, nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte{0, 0, 0, 42}, buf)\n}\n\n// https://github.com/jackc/pgx/issues/1445\nfunc TestMapEncodeDatabaseValuerThatReturnsStringIntoUnregisteredTypeTextFormat(t *testing.T) {\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, driverValuerFunc(func() (driver.Value, error) { return \"foo\", nil }), nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(\"foo\"), buf)\n}\n\n// https://github.com/jackc/pgx/issues/1445\nfunc TestMapEncodeDatabaseValuerThatReturnsByteSliceIntoUnregisteredTypeTextFormat(t *testing.T) {\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, driverValuerFunc(func() (driver.Value, error) { return []byte{0, 1, 2, 3}, nil }), nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(`\\x00010203`), buf)\n}\n\nfunc TestMapEncodeStringIntoUnregisteredTypeTextFormat(t *testing.T) {\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, \"foo\", nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(\"foo\"), buf)\n}\n\nfunc TestMapEncodeByteSliceIntoUnregisteredTypeTextFormat(t *testing.T) {\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, []byte{0, 1, 2, 3}, nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(`\\x00010203`), buf)\n}\n\n// https://github.com/jackc/pgx/issues/1763\nfunc TestMapEncodeNamedTypeOfByteSliceIntoTextTextFormat(t *testing.T) {\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, json.RawMessage(`{\"foo\": \"bar\"}`), nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(`{\"foo\": \"bar\"}`), buf)\n}\n\n// https://github.com/jackc/pgx/issues/1326\nfunc TestMapScanPointerToRenamedType(t *testing.T) {\n\tsrcBuf := []byte(\"foo\")\n\tm := pgtype.NewMap()\n\n\tvar rs *_string\n\terr := m.Scan(pgtype.TextOID, pgx.TextFormatCode, srcBuf, &rs)\n\tassert.NoError(t, err)\n\trequire.NotNil(t, rs)\n\tassert.Equal(t, \"foo\", string(*rs))\n}\n\n// https://github.com/jackc/pgx/issues/1326\nfunc TestMapScanNullToWrongType(t *testing.T) {\n\tm := pgtype.NewMap()\n\n\tvar n *int32\n\terr := m.Scan(pgtype.TextOID, pgx.TextFormatCode, nil, &n)\n\tassert.NoError(t, err)\n\tassert.Nil(t, n)\n\n\tvar pn pgtype.Int4\n\terr = m.Scan(pgtype.TextOID, pgx.TextFormatCode, nil, &pn)\n\tassert.NoError(t, err)\n\tassert.False(t, pn.Valid)\n}\n\nfunc TestScanToSliceOfRenamedUint8(t *testing.T) {\n\tm := pgtype.NewMap()\n\tvar ruint8 []_uint8\n\terr := m.Scan(pgtype.Int2ArrayOID, pgx.TextFormatCode, []byte(\"{2,4}\"), &ruint8)\n\tassert.NoError(t, err)\n\tassert.Equal(t, []_uint8{2, 4}, ruint8)\n}\n\nfunc TestMapScanTextToBool(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tsrc  []byte\n\t\twant bool\n\t}{\n\t\t{\"t\", []byte(\"t\"), true},\n\t\t{\"f\", []byte(\"f\"), false},\n\t\t{\"y\", []byte(\"y\"), true},\n\t\t{\"n\", []byte(\"n\"), false},\n\t\t{\"1\", []byte(\"1\"), true},\n\t\t{\"0\", []byte(\"0\"), false},\n\t\t{\"true\", []byte(\"true\"), true},\n\t\t{\"false\", []byte(\"false\"), false},\n\t\t{\"yes\", []byte(\"yes\"), true},\n\t\t{\"no\", []byte(\"no\"), false},\n\t\t{\"on\", []byte(\"on\"), true},\n\t\t{\"off\", []byte(\"off\"), false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tm := pgtype.NewMap()\n\n\t\t\tvar v bool\n\t\t\terr := m.Scan(pgtype.BoolOID, pgx.TextFormatCode, tt.src, &v)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.Equal(t, tt.want, v)\n\t\t})\n\t}\n}\n\nfunc TestMapScanTextToBoolError(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tsrc  []byte\n\t\twant string\n\t}{\n\t\t{\"nil\", nil, \"cannot scan NULL into *bool\"},\n\t\t{\"empty\", []byte{}, \"cannot scan empty string into *bool\"},\n\t\t{\"foo\", []byte(\"foo\"), \"unknown boolean string representation \\\"foo\\\"\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tm := pgtype.NewMap()\n\n\t\t\tvar v bool\n\t\t\terr := m.Scan(pgtype.BoolOID, pgx.TextFormatCode, tt.src, &v)\n\t\t\trequire.ErrorContains(t, err, tt.want)\n\t\t})\n\t}\n}\n\ntype databaseValuerUUID [16]byte\n\nfunc (v databaseValuerUUID) Value() (driver.Value, error) {\n\treturn fmt.Sprintf(\"%x\", v), nil\n}\n\n// https://github.com/jackc/pgx/issues/1502\nfunc TestMapEncodePlanCacheUUIDTypeConfusion(t *testing.T) {\n\texpected := []byte{\n\t\t0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0xb, 0x86, 0, 0, 0, 2, 0, 0, 0, 1,\n\t\t0, 0, 0, 16,\n\t\t0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n\t\t0, 0, 0, 16,\n\t\t15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,\n\t}\n\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(pgtype.UUIDArrayOID, pgtype.BinaryFormatCode,\n\t\t[]databaseValuerUUID{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}},\n\t\tnil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, expected, buf)\n\n\t// This actually *should* fail. In the actual query path this error is detected and the encoding falls back to the\n\t// text format. In the bug this test is guarding against regression this would panic.\n\t_, err = m.Encode(pgtype.UUIDArrayOID, pgtype.BinaryFormatCode,\n\t\t[]string{\"00010203-0405-0607-0809-0a0b0c0d0e0f\", \"0f0e0d0c-0b0a-0908-0706-0504-03020100\"},\n\t\tnil)\n\trequire.Error(t, err)\n}\n\n// https://github.com/jackc/pgx/issues/1763\nfunc TestMapEncodeRawJSONIntoUnknownOID(t *testing.T) {\n\tm := pgtype.NewMap()\n\tbuf, err := m.Encode(0, pgtype.TextFormatCode, json.RawMessage(`{\"foo\": \"bar\"}`), nil)\n\trequire.NoError(t, err)\n\trequire.Equal(t, []byte(`{\"foo\": \"bar\"}`), buf)\n}\n\n// PlanScan previously used a cache to improve performance. However, the cache could get confused in certain cases. The\n// example below was one such failure case.\nfunc TestCachedPlanScanConfusion(t *testing.T) {\n\tm := pgtype.NewMap()\n\tvar err error\n\n\tvar tags any\n\terr = m.Scan(pgtype.TextArrayOID, pgx.TextFormatCode, []byte(\"{foo,bar,baz}\"), &tags)\n\trequire.NoError(t, err)\n\n\tvar cells [][]string\n\terr = m.Scan(pgtype.TextArrayOID, pgx.TextFormatCode, []byte(\"{{foo,bar},{baz,quz}}\"), &cells)\n\trequire.NoError(t, err)\n}\n\nfunc BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) {\n\tm := pgtype.NewMap()\n\tsrc := []byte{0, 0, 0, 42}\n\tvar v pgtype.Int4\n\n\tfor b.Loop() {\n\t\tv = pgtype.Int4{}\n\t\terr := m.Scan(pgtype.Int4OID, pgtype.BinaryFormatCode, src, &v)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif v != (pgtype.Int4{Int32: 42, Valid: true}) {\n\t\t\tb.Fatal(\"scan failed due to bad value\")\n\t\t}\n\t}\n}\n\nfunc BenchmarkMapScanInt4IntoGoInt32(b *testing.B) {\n\tm := pgtype.NewMap()\n\tsrc := []byte{0, 0, 0, 42}\n\tvar v int32\n\n\tfor b.Loop() {\n\t\tv = 0\n\t\terr := m.Scan(pgtype.Int4OID, pgtype.BinaryFormatCode, src, &v)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif v != 42 {\n\t\t\tb.Fatal(\"scan failed due to bad value\")\n\t\t}\n\t}\n}\n\nfunc BenchmarkScanPlanScanInt4IntoBinaryDecoder(b *testing.B) {\n\tm := pgtype.NewMap()\n\tsrc := []byte{0, 0, 0, 42}\n\tvar v pgtype.Int4\n\n\tplan := m.PlanScan(pgtype.Int4OID, pgtype.BinaryFormatCode, &v)\n\n\tfor b.Loop() {\n\t\tv = pgtype.Int4{}\n\t\terr := plan.Scan(src, &v)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif v != (pgtype.Int4{Int32: 42, Valid: true}) {\n\t\t\tb.Fatal(\"scan failed due to bad value\")\n\t\t}\n\t}\n}\n\nfunc BenchmarkScanPlanScanInt4IntoGoInt32(b *testing.B) {\n\tm := pgtype.NewMap()\n\tsrc := []byte{0, 0, 0, 42}\n\tvar v int32\n\n\tplan := m.PlanScan(pgtype.Int4OID, pgtype.BinaryFormatCode, &v)\n\n\tfor b.Loop() {\n\t\tv = 0\n\t\terr := plan.Scan(src, &v)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif v != 42 {\n\t\t\tb.Fatal(\"scan failed due to bad value\")\n\t\t}\n\t}\n}\n\nfunc isExpectedEq(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\treturn a == v\n\t}\n}\n\nfunc isPtrExpectedEq(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tval := reflect.ValueOf(v)\n\t\treturn a == val.Elem().Interface()\n\t}\n}\n"
  },
  {
    "path": "pgtype/point.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Vec2 struct {\n\tX float64\n\tY float64\n}\n\ntype PointScanner interface {\n\tScanPoint(v Point) error\n}\n\ntype PointValuer interface {\n\tPointValue() (Point, error)\n}\n\ntype Point struct {\n\tP     Vec2\n\tValid bool\n}\n\n// ScanPoint implements the [PointScanner] interface.\nfunc (p *Point) ScanPoint(v Point) error {\n\t*p = v\n\treturn nil\n}\n\n// PointValue implements the [PointValuer] interface.\nfunc (p Point) PointValue() (Point, error) {\n\treturn p, nil\n}\n\nfunc parsePoint(src []byte) (*Point, error) {\n\tif src == nil || bytes.Equal(src, []byte(\"null\")) {\n\t\treturn &Point{}, nil\n\t}\n\n\tif len(src) < 5 {\n\t\treturn nil, fmt.Errorf(\"invalid length for point: %v\", len(src))\n\t}\n\tif src[0] == '\"' && src[len(src)-1] == '\"' {\n\t\tsrc = src[1 : len(src)-1]\n\t}\n\tsx, sy, found := strings.Cut(string(src[1:len(src)-1]), \",\")\n\tif !found {\n\t\treturn nil, fmt.Errorf(\"invalid format for point\")\n\t}\n\n\tx, err := strconv.ParseFloat(sx, 64)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ty, err := strconv.ParseFloat(sy, 64)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Point{P: Vec2{x, y}, Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Point) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Point{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToPointScanner{}.Scan([]byte(src), dst)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Point) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := PointCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Point) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tvar buff bytes.Buffer\n\tbuff.WriteByte('\"')\n\tbuff.WriteString(fmt.Sprintf(\"(%g,%g)\", src.P.X, src.P.Y))\n\tbuff.WriteByte('\"')\n\treturn buff.Bytes(), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Point) UnmarshalJSON(point []byte) error {\n\tp, err := parsePoint(point)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*dst = *p\n\treturn nil\n}\n\ntype PointCodec struct{}\n\nfunc (PointCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (PointCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (PointCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(PointValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanPointCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanPointCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanPointCodecBinary struct{}\n\nfunc (encodePlanPointCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tpoint, err := value.(PointValuer).PointValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !point.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(point.P.X))\n\tbuf = pgio.AppendUint64(buf, math.Float64bits(point.P.Y))\n\treturn buf, nil\n}\n\ntype encodePlanPointCodecText struct{}\n\nfunc (encodePlanPointCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tpoint, err := value.(PointValuer).PointValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !point.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, fmt.Sprintf(`(%s,%s)`,\n\t\tstrconv.FormatFloat(point.P.X, 'f', -1, 64),\n\t\tstrconv.FormatFloat(point.P.Y, 'f', -1, 64),\n\t)...), nil\n}\n\nfunc (PointCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase PointScanner:\n\t\t\treturn scanPlanBinaryPointToPointScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase PointScanner:\n\t\t\treturn scanPlanTextAnyToPointScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c PointCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c PointCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar point Point\n\terr := codecScan(c, m, oid, format, src, &point)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn point, nil\n}\n\ntype scanPlanBinaryPointToPointScanner struct{}\n\nfunc (scanPlanBinaryPointToPointScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(PointScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanPoint(Point{})\n\t}\n\n\tif len(src) != 16 {\n\t\treturn fmt.Errorf(\"invalid length for point: %v\", len(src))\n\t}\n\n\tx := binary.BigEndian.Uint64(src)\n\ty := binary.BigEndian.Uint64(src[8:])\n\n\treturn scanner.ScanPoint(Point{\n\t\tP:     Vec2{math.Float64frombits(x), math.Float64frombits(y)},\n\t\tValid: true,\n\t})\n}\n\ntype scanPlanTextAnyToPointScanner struct{}\n\nfunc (scanPlanTextAnyToPointScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(PointScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanPoint(Point{})\n\t}\n\n\tif len(src) < 5 {\n\t\treturn fmt.Errorf(\"invalid length for point: %v\", len(src))\n\t}\n\n\tsx, sy, found := strings.Cut(string(src[1:len(src)-1]), \",\")\n\tif !found {\n\t\treturn fmt.Errorf(\"invalid format for point\")\n\t}\n\n\tx, err := strconv.ParseFloat(sx, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ty, err := strconv.ParseFloat(sy, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanPoint(Point{P: Vec2{x, y}, Valid: true})\n}\n"
  },
  {
    "path": "pgtype/point_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPointCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type point\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"point\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Valid: true},\n\t\t\tResult: new(pgtype.Point),\n\t\t\tTest:   isExpectedEq(pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Point{P: pgtype.Vec2{-1.234, -5.6789}, Valid: true},\n\t\t\tResult: new(pgtype.Point),\n\t\t\tTest:   isExpectedEq(pgtype.Point{P: pgtype.Vec2{-1.234, -5.6789}, Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Point{}, Result: new(pgtype.Point), Test: isExpectedEq(pgtype.Point{})},\n\t\t{Param: nil, Result: new(pgtype.Point), Test: isExpectedEq(pgtype.Point{})},\n\t})\n}\n\nfunc TestPoint_MarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tpoint pgtype.Point\n\t\twant  []byte\n\t}{\n\t\t{\n\t\t\tname: \"second\",\n\t\t\tpoint: pgtype.Point{\n\t\t\t\tP:     pgtype.Vec2{X: 12.245, Y: 432.12},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\twant: []byte(`\"(12.245,432.12)\"`),\n\t\t},\n\t\t{\n\t\t\tname: \"third\",\n\t\t\tpoint: pgtype.Point{\n\t\t\t\tP: pgtype.Vec2{},\n\t\t\t},\n\t\t\twant: []byte(\"null\"),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := tt.point.MarshalJSON()\n\t\t\trequire.NoError(t, err)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"MarshalJSON() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPoint_UnmarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tvalid   bool\n\t\targ     []byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"first\",\n\t\t\tvalid:   true,\n\t\t\targ:     []byte(`\"(123.123,54.12)\"`),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"second\",\n\t\t\tvalid:   false,\n\t\t\targ:     []byte(`\"(123.123,54.1sad2)\"`),\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"third\",\n\t\t\tvalid:   false,\n\t\t\targ:     []byte(\"null\"),\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tdst := &pgtype.Point{}\n\t\t\tif err := dst.UnmarshalJSON(tt.arg); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"UnmarshalJSON() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t\tif dst.Valid != tt.valid {\n\t\t\t\tt.Errorf(\"Valid mismatch: %v != %v\", dst.Valid, tt.valid)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgtype/polygon.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype PolygonScanner interface {\n\tScanPolygon(v Polygon) error\n}\n\ntype PolygonValuer interface {\n\tPolygonValue() (Polygon, error)\n}\n\ntype Polygon struct {\n\tP     []Vec2\n\tValid bool\n}\n\n// ScanPolygon implements the [PolygonScanner] interface.\nfunc (p *Polygon) ScanPolygon(v Polygon) error {\n\t*p = v\n\treturn nil\n}\n\n// PolygonValue implements the [PolygonValuer] interface.\nfunc (p Polygon) PolygonValue() (Polygon, error) {\n\treturn p, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (p *Polygon) Scan(src any) error {\n\tif src == nil {\n\t\t*p = Polygon{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToPolygonScanner{}.Scan([]byte(src), p)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (p Polygon) Value() (driver.Value, error) {\n\tif !p.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := PolygonCodec{}.PlanEncode(nil, 0, TextFormatCode, p).Encode(p, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn string(buf), err\n}\n\ntype PolygonCodec struct{}\n\nfunc (PolygonCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (PolygonCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (PolygonCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(PolygonValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanPolygonCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanPolygonCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanPolygonCodecBinary struct{}\n\nfunc (encodePlanPolygonCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tpolygon, err := value.(PolygonValuer).PolygonValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !polygon.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendInt32(buf, int32(len(polygon.P)))\n\n\tfor _, p := range polygon.P {\n\t\tbuf = pgio.AppendUint64(buf, math.Float64bits(p.X))\n\t\tbuf = pgio.AppendUint64(buf, math.Float64bits(p.Y))\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanPolygonCodecText struct{}\n\nfunc (encodePlanPolygonCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tpolygon, err := value.(PolygonValuer).PolygonValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !polygon.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, '(')\n\n\tfor i, p := range polygon.P {\n\t\tif i > 0 {\n\t\t\tbuf = append(buf, ',')\n\t\t}\n\t\tbuf = append(buf, fmt.Sprintf(`(%s,%s)`,\n\t\t\tstrconv.FormatFloat(p.X, 'f', -1, 64),\n\t\t\tstrconv.FormatFloat(p.Y, 'f', -1, 64),\n\t\t)...)\n\t}\n\n\tbuf = append(buf, ')')\n\n\treturn buf, nil\n}\n\nfunc (PolygonCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase PolygonScanner:\n\t\t\treturn scanPlanBinaryPolygonToPolygonScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase PolygonScanner:\n\t\t\treturn scanPlanTextAnyToPolygonScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryPolygonToPolygonScanner struct{}\n\nfunc (scanPlanBinaryPolygonToPolygonScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(PolygonScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanPolygon(Polygon{})\n\t}\n\n\tif len(src) < 5 {\n\t\treturn fmt.Errorf(\"invalid length for polygon: %v\", len(src))\n\t}\n\n\tpointCount := int(binary.BigEndian.Uint32(src))\n\trp := 4\n\n\tif 4+pointCount*16 != len(src) {\n\t\treturn fmt.Errorf(\"invalid length for Polygon with %d points: %v\", pointCount, len(src))\n\t}\n\n\tpoints := make([]Vec2, pointCount)\n\tfor i := range points {\n\t\tx := binary.BigEndian.Uint64(src[rp:])\n\t\trp += 8\n\t\ty := binary.BigEndian.Uint64(src[rp:])\n\t\trp += 8\n\t\tpoints[i] = Vec2{math.Float64frombits(x), math.Float64frombits(y)}\n\t}\n\n\treturn scanner.ScanPolygon(Polygon{\n\t\tP:     points,\n\t\tValid: true,\n\t})\n}\n\ntype scanPlanTextAnyToPolygonScanner struct{}\n\nfunc (scanPlanTextAnyToPolygonScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(PolygonScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanPolygon(Polygon{})\n\t}\n\n\tif len(src) < 7 {\n\t\treturn fmt.Errorf(\"invalid length for Polygon: %v\", len(src))\n\t}\n\n\tpoints := make([]Vec2, 0)\n\n\tstr := string(src[2:])\n\n\tfor {\n\t\tend := strings.IndexByte(str, ',')\n\t\tx, err := strconv.ParseFloat(str[:end], 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstr = str[end+1:]\n\t\tend = strings.IndexByte(str, ')')\n\n\t\ty, err := strconv.ParseFloat(str[:end], 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tpoints = append(points, Vec2{x, y})\n\n\t\tif end+3 < len(str) {\n\t\t\tstr = str[end+3:]\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn scanner.ScanPolygon(Polygon{P: points, Valid: true})\n}\n\nfunc (c PolygonCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c PolygonCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar polygon Polygon\n\terr := codecScan(c, m, oid, format, src, &polygon)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn polygon, nil\n}\n"
  },
  {
    "path": "pgtype/polygon_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqPolygon(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tap := a.(pgtype.Polygon)\n\t\tvp := v.(pgtype.Polygon)\n\n\t\tif !(ap.Valid == vp.Valid && len(ap.P) == len(vp.P)) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor i := range ap.P {\n\t\t\tif ap.P[i] != vp.P[i] {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n}\n\nfunc TestPolygonTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type polygon\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"polygon\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam: pgtype.Polygon{\n\t\t\t\tP:     []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Polygon),\n\t\t\tTest: isExpectedEqPolygon(pgtype.Polygon{\n\t\t\t\tP:     []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}},\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Polygon{\n\t\t\t\tP:     []pgtype.Vec2{{3.14, -1.678}, {7.1, -5.234}, {23.1, 9.34}},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Polygon),\n\t\t\tTest: isExpectedEqPolygon(pgtype.Polygon{\n\t\t\t\tP:     []pgtype.Vec2{{3.14, -1.678}, {7.1, -5.234}, {23.1, 9.34}},\n\t\t\t\tValid: true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Polygon{}, Result: new(pgtype.Polygon), Test: isExpectedEqPolygon(pgtype.Polygon{})},\n\t\t{Param: nil, Result: new(pgtype.Polygon), Test: isExpectedEqPolygon(pgtype.Polygon{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/qchar.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"math\"\n)\n\n// QCharCodec is for PostgreSQL's special 8-bit-only \"char\" type more akin to the C\n// language's char type, or Go's byte type. (Note that the name in PostgreSQL\n// itself is \"char\", in double-quotes, and not char.) It gets used a lot in\n// PostgreSQL's system tables to hold a single ASCII character value (eg\n// pg_class.relkind). It is named Qchar for quoted char to disambiguate from SQL\n// standard type char.\ntype QCharCodec struct{}\n\nfunc (QCharCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (QCharCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (QCharCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase TextFormatCode, BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase byte:\n\t\t\treturn encodePlanQcharCodecByte{}\n\t\tcase rune:\n\t\t\treturn encodePlanQcharCodecRune{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanQcharCodecByte struct{}\n\nfunc (encodePlanQcharCodecByte) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tb := value.(byte)\n\tbuf = append(buf, b)\n\treturn buf, nil\n}\n\ntype encodePlanQcharCodecRune struct{}\n\nfunc (encodePlanQcharCodecRune) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tr := value.(rune)\n\tif r > math.MaxUint8 {\n\t\treturn nil, fmt.Errorf(`%v cannot be encoded to \"char\"`, r)\n\t}\n\tb := byte(r)\n\tbuf = append(buf, b)\n\treturn buf, nil\n}\n\nfunc (QCharCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase TextFormatCode, BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *byte:\n\t\t\treturn scanPlanQcharCodecByte{}\n\t\tcase *rune:\n\t\t\treturn scanPlanQcharCodecRune{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanQcharCodecByte struct{}\n\nfunc (scanPlanQcharCodecByte) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) > 1 {\n\t\treturn fmt.Errorf(`invalid length for \"char\": %v`, len(src))\n\t}\n\n\tb := dst.(*byte)\n\t// In the text format the zero value is returned as a zero byte value instead of 0\n\tif len(src) == 0 {\n\t\t*b = 0\n\t} else {\n\t\t*b = src[0]\n\t}\n\n\treturn nil\n}\n\ntype scanPlanQcharCodecRune struct{}\n\nfunc (scanPlanQcharCodecRune) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) > 1 {\n\t\treturn fmt.Errorf(`invalid length for \"char\": %v`, len(src))\n\t}\n\n\tr := dst.(*rune)\n\t// In the text format the zero value is returned as a zero byte value instead of 0\n\tif len(src) == 0 {\n\t\t*r = 0\n\t} else {\n\t\t*r = rune(src[0])\n\t}\n\n\treturn nil\n}\n\nfunc (c QCharCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar r rune\n\terr := codecScan(c, m, oid, format, src, &r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(r), nil\n}\n\nfunc (c QCharCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar r rune\n\terr := codecScan(c, m, oid, format, src, &r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r, nil\n}\n"
  },
  {
    "path": "pgtype/qchar_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestQcharTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support qchar\")\n\n\tvar tests []pgxtest.ValueRoundTripTest\n\tfor i := 0; i <= math.MaxUint8; i++ {\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{Param: rune(i), Result: new(rune), Test: isExpectedEq(rune(i))})\n\t\ttests = append(tests, pgxtest.ValueRoundTripTest{Param: byte(i), Result: new(byte), Test: isExpectedEq(byte(i))})\n\t}\n\ttests = append(tests, pgxtest.ValueRoundTripTest{Param: nil, Result: new(*rune), Test: isExpectedEq((*rune)(nil))})\n\ttests = append(tests, pgxtest.ValueRoundTripTest{Param: nil, Result: new(*byte), Test: isExpectedEq((*byte)(nil))})\n\n\t// Can only test with known OIDs as rune and byte would be considered numbers.\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, `\"char\"`, tests)\n}\n"
  },
  {
    "path": "pgtype/range.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n)\n\ntype BoundType byte\n\nconst (\n\tInclusive = BoundType('i')\n\tExclusive = BoundType('e')\n\tUnbounded = BoundType('U')\n\tEmpty     = BoundType('E')\n)\n\nfunc (bt BoundType) String() string {\n\treturn string(bt)\n}\n\ntype untypedTextRange struct {\n\tLower     string\n\tUpper     string\n\tLowerType BoundType\n\tUpperType BoundType\n}\n\nfunc parseUntypedTextRange(src string) (*untypedTextRange, error) {\n\tutr := &untypedTextRange{}\n\tif src == \"empty\" {\n\t\tutr.LowerType = Empty\n\t\tutr.UpperType = Empty\n\t\treturn utr, nil\n\t}\n\n\tbuf := bytes.NewBufferString(src)\n\n\tskipWhitespace(buf)\n\n\tr, _, err := buf.ReadRune()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid lower bound: %w\", err)\n\t}\n\tswitch r {\n\tcase '(':\n\t\tutr.LowerType = Exclusive\n\tcase '[':\n\t\tutr.LowerType = Inclusive\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"missing lower bound, instead got: %v\", string(r))\n\t}\n\n\tr, _, err = buf.ReadRune()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid lower value: %w\", err)\n\t}\n\tbuf.UnreadRune()\n\n\tif r == ',' {\n\t\tutr.LowerType = Unbounded\n\t} else {\n\t\tutr.Lower, err = rangeParseValue(buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid lower value: %w\", err)\n\t\t}\n\t}\n\n\tr, _, err = buf.ReadRune()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"missing range separator: %w\", err)\n\t}\n\tif r != ',' {\n\t\treturn nil, fmt.Errorf(\"missing range separator: %v\", r)\n\t}\n\n\tr, _, err = buf.ReadRune()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid upper value: %w\", err)\n\t}\n\n\tif r == ')' || r == ']' {\n\t\tutr.UpperType = Unbounded\n\t} else {\n\t\tbuf.UnreadRune()\n\t\tutr.Upper, err = rangeParseValue(buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid upper value: %w\", err)\n\t\t}\n\n\t\tr, _, err = buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"missing upper bound: %w\", err)\n\t\t}\n\t\tswitch r {\n\t\tcase ')':\n\t\t\tutr.UpperType = Exclusive\n\t\tcase ']':\n\t\t\tutr.UpperType = Inclusive\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"missing upper bound, instead got: %v\", string(r))\n\t\t}\n\t}\n\n\tskipWhitespace(buf)\n\n\tif buf.Len() > 0 {\n\t\treturn nil, fmt.Errorf(\"unexpected trailing data: %v\", buf.String())\n\t}\n\n\treturn utr, nil\n}\n\nfunc rangeParseValue(buf *bytes.Buffer) (string, error) {\n\tr, _, err := buf.ReadRune()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif r == '\"' {\n\t\treturn rangeParseQuotedValue(buf)\n\t}\n\tbuf.UnreadRune()\n\n\ts := &bytes.Buffer{}\n\n\tfor {\n\t\tr, _, err := buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\tcase ',', '[', ']', '(', ')':\n\t\t\tbuf.UnreadRune()\n\t\t\treturn s.String(), nil\n\t\t}\n\n\t\ts.WriteRune(r)\n\t}\n}\n\nfunc rangeParseQuotedValue(buf *bytes.Buffer) (string, error) {\n\ts := &bytes.Buffer{}\n\n\tfor {\n\t\tr, _, err := buf.ReadRune()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\tcase '\"':\n\t\t\tr, _, err = buf.ReadRune()\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\tif r != '\"' {\n\t\t\t\tbuf.UnreadRune()\n\t\t\t\treturn s.String(), nil\n\t\t\t}\n\t\t}\n\t\ts.WriteRune(r)\n\t}\n}\n\ntype untypedBinaryRange struct {\n\tLower     []byte\n\tUpper     []byte\n\tLowerType BoundType\n\tUpperType BoundType\n}\n\n// 0 = ()      = 00000\n// 1 = empty   = 00001\n// 2 = [)      = 00010\n// 4 = (]      = 00100\n// 6 = []      = 00110\n// 8 = )       = 01000\n// 12 = ]      = 01100\n// 16 = (      = 10000\n// 18 = [      = 10010\n// 24 =        = 11000\n\nconst (\n\temptyMask          = 1\n\tlowerInclusiveMask = 2\n\tupperInclusiveMask = 4\n\tlowerUnboundedMask = 8\n\tupperUnboundedMask = 16\n)\n\nfunc parseUntypedBinaryRange(src []byte) (*untypedBinaryRange, error) {\n\tubr := &untypedBinaryRange{}\n\n\tif len(src) == 0 {\n\t\treturn nil, fmt.Errorf(\"range too short: %v\", len(src))\n\t}\n\n\trangeType := src[0]\n\trp := 1\n\n\tif rangeType&emptyMask > 0 {\n\t\tif len(src[rp:]) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"unexpected trailing bytes parsing empty range: %v\", len(src[rp:]))\n\t\t}\n\t\tubr.LowerType = Empty\n\t\tubr.UpperType = Empty\n\t\treturn ubr, nil\n\t}\n\n\tif rangeType&lowerInclusiveMask > 0 {\n\t\tubr.LowerType = Inclusive\n\t} else if rangeType&lowerUnboundedMask > 0 {\n\t\tubr.LowerType = Unbounded\n\t} else {\n\t\tubr.LowerType = Exclusive\n\t}\n\n\tif rangeType&upperInclusiveMask > 0 {\n\t\tubr.UpperType = Inclusive\n\t} else if rangeType&upperUnboundedMask > 0 {\n\t\tubr.UpperType = Unbounded\n\t} else {\n\t\tubr.UpperType = Exclusive\n\t}\n\n\tif ubr.LowerType == Unbounded && ubr.UpperType == Unbounded {\n\t\tif len(src[rp:]) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"unexpected trailing bytes parsing unbounded range: %v\", len(src[rp:]))\n\t\t}\n\t\treturn ubr, nil\n\t}\n\n\tif len(src[rp:]) < 4 {\n\t\treturn nil, fmt.Errorf(\"too few bytes for size: %v\", src[rp:])\n\t}\n\tvalueLen := int(binary.BigEndian.Uint32(src[rp:]))\n\trp += 4\n\n\tval := src[rp : rp+valueLen]\n\trp += valueLen\n\n\tif ubr.LowerType != Unbounded {\n\t\tubr.Lower = val\n\t} else {\n\t\tubr.Upper = val\n\t\tif len(src[rp:]) > 0 {\n\t\t\treturn nil, fmt.Errorf(\"unexpected trailing bytes parsing range: %v\", len(src[rp:]))\n\t\t}\n\t\treturn ubr, nil\n\t}\n\n\tif ubr.UpperType != Unbounded {\n\t\tif len(src[rp:]) < 4 {\n\t\t\treturn nil, fmt.Errorf(\"too few bytes for size: %v\", src[rp:])\n\t\t}\n\t\tvalueLen := int(binary.BigEndian.Uint32(src[rp:]))\n\t\trp += 4\n\t\tubr.Upper = src[rp : rp+valueLen]\n\t\trp += valueLen\n\t}\n\n\tif len(src[rp:]) > 0 {\n\t\treturn nil, fmt.Errorf(\"unexpected trailing bytes parsing range: %v\", len(src[rp:]))\n\t}\n\n\treturn ubr, nil\n}\n\n// Range is a generic range type.\ntype Range[T any] struct {\n\tLower     T\n\tUpper     T\n\tLowerType BoundType\n\tUpperType BoundType\n\tValid     bool\n}\n\nfunc (r Range[T]) IsNull() bool {\n\treturn !r.Valid\n}\n\nfunc (r Range[T]) BoundTypes() (lower, upper BoundType) {\n\treturn r.LowerType, r.UpperType\n}\n\nfunc (r Range[T]) Bounds() (lower, upper any) {\n\treturn &r.Lower, &r.Upper\n}\n\nfunc (r *Range[T]) ScanNull() error {\n\t*r = Range[T]{}\n\treturn nil\n}\n\nfunc (r *Range[T]) ScanBounds() (lowerTarget, upperTarget any) {\n\treturn &r.Lower, &r.Upper\n}\n\nfunc (r *Range[T]) SetBoundTypes(lower, upper BoundType) error {\n\tif lower == Unbounded || lower == Empty {\n\t\tvar zero T\n\t\tr.Lower = zero\n\t}\n\tif upper == Unbounded || upper == Empty {\n\t\tvar zero T\n\t\tr.Upper = zero\n\t}\n\tr.LowerType = lower\n\tr.UpperType = upper\n\tr.Valid = true\n\treturn nil\n}\n"
  },
  {
    "path": "pgtype/range_codec.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\n// RangeValuer is a type that can be converted into a PostgreSQL range.\ntype RangeValuer interface {\n\t// IsNull returns true if the value is SQL NULL.\n\tIsNull() bool\n\n\t// BoundTypes returns the lower and upper bound types.\n\tBoundTypes() (lower, upper BoundType)\n\n\t// Bounds returns the lower and upper range values.\n\tBounds() (lower, upper any)\n}\n\n// RangeScanner is a type can be scanned from a PostgreSQL range.\ntype RangeScanner interface {\n\t// ScanNull sets the value to SQL NULL.\n\tScanNull() error\n\n\t// ScanBounds returns values usable as a scan target. The returned values may not be scanned if the range is empty or\n\t// the bound type is unbounded.\n\tScanBounds() (lowerTarget, upperTarget any)\n\n\t// SetBoundTypes sets the lower and upper bound types. ScanBounds will be called and the returned values scanned\n\t// (if appropriate) before SetBoundTypes is called. If the bound types are unbounded or empty this method must\n\t// also set the bound values.\n\tSetBoundTypes(lower, upper BoundType) error\n}\n\n// RangeCodec is a codec for any range type.\ntype RangeCodec struct {\n\tElementType *Type\n}\n\nfunc (c *RangeCodec) FormatSupported(format int16) bool {\n\treturn c.ElementType.Codec.FormatSupported(format)\n}\n\nfunc (c *RangeCodec) PreferredFormat() int16 {\n\tif c.FormatSupported(BinaryFormatCode) {\n\t\treturn BinaryFormatCode\n\t}\n\treturn TextFormatCode\n}\n\nfunc (c *RangeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(RangeValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn &encodePlanRangeCodecRangeValuerToBinary{rc: c, m: m}\n\tcase TextFormatCode:\n\t\treturn &encodePlanRangeCodecRangeValuerToText{rc: c, m: m}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanRangeCodecRangeValuerToBinary struct {\n\trc *RangeCodec\n\tm  *Map\n}\n\nfunc (plan *encodePlanRangeCodecRangeValuerToBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tgetter := value.(RangeValuer)\n\n\tif getter.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\tlowerType, upperType := getter.BoundTypes()\n\tlower, upper := getter.Bounds()\n\n\tvar rangeType byte\n\tswitch lowerType {\n\tcase Inclusive:\n\t\trangeType |= lowerInclusiveMask\n\tcase Unbounded:\n\t\trangeType |= lowerUnboundedMask\n\tcase Exclusive:\n\tcase Empty:\n\t\treturn append(buf, emptyMask), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown LowerType: %v\", lowerType)\n\t}\n\n\tswitch upperType {\n\tcase Inclusive:\n\t\trangeType |= upperInclusiveMask\n\tcase Unbounded:\n\t\trangeType |= upperUnboundedMask\n\tcase Exclusive:\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown UpperType: %v\", upperType)\n\t}\n\n\tbuf = append(buf, rangeType)\n\n\tif lowerType != Unbounded {\n\t\tif lower == nil {\n\t\t\treturn nil, fmt.Errorf(\"Lower cannot be NULL unless LowerType is Unbounded\")\n\t\t}\n\n\t\tsp := len(buf)\n\t\tbuf = pgio.AppendInt32(buf, -1)\n\n\t\tlowerPlan := plan.m.PlanEncode(plan.rc.ElementType.OID, BinaryFormatCode, lower)\n\t\tif lowerPlan == nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot encode %v as element of range\", lower)\n\t\t}\n\n\t\tbuf, err = lowerPlan.Encode(lower, buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode %v as element of range: %w\", lower, err)\n\t\t}\n\t\tif buf == nil {\n\t\t\treturn nil, fmt.Errorf(\"Lower cannot be NULL unless LowerType is Unbounded\")\n\t\t}\n\n\t\tpgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))\n\t}\n\n\tif upperType != Unbounded {\n\t\tif upper == nil {\n\t\t\treturn nil, fmt.Errorf(\"Upper cannot be NULL unless UpperType is Unbounded\")\n\t\t}\n\n\t\tsp := len(buf)\n\t\tbuf = pgio.AppendInt32(buf, -1)\n\n\t\tupperPlan := plan.m.PlanEncode(plan.rc.ElementType.OID, BinaryFormatCode, upper)\n\t\tif upperPlan == nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot encode %v as element of range\", upper)\n\t\t}\n\n\t\tbuf, err = upperPlan.Encode(upper, buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode %v as element of range: %w\", upper, err)\n\t\t}\n\t\tif buf == nil {\n\t\t\treturn nil, fmt.Errorf(\"Upper cannot be NULL unless UpperType is Unbounded\")\n\t\t}\n\n\t\tpgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))\n\t}\n\n\treturn buf, nil\n}\n\ntype encodePlanRangeCodecRangeValuerToText struct {\n\trc *RangeCodec\n\tm  *Map\n}\n\nfunc (plan *encodePlanRangeCodecRangeValuerToText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tgetter := value.(RangeValuer)\n\n\tif getter.IsNull() {\n\t\treturn nil, nil\n\t}\n\n\tlowerType, upperType := getter.BoundTypes()\n\tlower, upper := getter.Bounds()\n\n\tswitch lowerType {\n\tcase Exclusive, Unbounded:\n\t\tbuf = append(buf, '(')\n\tcase Inclusive:\n\t\tbuf = append(buf, '[')\n\tcase Empty:\n\t\treturn append(buf, \"empty\"...), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown lower bound type %v\", lowerType)\n\t}\n\n\tif lowerType != Unbounded {\n\t\tif lower == nil {\n\t\t\treturn nil, fmt.Errorf(\"Lower cannot be NULL unless LowerType is Unbounded\")\n\t\t}\n\n\t\tlowerPlan := plan.m.PlanEncode(plan.rc.ElementType.OID, TextFormatCode, lower)\n\t\tif lowerPlan == nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot encode %v as element of range\", lower)\n\t\t}\n\n\t\tbuf, err = lowerPlan.Encode(lower, buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode %v as element of range: %w\", lower, err)\n\t\t}\n\t\tif buf == nil {\n\t\t\treturn nil, fmt.Errorf(\"Lower cannot be NULL unless LowerType is Unbounded\")\n\t\t}\n\t}\n\n\tbuf = append(buf, ',')\n\n\tif upperType != Unbounded {\n\t\tif upper == nil {\n\t\t\treturn nil, fmt.Errorf(\"Upper cannot be NULL unless UpperType is Unbounded\")\n\t\t}\n\n\t\tupperPlan := plan.m.PlanEncode(plan.rc.ElementType.OID, TextFormatCode, upper)\n\t\tif upperPlan == nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot encode %v as element of range\", upper)\n\t\t}\n\n\t\tbuf, err = upperPlan.Encode(upper, buf)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode %v as element of range: %w\", upper, err)\n\t\t}\n\t\tif buf == nil {\n\t\t\treturn nil, fmt.Errorf(\"Upper cannot be NULL unless UpperType is Unbounded\")\n\t\t}\n\t}\n\n\tswitch upperType {\n\tcase Exclusive, Unbounded:\n\t\tbuf = append(buf, ')')\n\tcase Inclusive:\n\t\tbuf = append(buf, ']')\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown upper bound type %v\", upperType)\n\t}\n\n\treturn buf, nil\n}\n\nfunc (c *RangeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase RangeScanner:\n\t\t\treturn &scanPlanBinaryRangeToRangeScanner{rc: c, m: m}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase RangeScanner:\n\t\t\treturn &scanPlanTextRangeToRangeScanner{rc: c, m: m}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryRangeToRangeScanner struct {\n\trc *RangeCodec\n\tm  *Map\n}\n\nfunc (plan *scanPlanBinaryRangeToRangeScanner) Scan(src []byte, target any) error {\n\trangeScanner := (target).(RangeScanner)\n\n\tif src == nil {\n\t\treturn rangeScanner.ScanNull()\n\t}\n\n\tubr, err := parseUntypedBinaryRange(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif ubr.LowerType == Empty {\n\t\treturn rangeScanner.SetBoundTypes(ubr.LowerType, ubr.UpperType)\n\t}\n\n\tlowerTarget, upperTarget := rangeScanner.ScanBounds()\n\n\tif ubr.LowerType == Inclusive || ubr.LowerType == Exclusive {\n\t\tlowerPlan := plan.m.PlanScan(plan.rc.ElementType.OID, BinaryFormatCode, lowerTarget)\n\t\tif lowerPlan == nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element\", lowerTarget)\n\t\t}\n\n\t\terr = lowerPlan.Scan(ubr.Lower, lowerTarget)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element: %w\", lowerTarget, err)\n\t\t}\n\t}\n\n\tif ubr.UpperType == Inclusive || ubr.UpperType == Exclusive {\n\t\tupperPlan := plan.m.PlanScan(plan.rc.ElementType.OID, BinaryFormatCode, upperTarget)\n\t\tif upperPlan == nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element\", upperTarget)\n\t\t}\n\n\t\terr = upperPlan.Scan(ubr.Upper, upperTarget)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element: %w\", upperTarget, err)\n\t\t}\n\t}\n\n\treturn rangeScanner.SetBoundTypes(ubr.LowerType, ubr.UpperType)\n}\n\ntype scanPlanTextRangeToRangeScanner struct {\n\trc *RangeCodec\n\tm  *Map\n}\n\nfunc (plan *scanPlanTextRangeToRangeScanner) Scan(src []byte, target any) error {\n\trangeScanner := (target).(RangeScanner)\n\n\tif src == nil {\n\t\treturn rangeScanner.ScanNull()\n\t}\n\n\tutr, err := parseUntypedTextRange(string(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif utr.LowerType == Empty {\n\t\treturn rangeScanner.SetBoundTypes(utr.LowerType, utr.UpperType)\n\t}\n\n\tlowerTarget, upperTarget := rangeScanner.ScanBounds()\n\n\tif utr.LowerType == Inclusive || utr.LowerType == Exclusive {\n\t\tlowerPlan := plan.m.PlanScan(plan.rc.ElementType.OID, TextFormatCode, lowerTarget)\n\t\tif lowerPlan == nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element\", lowerTarget)\n\t\t}\n\n\t\terr = lowerPlan.Scan([]byte(utr.Lower), lowerTarget)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element: %w\", lowerTarget, err)\n\t\t}\n\t}\n\n\tif utr.UpperType == Inclusive || utr.UpperType == Exclusive {\n\t\tupperPlan := plan.m.PlanScan(plan.rc.ElementType.OID, TextFormatCode, upperTarget)\n\t\tif upperPlan == nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element\", upperTarget)\n\t\t}\n\n\t\terr = upperPlan.Scan([]byte(utr.Upper), upperTarget)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot scan into %v from range element: %w\", upperTarget, err)\n\t\t}\n\t}\n\n\treturn rangeScanner.SetBoundTypes(utr.LowerType, utr.UpperType)\n}\n\nfunc (c *RangeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn string(src), nil\n\tcase BinaryFormatCode:\n\t\tbuf := make([]byte, len(src))\n\t\tcopy(buf, src)\n\t\treturn buf, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n\nfunc (c *RangeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar r Range[any]\n\terr := c.PlanScan(m, oid, format, &r).Scan(src, &r)\n\treturn r, err\n}\n"
  },
  {
    "path": "pgtype/range_codec_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRangeCodecTranscode(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int4range\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Range[pgtype.Int4]{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true},\n\t\t\tResult: new(pgtype.Range[pgtype.Int4]),\n\t\t\tTest:   isExpectedEq(pgtype.Range[pgtype.Int4]{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Range[pgtype.Int4]{\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Int4{Int32: 5, Valid: true},\n\t\t\t\tUpperType: pgtype.Exclusive, Valid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Range[pgtype.Int4]),\n\t\t\tTest: isExpectedEq(pgtype.Range[pgtype.Int4]{\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Int4{Int32: 5, Valid: true},\n\t\t\t\tUpperType: pgtype.Exclusive, Valid: true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Range[pgtype.Int4]{}, Result: new(pgtype.Range[pgtype.Int4]), Test: isExpectedEq(pgtype.Range[pgtype.Int4]{})},\n\t\t{Param: nil, Result: new(pgtype.Range[pgtype.Int4]), Test: isExpectedEq(pgtype.Range[pgtype.Int4]{})},\n\t})\n}\n\nfunc TestRangeCodecTranscodeCompatibleRangeElementTypes(t *testing.T) {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)\")\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, \"numrange\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Range[pgtype.Float8]{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true},\n\t\t\tResult: new(pgtype.Range[pgtype.Float8]),\n\t\t\tTest:   isExpectedEq(pgtype.Range[pgtype.Float8]{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam: pgtype.Range[pgtype.Float8]{\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tLower:     pgtype.Float8{Float64: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Float8{Float64: 5, Valid: true},\n\t\t\t\tUpperType: pgtype.Exclusive, Valid: true,\n\t\t\t},\n\t\t\tResult: new(pgtype.Range[pgtype.Float8]),\n\t\t\tTest: isExpectedEq(pgtype.Range[pgtype.Float8]{\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tLower:     pgtype.Float8{Float64: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Float8{Float64: 5, Valid: true},\n\t\t\t\tUpperType: pgtype.Exclusive, Valid: true,\n\t\t\t}),\n\t\t},\n\t\t{Param: pgtype.Range[pgtype.Float8]{}, Result: new(pgtype.Range[pgtype.Float8]), Test: isExpectedEq(pgtype.Range[pgtype.Float8]{})},\n\t\t{Param: nil, Result: new(pgtype.Range[pgtype.Float8]), Test: isExpectedEq(pgtype.Range[pgtype.Float8]{})},\n\t})\n}\n\nfunc TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar r pgtype.Range[pgtype.Int4]\n\n\t\terr := conn.QueryRow(context.Background(), `select '[1,5)'::int4range`).Scan(&r)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(\n\t\t\tt,\n\t\t\tpgtype.Range[pgtype.Int4]{\n\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Int4{Int32: 5, Valid: true},\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\tValid:     true,\n\t\t\t},\n\t\t\tr,\n\t\t)\n\n\t\terr = conn.QueryRow(ctx, `select '[1,)'::int4range`).Scan(&r)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(\n\t\t\tt,\n\t\t\tpgtype.Range[pgtype.Int4]{\n\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Int4{},\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tUpperType: pgtype.Unbounded,\n\t\t\t\tValid:     true,\n\t\t\t},\n\t\t\tr,\n\t\t)\n\n\t\terr = conn.QueryRow(ctx, `select 'empty'::int4range`).Scan(&r)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(\n\t\t\tt,\n\t\t\tpgtype.Range[pgtype.Int4]{\n\t\t\t\tLower:     pgtype.Int4{},\n\t\t\t\tUpper:     pgtype.Int4{},\n\t\t\t\tLowerType: pgtype.Empty,\n\t\t\t\tUpperType: pgtype.Empty,\n\t\t\t\tValid:     true,\n\t\t\t},\n\t\t\tr,\n\t\t)\n\t})\n}\n\nfunc TestRangeCodecDecodeValue(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tfor _, tt := range []struct {\n\t\t\tsql      string\n\t\t\texpected any\n\t\t}{\n\t\t\t{\n\t\t\t\tsql: `select '[1,5)'::int4range`,\n\t\t\t\texpected: pgtype.Range[any]{\n\t\t\t\t\tLower:     int32(1),\n\t\t\t\t\tUpper:     int32(5),\n\t\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\t\tValid:     true,\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(tt.sql, func(t *testing.T) {\n\t\t\t\trows, err := conn.Query(ctx, tt.sql)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\tvalues, err := rows.Values()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, values, 1)\n\t\t\t\t\trequire.Equal(t, tt.expected, values[0])\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, rows.Err())\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/range_test.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestParseUntypedTextRange(t *testing.T) {\n\ttests := []struct {\n\t\tsrc    string\n\t\tresult untypedTextRange\n\t\terr    error\n\t}{\n\t\t{\n\t\t\tsrc:    `[1,2)`,\n\t\t\tresult: untypedTextRange{Lower: \"1\", Upper: \"2\", LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[1,2]`,\n\t\t\tresult: untypedTextRange{Lower: \"1\", Upper: \"2\", LowerType: Inclusive, UpperType: Inclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `(1,3)`,\n\t\t\tresult: untypedTextRange{Lower: \"1\", Upper: \"3\", LowerType: Exclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    ` [1,2) `,\n\t\t\tresult: untypedTextRange{Lower: \"1\", Upper: \"2\", LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[ foo , bar )`,\n\t\t\tresult: untypedTextRange{Lower: \" foo \", Upper: \" bar \", LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[\"foo\",\"bar\")`,\n\t\t\tresult: untypedTextRange{Lower: \"foo\", Upper: \"bar\", LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[\"f\"\"oo\",\"b\"\"ar\")`,\n\t\t\tresult: untypedTextRange{Lower: `f\"oo`, Upper: `b\"ar`, LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[\"f\"\"oo\",\"b\"\"ar\")`,\n\t\t\tresult: untypedTextRange{Lower: `f\"oo`, Upper: `b\"ar`, LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[\"\",\"bar\")`,\n\t\t\tresult: untypedTextRange{Lower: ``, Upper: `bar`, LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `[f\\\"oo\\,,b\\\\ar\\))`,\n\t\t\tresult: untypedTextRange{Lower: `f\"oo,`, Upper: `b\\ar)`, LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    `empty`,\n\t\t\tresult: untypedTextRange{Lower: \"\", Upper: \"\", LowerType: Empty, UpperType: Empty},\n\t\t\terr:    nil,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tr, err := parseUntypedTextRange(tt.src)\n\t\tif err != tt.err {\n\t\t\tt.Errorf(\"%d. `%v`: expected err %v, got %v\", i, tt.src, tt.err, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif r.LowerType != tt.result.LowerType {\n\t\t\tt.Errorf(\"%d. `%v`: expected result lower type %v, got %v\", i, tt.src, string(tt.result.LowerType), string(r.LowerType))\n\t\t}\n\n\t\tif r.UpperType != tt.result.UpperType {\n\t\t\tt.Errorf(\"%d. `%v`: expected result upper type %v, got %v\", i, tt.src, string(tt.result.UpperType), string(r.UpperType))\n\t\t}\n\n\t\tif r.Lower != tt.result.Lower {\n\t\t\tt.Errorf(\"%d. `%v`: expected result lower %v, got %v\", i, tt.src, tt.result.Lower, r.Lower)\n\t\t}\n\n\t\tif r.Upper != tt.result.Upper {\n\t\t\tt.Errorf(\"%d. `%v`: expected result upper %v, got %v\", i, tt.src, tt.result.Upper, r.Upper)\n\t\t}\n\t}\n}\n\nfunc TestParseUntypedBinaryRange(t *testing.T) {\n\ttests := []struct {\n\t\tsrc    []byte\n\t\tresult untypedBinaryRange\n\t\terr    error\n\t}{\n\t\t{\n\t\t\tsrc:    []byte{0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5},\n\t\t\tresult: untypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Exclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{1},\n\t\t\tresult: untypedBinaryRange{Lower: nil, Upper: nil, LowerType: Empty, UpperType: Empty},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{2, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5},\n\t\t\tresult: untypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Inclusive, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{4, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5},\n\t\t\tresult: untypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Exclusive, UpperType: Inclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{6, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5},\n\t\t\tresult: untypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Inclusive, UpperType: Inclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{8, 0, 0, 0, 2, 0, 5},\n\t\t\tresult: untypedBinaryRange{Lower: nil, Upper: []byte{0, 5}, LowerType: Unbounded, UpperType: Exclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{12, 0, 0, 0, 2, 0, 5},\n\t\t\tresult: untypedBinaryRange{Lower: nil, Upper: []byte{0, 5}, LowerType: Unbounded, UpperType: Inclusive},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{16, 0, 0, 0, 2, 0, 4},\n\t\t\tresult: untypedBinaryRange{Lower: []byte{0, 4}, Upper: nil, LowerType: Exclusive, UpperType: Unbounded},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{18, 0, 0, 0, 2, 0, 4},\n\t\t\tresult: untypedBinaryRange{Lower: []byte{0, 4}, Upper: nil, LowerType: Inclusive, UpperType: Unbounded},\n\t\t\terr:    nil,\n\t\t},\n\t\t{\n\t\t\tsrc:    []byte{24},\n\t\t\tresult: untypedBinaryRange{Lower: nil, Upper: nil, LowerType: Unbounded, UpperType: Unbounded},\n\t\t\terr:    nil,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tr, err := parseUntypedBinaryRange(tt.src)\n\t\tif err != tt.err {\n\t\t\tt.Errorf(\"%d. `%v`: expected err %v, got %v\", i, tt.src, tt.err, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif r.LowerType != tt.result.LowerType {\n\t\t\tt.Errorf(\"%d. `%v`: expected result lower type %v, got %v\", i, tt.src, string(tt.result.LowerType), string(r.LowerType))\n\t\t}\n\n\t\tif r.UpperType != tt.result.UpperType {\n\t\t\tt.Errorf(\"%d. `%v`: expected result upper type %v, got %v\", i, tt.src, string(tt.result.UpperType), string(r.UpperType))\n\t\t}\n\n\t\tif !bytes.Equal(r.Lower, tt.result.Lower) {\n\t\t\tt.Errorf(\"%d. `%v`: expected result lower %v, got %v\", i, tt.src, tt.result.Lower, r.Lower)\n\t\t}\n\n\t\tif !bytes.Equal(r.Upper, tt.result.Upper) {\n\t\t\tt.Errorf(\"%d. `%v`: expected result upper %v, got %v\", i, tt.src, tt.result.Upper, r.Upper)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/record_codec.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n)\n\n// ArrayGetter is a type that can be converted into a PostgreSQL array.\n\n// RecordCodec is a codec for the generic PostgreSQL record type such as is created with the \"row\" function. Record can\n// only decode the binary format. The text format output format from PostgreSQL does not include type information and\n// is therefore impossible to decode. Encoding is impossible because PostgreSQL does not support input of generic\n// records.\ntype RecordCodec struct{}\n\nfunc (RecordCodec) FormatSupported(format int16) bool {\n\treturn format == BinaryFormatCode\n}\n\nfunc (RecordCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (RecordCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\treturn nil\n}\n\nfunc (RecordCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tif format == BinaryFormatCode {\n\t\tswitch target.(type) {\n\t\tcase CompositeIndexScanner:\n\t\t\treturn &scanPlanBinaryRecordToCompositeIndexScanner{m: m}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryRecordToCompositeIndexScanner struct {\n\tm *Map\n}\n\nfunc (plan *scanPlanBinaryRecordToCompositeIndexScanner) Scan(src []byte, target any) error {\n\ttargetScanner := (target).(CompositeIndexScanner)\n\n\tif src == nil {\n\t\treturn targetScanner.ScanNull()\n\t}\n\n\tscanner := NewCompositeBinaryScanner(plan.m, src)\n\tfor i := 0; scanner.Next(); i++ {\n\t\tfieldTarget := targetScanner.ScanIndex(i)\n\t\tif fieldTarget != nil {\n\t\t\tfieldPlan := plan.m.PlanScan(scanner.OID(), BinaryFormatCode, fieldTarget)\n\t\t\tif fieldPlan == nil {\n\t\t\t\treturn fmt.Errorf(\"unable to scan OID %d in binary format into %v\", scanner.OID(), fieldTarget)\n\t\t\t}\n\n\t\t\terr := fieldPlan.Scan(scanner.Bytes(), fieldTarget)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (RecordCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn string(src), nil\n\tcase BinaryFormatCode:\n\t\tbuf := make([]byte, len(src))\n\t\tcopy(buf, src)\n\t\treturn buf, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n\nfunc (RecordCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tswitch format {\n\tcase TextFormatCode:\n\t\treturn string(src), nil\n\tcase BinaryFormatCode:\n\t\tscanner := NewCompositeBinaryScanner(m, src)\n\t\tvalues := make([]any, scanner.FieldCount())\n\t\tfor i := 0; scanner.Next(); i++ {\n\t\t\tvar v any\n\t\t\tfieldPlan := m.PlanScan(scanner.OID(), BinaryFormatCode, &v)\n\t\t\tif fieldPlan == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to scan OID %d in binary format into %v\", scanner.OID(), v)\n\t\t\t}\n\n\t\t\terr := fieldPlan.Scan(scanner.Bytes(), &v)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tvalues[i] = v\n\t\t}\n\n\t\tif err := scanner.Err(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn values, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown format code %d\", format)\n\t}\n}\n"
  },
  {
    "path": "pgtype/record_codec_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRecordCodec(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar a string\n\t\tvar b int32\n\t\terr := conn.QueryRow(ctx, `select row('foo'::text, 42::int4)`).Scan(pgtype.CompositeFields{&a, &b})\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, \"foo\", a)\n\t\trequire.Equal(t, int32(42), b)\n\t})\n}\n\nfunc TestRecordCodecDecodeValue(t *testing.T) {\n\tskipCockroachDB(t, \"Server converts row int4 to int8\")\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tfor _, tt := range []struct {\n\t\t\tsql      string\n\t\t\texpected any\n\t\t}{\n\t\t\t{\n\t\t\t\tsql:      `select row()`,\n\t\t\t\texpected: []any{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select row('foo'::text, 42::int4)`,\n\t\t\t\texpected: []any{\"foo\", int32(42)},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select row(100.0::float4, 1.09::float4)`,\n\t\t\t\texpected: []any{float32(100), float32(1.09)},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select row('foo'::text, array[1, 2, null, 4]::int4[], 42::int4)`,\n\t\t\t\texpected: []any{\"foo\", []any{int32(1), int32(2), nil, int32(4)}, int32(42)},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select row(null)`,\n\t\t\t\texpected: []any{nil},\n\t\t\t},\n\t\t\t{\n\t\t\t\tsql:      `select null::record`,\n\t\t\t\texpected: nil,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(tt.sql, func(t *testing.T) {\n\t\t\t\trows, err := conn.Query(context.Background(), tt.sql)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tdefer rows.Close()\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\tvalues, err := rows.Values()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, values, 1)\n\t\t\t\t\trequire.Equal(t, tt.expected, values[0])\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, rows.Err())\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/register_default_pg_types.go",
    "content": "//go:build !nopgxregisterdefaulttypes\n\npackage pgtype\n\nfunc registerDefaultPgTypeVariants[T any](m *Map, name string) {\n\tarrayName := \"_\" + name\n\n\tvar value T\n\tm.RegisterDefaultPgType(value, name)  // T\n\tm.RegisterDefaultPgType(&value, name) // *T\n\n\tvar sliceT []T\n\tm.RegisterDefaultPgType(sliceT, arrayName)  // []T\n\tm.RegisterDefaultPgType(&sliceT, arrayName) // *[]T\n\n\tvar slicePtrT []*T\n\tm.RegisterDefaultPgType(slicePtrT, arrayName)  // []*T\n\tm.RegisterDefaultPgType(&slicePtrT, arrayName) // *[]*T\n\n\tvar arrayOfT Array[T]\n\tm.RegisterDefaultPgType(arrayOfT, arrayName)  // Array[T]\n\tm.RegisterDefaultPgType(&arrayOfT, arrayName) // *Array[T]\n\n\tvar arrayOfPtrT Array[*T]\n\tm.RegisterDefaultPgType(arrayOfPtrT, arrayName)  // Array[*T]\n\tm.RegisterDefaultPgType(&arrayOfPtrT, arrayName) // *Array[*T]\n\n\tvar flatArrayOfT FlatArray[T]\n\tm.RegisterDefaultPgType(flatArrayOfT, arrayName)  // FlatArray[T]\n\tm.RegisterDefaultPgType(&flatArrayOfT, arrayName) // *FlatArray[T]\n\n\tvar flatArrayOfPtrT FlatArray[*T]\n\tm.RegisterDefaultPgType(flatArrayOfPtrT, arrayName)  // FlatArray[*T]\n\tm.RegisterDefaultPgType(&flatArrayOfPtrT, arrayName) // *FlatArray[*T]\n}\n"
  },
  {
    "path": "pgtype/register_default_pg_types_disabled.go",
    "content": "//go:build nopgxregisterdefaulttypes\n\npackage pgtype\n\nfunc registerDefaultPgTypeVariants[T any](m *Map, name string) {\n}\n"
  },
  {
    "path": "pgtype/text.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"fmt\"\n)\n\ntype TextScanner interface {\n\tScanText(v Text) error\n}\n\ntype TextValuer interface {\n\tTextValue() (Text, error)\n}\n\ntype Text struct {\n\tString string\n\tValid  bool\n}\n\n// ScanText implements the [TextScanner] interface.\nfunc (t *Text) ScanText(v Text) error {\n\t*t = v\n\treturn nil\n}\n\n// TextValue implements the [TextValuer] interface.\nfunc (t Text) TextValue() (Text, error) {\n\treturn t, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Text) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Text{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\t*dst = Text{String: src, Valid: true}\n\t\treturn nil\n\tcase []byte:\n\t\t*dst = Text{String: string(src), Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Text) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\treturn src.String, nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Text) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\treturn json.Marshal(src.String)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Text) UnmarshalJSON(b []byte) error {\n\tvar s *string\n\terr := json.Unmarshal(b, &s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif s == nil {\n\t\t*dst = Text{}\n\t} else {\n\t\t*dst = Text{String: *s, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype TextCodec struct{}\n\nfunc (TextCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (TextCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n\nfunc (TextCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase TextFormatCode, BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase string:\n\t\t\treturn encodePlanTextCodecString{}\n\t\tcase []byte:\n\t\t\treturn encodePlanTextCodecByteSlice{}\n\t\tcase TextValuer:\n\t\t\treturn encodePlanTextCodecTextValuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanTextCodecString struct{}\n\nfunc (encodePlanTextCodecString) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ts := value.(string)\n\tbuf = append(buf, s...)\n\treturn buf, nil\n}\n\ntype encodePlanTextCodecByteSlice struct{}\n\nfunc (encodePlanTextCodecByteSlice) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ts := value.([]byte)\n\tbuf = append(buf, s...)\n\treturn buf, nil\n}\n\ntype encodePlanTextCodecStringer struct{}\n\nfunc (encodePlanTextCodecStringer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ts := value.(fmt.Stringer)\n\tbuf = append(buf, s.String()...)\n\treturn buf, nil\n}\n\ntype encodePlanTextCodecTextValuer struct{}\n\nfunc (encodePlanTextCodecTextValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ttext, err := value.(TextValuer).TextValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !text.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, text.String...)\n\treturn buf, nil\n}\n\nfunc (TextCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase TextFormatCode, BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *string:\n\t\t\treturn scanPlanTextAnyToString{}\n\t\tcase *[]byte:\n\t\t\treturn scanPlanAnyToNewByteSlice{}\n\t\tcase BytesScanner:\n\t\t\treturn scanPlanAnyToByteScanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanTextAnyToTextScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c TextCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn c.DecodeValue(m, oid, format, src)\n}\n\nfunc (c TextCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\treturn string(src), nil\n}\n\ntype scanPlanTextAnyToString struct{}\n\nfunc (scanPlanTextAnyToString) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tp := (dst).(*string)\n\t*p = string(src)\n\n\treturn nil\n}\n\ntype scanPlanAnyToNewByteSlice struct{}\n\nfunc (scanPlanAnyToNewByteSlice) Scan(src []byte, dst any) error {\n\tp := (dst).(*[]byte)\n\tif src == nil {\n\t\t*p = nil\n\t} else {\n\t\t*p = make([]byte, len(src))\n\t\tcopy(*p, src)\n\t}\n\n\treturn nil\n}\n\ntype scanPlanAnyToByteScanner struct{}\n\nfunc (scanPlanAnyToByteScanner) Scan(src []byte, dst any) error {\n\tp := (dst).(BytesScanner)\n\treturn p.ScanBytes(src)\n}\n\ntype scanPlanTextAnyToTextScanner struct{}\n\nfunc (scanPlanTextAnyToTextScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanText(Text{})\n\t}\n\n\treturn scanner.ScanText(Text{String: string(src), Valid: true})\n}\n"
  },
  {
    "path": "pgtype/text_format_only_codec.go",
    "content": "package pgtype\n\ntype TextFormatOnlyCodec struct {\n\tCodec\n}\n\nfunc (c *TextFormatOnlyCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode && c.Codec.FormatSupported(format)\n}\n\nfunc (TextFormatOnlyCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n"
  },
  {
    "path": "pgtype/text_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype someFmtStringer struct{}\n\nfunc (someFmtStringer) String() string {\n\treturn \"some fmt.Stringer\"\n}\n\nfunc TestTextCodec(t *testing.T) {\n\tfor _, pgTypeName := range []string{\"text\", \"varchar\"} {\n\t\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, pgTypeName, []pgxtest.ValueRoundTripTest{\n\t\t\t{\n\t\t\t\tParam:  pgtype.Text{String: \"\", Valid: true},\n\t\t\t\tResult: new(pgtype.Text),\n\t\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"\", Valid: true}),\n\t\t\t},\n\t\t\t{\n\t\t\t\tParam:  pgtype.Text{String: \"foo\", Valid: true},\n\t\t\t\tResult: new(pgtype.Text),\n\t\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"foo\", Valid: true}),\n\t\t\t},\n\t\t\t{Param: nil, Result: new(pgtype.Text), Test: isExpectedEq(pgtype.Text{})},\n\t\t\t{Param: \"foo\", Result: new(string), Test: isExpectedEq(\"foo\")},\n\t\t\t{Param: someFmtStringer{}, Result: new(string), Test: isExpectedEq(\"some fmt.Stringer\")},\n\t\t})\n\t}\n}\n\n// name is PostgreSQL's special 63-byte data type, used for identifiers like table names.  The pg_class.relname column\n// is a good example of where the name data type is used.\n//\n// TextCodec does not do length checking. Inputting a longer name into PostgreSQL will result in silent truncation to\n// 63 bytes.\n//\n// Length checking would be possible with a Codec specialized for \"name\" but it would be perfect because a\n// custom-compiled PostgreSQL could have set NAMEDATALEN to a different value rather than the default 63.\n//\n// So this is simply a smoke test of the name type.\nfunc TestTextCodecName(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"name\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Text{String: \"\", Valid: true},\n\t\t\tResult: new(pgtype.Text),\n\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"\", Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Text{String: \"foo\", Valid: true},\n\t\t\tResult: new(pgtype.Text),\n\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"foo\", Valid: true}),\n\t\t},\n\t\t{Param: nil, Result: new(pgtype.Text), Test: isExpectedEq(pgtype.Text{})},\n\t\t{Param: \"foo\", Result: new(string), Test: isExpectedEq(\"foo\")},\n\t})\n}\n\n// Test fixed length char types like char(3)\nfunc TestTextCodecBPChar(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not properly handle bpchar with multi-byte character\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"char(3)\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Text{String: \"a  \", Valid: true},\n\t\t\tResult: new(pgtype.Text),\n\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"a  \", Valid: true}),\n\t\t},\n\t\t{Param: nil, Result: new(pgtype.Text), Test: isExpectedEq(pgtype.Text{})},\n\t\t{Param: \"   \", Result: new(string), Test: isExpectedEq(\"   \")},\n\t\t{Param: \"\", Result: new(string), Test: isExpectedEq(\"   \")},\n\t\t{Param: \" 嗨 \", Result: new(string), Test: isExpectedEq(\" 嗨 \")},\n\t})\n}\n\n// ACLItem is used for PostgreSQL's aclitem data type. A sample aclitem\n// might look like this:\n//\n//\tpostgres=arwdDxt/postgres\n//\n// Note, however, that because the user/role name part of an aclitem is\n// an identifier, it follows all the usual formatting rules for SQL\n// identifiers: if it contains spaces and other special characters,\n// it should appear in double-quotes:\n//\n//\tpostgres=arwdDxt/\"role with spaces\"\n//\n// It only supports the text format.\nfunc TestTextCodecACLItem(t *testing.T) {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support type aclitem\")\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, \"aclitem\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Text{String: \"postgres=arwdDxt/postgres\", Valid: true},\n\t\t\tResult: new(pgtype.Text),\n\t\t\tTest:   isExpectedEq(pgtype.Text{String: \"postgres=arwdDxt/postgres\", Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Text{}, Result: new(pgtype.Text), Test: isExpectedEq(pgtype.Text{})},\n\t\t{Param: nil, Result: new(pgtype.Text), Test: isExpectedEq(pgtype.Text{})},\n\t})\n}\n\nfunc TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support type aclitem\")\n\n\t\t// The tricky test user, below, has to actually exist so that it can be used in a test\n\t\t// of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.\n\t\troleWithSpecialCharacters := ` tricky, ' } \" \\ test user `\n\n\t\tcommandTag, err := conn.Exec(ctx, `select * from pg_roles where rolname = $1`, roleWithSpecialCharacters)\n\t\trequire.NoError(t, err)\n\n\t\tif commandTag.RowsAffected() == 0 {\n\t\t\tt.Skipf(\"Role with special characters does not exist.\")\n\t\t}\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, \"aclitem\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Text{String: `postgres=arwdDxt/\" tricky, ' } \"\" \\ test user \"`, Valid: true},\n\t\t\tResult: new(pgtype.Text),\n\t\t\tTest:   isExpectedEq(pgtype.Text{String: `postgres=arwdDxt/\" tricky, ' } \"\" \\ test user \"`, Valid: true}),\n\t\t},\n\t})\n}\n\nfunc TestTextMarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Text\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Text{String: \"\"}, result: \"null\"},\n\t\t{source: pgtype.Text{String: \"a\", Valid: true}, result: \"\\\"a\\\"\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestTextUnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Text\n\t}{\n\t\t{source: \"null\", result: pgtype.Text{String: \"\"}},\n\t\t{source: \"\\\"a\\\"\", result: pgtype.Text{String: \"a\", Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Text\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif r != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/tid.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype TIDScanner interface {\n\tScanTID(v TID) error\n}\n\ntype TIDValuer interface {\n\tTIDValue() (TID, error)\n}\n\n// TID is PostgreSQL's Tuple Identifier type.\n//\n// When one does\n//\n//\tselect ctid, * from some_table;\n//\n// it is the data type of the ctid hidden system column.\n//\n// It is currently implemented as a pair unsigned two byte integers.\n// Its conversion functions can be found in src/backend/utils/adt/tid.c\n// in the PostgreSQL sources.\ntype TID struct {\n\tBlockNumber  uint32\n\tOffsetNumber uint16\n\tValid        bool\n}\n\n// ScanTID implements the [TIDScanner] interface.\nfunc (b *TID) ScanTID(v TID) error {\n\t*b = v\n\treturn nil\n}\n\n// TIDValue implements the [TIDValuer] interface.\nfunc (b TID) TIDValue() (TID, error) {\n\treturn b, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *TID) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = TID{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToTIDScanner{}.Scan([]byte(src), dst)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src TID) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := TIDCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype TIDCodec struct{}\n\nfunc (TIDCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (TIDCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (TIDCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(TIDValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanTIDCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanTIDCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanTIDCodecBinary struct{}\n\nfunc (encodePlanTIDCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ttid, err := value.(TIDValuer).TIDValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !tid.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendUint32(buf, tid.BlockNumber)\n\tbuf = pgio.AppendUint16(buf, tid.OffsetNumber)\n\treturn buf, nil\n}\n\ntype encodePlanTIDCodecText struct{}\n\nfunc (encodePlanTIDCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\ttid, err := value.(TIDValuer).TIDValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !tid.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, fmt.Sprintf(`(%d,%d)`, tid.BlockNumber, tid.OffsetNumber)...)\n\treturn buf, nil\n}\n\nfunc (TIDCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TIDScanner:\n\t\t\treturn scanPlanBinaryTIDToTIDScanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryTIDToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TIDScanner:\n\t\t\treturn scanPlanTextAnyToTIDScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryTIDToTIDScanner struct{}\n\nfunc (scanPlanBinaryTIDToTIDScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TIDScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTID(TID{})\n\t}\n\n\tif len(src) != 6 {\n\t\treturn fmt.Errorf(\"invalid length for tid: %v\", len(src))\n\t}\n\n\treturn scanner.ScanTID(TID{\n\t\tBlockNumber:  binary.BigEndian.Uint32(src),\n\t\tOffsetNumber: binary.BigEndian.Uint16(src[4:]),\n\t\tValid:        true,\n\t})\n}\n\ntype scanPlanBinaryTIDToTextScanner struct{}\n\nfunc (scanPlanBinaryTIDToTextScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanText(Text{})\n\t}\n\n\tif len(src) != 6 {\n\t\treturn fmt.Errorf(\"invalid length for tid: %v\", len(src))\n\t}\n\n\tblockNumber := binary.BigEndian.Uint32(src)\n\toffsetNumber := binary.BigEndian.Uint16(src[4:])\n\n\treturn scanner.ScanText(Text{\n\t\tString: fmt.Sprintf(`(%d,%d)`, blockNumber, offsetNumber),\n\t\tValid:  true,\n\t})\n}\n\ntype scanPlanTextAnyToTIDScanner struct{}\n\nfunc (scanPlanTextAnyToTIDScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TIDScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTID(TID{})\n\t}\n\n\tif len(src) < 5 {\n\t\treturn fmt.Errorf(\"invalid length for tid: %v\", len(src))\n\t}\n\n\tblock, offset, found := strings.Cut(string(src[1:len(src)-1]), \",\")\n\tif !found {\n\t\treturn fmt.Errorf(\"invalid format for tid\")\n\t}\n\n\tblockNumber, err := strconv.ParseUint(block, 10, 32)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\toffsetNumber, err := strconv.ParseUint(offset, 10, 16)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanTID(TID{BlockNumber: uint32(blockNumber), OffsetNumber: uint16(offsetNumber), Valid: true})\n}\n\nfunc (c TIDCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c TIDCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar tid TID\n\terr := codecScan(c, m, oid, format, src, &tid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn tid, nil\n}\n"
  },
  {
    "path": "pgtype/tid_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestTIDCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support type tid\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"tid\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true},\n\t\t\tResult: new(pgtype.TID),\n\t\t\tTest:   isExpectedEq(pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Valid: true},\n\t\t\tResult: new(pgtype.TID),\n\t\t\tTest:   isExpectedEq(pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true},\n\t\t\tResult: new(string),\n\t\t\tTest:   isExpectedEq(\"(42,43)\"),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Valid: true},\n\t\t\tResult: new(string),\n\t\t\tTest:   isExpectedEq(\"(4294967295,65535)\"),\n\t\t},\n\t\t{Param: pgtype.TID{}, Result: new(pgtype.TID), Test: isExpectedEq(pgtype.TID{})},\n\t\t{Param: nil, Result: new(pgtype.TID), Test: isExpectedEq(pgtype.TID{})},\n\t})\n}\n"
  },
  {
    "path": "pgtype/time.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype TimeScanner interface {\n\tScanTime(v Time) error\n}\n\ntype TimeValuer interface {\n\tTimeValue() (Time, error)\n}\n\n// Time represents the PostgreSQL time type. The PostgreSQL time is a time of day without time zone.\n//\n// Time is represented as the number of microseconds since midnight in the same way that PostgreSQL does. Other time and\n// date types in pgtype can use time.Time as the underlying representation. However, pgtype.Time type cannot due to\n// needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day.\n//\n// The time with time zone type is not supported. Use of time with time zone is discouraged by the PostgreSQL documentation.\ntype Time struct {\n\tMicroseconds int64 // Number of microseconds since midnight\n\tValid        bool\n}\n\n// ScanTime implements the [TimeScanner] interface.\nfunc (t *Time) ScanTime(v Time) error {\n\t*t = v\n\treturn nil\n}\n\n// TimeValue implements the [TimeValuer] interface.\nfunc (t Time) TimeValue() (Time, error) {\n\treturn t, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (t *Time) Scan(src any) error {\n\tif src == nil {\n\t\t*t = Time{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\terr := scanPlanTextAnyToTimeScanner{}.Scan([]byte(src), t)\n\t\tif err != nil {\n\t\t\tt.Microseconds = 0\n\t\t\tt.Valid = false\n\t\t}\n\t\treturn err\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (t Time) Value() (driver.Value, error) {\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := TimeCodec{}.PlanEncode(nil, 0, TextFormatCode, t).Encode(t, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn string(buf), err\n}\n\ntype TimeCodec struct{}\n\nfunc (TimeCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (TimeCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (TimeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(TimeValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanTimeCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanTimeCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanTimeCodecBinary struct{}\n\nfunc (encodePlanTimeCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tt, err := value.(TimeValuer).TimeValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn pgio.AppendInt64(buf, t.Microseconds), nil\n}\n\ntype encodePlanTimeCodecText struct{}\n\nfunc (encodePlanTimeCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tt, err := value.(TimeValuer).TimeValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\tusec := t.Microseconds\n\thours := usec / microsecondsPerHour\n\tusec -= hours * microsecondsPerHour\n\tminutes := usec / microsecondsPerMinute\n\tusec -= minutes * microsecondsPerMinute\n\tseconds := usec / microsecondsPerSecond\n\tusec -= seconds * microsecondsPerSecond\n\n\ts := fmt.Sprintf(\"%02d:%02d:%02d.%06d\", hours, minutes, seconds, usec)\n\n\treturn append(buf, s...), nil\n}\n\nfunc (TimeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TimeScanner:\n\t\t\treturn scanPlanBinaryTimeToTimeScanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryTimeToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TimeScanner:\n\t\t\treturn scanPlanTextAnyToTimeScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryTimeToTimeScanner struct{}\n\nfunc (scanPlanBinaryTimeToTimeScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TimeScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTime(Time{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for time: %v\", len(src))\n\t}\n\n\tusec := int64(binary.BigEndian.Uint64(src))\n\n\treturn scanner.ScanTime(Time{Microseconds: usec, Valid: true})\n}\n\ntype scanPlanBinaryTimeToTextScanner struct{}\n\nfunc (scanPlanBinaryTimeToTextScanner) Scan(src []byte, dst any) error {\n\tts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn ts.ScanText(Text{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for time: %v\", len(src))\n\t}\n\n\tusec := int64(binary.BigEndian.Uint64(src))\n\n\ttim := Time{Microseconds: usec, Valid: true}\n\n\tbuf, err := TimeCodec{}.PlanEncode(nil, 0, TextFormatCode, tim).Encode(tim, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn ts.ScanText(Text{String: string(buf), Valid: true})\n}\n\ntype scanPlanTextAnyToTimeScanner struct{}\n\nfunc (scanPlanTextAnyToTimeScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TimeScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTime(Time{})\n\t}\n\n\ts := string(src)\n\n\tif len(s) < 8 || s[2] != ':' || s[5] != ':' {\n\t\treturn fmt.Errorf(\"cannot decode %v into Time\", s)\n\t}\n\n\thours, err := strconv.ParseInt(s[0:2], 10, 64)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot decode %v into Time\", s)\n\t}\n\tusec := hours * microsecondsPerHour\n\n\tminutes, err := strconv.ParseInt(s[3:5], 10, 64)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot decode %v into Time\", s)\n\t}\n\tusec += minutes * microsecondsPerMinute\n\n\tseconds, err := strconv.ParseInt(s[6:8], 10, 64)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot decode %v into Time\", s)\n\t}\n\tusec += seconds * microsecondsPerSecond\n\n\tif len(s) > 9 {\n\t\tif s[8] != '.' || len(s) > 15 {\n\t\t\treturn fmt.Errorf(\"cannot decode %v into Time\", s)\n\t\t}\n\n\t\tfraction := s[9:]\n\t\tn, err := strconv.ParseInt(fraction, 10, 64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot decode %v into Time\", s)\n\t\t}\n\n\t\tfor i := len(fraction); i < 6; i++ {\n\t\t\tn *= 10\n\t\t}\n\n\t\tusec += n\n\t}\n\n\treturn scanner.ScanTime(Time{Microseconds: usec, Valid: true})\n}\n\nfunc (c TimeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c TimeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar t Time\n\terr := codecScan(c, m, oid, format, src, &t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn t, nil\n}\n"
  },
  {
    "path": "pgtype/time_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTimeCodec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"time\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Time{Microseconds: 0, Valid: true},\n\t\t\tResult: new(pgtype.Time),\n\t\t\tTest:   isExpectedEq(pgtype.Time{Microseconds: 0, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Time{Microseconds: 1, Valid: true},\n\t\t\tResult: new(pgtype.Time),\n\t\t\tTest:   isExpectedEq(pgtype.Time{Microseconds: 1, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Time{Microseconds: 86_399_999_999, Valid: true},\n\t\t\tResult: new(pgtype.Time),\n\t\t\tTest:   isExpectedEq(pgtype.Time{Microseconds: 86_399_999_999, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Time{Microseconds: 86_400_000_000, Valid: true},\n\t\t\tResult: new(pgtype.Time),\n\t\t\tTest:   isExpectedEq(pgtype.Time{Microseconds: 86_400_000_000, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\tResult: new(pgtype.Time),\n\t\t\tTest:   isExpectedEq(pgtype.Time{Microseconds: 0, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.Time{Microseconds: 0, Valid: true},\n\t\t\tResult: new(time.Time),\n\t\t\tTest:   isExpectedEq(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)),\n\t\t},\n\t\t{Param: pgtype.Time{}, Result: new(pgtype.Time), Test: isExpectedEq(pgtype.Time{})},\n\t\t{Param: nil, Result: new(pgtype.Time), Test: isExpectedEq(pgtype.Time{})},\n\t})\n}\n\nfunc TestTimeTextScanner(t *testing.T) {\n\tvar pgTime pgtype.Time\n\n\tassert.NoError(t, pgTime.Scan(\"07:37:16\"))\n\tassert.Equal(t, true, pgTime.Valid)\n\tassert.Equal(t, int64(7*time.Hour+37*time.Minute+16*time.Second), pgTime.Microseconds*int64(time.Microsecond))\n\n\tassert.NoError(t, pgTime.Scan(\"15:04:05\"))\n\tassert.Equal(t, true, pgTime.Valid)\n\tassert.Equal(t, int64(15*time.Hour+4*time.Minute+5*time.Second), pgTime.Microseconds*int64(time.Microsecond))\n\n\t// parsing of fractional digits\n\tassert.NoError(t, pgTime.Scan(\"15:04:05.00\"))\n\tassert.Equal(t, true, pgTime.Valid)\n\tassert.Equal(t, int64(15*time.Hour+4*time.Minute+5*time.Second), pgTime.Microseconds*int64(time.Microsecond))\n\n\tconst mirco = \"789123\"\n\tconst woFraction = int64(4*time.Hour + 5*time.Minute + 6*time.Second) // time without fraction\n\tfor i := 0; i <= len(mirco); i++ {\n\t\tassert.NoError(t, pgTime.Scan(\"04:05:06.\"+mirco[:i]))\n\t\tassert.Equal(t, true, pgTime.Valid)\n\n\t\tfrac, _ := strconv.ParseInt(mirco[:i], 10, 64)\n\t\tfor k := i; k < 6; k++ {\n\t\t\tfrac *= 10\n\t\t}\n\t\tassert.Equal(t, woFraction+frac*int64(time.Microsecond), pgTime.Microseconds*int64(time.Microsecond))\n\t}\n\n\t// parsing of too long fraction errors\n\tassert.Error(t, pgTime.Scan(\"04:05:06.7891234\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\t// parsing of timetz errors\n\tassert.Error(t, pgTime.Scan(\"04:05:06.789-08\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\tassert.Error(t, pgTime.Scan(\"04:05:06-08:00\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\t// parsing of date errors\n\tassert.Error(t, pgTime.Scan(\"1997-12-17\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\t// parsing of text errors\n\tassert.Error(t, pgTime.Scan(\"12345678\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\tassert.Error(t, pgTime.Scan(\"12-34-56\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\tassert.Error(t, pgTime.Scan(\"12:34-56\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n\n\tassert.Error(t, pgTime.Scan(\"12-34:56\"))\n\tassert.Equal(t, false, pgTime.Valid)\n\tassert.Equal(t, int64(0), pgTime.Microseconds)\n}\n"
  },
  {
    "path": "pgtype/timestamp.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst (\n\tpgTimestampFormat = \"2006-01-02 15:04:05.999999999\"\n\tjsonISO8601       = \"2006-01-02T15:04:05.999999999\"\n)\n\ntype TimestampScanner interface {\n\tScanTimestamp(v Timestamp) error\n}\n\ntype TimestampValuer interface {\n\tTimestampValue() (Timestamp, error)\n}\n\n// Timestamp represents the PostgreSQL timestamp type.\ntype Timestamp struct {\n\tTime             time.Time // Time zone will be ignored when encoding to PostgreSQL.\n\tInfinityModifier InfinityModifier\n\tValid            bool\n}\n\n// ScanTimestamp implements the [TimestampScanner] interface.\nfunc (ts *Timestamp) ScanTimestamp(v Timestamp) error {\n\t*ts = v\n\treturn nil\n}\n\n// TimestampValue implements the [TimestampValuer] interface.\nfunc (ts Timestamp) TimestampValue() (Timestamp, error) {\n\treturn ts, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (ts *Timestamp) Scan(src any) error {\n\tif src == nil {\n\t\t*ts = Timestamp{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn (&scanPlanTextTimestampToTimestampScanner{}).Scan([]byte(src), ts)\n\tcase time.Time:\n\t\t*ts = Timestamp{Time: src, Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (ts Timestamp) Value() (driver.Value, error) {\n\tif !ts.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif ts.InfinityModifier != Finite {\n\t\treturn ts.InfinityModifier.String(), nil\n\t}\n\treturn ts.Time, nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (ts Timestamp) MarshalJSON() ([]byte, error) {\n\tif !ts.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tvar s string\n\n\tswitch ts.InfinityModifier {\n\tcase Finite:\n\t\ts = ts.Time.Format(jsonISO8601)\n\tcase Infinity:\n\t\ts = \"infinity\"\n\tcase NegativeInfinity:\n\t\ts = \"-infinity\"\n\t}\n\n\treturn json.Marshal(s)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (ts *Timestamp) UnmarshalJSON(b []byte) error {\n\tvar s *string\n\terr := json.Unmarshal(b, &s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif s == nil {\n\t\t*ts = Timestamp{}\n\t\treturn nil\n\t}\n\n\tswitch *s {\n\tcase \"infinity\":\n\t\t*ts = Timestamp{Valid: true, InfinityModifier: Infinity}\n\tcase \"-infinity\":\n\t\t*ts = Timestamp{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\t// Parse time with or without timezone\n\t\ttss := *s\n\t\t// PostgreSQL uses ISO 8601 without timezone for to_json function and casting from a string to timestamp\n\t\ttim, err := time.Parse(time.RFC3339Nano, tss)\n\t\tif err == nil {\n\t\t\t*ts = Timestamp{Time: tim, Valid: true}\n\t\t\treturn nil\n\t\t}\n\t\ttim, err = time.ParseInLocation(jsonISO8601, tss, time.UTC)\n\t\tif err == nil {\n\t\t\t*ts = Timestamp{Time: tim, Valid: true}\n\t\t\treturn nil\n\t\t}\n\t\tts.Valid = false\n\t\treturn fmt.Errorf(\"cannot unmarshal %s to timestamp with layout %s or %s (%w)\",\n\t\t\t*s, time.RFC3339Nano, jsonISO8601, err)\n\t}\n\treturn nil\n}\n\ntype TimestampCodec struct {\n\t// ScanLocation is the location that the time is assumed to be in for scanning. This is different from\n\t// TimestamptzCodec.ScanLocation in that this setting does change the instant in time that the timestamp represents.\n\tScanLocation *time.Location\n}\n\nfunc (*TimestampCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (*TimestampCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (*TimestampCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(TimestampValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanTimestampCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanTimestampCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanTimestampCodecBinary struct{}\n\nfunc (encodePlanTimestampCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tts, err := value.(TimestampValuer).TimestampValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !ts.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar microsecSinceY2K int64\n\tswitch ts.InfinityModifier {\n\tcase Finite:\n\t\tt := discardTimeZone(ts.Time)\n\t\tmicrosecSinceUnixEpoch := t.Unix()*1_000_000 + int64(t.Nanosecond())/1000\n\t\tmicrosecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K\n\tcase Infinity:\n\t\tmicrosecSinceY2K = infinityMicrosecondOffset\n\tcase NegativeInfinity:\n\t\tmicrosecSinceY2K = negativeInfinityMicrosecondOffset\n\t}\n\n\tbuf = pgio.AppendInt64(buf, microsecSinceY2K)\n\n\treturn buf, nil\n}\n\ntype encodePlanTimestampCodecText struct{}\n\nfunc (encodePlanTimestampCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tts, err := value.(TimestampValuer).TimestampValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !ts.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar s string\n\n\tswitch ts.InfinityModifier {\n\tcase Finite:\n\t\tt := discardTimeZone(ts.Time)\n\n\t\t// Year 0000 is 1 BC\n\t\tbc := false\n\t\tif year := t.Year(); year <= 0 {\n\t\t\tyear = -year + 1\n\t\t\tt = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC)\n\t\t\tbc = true\n\t\t}\n\n\t\ts = t.Truncate(time.Microsecond).Format(pgTimestampFormat)\n\n\t\tif bc {\n\t\t\ts = s + \" BC\"\n\t\t}\n\tcase Infinity:\n\t\ts = \"infinity\"\n\tcase NegativeInfinity:\n\t\ts = \"-infinity\"\n\t}\n\n\tbuf = append(buf, s...)\n\n\treturn buf, nil\n}\n\nfunc discardTimeZone(t time.Time) time.Time {\n\tif t.Location() != time.UTC {\n\t\treturn time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC)\n\t}\n\n\treturn t\n}\n\nfunc (c *TimestampCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TimestampScanner:\n\t\t\treturn &scanPlanBinaryTimestampToTimestampScanner{location: c.ScanLocation}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TimestampScanner:\n\t\t\treturn &scanPlanTextTimestampToTimestampScanner{location: c.ScanLocation}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryTimestampToTimestampScanner struct{ location *time.Location }\n\nfunc (plan *scanPlanBinaryTimestampToTimestampScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TimestampScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTimestamp(Timestamp{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for timestamp: %v\", len(src))\n\t}\n\n\tvar ts Timestamp\n\tmicrosecSinceY2K := int64(binary.BigEndian.Uint64(src))\n\n\tswitch microsecSinceY2K {\n\tcase infinityMicrosecondOffset:\n\t\tts = Timestamp{Valid: true, InfinityModifier: Infinity}\n\tcase negativeInfinityMicrosecondOffset:\n\t\tts = Timestamp{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\ttim := time.Unix(\n\t\t\tmicrosecFromUnixEpochToY2K/1_000_000+microsecSinceY2K/1_000_000,\n\t\t\t(microsecFromUnixEpochToY2K%1_000_000*1_000)+(microsecSinceY2K%1_000_000*1000),\n\t\t).UTC()\n\t\tif plan.location != nil {\n\t\t\ttim = time.Date(tim.Year(), tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), plan.location)\n\t\t}\n\t\tts = Timestamp{Time: tim, Valid: true}\n\t}\n\n\treturn scanner.ScanTimestamp(ts)\n}\n\ntype scanPlanTextTimestampToTimestampScanner struct{ location *time.Location }\n\nfunc (plan *scanPlanTextTimestampToTimestampScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TimestampScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTimestamp(Timestamp{})\n\t}\n\n\tvar ts Timestamp\n\tsbuf := string(src)\n\tswitch sbuf {\n\tcase \"infinity\":\n\t\tts = Timestamp{Valid: true, InfinityModifier: Infinity}\n\tcase \"-infinity\":\n\t\tts = Timestamp{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\tbc := false\n\t\tif strings.HasSuffix(sbuf, \" BC\") {\n\t\t\tsbuf = sbuf[:len(sbuf)-3]\n\t\t\tbc = true\n\t\t}\n\t\ttim, err := time.Parse(pgTimestampFormat, sbuf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif bc {\n\t\t\tyear := -tim.Year() + 1\n\t\t\ttim = time.Date(year, tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), tim.Location())\n\t\t}\n\n\t\tif plan.location != nil {\n\t\t\ttim = time.Date(tim.Year(), tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), plan.location)\n\t\t}\n\n\t\tts = Timestamp{Time: tim, Valid: true}\n\t}\n\n\treturn scanner.ScanTimestamp(ts)\n}\n\nfunc (c *TimestampCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar ts Timestamp\n\terr := codecScan(c, m, oid, format, src, &ts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ts.InfinityModifier != Finite {\n\t\treturn ts.InfinityModifier.String(), nil\n\t}\n\n\treturn ts.Time, nil\n}\n\nfunc (c *TimestampCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar ts Timestamp\n\terr := codecScan(c, m, oid, format, src, &ts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ts.InfinityModifier != Finite {\n\t\treturn ts.InfinityModifier, nil\n\t}\n\n\treturn ts.Time, nil\n}\n"
  },
  {
    "path": "pgtype/timestamp_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"testing\"\n\t\"time\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTimestampCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"timestamp\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC))},\n\n\t\t{Param: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC))},\n\t\t{Param: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC))},\n\n\t\t// Nanosecond truncation\n\t\t{Param: time.Date(2020, 1, 1, 0, 0, 0, 999999999, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2020, 1, 1, 0, 0, 0, 999999000, time.UTC))},\n\t\t{Param: time.Date(2020, 1, 1, 0, 0, 0, 999999001, time.UTC), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2020, 1, 1, 0, 0, 0, 999999000, time.UTC))},\n\n\t\t{Param: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true}, Result: new(pgtype.Timestamp), Test: isExpectedEq(pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true})},\n\t\t{Param: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, Result: new(pgtype.Timestamp), Test: isExpectedEq(pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true})},\n\t\t{Param: pgtype.Timestamp{}, Result: new(pgtype.Timestamp), Test: isExpectedEq(pgtype.Timestamp{})},\n\t\t{Param: nil, Result: new(*time.Time), Test: isExpectedEq((*time.Time)(nil))},\n\t})\n}\n\nfunc TestTimestampCodecWithScanLocationUTC(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)\")\n\n\tconnTestRunner := defaultConnTestRunner\n\tconnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{\n\t\t\tName:  \"timestamp\",\n\t\t\tOID:   pgtype.TimestampOID,\n\t\t\tCodec: &pgtype.TimestampCodec{ScanLocation: time.UTC},\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, nil, \"timestamp\", []pgxtest.ValueRoundTripTest{\n\t\t// Have to use pgtype.Timestamp instead of time.Time as source because otherwise the simple and exec query exec\n\t\t// modes will encode the time for timestamptz. That is, they will convert it from local time zone.\n\t\t{Param: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, Result: new(time.Time), Test: isExpectedEq(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t})\n}\n\nfunc TestTimestampCodecWithScanLocationLocal(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)\")\n\n\tconnTestRunner := defaultConnTestRunner\n\tconnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{\n\t\t\tName:  \"timestamp\",\n\t\t\tOID:   pgtype.TimestampOID,\n\t\t\tCodec: &pgtype.TimestampCodec{ScanLocation: time.Local},\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, nil, \"timestamp\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEq(time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local))},\n\t})\n}\n\n// https://github.com/jackc/pgx/v4/pgtype/pull/128\nfunc TestTimestampTranscodeBigTimeBinary(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tin := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}\n\t\tvar out pgtype.Timestamp\n\n\t\terr := conn.QueryRow(ctx, \"select $1::timestamp\", in).Scan(&out)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\trequire.Equal(t, in.Valid, out.Valid)\n\t\trequire.Truef(t, in.Time.Equal(out.Time), \"expected %v got %v\", in.Time, out.Time)\n\t})\n}\n\n// https://github.com/jackc/pgtype/issues/74\nfunc TestTimestampCodecDecodeTextInvalid(t *testing.T) {\n\tc := &pgtype.TimestampCodec{}\n\tvar ts pgtype.Timestamp\n\tplan := c.PlanScan(nil, pgtype.TimestampOID, pgtype.TextFormatCode, &ts)\n\terr := plan.Scan([]byte(`eeeee`), &ts)\n\trequire.Error(t, err)\n}\n\nfunc TestTimestampMarshalJSON(t *testing.T) {\n\ttsStruct := struct {\n\t\tTS pgtype.Timestamp `json:\"ts\"`\n\t}{}\n\n\ttm := time.Date(2012, 3, 29, 10, 5, 45, 0, time.UTC)\n\ttsString := \"\\\"\" + tm.Format(\"2006-01-02T15:04:05\") + \"\\\"\" //  `\"2012-03-29T10:05:45\"`\n\tvar pgt pgtype.Timestamp\n\t_ = pgt.Scan(tm)\n\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Timestamp\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Timestamp{}, result: \"null\"},\n\t\t{source: pgtype.Timestamp{Time: tm, Valid: true}, result: tsString},\n\t\t{source: pgt, result: tsString},\n\t\t{source: pgtype.Timestamp{Time: tm.Add(time.Second * 555 / 1000), Valid: true}, result: `\"2012-03-29T10:05:45.555\"`},\n\t\t{source: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true}, result: \"\\\"infinity\\\"\"},\n\t\t{source: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: \"\\\"-infinity\\\"\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif !assert.Equal(t, tt.result, string(r)) {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t\ttsStruct.TS = tt.source\n\t\tb, err := json.Marshal(tsStruct)\n\t\tassert.NoErrorf(t, err, \"failed to marshal %v %s\", tt.source, err)\n\t\tt2 := tsStruct\n\t\tt2.TS = pgtype.Timestamp{} // Clear out the value so that we can compare after unmarshalling\n\t\terr = json.Unmarshal(b, &t2)\n\t\tassert.NoErrorf(t, err, \"failed to unmarshal %v with %s\", tt.source, err)\n\t\tassert.True(t, tsStruct.TS.Time.Unix() == t2.TS.Time.Unix())\n\t}\n}\n\nfunc TestTimestampUnmarshalJSONErrors(t *testing.T) {\n\ttsStruct := struct {\n\t\tTS pgtype.Timestamp `json:\"ts\"`\n\t}{}\n\tgoodJson1 := []byte(`{\"ts\":\"2012-03-29T10:05:45\"}`)\n\tassert.NoError(t, json.Unmarshal(goodJson1, &tsStruct))\n\tgoodJson2 := []byte(`{\"ts\":\"2012-03-29T10:05:45Z\"}`)\n\tassert.NoError(t, json.Unmarshal(goodJson2, &tsStruct))\n\tbadJson := []byte(`{\"ts\":\"2012-03-29\"}`)\n\tassert.Error(t, json.Unmarshal(badJson, &tsStruct))\n}\n\nfunc TestTimestampUnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Timestamp\n\t}{\n\t\t{source: \"null\", result: pgtype.Timestamp{}},\n\t\t{source: \"\\\"2012-03-29T10:05:45\\\"\", result: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.UTC), Valid: true}},\n\t\t{source: \"\\\"2012-03-29T10:05:45.555\\\"\", result: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.UTC), Valid: true}},\n\t\t{source: \"\\\"infinity\\\"\", result: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true}},\n\t\t{source: \"\\\"-infinity\\\"\", result: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Timestamp\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif !r.Time.Equal(tt.result.Time) || r.Valid != tt.result.Valid || r.InfinityModifier != tt.result.InfinityModifier {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/timestamptz.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\nconst (\n\tpgTimestamptzHourFormat    = \"2006-01-02 15:04:05.999999999Z07\"\n\tpgTimestamptzMinuteFormat  = \"2006-01-02 15:04:05.999999999Z07:00\"\n\tpgTimestamptzSecondFormat  = \"2006-01-02 15:04:05.999999999Z07:00:00\"\n\tmicrosecFromUnixEpochToY2K = 946_684_800 * 1_000_000\n)\n\nconst (\n\tnegativeInfinityMicrosecondOffset = -9223372036854775808\n\tinfinityMicrosecondOffset         = 9223372036854775807\n)\n\ntype TimestamptzScanner interface {\n\tScanTimestamptz(v Timestamptz) error\n}\n\ntype TimestamptzValuer interface {\n\tTimestamptzValue() (Timestamptz, error)\n}\n\n// Timestamptz represents the PostgreSQL timestamptz type.\ntype Timestamptz struct {\n\tTime             time.Time\n\tInfinityModifier InfinityModifier\n\tValid            bool\n}\n\n// ScanTimestamptz implements the [TimestamptzScanner] interface.\nfunc (tstz *Timestamptz) ScanTimestamptz(v Timestamptz) error {\n\t*tstz = v\n\treturn nil\n}\n\n// TimestamptzValue implements the [TimestamptzValuer] interface.\nfunc (tstz Timestamptz) TimestamptzValue() (Timestamptz, error) {\n\treturn tstz, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (tstz *Timestamptz) Scan(src any) error {\n\tif src == nil {\n\t\t*tstz = Timestamptz{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn (&scanPlanTextTimestamptzToTimestamptzScanner{}).Scan([]byte(src), tstz)\n\tcase time.Time:\n\t\t*tstz = Timestamptz{Time: src, Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (tstz Timestamptz) Value() (driver.Value, error) {\n\tif !tstz.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif tstz.InfinityModifier != Finite {\n\t\treturn tstz.InfinityModifier.String(), nil\n\t}\n\treturn tstz.Time, nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (tstz Timestamptz) MarshalJSON() ([]byte, error) {\n\tif !tstz.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tvar s string\n\n\tswitch tstz.InfinityModifier {\n\tcase Finite:\n\t\ts = tstz.Time.Format(time.RFC3339Nano)\n\tcase Infinity:\n\t\ts = \"infinity\"\n\tcase NegativeInfinity:\n\t\ts = \"-infinity\"\n\t}\n\n\treturn json.Marshal(s)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (tstz *Timestamptz) UnmarshalJSON(b []byte) error {\n\tvar s *string\n\terr := json.Unmarshal(b, &s)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif s == nil {\n\t\t*tstz = Timestamptz{}\n\t\treturn nil\n\t}\n\n\tswitch *s {\n\tcase \"infinity\":\n\t\t*tstz = Timestamptz{Valid: true, InfinityModifier: Infinity}\n\tcase \"-infinity\":\n\t\t*tstz = Timestamptz{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\t// PostgreSQL uses ISO 8601 for to_json function and casting from a string to timestamptz\n\t\ttim, err := time.Parse(time.RFC3339Nano, *s)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t*tstz = Timestamptz{Time: tim, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype TimestamptzCodec struct {\n\t// ScanLocation is the location to return scanned timestamptz values in. This does not change the instant in time that\n\t// the timestamptz represents.\n\tScanLocation *time.Location\n}\n\nfunc (*TimestamptzCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (*TimestamptzCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (*TimestamptzCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(TimestamptzValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanTimestamptzCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanTimestamptzCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanTimestamptzCodecBinary struct{}\n\nfunc (encodePlanTimestamptzCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tts, err := value.(TimestamptzValuer).TimestamptzValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !ts.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar microsecSinceY2K int64\n\tswitch ts.InfinityModifier {\n\tcase Finite:\n\t\tmicrosecSinceUnixEpoch := ts.Time.Unix()*1000000 + int64(ts.Time.Nanosecond())/1000\n\t\tmicrosecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K\n\tcase Infinity:\n\t\tmicrosecSinceY2K = infinityMicrosecondOffset\n\tcase NegativeInfinity:\n\t\tmicrosecSinceY2K = negativeInfinityMicrosecondOffset\n\t}\n\n\tbuf = pgio.AppendInt64(buf, microsecSinceY2K)\n\n\treturn buf, nil\n}\n\ntype encodePlanTimestamptzCodecText struct{}\n\nfunc (encodePlanTimestamptzCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tts, err := value.(TimestamptzValuer).TimestamptzValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !ts.Valid {\n\t\treturn nil, nil\n\t}\n\n\tvar s string\n\n\tswitch ts.InfinityModifier {\n\tcase Finite:\n\n\t\tt := ts.Time.UTC().Truncate(time.Microsecond)\n\n\t\t// Year 0000 is 1 BC\n\t\tbc := false\n\t\tif year := t.Year(); year <= 0 {\n\t\t\tyear = -year + 1\n\t\t\tt = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC)\n\t\t\tbc = true\n\t\t}\n\n\t\ts = t.Format(pgTimestamptzSecondFormat)\n\n\t\tif bc {\n\t\t\ts = s + \" BC\"\n\t\t}\n\tcase Infinity:\n\t\ts = \"infinity\"\n\tcase NegativeInfinity:\n\t\ts = \"-infinity\"\n\t}\n\n\tbuf = append(buf, s...)\n\n\treturn buf, nil\n}\n\nfunc (c *TimestamptzCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TimestamptzScanner:\n\t\t\treturn &scanPlanBinaryTimestamptzToTimestamptzScanner{location: c.ScanLocation}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TimestamptzScanner:\n\t\t\treturn &scanPlanTextTimestamptzToTimestamptzScanner{location: c.ScanLocation}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryTimestamptzToTimestamptzScanner struct{ location *time.Location }\n\nfunc (plan *scanPlanBinaryTimestamptzToTimestamptzScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TimestamptzScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTimestamptz(Timestamptz{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for timestamptz: %v\", len(src))\n\t}\n\n\tvar tstz Timestamptz\n\tmicrosecSinceY2K := int64(binary.BigEndian.Uint64(src))\n\n\tswitch microsecSinceY2K {\n\tcase infinityMicrosecondOffset:\n\t\ttstz = Timestamptz{Valid: true, InfinityModifier: Infinity}\n\tcase negativeInfinityMicrosecondOffset:\n\t\ttstz = Timestamptz{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\ttim := time.Unix(\n\t\t\tmicrosecFromUnixEpochToY2K/1_000_000+microsecSinceY2K/1_000_000,\n\t\t\t(microsecFromUnixEpochToY2K%1_000_000*1_000)+(microsecSinceY2K%1_000_000*1_000),\n\t\t)\n\t\tif plan.location != nil {\n\t\t\ttim = tim.In(plan.location)\n\t\t}\n\t\ttstz = Timestamptz{Time: tim, Valid: true}\n\t}\n\n\treturn scanner.ScanTimestamptz(tstz)\n}\n\ntype scanPlanTextTimestamptzToTimestamptzScanner struct{ location *time.Location }\n\nfunc (plan *scanPlanTextTimestamptzToTimestamptzScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TimestamptzScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTimestamptz(Timestamptz{})\n\t}\n\n\tvar tstz Timestamptz\n\tsbuf := string(src)\n\tswitch sbuf {\n\tcase \"infinity\":\n\t\ttstz = Timestamptz{Valid: true, InfinityModifier: Infinity}\n\tcase \"-infinity\":\n\t\ttstz = Timestamptz{Valid: true, InfinityModifier: -Infinity}\n\tdefault:\n\t\tbc := false\n\t\tif strings.HasSuffix(sbuf, \" BC\") {\n\t\t\tsbuf = sbuf[:len(sbuf)-3]\n\t\t\tbc = true\n\t\t}\n\n\t\tvar format string\n\t\tif len(sbuf) >= 9 && (sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+') {\n\t\t\tformat = pgTimestamptzSecondFormat\n\t\t} else if len(sbuf) >= 6 && (sbuf[len(sbuf)-6] == '-' || sbuf[len(sbuf)-6] == '+') {\n\t\t\tformat = pgTimestamptzMinuteFormat\n\t\t} else {\n\t\t\tformat = pgTimestamptzHourFormat\n\t\t}\n\n\t\ttim, err := time.Parse(format, sbuf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif bc {\n\t\t\tyear := -tim.Year() + 1\n\t\t\ttim = time.Date(year, tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), tim.Location())\n\t\t}\n\n\t\tif plan.location != nil {\n\t\t\ttim = tim.In(plan.location)\n\t\t}\n\n\t\ttstz = Timestamptz{Time: tim, Valid: true}\n\t}\n\n\treturn scanner.ScanTimestamptz(tstz)\n}\n\nfunc (c *TimestamptzCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar tstz Timestamptz\n\terr := codecScan(c, m, oid, format, src, &tstz)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif tstz.InfinityModifier != Finite {\n\t\treturn tstz.InfinityModifier.String(), nil\n\t}\n\n\treturn tstz.Time, nil\n}\n\nfunc (c *TimestamptzCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar tstz Timestamptz\n\terr := codecScan(c, m, oid, format, src, &tstz)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif tstz.InfinityModifier != Finite {\n\t\treturn tstz.InfinityModifier, nil\n\t}\n\n\treturn tstz.Time, nil\n}\n"
  },
  {
    "path": "pgtype/timestamptz_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTimestamptzCodec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)\")\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"timestamptz\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: time.Date(-100, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(0, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(1, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1, 1, 1, 0, 0, 0, 0, time.Local))},\n\n\t\t{Param: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(2000, 1, 2, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2000, 1, 2, 0, 0, 0, 0, time.Local))},\n\t\t{Param: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local))},\n\n\t\t// Nanosecond truncation\n\t\t{Param: time.Date(2020, 1, 1, 0, 0, 0, 999999999, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2020, 1, 1, 0, 0, 0, 999999000, time.Local))},\n\t\t{Param: time.Date(2020, 1, 1, 0, 0, 0, 999999001, time.Local), Result: new(time.Time), Test: isExpectedEqTime(time.Date(2020, 1, 1, 0, 0, 0, 999999000, time.Local))},\n\n\t\t{Param: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true}, Result: new(pgtype.Timestamptz), Test: isExpectedEq(pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true})},\n\t\t{Param: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, Result: new(pgtype.Timestamptz), Test: isExpectedEq(pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true})},\n\t\t{Param: pgtype.Timestamptz{}, Result: new(pgtype.Timestamptz), Test: isExpectedEq(pgtype.Timestamptz{})},\n\t\t{Param: nil, Result: new(*time.Time), Test: isExpectedEq((*time.Time)(nil))},\n\t})\n}\n\nfunc TestTimestamptzCodecWithLocationUTC(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)\")\n\n\tconnTestRunner := defaultConnTestRunner\n\tconnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{\n\t\t\tName:  \"timestamptz\",\n\t\t\tOID:   pgtype.TimestamptzOID,\n\t\t\tCodec: &pgtype.TimestamptzCodec{ScanLocation: time.UTC},\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, nil, \"timestamptz\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Result: new(time.Time), Test: isExpectedEq(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC))},\n\t})\n}\n\nfunc TestTimestamptzCodecWithLocationLocal(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)\")\n\n\tconnTestRunner := defaultConnTestRunner\n\tconnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{\n\t\t\tName:  \"timestamptz\",\n\t\t\tOID:   pgtype.TimestamptzOID,\n\t\t\tCodec: &pgtype.TimestamptzCodec{ScanLocation: time.Local},\n\t\t})\n\t}\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, nil, \"timestamptz\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Result: new(time.Time), Test: isExpectedEq(time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local))},\n\t})\n}\n\n// https://github.com/jackc/pgx/v4/pgtype/pull/128\nfunc TestTimestamptzTranscodeBigTimeBinary(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tin := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}\n\t\tvar out pgtype.Timestamptz\n\n\t\terr := conn.QueryRow(ctx, \"select $1::timestamptz\", in).Scan(&out)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\trequire.Equal(t, in.Valid, out.Valid)\n\t\trequire.Truef(t, in.Time.Equal(out.Time), \"expected %v got %v\", in.Time, out.Time)\n\t})\n}\n\n// https://github.com/jackc/pgtype/issues/74\nfunc TestTimestamptzDecodeTextInvalid(t *testing.T) {\n\tc := &pgtype.TimestamptzCodec{}\n\tvar tstz pgtype.Timestamptz\n\tplan := c.PlanScan(nil, pgtype.TimestamptzOID, pgtype.TextFormatCode, &tstz)\n\terr := plan.Scan([]byte(`eeeee`), &tstz)\n\trequire.Error(t, err)\n}\n\nfunc TestTimestamptzMarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource pgtype.Timestamptz\n\t\tresult string\n\t}{\n\t\t{source: pgtype.Timestamptz{}, result: \"null\"},\n\t\t{source: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone(\"\", -6*60*60)), Valid: true}, result: \"\\\"2012-03-29T10:05:45-06:00\\\"\"},\n\t\t{source: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone(\"\", -6*60*60)), Valid: true}, result: \"\\\"2012-03-29T10:05:45.555-06:00\\\"\"},\n\t\t{source: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true}, result: \"\\\"infinity\\\"\"},\n\t\t{source: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: \"\\\"-infinity\\\"\"},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tr, err := tt.source.MarshalJSON()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif string(r) != tt.result {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, string(r))\n\t\t}\n\t}\n}\n\nfunc TestTimestamptzUnmarshalJSON(t *testing.T) {\n\tsuccessfulTests := []struct {\n\t\tsource string\n\t\tresult pgtype.Timestamptz\n\t}{\n\t\t{source: \"null\", result: pgtype.Timestamptz{}},\n\t\t{source: \"\\\"2012-03-29T10:05:45-06:00\\\"\", result: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone(\"\", -6*60*60)), Valid: true}},\n\t\t{source: \"\\\"2012-03-29T10:05:45.555-06:00\\\"\", result: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone(\"\", -6*60*60)), Valid: true}},\n\t\t{source: \"\\\"infinity\\\"\", result: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true}},\n\t\t{source: \"\\\"-infinity\\\"\", result: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true}},\n\t}\n\tfor i, tt := range successfulTests {\n\t\tvar r pgtype.Timestamptz\n\t\terr := r.UnmarshalJSON([]byte(tt.source))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d: %v\", i, err)\n\t\t}\n\n\t\tif !r.Time.Equal(tt.result.Time) || r.Valid != tt.result.Valid || r.InfinityModifier != tt.result.InfinityModifier {\n\t\t\tt.Errorf(\"%d: expected %v to convert to %v, but it was %v\", i, tt.source, tt.result, r)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgtype/tsvector.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype TSVectorScanner interface {\n\tScanTSVector(TSVector) error\n}\n\ntype TSVectorValuer interface {\n\tTSVectorValue() (TSVector, error)\n}\n\n// TSVector represents a PostgreSQL tsvector value.\ntype TSVector struct {\n\tLexemes []TSVectorLexeme\n\tValid   bool\n}\n\n// TSVectorLexeme represents a lexeme within a tsvector, consisting of a word and its positions.\ntype TSVectorLexeme struct {\n\tWord      string\n\tPositions []TSVectorPosition\n}\n\n// ScanTSVector implements the [TSVectorScanner] interface.\nfunc (t *TSVector) ScanTSVector(v TSVector) error {\n\t*t = v\n\treturn nil\n}\n\n// TSVectorValue implements the [TSVectorValuer] interface.\nfunc (t TSVector) TSVectorValue() (TSVector, error) {\n\treturn t, nil\n}\n\nfunc (t TSVector) String() string {\n\tbuf, _ := encodePlanTSVectorCodecText{}.Encode(t, nil)\n\treturn string(buf)\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (t *TSVector) Scan(src any) error {\n\tif src == nil {\n\t\t*t = TSVector{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\treturn scanPlanTextAnyToTSVectorScanner{}.scanString(src, t)\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (t TSVector) Value() (driver.Value, error) {\n\tif !t.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := TSVectorCodec{}.PlanEncode(nil, 0, TextFormatCode, t).Encode(t, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn string(buf), nil\n}\n\n// TSVectorWeight represents the weight label of a lexeme position in a tsvector.\ntype TSVectorWeight byte\n\nconst (\n\tTSVectorWeightA = TSVectorWeight('A')\n\tTSVectorWeightB = TSVectorWeight('B')\n\tTSVectorWeightC = TSVectorWeight('C')\n\tTSVectorWeightD = TSVectorWeight('D')\n)\n\n// tsvectorWeightToBinary converts a TSVectorWeight to the 2-bit binary encoding used by PostgreSQL.\nfunc tsvectorWeightToBinary(w TSVectorWeight) uint16 {\n\tswitch w {\n\tcase TSVectorWeightA:\n\t\treturn 3\n\tcase TSVectorWeightB:\n\t\treturn 2\n\tcase TSVectorWeightC:\n\t\treturn 1\n\tdefault:\n\t\treturn 0 // D or unset\n\t}\n}\n\n// tsvectorWeightFromBinary converts a 2-bit binary weight value to a TSVectorWeight.\nfunc tsvectorWeightFromBinary(b uint16) TSVectorWeight {\n\tswitch b {\n\tcase 3:\n\t\treturn TSVectorWeightA\n\tcase 2:\n\t\treturn TSVectorWeightB\n\tcase 1:\n\t\treturn TSVectorWeightC\n\tdefault:\n\t\treturn TSVectorWeightD\n\t}\n}\n\n// TSVectorPosition represents a lexeme position and its optional weight within a tsvector.\ntype TSVectorPosition struct {\n\tPosition uint16\n\tWeight   TSVectorWeight\n}\n\nfunc (p TSVectorPosition) String() string {\n\ts := strconv.FormatUint(uint64(p.Position), 10)\n\tif p.Weight != 0 && p.Weight != TSVectorWeightD {\n\t\ts += string(p.Weight)\n\t}\n\treturn s\n}\n\ntype TSVectorCodec struct{}\n\nfunc (TSVectorCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (TSVectorCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (TSVectorCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(TSVectorValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanTSVectorCodecBinary{}\n\tcase TextFormatCode:\n\t\treturn encodePlanTSVectorCodecText{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanTSVectorCodecBinary struct{}\n\nfunc (encodePlanTSVectorCodecBinary) Encode(value any, buf []byte) ([]byte, error) {\n\ttsv, err := value.(TSVectorValuer).TSVectorValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !tsv.Valid {\n\t\treturn nil, nil\n\t}\n\n\tbuf = pgio.AppendInt32(buf, int32(len(tsv.Lexemes)))\n\n\tfor _, entry := range tsv.Lexemes {\n\t\tbuf = append(buf, entry.Word...)\n\t\tbuf = append(buf, 0x00)\n\t\tbuf = pgio.AppendUint16(buf, uint16(len(entry.Positions)))\n\n\t\t// Each position is a uint16: weight (2 bits) | position (14 bits)\n\t\tfor _, pos := range entry.Positions {\n\t\t\tpacked := tsvectorWeightToBinary(pos.Weight)<<14 | uint16(pos.Position)&0x3FFF\n\t\t\tbuf = pgio.AppendUint16(buf, packed)\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\ntype scanPlanBinaryTSVectorToTSVectorScanner struct{}\n\nfunc (scanPlanBinaryTSVectorToTSVectorScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TSVectorScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTSVector(TSVector{})\n\t}\n\n\trp := 0\n\n\tconst (\n\t\tuint16Len = 2\n\t\tuint32Len = 4\n\t)\n\n\tif len(src[rp:]) < uint32Len {\n\t\treturn fmt.Errorf(\"tsvector incomplete %v\", src)\n\t}\n\tentryCount := int(int32(binary.BigEndian.Uint32(src[rp:])))\n\trp += uint32Len\n\n\tvar tsv TSVector\n\tif entryCount > 0 {\n\t\ttsv.Lexemes = make([]TSVectorLexeme, entryCount)\n\t}\n\n\tfor i := range entryCount {\n\t\tnullIndex := bytes.IndexByte(src[rp:], 0x00)\n\t\tif nullIndex == -1 {\n\t\t\treturn fmt.Errorf(\"invalid tsvector binary format: missing null terminator\")\n\t\t}\n\n\t\tlexeme := TSVectorLexeme{Word: string(src[rp : rp+nullIndex])}\n\t\trp += nullIndex + 1 // skip past null terminator\n\n\t\t// Read position count.\n\t\tif len(src[rp:]) < uint16Len {\n\t\t\treturn fmt.Errorf(\"invalid tsvector binary format: incomplete position count\")\n\t\t}\n\n\t\tnumPositions := int(binary.BigEndian.Uint16(src[rp:]))\n\t\trp += uint16Len\n\n\t\t// Read each packed position: weight (2 bits) | position (14 bits)\n\t\tif len(src[rp:]) < numPositions*uint16Len {\n\t\t\treturn fmt.Errorf(\"invalid tsvector binary format: incomplete positions\")\n\t\t}\n\n\t\tif numPositions > 0 {\n\t\t\tlexeme.Positions = make([]TSVectorPosition, numPositions)\n\t\t\tfor pos := range numPositions {\n\t\t\t\tpacked := binary.BigEndian.Uint16(src[rp:])\n\t\t\t\trp += uint16Len\n\t\t\t\tlexeme.Positions[pos] = TSVectorPosition{\n\t\t\t\t\tPosition: packed & 0x3FFF,\n\t\t\t\t\tWeight:   tsvectorWeightFromBinary(packed >> 14),\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttsv.Lexemes[i] = lexeme\n\t}\n\ttsv.Valid = true\n\n\treturn scanner.ScanTSVector(tsv)\n}\n\nvar tsvectorLexemeReplacer = strings.NewReplacer(\n\t`\\`, `\\\\`,\n\t`'`, `\\'`,\n)\n\ntype encodePlanTSVectorCodecText struct{}\n\nfunc (encodePlanTSVectorCodecText) Encode(value any, buf []byte) ([]byte, error) {\n\ttsv, err := value.(TSVectorValuer).TSVectorValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !tsv.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif buf == nil {\n\t\tbuf = []byte{}\n\t}\n\n\tfor i, lex := range tsv.Lexemes {\n\t\tif i > 0 {\n\t\t\tbuf = append(buf, ' ')\n\t\t}\n\n\t\tbuf = append(buf, '\\'')\n\t\tbuf = append(buf, tsvectorLexemeReplacer.Replace(lex.Word)...)\n\t\tbuf = append(buf, '\\'')\n\n\t\tsep := byte(':')\n\t\tfor _, p := range lex.Positions {\n\t\t\tbuf = append(buf, sep)\n\t\t\tbuf = append(buf, p.String()...)\n\t\t\tsep = ','\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (TSVectorCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TSVectorScanner:\n\t\t\treturn scanPlanBinaryTSVectorToTSVectorScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase TSVectorScanner:\n\t\t\treturn scanPlanTextAnyToTSVectorScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanTextAnyToTSVectorScanner struct{}\n\nfunc (s scanPlanTextAnyToTSVectorScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TSVectorScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanTSVector(TSVector{})\n\t}\n\n\treturn s.scanString(string(src), scanner)\n}\n\nfunc (scanPlanTextAnyToTSVectorScanner) scanString(src string, scanner TSVectorScanner) error {\n\ttsv, err := parseTSVector(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn scanner.ScanTSVector(tsv)\n}\n\nfunc (c TSVectorCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\treturn codecDecodeToTextFormat(c, m, oid, format, src)\n}\n\nfunc (c TSVectorCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar tsv TSVector\n\terr := codecScan(c, m, oid, format, src, &tsv)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn tsv, nil\n}\n\ntype tsvectorParser struct {\n\tstr string\n\tpos int\n}\n\nfunc (p *tsvectorParser) atEnd() bool {\n\treturn p.pos >= len(p.str)\n}\n\nfunc (p *tsvectorParser) peek() byte {\n\treturn p.str[p.pos]\n}\n\nfunc (p *tsvectorParser) consume() (byte, bool) {\n\tif p.pos >= len(p.str) {\n\t\treturn 0, true\n\t}\n\tb := p.str[p.pos]\n\tp.pos++\n\treturn b, false\n}\n\nfunc (p *tsvectorParser) consumeSpaces() {\n\tfor !p.atEnd() && p.peek() == ' ' {\n\t\tp.consume()\n\t}\n}\n\n// consumeLexeme consumes a single-quoted lexeme, handling single quotes and backslash escapes.\nfunc (p *tsvectorParser) consumeLexeme() (string, error) {\n\tch, end := p.consume()\n\tif end || ch != '\\'' {\n\t\treturn \"\", fmt.Errorf(\"invalid tsvector format: lexeme must start with a single quote\")\n\t}\n\n\tvar buf strings.Builder\n\tfor {\n\t\tch, end := p.consume()\n\t\tif end {\n\t\t\treturn \"\", fmt.Errorf(\"invalid tsvector format: unterminated quoted lexeme\")\n\t\t}\n\n\t\tswitch ch {\n\t\tcase '\\'':\n\t\t\t// Escaped quote ('') — write a literal single quote\n\t\t\tif !p.atEnd() && p.peek() == '\\'' {\n\t\t\t\tp.consume()\n\t\t\t\tbuf.WriteByte('\\'')\n\t\t\t} else {\n\t\t\t\t// Closing quote — lexeme is complete\n\t\t\t\treturn buf.String(), nil\n\t\t\t}\n\t\tcase '\\\\':\n\t\t\tnext, end := p.consume()\n\t\t\tif end {\n\t\t\t\treturn \"\", fmt.Errorf(\"invalid tsvector format: unexpected end after backslash\")\n\t\t\t}\n\t\t\tbuf.WriteByte(next)\n\t\tdefault:\n\t\t\tbuf.WriteByte(ch)\n\t\t}\n\t}\n}\n\n// consumePositions consumes a comma-separated list of position[weight] values.\nfunc (p *tsvectorParser) consumePositions() ([]TSVectorPosition, error) {\n\tvar positions []TSVectorPosition\n\n\tfor {\n\t\tpos, err := p.consumePosition()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpositions = append(positions, pos)\n\n\t\tif p.atEnd() || p.peek() != ',' {\n\t\t\tbreak\n\t\t}\n\n\t\tp.consume() // skip ','\n\t}\n\n\treturn positions, nil\n}\n\n// consumePosition consumes a single position number with optional weight letter.\nfunc (p *tsvectorParser) consumePosition() (TSVectorPosition, error) {\n\tstart := p.pos\n\n\tfor !p.atEnd() && p.peek() >= '0' && p.peek() <= '9' {\n\t\tp.consume()\n\t}\n\n\tif p.pos == start {\n\t\treturn TSVectorPosition{}, fmt.Errorf(\"invalid tsvector format: expected position number\")\n\t}\n\n\tnum, err := strconv.ParseUint(p.str[start:p.pos], 10, 16)\n\tif err != nil {\n\t\treturn TSVectorPosition{}, fmt.Errorf(\"invalid tsvector format: invalid position number %q\", p.str[start:p.pos])\n\t}\n\n\tpos := TSVectorPosition{Position: uint16(num), Weight: TSVectorWeightD}\n\n\t// Check for optional weight letter\n\tif !p.atEnd() {\n\t\tswitch p.peek() {\n\t\tcase 'A', 'a':\n\t\t\tpos.Weight = TSVectorWeightA\n\t\tcase 'B', 'b':\n\t\t\tpos.Weight = TSVectorWeightB\n\t\tcase 'C', 'c':\n\t\t\tpos.Weight = TSVectorWeightC\n\t\tcase 'D', 'd':\n\t\t\tpos.Weight = TSVectorWeightD\n\t\tdefault:\n\t\t\treturn pos, nil\n\t\t}\n\t\tp.consume()\n\t}\n\n\treturn pos, nil\n}\n\n// parseTSVector parses a PostgreSQL tsvector text representation.\nfunc parseTSVector(s string) (TSVector, error) {\n\tresult := TSVector{}\n\tp := &tsvectorParser{str: strings.TrimSpace(s), pos: 0}\n\n\tfor !p.atEnd() {\n\t\tp.consumeSpaces()\n\t\tif p.atEnd() {\n\t\t\tbreak\n\t\t}\n\n\t\tword, err := p.consumeLexeme()\n\t\tif err != nil {\n\t\t\treturn TSVector{}, err\n\t\t}\n\n\t\tentry := TSVectorLexeme{Word: word}\n\n\t\t// Check for optional positions after ':'\n\t\tif !p.atEnd() && p.peek() == ':' {\n\t\t\tp.consume() // skip ':'\n\n\t\t\tpositions, err := p.consumePositions()\n\t\t\tif err != nil {\n\t\t\t\treturn TSVector{}, err\n\t\t\t}\n\t\t\tentry.Positions = positions\n\t\t}\n\n\t\tresult.Lexemes = append(result.Lexemes, entry)\n\t}\n\n\tresult.Valid = true\n\n\treturn result, nil\n}\n"
  },
  {
    "path": "pgtype/tsvector_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqTSVector(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tat := a.(pgtype.TSVector)\n\t\tvt := v.(pgtype.TSVector)\n\n\t\tif len(at.Lexemes) != len(vt.Lexemes) {\n\t\t\treturn false\n\t\t}\n\n\t\tif at.Valid != vt.Valid {\n\t\t\treturn false\n\t\t}\n\n\t\tfor i := range at.Lexemes {\n\t\t\tatLexeme := at.Lexemes[i]\n\t\t\tvtLexeme := vt.Lexemes[i]\n\n\t\t\tif atLexeme.Word != vtLexeme.Word {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif len(atLexeme.Positions) != len(vtLexeme.Positions) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tfor j := range atLexeme.Positions {\n\t\t\t\tif atLexeme.Positions[j] != vtLexeme.Positions[j] {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true\n\t}\n}\n\nfunc tsvectorConnTestRunner(t *testing.T) pgxtest.ConnTestRunner {\n\tctr := defaultConnTestRunner\n\tctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar tsvectorOID uint32\n\t\terr := conn.QueryRow(context.Background(), `select oid from pg_type where typname = 'tsvector'`).Scan(&tsvectorOID)\n\t\tif err != nil {\n\t\t\tt.Skipf(\"Skipping; cannot find tsvector OID\")\n\t\t}\n\n\t\tconn.TypeMap().RegisterType(&pgtype.Type{Name: \"tsvector\", OID: tsvectorOID, Codec: pgtype.TSVectorCodec{}})\n\t}\n\treturn ctr\n}\n\nfunc TestTSVectorCodecBinary(t *testing.T) {\n\tt.Run(\"Core\", func(t *testing.T) {\n\t\ttests := []pgxtest.ValueRoundTripTest{\n\t\t\t// NULL.\n\t\t\t{\n\t\t\t\tParam:  pgtype.TSVector{},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest:   isExpectedEqTSVector(pgtype.TSVector{}),\n\t\t\t},\n\t\t\t// Empty but valid tsvector (no lexemes).\n\t\t\t{\n\t\t\t\tParam:  pgtype.TSVector{Valid: true},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest:   isExpectedEqTSVector(pgtype.TSVector{Valid: true}),\n\t\t\t},\n\t\t\t// Single lexeme with no positions.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{{\"fat\", nil}},\n\t\t\t\t\tValid:   true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{{\"fat\", nil}},\n\t\t\t\t\tValid:   true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Multiple lexemes with positions and weights.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"cat\", []pgtype.TSVectorPosition{{1, pgtype.TSVectorWeightA}}},\n\t\t\t\t\t\t{\"dog\", []pgtype.TSVectorPosition{{2, pgtype.TSVectorWeightB}}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"cat\", []pgtype.TSVectorPosition{{1, pgtype.TSVectorWeightA}}},\n\t\t\t\t\t\t{\"dog\", []pgtype.TSVectorPosition{{2, pgtype.TSVectorWeightB}}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// All four weight types (A, B, C, D) on a single lexeme.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"word\", []pgtype.TSVectorPosition{\n\t\t\t\t\t\t\t{1, pgtype.TSVectorWeightA},\n\t\t\t\t\t\t\t{2, pgtype.TSVectorWeightB},\n\t\t\t\t\t\t\t{3, pgtype.TSVectorWeightC},\n\t\t\t\t\t\t\t{4, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"word\", []pgtype.TSVectorPosition{\n\t\t\t\t\t\t\t{1, pgtype.TSVectorWeightA},\n\t\t\t\t\t\t\t{2, pgtype.TSVectorWeightB},\n\t\t\t\t\t\t\t{3, pgtype.TSVectorWeightC},\n\t\t\t\t\t\t\t{4, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Multiple positions per lexeme.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"spaceship\", []pgtype.TSVectorPosition{\n\t\t\t\t\t\t\t{2, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t\t{33, pgtype.TSVectorWeightA},\n\t\t\t\t\t\t\t{34, pgtype.TSVectorWeightB},\n\t\t\t\t\t\t\t{35, pgtype.TSVectorWeightC},\n\t\t\t\t\t\t\t{36, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"spaceship\", []pgtype.TSVectorPosition{\n\t\t\t\t\t\t\t{2, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t\t{33, pgtype.TSVectorWeightA},\n\t\t\t\t\t\t\t{34, pgtype.TSVectorWeightB},\n\t\t\t\t\t\t\t{35, pgtype.TSVectorWeightC},\n\t\t\t\t\t\t\t{36, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Lexeme word containing a space.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"1 2\", nil},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"1 2\", nil},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t}\n\n\t\tpgxtest.RunValueRoundTripTests(context.Background(), t, tsvectorConnTestRunner(t), pgxtest.KnownOIDQueryExecModes, \"tsvector\", tests)\n\t})\n\n\tt.Run(\"SpecialCharacters\", func(t *testing.T) {\n\t\ttests := []pgxtest.ValueRoundTripTest{\n\t\t\t// Lexeme words containing a single quote.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"D'Artagnan\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"cats'\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"don't\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"D'Artagnan\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"cats'\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"don't\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Unicode lexemes.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"café\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"naïve\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"日本語\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"café\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"naïve\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"日本語\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Lexeme words containing backslashes.\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{`ab\\c`, []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{`back\\slash`, []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{`ab\\c`, []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{`back\\slash`, []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Lexeme words containing delimiter characters (colon, comma).\n\t\t\t{\n\t\t\t\tParam: pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"a:b\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"c,d\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"a:b\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"c,d\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t}\n\n\t\tpgxtest.RunValueRoundTripTests(context.Background(), t, tsvectorConnTestRunner(t), pgxtest.KnownOIDQueryExecModes, \"tsvector\", tests)\n\t})\n}\n\nfunc TestTSVectorCodecText(t *testing.T) {\n\tt.Run(\"Core\", func(t *testing.T) {\n\t\ttests := []pgxtest.ValueRoundTripTest{\n\t\t\t// NULL.\n\t\t\t{\n\t\t\t\tParam:  pgtype.TSVector{},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest:   isExpectedEqTSVector(pgtype.TSVector{}),\n\t\t\t},\n\t\t\t// Empty but valid tsvector (no lexemes).\n\t\t\t{\n\t\t\t\tParam:  pgtype.TSVector{Valid: true},\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest:   isExpectedEqTSVector(pgtype.TSVector{Valid: true}),\n\t\t\t},\n\t\t\t// Single lexeme with no positions.\n\t\t\t{\n\t\t\t\tParam:  \"'fat'\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{{\"fat\", nil}},\n\t\t\t\t\tValid:   true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Multiple lexemes with positions and weights.\n\t\t\t{\n\t\t\t\tParam:  \"'cat':1A 'dog':2B\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"cat\", []pgtype.TSVectorPosition{{1, pgtype.TSVectorWeightA}}},\n\t\t\t\t\t\t{\"dog\", []pgtype.TSVectorPosition{{2, pgtype.TSVectorWeightB}}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// All four weight types (A, B, C, D) on a single lexeme.\n\t\t\t{\n\t\t\t\tParam:  \"'word':1A,2B,3C,4D\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"word\", []pgtype.TSVectorPosition{\n\t\t\t\t\t\t\t{1, pgtype.TSVectorWeightA},\n\t\t\t\t\t\t\t{2, pgtype.TSVectorWeightB},\n\t\t\t\t\t\t\t{3, pgtype.TSVectorWeightC},\n\t\t\t\t\t\t\t{4, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Multiple positions per lexeme.\n\t\t\t{\n\t\t\t\tParam:  \"'spaceship':2,33A,34B,35C,36D\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"spaceship\", []pgtype.TSVectorPosition{\n\t\t\t\t\t\t\t{2, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t\t{33, pgtype.TSVectorWeightA},\n\t\t\t\t\t\t\t{34, pgtype.TSVectorWeightB},\n\t\t\t\t\t\t\t{35, pgtype.TSVectorWeightC},\n\t\t\t\t\t\t\t{36, pgtype.TSVectorWeightD},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Lowercase weight letters are accepted and normalized to uppercase.\n\t\t\t{\n\t\t\t\tParam:  \"'cat':2b\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"cat\", []pgtype.TSVectorPosition{{2, pgtype.TSVectorWeightB}}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Leading and trailing whitespace is trimmed.\n\t\t\t{\n\t\t\t\tParam:  \"  'fat'  \",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"fat\", nil},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Lexeme word containing a space.\n\t\t\t{\n\t\t\t\tParam:  \"'1 2'\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"1 2\", nil},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Backslash quote escape (\\').\n\t\t\t{\n\t\t\t\tParam:  `'D\\'Artagnan' 'cats\\'' 'don\\'t'`,\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"D'Artagnan\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"cats'\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"don't\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Lexeme words containing delimiter characters (colon, comma).\n\t\t\t{\n\t\t\t\tParam:  `'a:b' 'c,d'`,\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"a:b\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"c,d\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t}\n\n\t\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.AllQueryExecModes, \"tsvector\", tests)\n\t})\n\n\tt.Run(\"SpecialCharacters\", func(t *testing.T) {\n\t\ttests := []pgxtest.ValueRoundTripTest{\n\t\t\t// Unicode lexemes.\n\t\t\t{\n\t\t\t\tParam:  \"'café' 'naïve' '日本語'\",\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"café\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"naïve\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"日本語\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Escaped space in lexeme word.\n\t\t\t{\n\t\t\t\tParam:  `'\\ '`,\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\" \", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t}\n\n\t\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.AllQueryExecModes, \"tsvector\", tests)\n\t})\n\n\tt.Run(\"PostgreSQL\", func(t *testing.T) {\n\t\tskipCockroachDB(t, \"CockroachDB does not support these escape sequences in tsvector\")\n\n\t\ttests := []pgxtest.ValueRoundTripTest{\n\t\t\t// Doubled quote escape ('').\n\t\t\t{\n\t\t\t\tParam:  `'D''Artagnan' 'cats''' 'don''t'`,\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"D'Artagnan\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"cats'\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"don't\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t\t// Escaped backslashes in lexeme words.\n\t\t\t{\n\t\t\t\tParam:  `'AB\\\\\\c' '\\\\as' 'ab\\\\\\\\c' 'ab\\\\c' 'abc'`,\n\t\t\t\tResult: new(pgtype.TSVector),\n\t\t\t\tTest: isExpectedEqTSVector(pgtype.TSVector{\n\t\t\t\t\tLexemes: []pgtype.TSVectorLexeme{\n\t\t\t\t\t\t{\"AB\\\\c\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"\\\\as\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"ab\\\\\\\\c\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"ab\\\\c\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t\t{\"abc\", []pgtype.TSVectorPosition{}},\n\t\t\t\t\t},\n\t\t\t\t\tValid: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t}\n\n\t\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.AllQueryExecModes, \"tsvector\", tests)\n\t})\n}\n"
  },
  {
    "path": "pgtype/uint32.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Uint32Scanner interface {\n\tScanUint32(v Uint32) error\n}\n\ntype Uint32Valuer interface {\n\tUint32Value() (Uint32, error)\n}\n\n// Uint32 is the core type that is used to represent PostgreSQL types such as OID, CID, and XID.\ntype Uint32 struct {\n\tUint32 uint32\n\tValid  bool\n}\n\n// ScanUint32 implements the [Uint32Scanner] interface.\nfunc (n *Uint32) ScanUint32(v Uint32) error {\n\t*n = v\n\treturn nil\n}\n\n// Uint32Value implements the [Uint32Valuer] interface.\nfunc (n Uint32) Uint32Value() (Uint32, error) {\n\treturn n, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Uint32) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Uint32{}\n\t\treturn nil\n\t}\n\n\tvar n int64\n\n\tswitch src := src.(type) {\n\tcase int64:\n\t\tn = src\n\tcase string:\n\t\tun, err := strconv.ParseUint(src, 10, 32)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tn = int64(un)\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", src)\n\t}\n\n\tif n < 0 {\n\t\treturn fmt.Errorf(\"%d is less than the minimum value for Uint32\", n)\n\t}\n\tif n > math.MaxUint32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Uint32\", n)\n\t}\n\n\t*dst = Uint32{Uint32: uint32(n), Valid: true}\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Uint32) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\treturn int64(src.Uint32), nil\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src Uint32) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\treturn json.Marshal(src.Uint32)\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *Uint32) UnmarshalJSON(b []byte) error {\n\tvar n *uint32\n\terr := json.Unmarshal(b, &n)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif n == nil {\n\t\t*dst = Uint32{}\n\t} else {\n\t\t*dst = Uint32{Uint32: *n, Valid: true}\n\t}\n\n\treturn nil\n}\n\ntype Uint32Codec struct{}\n\nfunc (Uint32Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Uint32Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Uint32Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase uint32:\n\t\t\treturn encodePlanUint32CodecBinaryUint32{}\n\t\tcase Uint32Valuer:\n\t\t\treturn encodePlanUint32CodecBinaryUint32Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanUint32CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase uint32:\n\t\t\treturn encodePlanUint32CodecTextUint32{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanUint32CodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanUint32CodecBinaryUint32 struct{}\n\nfunc (encodePlanUint32CodecBinaryUint32) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv := value.(uint32)\n\treturn pgio.AppendUint32(buf, v), nil\n}\n\ntype encodePlanUint32CodecBinaryUint32Valuer struct{}\n\nfunc (encodePlanUint32CodecBinaryUint32Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Uint32Valuer).Uint32Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn pgio.AppendUint32(buf, v.Uint32), nil\n}\n\ntype encodePlanUint32CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanUint32CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for uint32\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxUint32 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for uint32\", v.Int64)\n\t}\n\n\treturn pgio.AppendUint32(buf, uint32(v.Int64)), nil\n}\n\ntype encodePlanUint32CodecTextUint32 struct{}\n\nfunc (encodePlanUint32CodecTextUint32) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv := value.(uint32)\n\treturn append(buf, strconv.FormatUint(uint64(v), 10)...), nil\n}\n\ntype encodePlanUint32CodecTextUint32Valuer struct{}\n\nfunc (encodePlanUint32CodecTextUint32Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Uint32Valuer).Uint32Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, strconv.FormatUint(uint64(v.Uint32), 10)...), nil\n}\n\ntype encodePlanUint32CodecTextInt64Valuer struct{}\n\nfunc (encodePlanUint32CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for uint32\", v.Int64)\n\t}\n\tif v.Int64 > math.MaxUint32 {\n\t\treturn nil, fmt.Errorf(\"%d is greater than maximum value for uint32\", v.Int64)\n\t}\n\n\treturn append(buf, strconv.FormatInt(v.Int64, 10)...), nil\n}\n\nfunc (Uint32Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *uint32:\n\t\t\treturn scanPlanBinaryUint32ToUint32{}\n\t\tcase Uint32Scanner:\n\t\t\treturn scanPlanBinaryUint32ToUint32Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryUint32ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *uint32:\n\t\t\treturn scanPlanTextAnyToUint32{}\n\t\tcase Uint32Scanner:\n\t\t\treturn scanPlanTextAnyToUint32Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c Uint32Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n uint32\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn int64(n), nil\n}\n\nfunc (c Uint32Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n uint32\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\ntype scanPlanBinaryUint32ToUint32 struct{}\n\nfunc (scanPlanBinaryUint32ToUint32) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint32: %v\", len(src))\n\t}\n\n\tp := (dst).(*uint32)\n\t*p = binary.BigEndian.Uint32(src)\n\n\treturn nil\n}\n\ntype scanPlanBinaryUint32ToUint32Scanner struct{}\n\nfunc (scanPlanBinaryUint32ToUint32Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Uint32Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanUint32(Uint32{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint32: %v\", len(src))\n\t}\n\n\tn := binary.BigEndian.Uint32(src)\n\n\treturn s.ScanUint32(Uint32{Uint32: n, Valid: true})\n}\n\ntype scanPlanBinaryUint32ToTextScanner struct{}\n\nfunc (scanPlanBinaryUint32ToTextScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 4 {\n\t\treturn fmt.Errorf(\"invalid length for uint32: %v\", len(src))\n\t}\n\n\tn := uint64(binary.BigEndian.Uint32(src))\n\treturn s.ScanText(Text{String: strconv.FormatUint(n, 10), Valid: true})\n}\n\ntype scanPlanTextAnyToUint32Scanner struct{}\n\nfunc (scanPlanTextAnyToUint32Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Uint32Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanUint32(Uint32{})\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 32)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.ScanUint32(Uint32{Uint32: uint32(n), Valid: true})\n}\n"
  },
  {
    "path": "pgtype/uint32_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestUint32Codec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"oid\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Uint32{Uint32: pgtype.TextOID, Valid: true},\n\t\t\tResult: new(pgtype.Uint32),\n\t\t\tTest:   isExpectedEq(pgtype.Uint32{Uint32: pgtype.TextOID, Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Uint32{}, Result: new(pgtype.Uint32), Test: isExpectedEq(pgtype.Uint32{})},\n\t\t{Param: nil, Result: new(pgtype.Uint32), Test: isExpectedEq(pgtype.Uint32{})},\n\t\t{Param: \"1147\", Result: new(string), Test: isExpectedEq(\"1147\")},\n\t})\n}\n"
  },
  {
    "path": "pgtype/uint64.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql/driver\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n)\n\ntype Uint64Scanner interface {\n\tScanUint64(v Uint64) error\n}\n\ntype Uint64Valuer interface {\n\tUint64Value() (Uint64, error)\n}\n\n// Uint64 is the core type that is used to represent PostgreSQL types such as XID8.\ntype Uint64 struct {\n\tUint64 uint64\n\tValid  bool\n}\n\n// ScanUint64 implements the [Uint64Scanner] interface.\nfunc (n *Uint64) ScanUint64(v Uint64) error {\n\t*n = v\n\treturn nil\n}\n\n// Uint64Value implements the [Uint64Valuer] interface.\nfunc (n Uint64) Uint64Value() (Uint64, error) {\n\treturn n, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Uint64) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = Uint64{}\n\t\treturn nil\n\t}\n\n\tvar n uint64\n\n\tswitch src := src.(type) {\n\tcase int64:\n\t\tif src < 0 {\n\t\t\treturn fmt.Errorf(\"%d is less than the minimum value for Uint64\", src)\n\t\t}\n\t\tn = uint64(src)\n\tcase string:\n\t\tun, err := strconv.ParseUint(src, 10, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tn = un\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot scan %T\", src)\n\t}\n\n\t*dst = Uint64{Uint64: n, Valid: true}\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Uint64) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\t// If the value is greater than the maximum value for int64, return it as a string instead of losing data or returning\n\t// an error.\n\tif src.Uint64 > math.MaxInt64 {\n\t\treturn strconv.FormatUint(src.Uint64, 10), nil\n\t}\n\n\treturn int64(src.Uint64), nil\n}\n\ntype Uint64Codec struct{}\n\nfunc (Uint64Codec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (Uint64Codec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (Uint64Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch value.(type) {\n\t\tcase uint64:\n\t\t\treturn encodePlanUint64CodecBinaryUint64{}\n\t\tcase Uint64Valuer:\n\t\t\treturn encodePlanUint64CodecBinaryUint64Valuer{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanUint64CodecBinaryInt64Valuer{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch value.(type) {\n\t\tcase uint64:\n\t\t\treturn encodePlanUint64CodecTextUint64{}\n\t\tcase Int64Valuer:\n\t\t\treturn encodePlanUint64CodecTextInt64Valuer{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanUint64CodecBinaryUint64 struct{}\n\nfunc (encodePlanUint64CodecBinaryUint64) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv := value.(uint64)\n\treturn pgio.AppendUint64(buf, v), nil\n}\n\ntype encodePlanUint64CodecBinaryUint64Valuer struct{}\n\nfunc (encodePlanUint64CodecBinaryUint64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Uint64Valuer).Uint64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn pgio.AppendUint64(buf, v.Uint64), nil\n}\n\ntype encodePlanUint64CodecBinaryInt64Valuer struct{}\n\nfunc (encodePlanUint64CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for uint64\", v.Int64)\n\t}\n\n\treturn pgio.AppendUint64(buf, uint64(v.Int64)), nil\n}\n\ntype encodePlanUint64CodecTextUint64 struct{}\n\nfunc (encodePlanUint64CodecTextUint64) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv := value.(uint64)\n\treturn append(buf, strconv.FormatUint(uint64(v), 10)...), nil\n}\n\ntype encodePlanUint64CodecTextUint64Valuer struct{}\n\nfunc (encodePlanUint64CodecTextUint64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Uint64Valuer).Uint64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, strconv.FormatUint(v.Uint64, 10)...), nil\n}\n\ntype encodePlanUint64CodecTextInt64Valuer struct{}\n\nfunc (encodePlanUint64CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tv, err := value.(Int64Valuer).Int64Value()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !v.Valid {\n\t\treturn nil, nil\n\t}\n\n\tif v.Int64 < 0 {\n\t\treturn nil, fmt.Errorf(\"%d is less than minimum value for uint64\", v.Int64)\n\t}\n\n\treturn append(buf, strconv.FormatInt(v.Int64, 10)...), nil\n}\n\nfunc (Uint64Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *uint64:\n\t\t\treturn scanPlanBinaryUint64ToUint64{}\n\t\tcase Uint64Scanner:\n\t\t\treturn scanPlanBinaryUint64ToUint64Scanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryUint64ToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase *uint64:\n\t\t\treturn scanPlanTextAnyToUint64{}\n\t\tcase Uint64Scanner:\n\t\t\treturn scanPlanTextAnyToUint64Scanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c Uint64Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n uint64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn int64(n), nil\n}\n\nfunc (c Uint64Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar n uint64\n\terr := codecScan(c, m, oid, format, src, &n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn n, nil\n}\n\ntype scanPlanBinaryUint64ToUint64 struct{}\n\nfunc (scanPlanBinaryUint64ToUint64) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint64: %v\", len(src))\n\t}\n\n\tp := (dst).(*uint64)\n\t*p = binary.BigEndian.Uint64(src)\n\n\treturn nil\n}\n\ntype scanPlanBinaryUint64ToUint64Scanner struct{}\n\nfunc (scanPlanBinaryUint64ToUint64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Uint64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanUint64(Uint64{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint64: %v\", len(src))\n\t}\n\n\tn := binary.BigEndian.Uint64(src)\n\n\treturn s.ScanUint64(Uint64{Uint64: n, Valid: true})\n}\n\ntype scanPlanBinaryUint64ToTextScanner struct{}\n\nfunc (scanPlanBinaryUint64ToTextScanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(TextScanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanText(Text{})\n\t}\n\n\tif len(src) != 8 {\n\t\treturn fmt.Errorf(\"invalid length for uint64: %v\", len(src))\n\t}\n\n\tn := uint64(binary.BigEndian.Uint64(src))\n\treturn s.ScanText(Text{String: strconv.FormatUint(n, 10), Valid: true})\n}\n\ntype scanPlanTextAnyToUint64Scanner struct{}\n\nfunc (scanPlanTextAnyToUint64Scanner) Scan(src []byte, dst any) error {\n\ts, ok := (dst).(Uint64Scanner)\n\tif !ok {\n\t\treturn ErrScanTargetTypeChanged\n\t}\n\n\tif src == nil {\n\t\treturn s.ScanUint64(Uint64{})\n\t}\n\n\tn, err := strconv.ParseUint(string(src), 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn s.ScanUint64(Uint64{Uint64: n, Valid: true})\n}\n"
  },
  {
    "path": "pgtype/uint64_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestUint64Codec(t *testing.T) {\n\tskipCockroachDB(t, \"Server does not support xid8 (https://github.com/cockroachdb/cockroach/issues/36815)\")\n\tskipPostgreSQLVersionLessThan(t, 13)\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"xid8\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.Uint64{Uint64: 1 << 36, Valid: true},\n\t\t\tResult: new(pgtype.Uint64),\n\t\t\tTest:   isExpectedEq(pgtype.Uint64{Uint64: 1 << 36, Valid: true}),\n\t\t},\n\t\t{Param: pgtype.Uint64{}, Result: new(pgtype.Uint64), Test: isExpectedEq(pgtype.Uint64{})},\n\t\t{Param: nil, Result: new(pgtype.Uint64), Test: isExpectedEq(pgtype.Uint64{})},\n\t\t{\n\t\t\tParam:  uint64(1 << 36),\n\t\t\tResult: new(uint64),\n\t\t\tTest:   isExpectedEq(uint64(1 << 36)),\n\t\t},\n\t\t{Param: \"1147\", Result: new(string), Test: isExpectedEq(\"1147\")},\n\t})\n}\n"
  },
  {
    "path": "pgtype/uuid.go",
    "content": "package pgtype\n\nimport (\n\t\"bytes\"\n\t\"database/sql/driver\"\n\t\"encoding/hex\"\n\t\"fmt\"\n)\n\ntype UUIDScanner interface {\n\tScanUUID(v UUID) error\n}\n\ntype UUIDValuer interface {\n\tUUIDValue() (UUID, error)\n}\n\ntype UUID struct {\n\tBytes [16]byte\n\tValid bool\n}\n\n// ScanUUID implements the [UUIDScanner] interface.\nfunc (b *UUID) ScanUUID(v UUID) error {\n\t*b = v\n\treturn nil\n}\n\n// UUIDValue implements the [UUIDValuer] interface.\nfunc (b UUID) UUIDValue() (UUID, error) {\n\treturn b, nil\n}\n\n// parseUUID converts a string UUID in standard form to a byte array.\nfunc parseUUID(src string) (dst [16]byte, err error) {\n\tswitch len(src) {\n\tcase 36:\n\t\tsrc = src[0:8] + src[9:13] + src[14:18] + src[19:23] + src[24:]\n\tcase 32:\n\t\t// dashes already stripped, assume valid\n\tdefault:\n\t\t// assume invalid.\n\t\treturn dst, fmt.Errorf(\"cannot parse UUID %v\", src)\n\t}\n\n\tbuf, err := hex.DecodeString(src)\n\tif err != nil {\n\t\treturn dst, err\n\t}\n\n\tcopy(dst[:], buf)\n\treturn dst, err\n}\n\n// encodeUUID converts a uuid byte array to UUID standard string form.\nfunc encodeUUID(src [16]byte) string {\n\tvar buf [36]byte\n\n\thex.Encode(buf[0:8], src[:4])\n\tbuf[8] = '-'\n\thex.Encode(buf[9:13], src[4:6])\n\tbuf[13] = '-'\n\thex.Encode(buf[14:18], src[6:8])\n\tbuf[18] = '-'\n\thex.Encode(buf[19:23], src[8:10])\n\tbuf[23] = '-'\n\thex.Encode(buf[24:], src[10:])\n\n\treturn string(buf[:])\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *UUID) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = UUID{}\n\t\treturn nil\n\t}\n\n\tswitch src := src.(type) {\n\tcase string:\n\t\tbuf, err := parseUUID(src)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*dst = UUID{Bytes: buf, Valid: true}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"cannot scan %T\", src)\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src UUID) Value() (driver.Value, error) {\n\tif !src.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn encodeUUID(src.Bytes), nil\n}\n\nfunc (src UUID) String() string {\n\tif !src.Valid {\n\t\treturn \"\"\n\t}\n\n\treturn encodeUUID(src.Bytes)\n}\n\n// MarshalJSON implements the [encoding/json.Marshaler] interface.\nfunc (src UUID) MarshalJSON() ([]byte, error) {\n\tif !src.Valid {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tvar buff bytes.Buffer\n\tbuff.WriteByte('\"')\n\tbuff.WriteString(encodeUUID(src.Bytes))\n\tbuff.WriteByte('\"')\n\treturn buff.Bytes(), nil\n}\n\n// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.\nfunc (dst *UUID) UnmarshalJSON(src []byte) error {\n\tif bytes.Equal(src, []byte(\"null\")) {\n\t\t*dst = UUID{}\n\t\treturn nil\n\t}\n\tif len(src) != 38 {\n\t\treturn fmt.Errorf(\"invalid length for UUID: %v\", len(src))\n\t}\n\tbuf, err := parseUUID(string(src[1 : len(src)-1]))\n\tif err != nil {\n\t\treturn err\n\t}\n\t*dst = UUID{Bytes: buf, Valid: true}\n\treturn nil\n}\n\ntype UUIDCodec struct{}\n\nfunc (UUIDCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (UUIDCodec) PreferredFormat() int16 {\n\treturn BinaryFormatCode\n}\n\nfunc (UUIDCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tif _, ok := value.(UUIDValuer); !ok {\n\t\treturn nil\n\t}\n\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\treturn encodePlanUUIDCodecBinaryUUIDValuer{}\n\tcase TextFormatCode:\n\t\treturn encodePlanUUIDCodecTextUUIDValuer{}\n\t}\n\n\treturn nil\n}\n\ntype encodePlanUUIDCodecBinaryUUIDValuer struct{}\n\nfunc (encodePlanUUIDCodecBinaryUUIDValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tuuid, err := value.(UUIDValuer).UUIDValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !uuid.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, uuid.Bytes[:]...), nil\n}\n\ntype encodePlanUUIDCodecTextUUIDValuer struct{}\n\nfunc (encodePlanUUIDCodecTextUUIDValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\tuuid, err := value.(UUIDValuer).UUIDValue()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !uuid.Valid {\n\t\treturn nil, nil\n\t}\n\n\treturn append(buf, encodeUUID(uuid.Bytes)...), nil\n}\n\nfunc (UUIDCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch format {\n\tcase BinaryFormatCode:\n\t\tswitch target.(type) {\n\t\tcase UUIDScanner:\n\t\t\treturn scanPlanBinaryUUIDToUUIDScanner{}\n\t\tcase TextScanner:\n\t\t\treturn scanPlanBinaryUUIDToTextScanner{}\n\t\t}\n\tcase TextFormatCode:\n\t\tswitch target.(type) {\n\t\tcase UUIDScanner:\n\t\t\treturn scanPlanTextAnyToUUIDScanner{}\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype scanPlanBinaryUUIDToUUIDScanner struct{}\n\nfunc (scanPlanBinaryUUIDToUUIDScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(UUIDScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanUUID(UUID{})\n\t}\n\n\tif len(src) != 16 {\n\t\treturn fmt.Errorf(\"invalid length for UUID: %v\", len(src))\n\t}\n\n\tuuid := UUID{Valid: true}\n\tcopy(uuid.Bytes[:], src)\n\n\treturn scanner.ScanUUID(uuid)\n}\n\ntype scanPlanBinaryUUIDToTextScanner struct{}\n\nfunc (scanPlanBinaryUUIDToTextScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(TextScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanText(Text{})\n\t}\n\n\tif len(src) != 16 {\n\t\treturn fmt.Errorf(\"invalid length for UUID: %v\", len(src))\n\t}\n\n\tvar buf [16]byte\n\tcopy(buf[:], src)\n\n\treturn scanner.ScanText(Text{String: encodeUUID(buf), Valid: true})\n}\n\ntype scanPlanTextAnyToUUIDScanner struct{}\n\nfunc (scanPlanTextAnyToUUIDScanner) Scan(src []byte, dst any) error {\n\tscanner := (dst).(UUIDScanner)\n\n\tif src == nil {\n\t\treturn scanner.ScanUUID(UUID{})\n\t}\n\n\tbuf, err := parseUUID(string(src))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn scanner.ScanUUID(UUID{Bytes: buf, Valid: true})\n}\n\nfunc (c UUIDCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar uuid UUID\n\terr := codecScan(c, m, oid, format, src, &uuid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn encodeUUID(uuid.Bytes), nil\n}\n\nfunc (c UUIDCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar uuid UUID\n\terr := codecScan(c, m, oid, format, src, &uuid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn uuid.Bytes, nil\n}\n"
  },
  {
    "path": "pgtype/uuid_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype renamedUUIDByteArray [16]byte\n\nfunc TestUUIDCodec(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"uuid\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true},\n\t\t\tResult: new(pgtype.UUID),\n\t\t\tTest:   isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"00010203-0405-0607-0809-0a0b0c0d0e0f\",\n\t\t\tResult: new(pgtype.UUID),\n\t\t\tTest:   isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  \"000102030405060708090a0b0c0d0e0f\",\n\t\t\tResult: new(pgtype.UUID),\n\t\t\tTest:   isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true},\n\t\t\tResult: new(string),\n\t\t\tTest:   isExpectedEq(\"00010203-0405-0607-0809-0a0b0c0d0e0f\"),\n\t\t},\n\t\t{Param: pgtype.UUID{}, Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: pgtype.UUID{}, Result: new(pgtype.UUID), Test: isExpectedEq(pgtype.UUID{})},\n\t\t{Param: nil, Result: new(pgtype.UUID), Test: isExpectedEq(pgtype.UUID{})},\n\t})\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"uuid\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},\n\t\t\tResult: new(pgtype.UUID),\n\t\t\tTest:   isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  renamedUUIDByteArray{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},\n\t\t\tResult: new(pgtype.UUID),\n\t\t\tTest:   isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),\n\t\t},\n\t\t{\n\t\t\tParam:  []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},\n\t\t\tResult: new(renamedUUIDByteArray),\n\t\t\tTest:   isExpectedEq(renamedUUIDByteArray{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),\n\t\t},\n\t\t{\n\t\t\tParam:  []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},\n\t\t\tResult: new(pgtype.UUID),\n\t\t\tTest:   isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),\n\t\t},\n\t})\n}\n\nfunc TestUUID_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tsrc  pgtype.UUID\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"first\",\n\t\t\tsrc: pgtype.UUID{\n\t\t\t\tBytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\twant: \"1d485a7a-6d18-4599-8c6c-34425616887a\",\n\t\t},\n\t\t{\n\t\t\tname: \"third\",\n\t\t\tsrc: pgtype.UUID{\n\t\t\t\tBytes: [16]byte{},\n\t\t\t},\n\t\t\twant: \"\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := tt.src.String()\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"MarshalJSON() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUUID_MarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tsrc  pgtype.UUID\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tname: \"first\",\n\t\t\tsrc: pgtype.UUID{\n\t\t\t\tBytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\twant: []byte(`\"1d485a7a-6d18-4599-8c6c-34425616887a\"`),\n\t\t},\n\t\t{\n\t\t\tname: \"third\",\n\t\t\tsrc: pgtype.UUID{\n\t\t\t\tBytes: [16]byte{},\n\t\t\t},\n\t\t\twant: []byte(\"null\"),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := tt.src.MarshalJSON()\n\t\t\trequire.NoError(t, err)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"MarshalJSON() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUUID_UnmarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\twant    *pgtype.UUID\n\t\tsrc     []byte\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"first\",\n\t\t\twant: &pgtype.UUID{\n\t\t\t\tBytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122},\n\t\t\t\tValid: true,\n\t\t\t},\n\t\t\tsrc:     []byte(`\"1d485a7a-6d18-4599-8c6c-34425616887a\"`),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"second\",\n\t\t\twant: &pgtype.UUID{\n\t\t\t\tBytes: [16]byte{},\n\t\t\t},\n\t\t\tsrc:     []byte(\"null\"),\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"third\",\n\t\t\twant: &pgtype.UUID{\n\t\t\t\tBytes: [16]byte{},\n\t\t\t\tValid: false,\n\t\t\t},\n\t\t\tsrc:     []byte(\"1d485a7a-6d18-4599-8c6c-34425616887a\"),\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := &pgtype.UUID{}\n\t\t\tif err := got.UnmarshalJSON(tt.src); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"UnmarshalJSON() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"UnmarshalJSON() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pgtype/xml.go",
    "content": "package pgtype\n\nimport (\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype XMLCodec struct {\n\tMarshal   func(v any) ([]byte, error)\n\tUnmarshal func(data []byte, v any) error\n}\n\nfunc (*XMLCodec) FormatSupported(format int16) bool {\n\treturn format == TextFormatCode || format == BinaryFormatCode\n}\n\nfunc (*XMLCodec) PreferredFormat() int16 {\n\treturn TextFormatCode\n}\n\nfunc (c *XMLCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {\n\tswitch value.(type) {\n\tcase string:\n\t\treturn encodePlanXMLCodecEitherFormatString{}\n\tcase []byte:\n\t\treturn encodePlanXMLCodecEitherFormatByteSlice{}\n\n\t// Cannot rely on driver.Valuer being handled later because anything can be marshalled.\n\t//\n\t// https://github.com/jackc/pgx/issues/1430\n\t//\n\t// Check for driver.Valuer must come before xml.Marshaler so that it is guaranteed to be used\n\t// when both are implemented https://github.com/jackc/pgx/issues/1805\n\tcase driver.Valuer:\n\t\treturn &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}\n\n\t// Must come before trying wrap encode plans because a pointer to a struct may be unwrapped to a struct that can be\n\t// marshalled.\n\t//\n\t// https://github.com/jackc/pgx/issues/1681\n\tcase xml.Marshaler:\n\t\treturn &encodePlanXMLCodecEitherFormatMarshal{\n\t\t\tmarshal: c.Marshal,\n\t\t}\n\t}\n\n\t// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the\n\t// appropriate wrappers here.\n\tfor _, f := range []TryWrapEncodePlanFunc{\n\t\tTryWrapDerefPointerEncodePlan,\n\t\tTryWrapFindUnderlyingTypeEncodePlan,\n\t} {\n\t\tif wrapperPlan, nextValue, ok := f(value); ok {\n\t\t\tif nextPlan := c.PlanEncode(m, oid, format, nextValue); nextPlan != nil {\n\t\t\t\twrapperPlan.SetNext(nextPlan)\n\t\t\t\treturn wrapperPlan\n\t\t\t}\n\t\t}\n\t}\n\n\treturn &encodePlanXMLCodecEitherFormatMarshal{\n\t\tmarshal: c.Marshal,\n\t}\n}\n\ntype encodePlanXMLCodecEitherFormatString struct{}\n\nfunc (encodePlanXMLCodecEitherFormatString) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\txmlString := value.(string)\n\tbuf = append(buf, xmlString...)\n\treturn buf, nil\n}\n\ntype encodePlanXMLCodecEitherFormatByteSlice struct{}\n\nfunc (encodePlanXMLCodecEitherFormatByteSlice) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\txmlBytes := value.([]byte)\n\tif xmlBytes == nil {\n\t\treturn nil, nil\n\t}\n\n\tbuf = append(buf, xmlBytes...)\n\treturn buf, nil\n}\n\ntype encodePlanXMLCodecEitherFormatMarshal struct {\n\tmarshal func(v any) ([]byte, error)\n}\n\nfunc (e *encodePlanXMLCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {\n\txmlBytes, err := e.marshal(value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbuf = append(buf, xmlBytes...)\n\treturn buf, nil\n}\n\nfunc (c *XMLCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {\n\tswitch target.(type) {\n\tcase *string:\n\t\treturn scanPlanAnyToString{}\n\n\tcase **string:\n\t\t// This is to fix **string scanning. It seems wrong to special case **string, but it's not clear what a better\n\t\t// solution would be.\n\t\t//\n\t\t// https://github.com/jackc/pgx/issues/1470 -- **string\n\t\t// https://github.com/jackc/pgx/issues/1691 -- ** anything else\n\n\t\tif wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {\n\t\t\tif nextPlan := m.planScan(oid, format, nextDst, 0); nextPlan != nil {\n\t\t\t\tif _, failed := nextPlan.(*scanPlanFail); !failed {\n\t\t\t\t\twrapperPlan.SetNext(nextPlan)\n\t\t\t\t\treturn wrapperPlan\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase *[]byte:\n\t\treturn scanPlanXMLToByteSlice{}\n\tcase BytesScanner:\n\t\treturn scanPlanBinaryBytesToBytesScanner{}\n\n\t// Cannot rely on sql.Scanner being handled later because scanPlanXMLToXMLUnmarshal will take precedence.\n\t//\n\t// https://github.com/jackc/pgx/issues/1418\n\tcase sql.Scanner:\n\t\treturn &scanPlanSQLScanner{formatCode: format}\n\t}\n\n\treturn &scanPlanXMLToXMLUnmarshal{\n\t\tunmarshal: c.Unmarshal,\n\t}\n}\n\ntype scanPlanXMLToByteSlice struct{}\n\nfunc (scanPlanXMLToByteSlice) Scan(src []byte, dst any) error {\n\tdstBuf := dst.(*[]byte)\n\tif src == nil {\n\t\t*dstBuf = nil\n\t\treturn nil\n\t}\n\n\t*dstBuf = make([]byte, len(src))\n\tcopy(*dstBuf, src)\n\treturn nil\n}\n\ntype scanPlanXMLToXMLUnmarshal struct {\n\tunmarshal func(data []byte, v any) error\n}\n\nfunc (s *scanPlanXMLToXMLUnmarshal) Scan(src []byte, dst any) error {\n\tif src == nil {\n\t\tdstValue := reflect.ValueOf(dst)\n\t\tif dstValue.Kind() == reflect.Ptr {\n\t\t\tel := dstValue.Elem()\n\t\t\tswitch el.Kind() {\n\t\t\tcase reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface, reflect.Struct:\n\t\t\t\tel.Set(reflect.Zero(el.Type()))\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\treturn fmt.Errorf(\"cannot scan NULL into %T\", dst)\n\t}\n\n\telem := reflect.ValueOf(dst).Elem()\n\telem.Set(reflect.Zero(elem.Type()))\n\n\treturn s.unmarshal(src, dst)\n}\n\nfunc (c *XMLCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tdstBuf := make([]byte, len(src))\n\tcopy(dstBuf, src)\n\treturn dstBuf, nil\n}\n\nfunc (c *XMLCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {\n\tif src == nil {\n\t\treturn nil, nil\n\t}\n\n\tvar dst any\n\terr := c.Unmarshal(src, &dst)\n\treturn dst, err\n}\n"
  },
  {
    "path": "pgtype/xml_test.go",
    "content": "package pgtype_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/xml\"\n\t\"testing\"\n\n\tpgx \"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype xmlStruct struct {\n\tXMLName xml.Name `xml:\"person\"`\n\tName    string   `xml:\"name\"`\n\tAge     int      `xml:\"age,attr\"`\n}\n\nfunc TestXMLCodec(t *testing.T) {\n\tskipCockroachDB(t, \"CockroachDB does not support XML.\")\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"xml\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: nil, Result: new(*xmlStruct), Test: isExpectedEq((*xmlStruct)(nil))},\n\t\t{Param: map[string]any(nil), Result: new(*string), Test: isExpectedEq((*string)(nil))},\n\t\t{Param: map[string]any(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: []byte(nil), Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\t\t{Param: nil, Result: new([]byte), Test: isExpectedEqBytes([]byte(nil))},\n\n\t\t// Test sql.Scanner.\n\t\t{Param: \"\", Result: new(sql.NullString), Test: isExpectedEq(sql.NullString{String: \"\", Valid: true})},\n\n\t\t// Test driver.Valuer.\n\t\t{Param: sql.NullString{String: \"\", Valid: true}, Result: new(sql.NullString), Test: isExpectedEq(sql.NullString{String: \"\", Valid: true})},\n\t})\n\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, \"xml\", []pgxtest.ValueRoundTripTest{\n\t\t{Param: []byte(`<?xml version=\"1.0\"?><Root></Root>`), Result: new([]byte), Test: isExpectedEqBytes([]byte(`<Root></Root>`))},\n\t\t{Param: []byte(`<?xml version=\"1.0\"?>`), Result: new([]byte), Test: isExpectedEqBytes([]byte(``))},\n\t\t{Param: []byte(`<?xml version=\"1.0\"?>`), Result: new(string), Test: isExpectedEq(``)},\n\t\t{Param: []byte(`<Root></Root>`), Result: new([]byte), Test: isExpectedEqBytes([]byte(`<Root></Root>`))},\n\t\t{Param: []byte(`<Root></Root>`), Result: new(string), Test: isExpectedEq(`<Root></Root>`)},\n\t\t{Param: []byte(\"\"), Result: new([]byte), Test: isExpectedEqBytes([]byte(\"\"))},\n\t\t{Param: xmlStruct{Name: \"Adam\", Age: 10}, Result: new(xmlStruct), Test: isExpectedEq(xmlStruct{XMLName: xml.Name{Local: \"person\"}, Name: \"Adam\", Age: 10})},\n\t\t{Param: xmlStruct{XMLName: xml.Name{Local: \"person\"}, Name: \"Adam\", Age: 10}, Result: new(xmlStruct), Test: isExpectedEq(xmlStruct{XMLName: xml.Name{Local: \"person\"}, Name: \"Adam\", Age: 10})},\n\t\t{Param: []byte(`<person age=\"10\"><name>Adam</name></person>`), Result: new(xmlStruct), Test: isExpectedEq(xmlStruct{XMLName: xml.Name{Local: \"person\"}, Name: \"Adam\", Age: 10})},\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1273#issuecomment-1221414648\nfunc TestXMLCodecUnmarshalSQLNull(t *testing.T) {\n\tskipCockroachDB(t, \"CockroachDB does not support XML.\")\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t// Byte arrays are nilified\n\t\tslice := []byte{10, 4}\n\t\terr := conn.QueryRow(ctx, \"select null::xml\").Scan(&slice)\n\t\tassert.NoError(t, err)\n\t\tassert.Nil(t, slice)\n\n\t\t// Non-pointer structs are zeroed\n\t\tm := xmlStruct{Name: \"Adam\"}\n\t\terr = conn.QueryRow(ctx, \"select null::xml\").Scan(&m)\n\t\tassert.NoError(t, err)\n\t\tassert.Empty(t, m)\n\n\t\t// Pointers to structs are nilified\n\t\tpm := &xmlStruct{Name: \"Adam\"}\n\t\terr = conn.QueryRow(ctx, \"select null::xml\").Scan(&pm)\n\t\tassert.NoError(t, err)\n\t\tassert.Nil(t, pm)\n\n\t\t// Pointer to pointer are nilified\n\t\tn := \"\"\n\t\tp := &n\n\t\terr = conn.QueryRow(ctx, \"select null::xml\").Scan(&p)\n\t\tassert.NoError(t, err)\n\t\tassert.Nil(t, p)\n\n\t\t// A string cannot scan a NULL.\n\t\tstr := \"foobar\"\n\t\terr = conn.QueryRow(ctx, \"select null::xml\").Scan(&str)\n\t\tassert.EqualError(t, err, \"can't scan into dest[0] (col: xml): cannot scan NULL into *string\")\n\t})\n}\n\nfunc TestXMLCodecPointerToPointerToString(t *testing.T) {\n\tskipCockroachDB(t, \"CockroachDB does not support XML.\")\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar s *string\n\t\terr := conn.QueryRow(ctx, \"select ''::xml\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, s)\n\t\trequire.Equal(t, \"\", *s)\n\n\t\terr = conn.QueryRow(ctx, \"select null::xml\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, s)\n\t})\n}\n\nfunc TestXMLCodecDecodeValue(t *testing.T) {\n\tskipCockroachDB(t, \"CockroachDB does not support XML.\")\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {\n\t\tfor _, tt := range []struct {\n\t\t\tsql      string\n\t\t\texpected any\n\t\t}{\n\t\t\t{\n\t\t\t\tsql:      `select '<foo>bar</foo>'::xml`,\n\t\t\t\texpected: []byte(\"<foo>bar</foo>\"),\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(tt.sql, func(t *testing.T) {\n\t\t\t\trows, err := conn.Query(ctx, tt.sql)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\tvalues, err := rows.Values()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\trequire.Len(t, values, 1)\n\t\t\t\t\trequire.Equal(t, tt.expected, values[0])\n\t\t\t\t}\n\n\t\t\t\trequire.NoError(t, rows.Err())\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/doc.go",
    "content": "// Package zeronull contains types that automatically convert between database NULLs and Go zero values.\n/*\nSometimes the distinction between a zero value and a NULL value is not useful at the application level. For example,\nin PostgreSQL an empty string may be stored as NULL. There is usually no application level distinction between an\nempty string and a NULL string. Package zeronull implements types that seamlessly convert between PostgreSQL NULL and\nthe zero value.\n\nIt is recommended to convert types at usage time rather than instantiate these types directly. In the example below,\nmiddlename would be stored as a NULL.\n\n\t\tfirstname := \"John\"\n\t\tmiddlename := \"\"\n\t\tlastname := \"Smith\"\n\t\t_, err := conn.Exec(\n\t\t\tctx,\n\t\t\t\"insert into people(firstname, middlename, lastname) values($1, $2, $3)\",\n\t\t\tzeronull.Text(firstname),\n\t\t\tzeronull.Text(middlename),\n\t\t\tzeronull.Text(lastname),\n\t\t)\n*/\npackage zeronull\n"
  },
  {
    "path": "pgtype/zeronull/float8.go",
    "content": "package zeronull\n\nimport (\n\t\"database/sql/driver\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\ntype Float8 float64\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Float8) SkipUnderlyingTypePlan() {}\n\n// ScanFloat64 implements the [pgtype.Float64Scanner] interface.\nfunc (f *Float8) ScanFloat64(n pgtype.Float8) error {\n\tif !n.Valid {\n\t\t*f = 0\n\t\treturn nil\n\t}\n\n\t*f = Float8(n.Float64)\n\n\treturn nil\n}\n\n// Float64Value implements the [pgtype.Float64Valuer] interface.\nfunc (f Float8) Float64Value() (pgtype.Float8, error) {\n\tif f == 0 {\n\t\treturn pgtype.Float8{}, nil\n\t}\n\treturn pgtype.Float8{Float64: float64(f), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (f *Float8) Scan(src any) error {\n\tif src == nil {\n\t\t*f = 0\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Float8\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*f = Float8(nullable.Float64)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (f Float8) Value() (driver.Value, error) {\n\tif f == 0 {\n\t\treturn nil, nil\n\t}\n\treturn float64(f), nil\n}\n"
  },
  {
    "path": "pgtype/zeronull/float8_test.go",
    "content": "package zeronull_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEq(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\treturn a == v\n\t}\n}\n\nfunc TestFloat8Transcode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"float8\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  (zeronull.Float8)(1),\n\t\t\tResult: new(zeronull.Float8),\n\t\t\tTest:   isExpectedEq((zeronull.Float8)(1)),\n\t\t},\n\t\t{\n\t\t\tParam:  nil,\n\t\t\tResult: new(zeronull.Float8),\n\t\t\tTest:   isExpectedEq((zeronull.Float8)(0)),\n\t\t},\n\t\t{\n\t\t\tParam:  (zeronull.Float8)(0),\n\t\t\tResult: new(any),\n\t\t\tTest:   isExpectedEq(nil),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/int.go",
    "content": "// Code generated from pgtype/zeronull/int.go.erb. DO NOT EDIT.\n\npackage zeronull\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\ntype Int2 int16\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Int2) SkipUnderlyingTypePlan() {}\n\n// ScanInt64 implements the [pgtype.Int64Scanner] interface.\nfunc (dst *Int2) ScanInt64(n pgtype.Int8) error {\n\tif !n.Valid {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt16 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int2\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt16 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int2\", n.Int64)\n\t}\n\t*dst = Int2(n.Int64)\n\n\treturn nil\n}\n\n// Int64Value implements the [pgtype.Int64Valuer] interface.\nfunc (src Int2) Int64Value() (pgtype.Int8, error) {\n\tif src == 0 {\n\t\treturn pgtype.Int8{}, nil\n\t}\n\treturn pgtype.Int8{Int64: int64(src), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int2) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Int2\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*dst = Int2(nullable.Int16)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int2) Value() (driver.Value, error) {\n\tif src == 0 {\n\t\treturn nil, nil\n\t}\n\treturn int64(src), nil\n}\n\ntype Int4 int32\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Int4) SkipUnderlyingTypePlan() {}\n\n// ScanInt64 implements the [pgtype.Int64Scanner] interface.\nfunc (dst *Int4) ScanInt64(n pgtype.Int8) error {\n\tif !n.Valid {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt32 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int4\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt32 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int4\", n.Int64)\n\t}\n\t*dst = Int4(n.Int64)\n\n\treturn nil\n}\n\n// Int64Value implements the [pgtype.Int64Valuer] interface.\nfunc (src Int4) Int64Value() (pgtype.Int8, error) {\n\tif src == 0 {\n\t\treturn pgtype.Int8{}, nil\n\t}\n\treturn pgtype.Int8{Int64: int64(src), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int4) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Int4\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*dst = Int4(nullable.Int32)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int4) Value() (driver.Value, error) {\n\tif src == 0 {\n\t\treturn nil, nil\n\t}\n\treturn int64(src), nil\n}\n\ntype Int8 int64\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Int8) SkipUnderlyingTypePlan() {}\n\n// ScanInt64 implements the [pgtype.Int64Scanner] interface.\nfunc (dst *Int8) ScanInt64(n pgtype.Int8) error {\n\tif !n.Valid {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt64 {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int8\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt64 {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int8\", n.Int64)\n\t}\n\t*dst = Int8(n.Int64)\n\n\treturn nil\n}\n\n// Int64Value implements the [pgtype.Int64Valuer] interface.\nfunc (src Int8) Int64Value() (pgtype.Int8, error) {\n\tif src == 0 {\n\t\treturn pgtype.Int8{}, nil\n\t}\n\treturn pgtype.Int8{Int64: int64(src), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int8) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Int8\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*dst = Int8(nullable.Int64)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int8) Value() (driver.Value, error) {\n\tif src == 0 {\n\t\treturn nil, nil\n\t}\n\treturn int64(src), nil\n}\n"
  },
  {
    "path": "pgtype/zeronull/int.go.erb",
    "content": "package zeronull\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"math\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n<% [2, 4, 8].each do |pg_byte_size| %>\n<% pg_bit_size = pg_byte_size * 8 %>\ntype Int<%= pg_byte_size %> int<%= pg_bit_size %>\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Int<%= pg_byte_size %>) SkipUnderlyingTypePlan() {}\n\n// ScanInt64 implements the [pgtype.Int64Scanner] interface.\nfunc (dst *Int<%= pg_byte_size %>) ScanInt64(n pgtype.Int8) error {\n\tif !n.Valid {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tif n.Int64 < math.MinInt<%= pg_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is less than minimum value for Int<%= pg_byte_size %>\", n.Int64)\n\t}\n\tif n.Int64 > math.MaxInt<%= pg_bit_size %> {\n\t\treturn fmt.Errorf(\"%d is greater than maximum value for Int<%= pg_byte_size %>\", n.Int64)\n\t}\n\t*dst = Int<%= pg_byte_size %>(n.Int64)\n\n\treturn nil\n}\n\n// Int64Value implements the [pgtype.Int64Valuer] interface.\nfunc (src Int<%= pg_byte_size %>) Int64Value() (pgtype.Int8, error) {\n\tif src == 0 {\n\t\treturn pgtype.Int8{}, nil\n\t}\n\treturn pgtype.Int8{Int64: int64(src), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Int<%= pg_byte_size %>) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = 0\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Int<%= pg_byte_size %>\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*dst = Int<%= pg_byte_size %>(nullable.Int<%= pg_bit_size %>)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Int<%= pg_byte_size %>) Value() (driver.Value, error) {\n\tif src == 0 {\n\t\treturn nil, nil\n\t}\n\treturn int64(src), nil\n}\n<% end %>\n"
  },
  {
    "path": "pgtype/zeronull/int_test.go",
    "content": "// Code generated from pgtype/zeronull/int_test.go.erb. DO NOT EDIT.\n\npackage zeronull_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestInt2Transcode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int2\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\t(zeronull.Int2)(1),\n\t\t\tnew(zeronull.Int2),\n\t\t\tisExpectedEq((zeronull.Int2)(1)),\n\t\t},\n\t\t{\n\t\t\tnil,\n\t\t\tnew(zeronull.Int2),\n\t\t\tisExpectedEq((zeronull.Int2)(0)),\n\t\t},\n\t\t{\n\t\t\t(zeronull.Int2)(0),\n\t\t\tnew(any),\n\t\t\tisExpectedEq(nil),\n\t\t},\n\t})\n}\n\nfunc TestInt4Transcode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int4\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\t(zeronull.Int4)(1),\n\t\t\tnew(zeronull.Int4),\n\t\t\tisExpectedEq((zeronull.Int4)(1)),\n\t\t},\n\t\t{\n\t\t\tnil,\n\t\t\tnew(zeronull.Int4),\n\t\t\tisExpectedEq((zeronull.Int4)(0)),\n\t\t},\n\t\t{\n\t\t\t(zeronull.Int4)(0),\n\t\t\tnew(any),\n\t\t\tisExpectedEq(nil),\n\t\t},\n\t})\n}\n\nfunc TestInt8Transcode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int8\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\t(zeronull.Int8)(1),\n\t\t\tnew(zeronull.Int8),\n\t\t\tisExpectedEq((zeronull.Int8)(1)),\n\t\t},\n\t\t{\n\t\t\tnil,\n\t\t\tnew(zeronull.Int8),\n\t\t\tisExpectedEq((zeronull.Int8)(0)),\n\t\t},\n\t\t{\n\t\t\t(zeronull.Int8)(0),\n\t\t\tnew(any),\n\t\t\tisExpectedEq(nil),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/int_test.go.erb",
    "content": "package zeronull_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/testutil\"\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n)\n\n<% [2, 4, 8].each do |pg_byte_size| %>\n<% pg_bit_size = pg_byte_size * 8 %>\nfunc TestInt<%= pg_byte_size %>Transcode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"int<%= pg_byte_size %>\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\t(zeronull.Int<%= pg_byte_size %>)(1),\n\t\t\tnew(zeronull.Int<%= pg_byte_size %>),\n\t\t\tisExpectedEq((zeronull.Int<%= pg_byte_size %>)(1)),\n\t\t},\n\t\t{\n\t\t\tnil,\n\t\t\tnew(zeronull.Int<%= pg_byte_size %>),\n\t\t\tisExpectedEq((zeronull.Int<%= pg_byte_size %>)(0)),\n\t\t},\n\t\t{\n\t\t\t(zeronull.Int<%= pg_byte_size %>)(0),\n\t\t\tnew(any),\n\t\t\tisExpectedEq(nil),\n\t\t},\n\t})\n}\n<% end %>\n"
  },
  {
    "path": "pgtype/zeronull/text.go",
    "content": "package zeronull\n\nimport (\n\t\"database/sql/driver\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\ntype Text string\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Text) SkipUnderlyingTypePlan() {}\n\n// ScanText implements the [pgtype.TextScanner] interface.\nfunc (dst *Text) ScanText(v pgtype.Text) error {\n\tif !v.Valid {\n\t\t*dst = \"\"\n\t\treturn nil\n\t}\n\n\t*dst = Text(v.String)\n\n\treturn nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (dst *Text) Scan(src any) error {\n\tif src == nil {\n\t\t*dst = \"\"\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Text\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*dst = Text(nullable.String)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (src Text) Value() (driver.Value, error) {\n\tif src == \"\" {\n\t\treturn nil, nil\n\t}\n\treturn string(src), nil\n}\n"
  },
  {
    "path": "pgtype/zeronull/text_test.go",
    "content": "package zeronull_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestTextTranscode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"text\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  (zeronull.Text)(\"foo\"),\n\t\t\tResult: new(zeronull.Text),\n\t\t\tTest:   isExpectedEq((zeronull.Text)(\"foo\")),\n\t\t},\n\t\t{\n\t\t\tParam:  nil,\n\t\t\tResult: new(zeronull.Text),\n\t\t\tTest:   isExpectedEq((zeronull.Text)(\"\")),\n\t\t},\n\t\t{\n\t\t\tParam:  (zeronull.Text)(\"\"),\n\t\t\tResult: new(any),\n\t\t\tTest:   isExpectedEq(nil),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/timestamp.go",
    "content": "package zeronull\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\ntype Timestamp time.Time\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Timestamp) SkipUnderlyingTypePlan() {}\n\n// ScanTimestamp implements the [pgtype.TimestampScanner] interface.\nfunc (ts *Timestamp) ScanTimestamp(v pgtype.Timestamp) error {\n\tif !v.Valid {\n\t\t*ts = Timestamp{}\n\t\treturn nil\n\t}\n\n\tswitch v.InfinityModifier {\n\tcase pgtype.Finite:\n\t\t*ts = Timestamp(v.Time)\n\t\treturn nil\n\tcase pgtype.Infinity:\n\t\treturn fmt.Errorf(\"cannot scan Infinity into *time.Time\")\n\tcase pgtype.NegativeInfinity:\n\t\treturn fmt.Errorf(\"cannot scan -Infinity into *time.Time\")\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid InfinityModifier: %v\", v.InfinityModifier)\n\t}\n}\n\n// TimestampValue implements the [pgtype.TimestampValuer] interface.\nfunc (ts Timestamp) TimestampValue() (pgtype.Timestamp, error) {\n\tif time.Time(ts).IsZero() {\n\t\treturn pgtype.Timestamp{}, nil\n\t}\n\n\treturn pgtype.Timestamp{Time: time.Time(ts), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (ts *Timestamp) Scan(src any) error {\n\tif src == nil {\n\t\t*ts = Timestamp{}\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Timestamp\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*ts = Timestamp(nullable.Time)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (ts Timestamp) Value() (driver.Value, error) {\n\tif time.Time(ts).IsZero() {\n\t\treturn nil, nil\n\t}\n\n\treturn time.Time(ts), nil\n}\n"
  },
  {
    "path": "pgtype/zeronull/timestamp_test.go",
    "content": "package zeronull_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqTimestamp(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tat := time.Time(a.(zeronull.Timestamp))\n\t\tvt := time.Time(v.(zeronull.Timestamp))\n\n\t\treturn at.Equal(vt)\n\t}\n}\n\nfunc TestTimestampTranscode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"timestamp\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  (zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),\n\t\t\tResult: new(zeronull.Timestamp),\n\t\t\tTest:   isExpectedEqTimestamp((zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC))),\n\t\t},\n\t\t{\n\t\t\tParam:  nil,\n\t\t\tResult: new(zeronull.Timestamp),\n\t\t\tTest:   isExpectedEqTimestamp((zeronull.Timestamp)(time.Time{})),\n\t\t},\n\t\t{\n\t\t\tParam:  (zeronull.Timestamp)(time.Time{}),\n\t\t\tResult: new(any),\n\t\t\tTest:   isExpectedEq(nil),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/timestamptz.go",
    "content": "package zeronull\n\nimport (\n\t\"database/sql/driver\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\ntype Timestamptz time.Time\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (Timestamptz) SkipUnderlyingTypePlan() {}\n\n// ScanTimestamptz implements the [pgtype.TimestamptzScanner] interface.\nfunc (ts *Timestamptz) ScanTimestamptz(v pgtype.Timestamptz) error {\n\tif !v.Valid {\n\t\t*ts = Timestamptz{}\n\t\treturn nil\n\t}\n\n\tswitch v.InfinityModifier {\n\tcase pgtype.Finite:\n\t\t*ts = Timestamptz(v.Time)\n\t\treturn nil\n\tcase pgtype.Infinity:\n\t\treturn fmt.Errorf(\"cannot scan Infinity into *time.Time\")\n\tcase pgtype.NegativeInfinity:\n\t\treturn fmt.Errorf(\"cannot scan -Infinity into *time.Time\")\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid InfinityModifier: %v\", v.InfinityModifier)\n\t}\n}\n\n// TimestamptzValue implements the [pgtype.TimestamptzValuer] interface.\nfunc (ts Timestamptz) TimestamptzValue() (pgtype.Timestamptz, error) {\n\tif time.Time(ts).IsZero() {\n\t\treturn pgtype.Timestamptz{}, nil\n\t}\n\n\treturn pgtype.Timestamptz{Time: time.Time(ts), Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (ts *Timestamptz) Scan(src any) error {\n\tif src == nil {\n\t\t*ts = Timestamptz{}\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.Timestamptz\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*ts = Timestamptz(nullable.Time)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (ts Timestamptz) Value() (driver.Value, error) {\n\tif time.Time(ts).IsZero() {\n\t\treturn nil, nil\n\t}\n\n\treturn time.Time(ts), nil\n}\n"
  },
  {
    "path": "pgtype/zeronull/timestamptz_test.go",
    "content": "package zeronull_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc isExpectedEqTimestamptz(a any) func(any) bool {\n\treturn func(v any) bool {\n\t\tat := time.Time(a.(zeronull.Timestamptz))\n\t\tvt := time.Time(v.(zeronull.Timestamptz))\n\n\t\treturn at.Equal(vt)\n\t}\n}\n\nfunc TestTimestamptzTranscode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"timestamptz\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  (zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),\n\t\t\tResult: new(zeronull.Timestamptz),\n\t\t\tTest:   isExpectedEqTimestamptz((zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC))),\n\t\t},\n\t\t{\n\t\t\tParam:  nil,\n\t\t\tResult: new(zeronull.Timestamptz),\n\t\t\tTest:   isExpectedEqTimestamptz((zeronull.Timestamptz)(time.Time{})),\n\t\t},\n\t\t{\n\t\t\tParam:  (zeronull.Timestamptz)(time.Time{}),\n\t\t\tResult: new(any),\n\t\t\tTest:   isExpectedEq(nil),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/uuid.go",
    "content": "package zeronull\n\nimport (\n\t\"database/sql/driver\"\n\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\ntype UUID [16]byte\n\n// SkipUnderlyingTypePlan implements the [pgtype.SkipUnderlyingTypePlanner] interface.\nfunc (UUID) SkipUnderlyingTypePlan() {}\n\n// ScanUUID implements the [pgtype.UUIDScanner] interface.\nfunc (u *UUID) ScanUUID(v pgtype.UUID) error {\n\tif !v.Valid {\n\t\t*u = UUID{}\n\t\treturn nil\n\t}\n\n\t*u = UUID(v.Bytes)\n\n\treturn nil\n}\n\n// UUIDValue implements the [pgtype.UUIDValuer] interface.\nfunc (u UUID) UUIDValue() (pgtype.UUID, error) {\n\tif u == (UUID{}) {\n\t\treturn pgtype.UUID{}, nil\n\t}\n\treturn pgtype.UUID{Bytes: u, Valid: true}, nil\n}\n\n// Scan implements the [database/sql.Scanner] interface.\nfunc (u *UUID) Scan(src any) error {\n\tif src == nil {\n\t\t*u = UUID{}\n\t\treturn nil\n\t}\n\n\tvar nullable pgtype.UUID\n\terr := nullable.Scan(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*u = UUID(nullable.Bytes)\n\n\treturn nil\n}\n\n// Value implements the [database/sql/driver.Valuer] interface.\nfunc (u UUID) Value() (driver.Value, error) {\n\tif u == (UUID{}) {\n\t\treturn nil, nil\n\t}\n\n\tbuf, err := pgtype.UUIDCodec{}.PlanEncode(nil, pgtype.UUIDOID, pgtype.TextFormatCode, u).Encode(u, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn string(buf), nil\n}\n"
  },
  {
    "path": "pgtype/zeronull/uuid_test.go",
    "content": "package zeronull_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\nfunc TestUUIDTranscode(t *testing.T) {\n\tpgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, \"uuid\", []pgxtest.ValueRoundTripTest{\n\t\t{\n\t\t\tParam:  (zeronull.UUID)([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),\n\t\t\tResult: new(zeronull.UUID),\n\t\t\tTest:   isExpectedEq((zeronull.UUID)([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})),\n\t\t},\n\t\t{\n\t\t\tParam:  nil,\n\t\t\tResult: new(zeronull.UUID),\n\t\t\tTest:   isExpectedEq((zeronull.UUID)([16]byte{})),\n\t\t},\n\t\t{\n\t\t\tParam:  (zeronull.UUID)([16]byte{}),\n\t\t\tResult: new(any),\n\t\t\tTest:   isExpectedEq(nil),\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pgtype/zeronull/zeronull.go",
    "content": "package zeronull\n\nimport (\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// Register registers the zeronull types so they can be used in query exec modes that do not know the server OIDs.\nfunc Register(m *pgtype.Map) {\n\tm.RegisterDefaultPgType(Float8(0), \"float8\")\n\tm.RegisterDefaultPgType(Int2(0), \"int2\")\n\tm.RegisterDefaultPgType(Int4(0), \"int4\")\n\tm.RegisterDefaultPgType(Int8(0), \"int8\")\n\tm.RegisterDefaultPgType(Text(\"\"), \"text\")\n\tm.RegisterDefaultPgType(Timestamp{}, \"timestamp\")\n\tm.RegisterDefaultPgType(Timestamptz{}, \"timestamptz\")\n\tm.RegisterDefaultPgType(UUID{}, \"uuid\")\n}\n"
  },
  {
    "path": "pgtype/zeronull/zeronull_test.go",
    "content": "package zeronull_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgtype/zeronull\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nvar defaultConnTestRunner pgxtest.ConnTestRunner\n\nfunc init() {\n\tdefaultConnTestRunner = pgxtest.DefaultConnTestRunner()\n\tdefaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\treturn config\n\t}\n\tdefaultConnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tzeronull.Register(conn.TypeMap())\n\t}\n}\n"
  },
  {
    "path": "pgx_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n)\n\nfunc skipCockroachDB(t testing.TB, msg string) {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer conn.Close(context.Background())\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(msg)\n\t}\n}\n"
  },
  {
    "path": "pgxpool/batch_results.go",
    "content": "package pgxpool\n\nimport (\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\ntype errBatchResults struct {\n\terr error\n}\n\nfunc (br errBatchResults) Exec() (pgconn.CommandTag, error) {\n\treturn pgconn.CommandTag{}, br.err\n}\n\nfunc (br errBatchResults) Query() (pgx.Rows, error) {\n\treturn errRows{err: br.err}, br.err\n}\n\nfunc (br errBatchResults) QueryRow() pgx.Row {\n\treturn errRow{err: br.err}\n}\n\nfunc (br errBatchResults) Close() error {\n\treturn br.err\n}\n\ntype poolBatchResults struct {\n\tbr pgx.BatchResults\n\tc  *Conn\n}\n\nfunc (br *poolBatchResults) Exec() (pgconn.CommandTag, error) {\n\treturn br.br.Exec()\n}\n\nfunc (br *poolBatchResults) Query() (pgx.Rows, error) {\n\treturn br.br.Query()\n}\n\nfunc (br *poolBatchResults) QueryRow() pgx.Row {\n\treturn br.br.QueryRow()\n}\n\nfunc (br *poolBatchResults) Close() error {\n\terr := br.br.Close()\n\tif br.c != nil {\n\t\tbr.c.Release()\n\t\tbr.c = nil\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "pgxpool/bench_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc BenchmarkAcquireAndRelease(b *testing.B) {\n\tpool, err := pgxpool.New(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(b, err)\n\tdefer pool.Close()\n\n\tfor b.Loop() {\n\t\tc, err := pool.Acquire(context.Background())\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tc.Release()\n\t}\n}\n\nfunc BenchmarkMinimalPreparedSelectBaseline(b *testing.B) {\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(b, err)\n\n\tconfig.AfterConnect = func(ctx context.Context, c *pgx.Conn) error {\n\t\t_, err := c.Prepare(ctx, \"ps1\", \"select $1::int8\")\n\t\treturn err\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(context.Background(), config)\n\trequire.NoError(b, err)\n\n\tconn, err := db.Acquire(context.Background())\n\trequire.NoError(b, err)\n\tdefer conn.Release()\n\n\tvar n int64\n\n\tfor i := 0; b.Loop(); i++ {\n\t\terr = conn.QueryRow(context.Background(), \"ps1\", i).Scan(&n)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif n != int64(i) {\n\t\t\tb.Fatalf(\"expected %d, got %d\", i, n)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMinimalPreparedSelect(b *testing.B) {\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(b, err)\n\n\tconfig.AfterConnect = func(ctx context.Context, c *pgx.Conn) error {\n\t\t_, err := c.Prepare(ctx, \"ps1\", \"select $1::int8\")\n\t\treturn err\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(context.Background(), config)\n\trequire.NoError(b, err)\n\n\tvar n int64\n\n\tfor i := 0; b.Loop(); i++ {\n\t\terr = db.QueryRow(context.Background(), \"ps1\", i).Scan(&n)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tif n != int64(i) {\n\t\t\tb.Fatalf(\"expected %d, got %d\", i, n)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgxpool/common_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// Conn.Release is an asynchronous process that returns immediately. There is no signal when the actual work is\n// completed. To test something that relies on the actual work for Conn.Release being completed we must simply wait.\n// This function wraps the sleep so there is more meaning for the callers.\nfunc waitForReleaseToComplete() {\n\ttime.Sleep(500 * time.Millisecond)\n}\n\ntype execer interface {\n\tExec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error)\n}\n\nfunc testExec(t *testing.T, ctx context.Context, db execer) {\n\tresults, err := db.Exec(ctx, \"set time zone 'America/Chicago'\")\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, \"SET\", results.String())\n}\n\ntype queryer interface {\n\tQuery(ctx context.Context, sql string, args ...any) (pgx.Rows, error)\n}\n\nfunc testQuery(t *testing.T, ctx context.Context, db queryer) {\n\tvar sum, rowCount int32\n\n\trows, err := db.Query(ctx, \"select generate_series(1,$1)\", 10)\n\trequire.NoError(t, err)\n\n\tfor rows.Next() {\n\t\tvar n int32\n\t\trows.Scan(&n)\n\t\tsum += n\n\t\trowCount++\n\t}\n\n\tassert.NoError(t, rows.Err())\n\tassert.Equal(t, int32(10), rowCount)\n\tassert.Equal(t, int32(55), sum)\n}\n\ntype queryRower interface {\n\tQueryRow(ctx context.Context, sql string, args ...any) pgx.Row\n}\n\nfunc testQueryRow(t *testing.T, ctx context.Context, db queryRower) {\n\tvar what, who string\n\terr := db.QueryRow(ctx, \"select 'hello', $1::text\", \"world\").Scan(&what, &who)\n\tassert.NoError(t, err)\n\tassert.Equal(t, \"hello\", what)\n\tassert.Equal(t, \"world\", who)\n}\n\ntype sendBatcher interface {\n\tSendBatch(context.Context, *pgx.Batch) pgx.BatchResults\n}\n\nfunc testSendBatch(t *testing.T, ctx context.Context, db sendBatcher) {\n\tbatch := &pgx.Batch{}\n\tbatch.Queue(\"select 1\")\n\tbatch.Queue(\"select 2\")\n\n\tbr := db.SendBatch(ctx, batch)\n\n\tvar err error\n\tvar n int32\n\terr = br.QueryRow().Scan(&n)\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 1, n)\n\n\terr = br.QueryRow().Scan(&n)\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 2, n)\n\n\terr = br.Close()\n\tassert.NoError(t, err)\n}\n\ntype copyFromer interface {\n\tCopyFrom(context.Context, pgx.Identifier, []string, pgx.CopyFromSource) (int64, error)\n}\n\nfunc testCopyFrom(t *testing.T, ctx context.Context, db interface {\n\texecer\n\tqueryer\n\tcopyFromer\n},\n) {\n\t_, err := db.Exec(ctx, `create temporary table foo(a int2, b int4, c int8, d varchar, e text, f date, g timestamptz)`)\n\trequire.NoError(t, err)\n\n\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\tinputRows := [][]any{\n\t\t{int16(0), int32(1), int64(2), \"abc\", \"efg\", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},\n\t\t{nil, nil, nil, nil, nil, nil, nil},\n\t}\n\n\tcopyCount, err := db.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\"}, pgx.CopyFromRows(inputRows))\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, len(inputRows), copyCount)\n\n\trows, err := db.Query(ctx, \"select * from foo\")\n\tassert.NoError(t, err)\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tassert.NoError(t, rows.Err())\n\tassert.Equal(t, inputRows, outputRows)\n}\n\nfunc assertConfigsEqual(t *testing.T, expected, actual *pgxpool.Config, testName string) {\n\tif !assert.NotNil(t, expected) {\n\t\treturn\n\t}\n\tif !assert.NotNil(t, actual) {\n\t\treturn\n\t}\n\n\tassert.Equalf(t, expected.ConnString(), actual.ConnString(), \"%s - ConnString\", testName)\n\n\t// Can't test function equality, so just test that they are set or not.\n\tassert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, \"%s - AfterConnect\", testName)\n\tassert.Equalf(t, expected.BeforeAcquire == nil, actual.BeforeAcquire == nil, \"%s - BeforeAcquire\", testName)\n\tassert.Equalf(t, expected.PrepareConn == nil, actual.PrepareConn == nil, \"%s - PrepareConn\", testName)\n\tassert.Equalf(t, expected.AfterRelease == nil, actual.AfterRelease == nil, \"%s - AfterRelease\", testName)\n\n\tassert.Equalf(t, expected.MaxConnLifetime, actual.MaxConnLifetime, \"%s - MaxConnLifetime\", testName)\n\tassert.Equalf(t, expected.MaxConnIdleTime, actual.MaxConnIdleTime, \"%s - MaxConnIdleTime\", testName)\n\tassert.Equalf(t, expected.MaxConns, actual.MaxConns, \"%s - MaxConns\", testName)\n\tassert.Equalf(t, expected.MinConns, actual.MinConns, \"%s - MinConns\", testName)\n\tassert.Equalf(t, expected.MinIdleConns, actual.MinIdleConns, \"%s - MinIdleConns\", testName)\n\tassert.Equalf(t, expected.HealthCheckPeriod, actual.HealthCheckPeriod, \"%s - HealthCheckPeriod\", testName)\n\n\tassertConnConfigsEqual(t, expected.ConnConfig, actual.ConnConfig, testName)\n}\n\nfunc assertConnConfigsEqual(t *testing.T, expected, actual *pgx.ConnConfig, testName string) {\n\tif !assert.NotNil(t, expected) {\n\t\treturn\n\t}\n\tif !assert.NotNil(t, actual) {\n\t\treturn\n\t}\n\n\tassert.Equalf(t, expected.Tracer, actual.Tracer, \"%s - Tracer\", testName)\n\tassert.Equalf(t, expected.ConnString(), actual.ConnString(), \"%s - ConnString\", testName)\n\tassert.Equalf(t, expected.StatementCacheCapacity, actual.StatementCacheCapacity, \"%s - StatementCacheCapacity\", testName)\n\tassert.Equalf(t, expected.DescriptionCacheCapacity, actual.DescriptionCacheCapacity, \"%s - DescriptionCacheCapacity\", testName)\n\tassert.Equalf(t, expected.DefaultQueryExecMode, actual.DefaultQueryExecMode, \"%s - DefaultQueryExecMode\", testName)\n\tassert.Equalf(t, expected.Host, actual.Host, \"%s - Host\", testName)\n\tassert.Equalf(t, expected.Database, actual.Database, \"%s - Database\", testName)\n\tassert.Equalf(t, expected.Port, actual.Port, \"%s - Port\", testName)\n\tassert.Equalf(t, expected.User, actual.User, \"%s - User\", testName)\n\tassert.Equalf(t, expected.Password, actual.Password, \"%s - Password\", testName)\n\tassert.Equalf(t, expected.ConnectTimeout, actual.ConnectTimeout, \"%s - ConnectTimeout\", testName)\n\tassert.Equalf(t, expected.RuntimeParams, actual.RuntimeParams, \"%s - RuntimeParams\", testName)\n\n\t// Can't test function equality, so just test that they are set or not.\n\tassert.Equalf(t, expected.ValidateConnect == nil, actual.ValidateConnect == nil, \"%s - ValidateConnect\", testName)\n\tassert.Equalf(t, expected.AfterConnect == nil, actual.AfterConnect == nil, \"%s - AfterConnect\", testName)\n\n\tif assert.Equalf(t, expected.TLSConfig == nil, actual.TLSConfig == nil, \"%s - TLSConfig\", testName) {\n\t\tif expected.TLSConfig != nil {\n\t\t\tassert.Equalf(t, expected.TLSConfig.InsecureSkipVerify, actual.TLSConfig.InsecureSkipVerify, \"%s - TLSConfig InsecureSkipVerify\", testName)\n\t\t\tassert.Equalf(t, expected.TLSConfig.ServerName, actual.TLSConfig.ServerName, \"%s - TLSConfig ServerName\", testName)\n\t\t}\n\t}\n\n\tif assert.Equalf(t, len(expected.Fallbacks), len(actual.Fallbacks), \"%s - Fallbacks\", testName) {\n\t\tfor i := range expected.Fallbacks {\n\t\t\tassert.Equalf(t, expected.Fallbacks[i].Host, actual.Fallbacks[i].Host, \"%s - Fallback %d - Host\", testName, i)\n\t\t\tassert.Equalf(t, expected.Fallbacks[i].Port, actual.Fallbacks[i].Port, \"%s - Fallback %d - Port\", testName, i)\n\n\t\t\tif assert.Equalf(t, expected.Fallbacks[i].TLSConfig == nil, actual.Fallbacks[i].TLSConfig == nil, \"%s - Fallback %d - TLSConfig\", testName, i) {\n\t\t\t\tif expected.Fallbacks[i].TLSConfig != nil {\n\t\t\t\t\tassert.Equalf(t, expected.Fallbacks[i].TLSConfig.InsecureSkipVerify, actual.Fallbacks[i].TLSConfig.InsecureSkipVerify, \"%s - Fallback %d - TLSConfig InsecureSkipVerify\", testName)\n\t\t\t\t\tassert.Equalf(t, expected.Fallbacks[i].TLSConfig.ServerName, actual.Fallbacks[i].TLSConfig.ServerName, \"%s - Fallback %d - TLSConfig ServerName\", testName)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pgxpool/conn.go",
    "content": "package pgxpool\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/puddle/v2\"\n)\n\n// Conn is an acquired *pgx.Conn from a Pool.\ntype Conn struct {\n\tres *puddle.Resource[*connResource]\n\tp   *Pool\n}\n\n// Release returns c to the pool it was acquired from. Once Release has been called, other methods must not be called.\n// However, it is safe to call Release multiple times. Subsequent calls after the first will be ignored.\nfunc (c *Conn) Release() {\n\tif c.res == nil {\n\t\treturn\n\t}\n\n\tconn := c.Conn()\n\tres := c.res\n\tc.res = nil\n\n\tif c.p.releaseTracer != nil {\n\t\tc.p.releaseTracer.TraceRelease(c.p, TraceReleaseData{Conn: conn})\n\t}\n\n\tif conn.IsClosed() || conn.PgConn().IsBusy() || conn.PgConn().TxStatus() != 'I' {\n\t\tres.Destroy()\n\t\t// Signal to the health check to run since we just destroyed a connections\n\t\t// and we might be below minConns now\n\t\tc.p.triggerHealthCheck()\n\t\treturn\n\t}\n\n\t// If the pool is consistently being used, we might never get to check the\n\t// lifetime of a connection since we only check idle connections in checkConnsHealth\n\t// so we also check the lifetime here and force a health check\n\tif c.p.isExpired(res) {\n\t\tatomic.AddInt64(&c.p.lifetimeDestroyCount, 1)\n\t\tres.Destroy()\n\t\t// Signal to the health check to run since we just destroyed a connections\n\t\t// and we might be below minConns now\n\t\tc.p.triggerHealthCheck()\n\t\treturn\n\t}\n\n\tif c.p.afterRelease == nil {\n\t\tres.Release()\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tif c.p.afterRelease(conn) {\n\t\t\tres.Release()\n\t\t} else {\n\t\t\tres.Destroy()\n\t\t\t// Signal to the health check to run since we just destroyed a connections\n\t\t\t// and we might be below minConns now\n\t\t\tc.p.triggerHealthCheck()\n\t\t}\n\t}()\n}\n\n// Hijack assumes ownership of the connection from the pool. Caller is responsible for closing the connection. Hijack\n// will panic if called on an already released or hijacked connection.\nfunc (c *Conn) Hijack() *pgx.Conn {\n\tif c.res == nil {\n\t\tpanic(\"cannot hijack already released or hijacked connection\")\n\t}\n\n\tconn := c.Conn()\n\tres := c.res\n\tc.res = nil\n\n\tres.Hijack()\n\n\treturn conn\n}\n\nfunc (c *Conn) Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) {\n\treturn c.Conn().Exec(ctx, sql, arguments...)\n}\n\nfunc (c *Conn) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) {\n\treturn c.Conn().Query(ctx, sql, args...)\n}\n\nfunc (c *Conn) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row {\n\treturn c.Conn().QueryRow(ctx, sql, args...)\n}\n\nfunc (c *Conn) SendBatch(ctx context.Context, b *pgx.Batch) pgx.BatchResults {\n\treturn c.Conn().SendBatch(ctx, b)\n}\n\nfunc (c *Conn) CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) {\n\treturn c.Conn().CopyFrom(ctx, tableName, columnNames, rowSrc)\n}\n\n// Begin starts a transaction block from the *Conn without explicitly setting a transaction mode (see BeginTx with TxOptions if transaction mode is required).\nfunc (c *Conn) Begin(ctx context.Context) (pgx.Tx, error) {\n\treturn c.Conn().Begin(ctx)\n}\n\n// BeginTx starts a transaction block from the *Conn with txOptions determining the transaction mode.\nfunc (c *Conn) BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error) {\n\treturn c.Conn().BeginTx(ctx, txOptions)\n}\n\nfunc (c *Conn) Ping(ctx context.Context) error {\n\treturn c.Conn().Ping(ctx)\n}\n\nfunc (c *Conn) Conn() *pgx.Conn {\n\treturn c.connResource().conn\n}\n\nfunc (c *Conn) connResource() *connResource {\n\treturn c.res.Value()\n}\n\nfunc (c *Conn) getPoolRow(r pgx.Row) *poolRow {\n\treturn c.connResource().getPoolRow(c, r)\n}\n\nfunc (c *Conn) getPoolRows(r pgx.Rows) *poolRows {\n\treturn c.connResource().getPoolRows(c, r)\n}\n"
  },
  {
    "path": "pgxpool/conn_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnExec(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer c.Release()\n\n\ttestExec(t, ctx, c)\n}\n\nfunc TestConnQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer c.Release()\n\n\ttestQuery(t, ctx, c)\n}\n\nfunc TestConnQueryRow(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer c.Release()\n\n\ttestQueryRow(t, ctx, c)\n}\n\nfunc TestConnSendBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer c.Release()\n\n\ttestSendBatch(t, ctx, c)\n}\n\nfunc TestConnCopyFrom(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer c.Release()\n\n\ttestCopyFrom(t, ctx, c)\n}\n"
  },
  {
    "path": "pgxpool/doc.go",
    "content": "// Package pgxpool is a concurrency-safe connection pool for pgx.\n/*\npgxpool implements a nearly identical interface to pgx connections.\n\nCreating a Pool\n\nThe primary way of creating a pool is with [pgxpool.New]:\n\n    pool, err := pgxpool.New(context.Background(), os.Getenv(\"DATABASE_URL\"))\n\nThe database connection string can be in URL or keyword/value format. PostgreSQL settings, pgx settings, and pool settings can be\nspecified here. In addition, a config struct can be created by [ParseConfig].\n\n    config, err := pgxpool.ParseConfig(os.Getenv(\"DATABASE_URL\"))\n    if err != nil {\n        // ...\n    }\n    config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {\n        // do something with every new connection\n    }\n\n    pool, err := pgxpool.NewWithConfig(context.Background(), config)\n\nA pool returns without waiting for any connections to be established. Acquire a connection immediately after creating\nthe pool to check if a connection can successfully be established.\n*/\npackage pgxpool\n"
  },
  {
    "path": "pgxpool/helper_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// delayProxy is a that introduces a configurable delay on reads from the database connection.\ntype delayProxy struct {\n\tnet.Conn\n\treadDelay time.Duration\n}\n\nfunc newDelayProxy(conn net.Conn, readDelay time.Duration) *delayProxy {\n\tp := &delayProxy{\n\t\tConn:      conn,\n\t\treadDelay: readDelay,\n\t}\n\n\treturn p\n}\n\nfunc (dp *delayProxy) Read(b []byte) (int, error) {\n\tif dp.readDelay > 0 {\n\t\ttime.Sleep(dp.readDelay)\n\t}\n\n\treturn dp.Conn.Read(b)\n}\n\nfunc newDelayProxyDialFunc(readDelay time.Duration) pgconn.DialFunc {\n\treturn func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\tconn, err := net.Dial(network, addr)\n\t\treturn newDelayProxy(conn, readDelay), err\n\t}\n}\n"
  },
  {
    "path": "pgxpool/pool.go",
    "content": "package pgxpool\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"math/rand/v2\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/puddle/v2\"\n)\n\nvar (\n\tdefaultMaxConns          = int32(4)\n\tdefaultMinConns          = int32(0)\n\tdefaultMinIdleConns      = int32(0)\n\tdefaultMaxConnLifetime   = time.Hour\n\tdefaultMaxConnIdleTime   = time.Minute * 30\n\tdefaultHealthCheckPeriod = time.Minute\n)\n\ntype connResource struct {\n\tconn       *pgx.Conn\n\tconns      []Conn\n\tpoolRows   []poolRow\n\tpoolRowss  []poolRows\n\tmaxAgeTime time.Time\n}\n\nfunc (cr *connResource) getConn(p *Pool, res *puddle.Resource[*connResource]) *Conn {\n\tif len(cr.conns) == 0 {\n\t\tcr.conns = make([]Conn, 128)\n\t}\n\n\tc := &cr.conns[len(cr.conns)-1]\n\tcr.conns = cr.conns[0 : len(cr.conns)-1]\n\n\tc.res = res\n\tc.p = p\n\n\treturn c\n}\n\nfunc (cr *connResource) getPoolRow(c *Conn, r pgx.Row) *poolRow {\n\tif len(cr.poolRows) == 0 {\n\t\tcr.poolRows = make([]poolRow, 128)\n\t}\n\n\tpr := &cr.poolRows[len(cr.poolRows)-1]\n\tcr.poolRows = cr.poolRows[0 : len(cr.poolRows)-1]\n\n\tpr.c = c\n\tpr.r = r\n\n\treturn pr\n}\n\nfunc (cr *connResource) getPoolRows(c *Conn, r pgx.Rows) *poolRows {\n\tif len(cr.poolRowss) == 0 {\n\t\tcr.poolRowss = make([]poolRows, 128)\n\t}\n\n\tpr := &cr.poolRowss[len(cr.poolRowss)-1]\n\tcr.poolRowss = cr.poolRowss[0 : len(cr.poolRowss)-1]\n\n\tpr.c = c\n\tpr.r = r\n\n\treturn pr\n}\n\n// Pool allows for connection reuse.\ntype Pool struct {\n\t// 64 bit fields accessed with atomics must be at beginning of struct to guarantee alignment for certain 32-bit\n\t// architectures. See BUGS section of https://pkg.go.dev/sync/atomic and https://github.com/jackc/pgx/issues/1288.\n\tnewConnsCount        int64\n\tlifetimeDestroyCount int64\n\tidleDestroyCount     int64\n\n\tp                     *puddle.Pool[*connResource]\n\tconfig                *Config\n\tbeforeConnect         func(context.Context, *pgx.ConnConfig) error\n\tafterConnect          func(context.Context, *pgx.Conn) error\n\tprepareConn           func(context.Context, *pgx.Conn) (bool, error)\n\tafterRelease          func(*pgx.Conn) bool\n\tbeforeClose           func(*pgx.Conn)\n\tshouldPing            func(context.Context, ShouldPingParams) bool\n\tminConns              int32\n\tminIdleConns          int32\n\tmaxConns              int32\n\tmaxConnLifetime       time.Duration\n\tmaxConnLifetimeJitter time.Duration\n\tmaxConnIdleTime       time.Duration\n\thealthCheckPeriod     time.Duration\n\tpingTimeout           time.Duration\n\n\thealthCheckMu    sync.Mutex\n\thealthCheckTimer *time.Timer\n\n\thealthCheckChan chan struct{}\n\n\tacquireTracer AcquireTracer\n\treleaseTracer ReleaseTracer\n\n\tcloseOnce sync.Once\n\tcloseChan chan struct{}\n}\n\n// ShouldPingParams are the parameters passed to ShouldPing.\ntype ShouldPingParams struct {\n\tConn         *pgx.Conn\n\tIdleDuration time.Duration\n}\n\n// Config is the configuration struct for creating a pool. It must be created by [ParseConfig] and then it can be\n// modified.\ntype Config struct {\n\tConnConfig *pgx.ConnConfig\n\n\t// BeforeConnect is called before a new connection is made. It is passed a copy of the underlying pgx.ConnConfig and\n\t// will not impact any existing open connections.\n\tBeforeConnect func(context.Context, *pgx.ConnConfig) error\n\n\t// AfterConnect is called after a connection is established, but before it is added to the pool.\n\tAfterConnect func(context.Context, *pgx.Conn) error\n\n\t// BeforeAcquire is called before a connection is acquired from the pool. It must return true to allow the\n\t// acquisition or false to indicate that the connection should be destroyed and a different connection should be\n\t// acquired.\n\t//\n\t// Deprecated: Use PrepareConn instead. If both PrepareConn and BeforeAcquire are set, PrepareConn will take\n\t// precedence, ignoring BeforeAcquire.\n\tBeforeAcquire func(context.Context, *pgx.Conn) bool\n\n\t// PrepareConn is called before a connection is acquired from the pool. If this function returns true, the connection\n\t// is considered valid, otherwise the connection is destroyed. If the function returns a non-nil error, the instigating\n\t// query will fail with the returned error.\n\t//\n\t// Specifically, this means that:\n\t//\n\t// \t- If it returns true and a nil error, the query proceeds as normal.\n\t// \t- If it returns true and an error, the connection will be returned to the pool, and the instigating query will fail with the returned error.\n\t// \t- If it returns false, and an error, the connection will be destroyed, and the query will fail with the returned error.\n\t// \t- If it returns false and a nil error, the connection will be destroyed, and the instigating query will be retried on a new connection.\n\tPrepareConn func(context.Context, *pgx.Conn) (bool, error)\n\n\t// AfterRelease is called after a connection is released, but before it is returned to the pool. It must return true to\n\t// return the connection to the pool or false to destroy the connection.\n\tAfterRelease func(*pgx.Conn) bool\n\n\t// BeforeClose is called right before a connection is closed and removed from the pool.\n\tBeforeClose func(*pgx.Conn)\n\n\t// ShouldPing is called after a connection is acquired from the pool. If it returns true, the connection is pinged to check for liveness.\n\t// If this func is not set, the default behavior is to ping connections that have been idle for at least 1 second.\n\tShouldPing func(context.Context, ShouldPingParams) bool\n\n\t// MaxConnLifetime is the duration since creation after which a connection will be automatically closed.\n\tMaxConnLifetime time.Duration\n\n\t// MaxConnLifetimeJitter is the duration after MaxConnLifetime to randomly decide to close a connection.\n\t// This helps prevent all connections from being closed at the exact same time, starving the pool.\n\tMaxConnLifetimeJitter time.Duration\n\n\t// MaxConnIdleTime is the duration after which an idle connection will be automatically closed by the health check.\n\tMaxConnIdleTime time.Duration\n\n\t// PingTimeout is the maximum amount of time to wait for a connection to pong before considering it as unhealthy and\n\t// destroying it. If zero, the default is no timeout.\n\tPingTimeout time.Duration\n\n\t// MaxConns is the maximum size of the pool. The default is the greater of 4 or runtime.NumCPU().\n\tMaxConns int32\n\n\t// MinConns is the minimum size of the pool. After connection closes, the pool might dip below MinConns. A low\n\t// number of MinConns might mean the pool is empty after MaxConnLifetime until the health check has a chance\n\t// to create new connections.\n\tMinConns int32\n\n\t// MinIdleConns is the minimum number of idle connections in the pool. You can increase this to ensure that\n\t// there are always idle connections available. This can help reduce tail latencies during request processing,\n\t// as you can avoid the latency of establishing a new connection while handling requests. It is superior\n\t// to MinConns for this purpose.\n\t// Similar to MinConns, the pool might temporarily dip below MinIdleConns after connection closes.\n\tMinIdleConns int32\n\n\t// HealthCheckPeriod is the duration between checks of the health of idle connections.\n\tHealthCheckPeriod time.Duration\n\n\tcreatedByParseConfig bool // Used to enforce created by ParseConfig rule.\n}\n\n// Copy returns a deep copy of the config that is safe to use and modify.\n// The only exception is the tls.Config:\n// according to the tls.Config docs it must not be modified after creation.\nfunc (c *Config) Copy() *Config {\n\tnewConfig := new(Config)\n\t*newConfig = *c\n\tnewConfig.ConnConfig = c.ConnConfig.Copy()\n\treturn newConfig\n}\n\n// ConnString returns the connection string as parsed by pgxpool.ParseConfig into pgxpool.Config.\nfunc (c *Config) ConnString() string { return c.ConnConfig.ConnString() }\n\n// New creates a new Pool. See [ParseConfig] for information on connString format.\nfunc New(ctx context.Context, connString string) (*Pool, error) {\n\tconfig, err := ParseConfig(connString)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewWithConfig(ctx, config)\n}\n\n// NewWithConfig creates a new Pool. config must have been created by [ParseConfig].\nfunc NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {\n\t// Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from\n\t// zero values.\n\tif !config.createdByParseConfig {\n\t\tpanic(\"config must be created by ParseConfig\")\n\t}\n\n\tprepareConn := config.PrepareConn\n\tif prepareConn == nil && config.BeforeAcquire != nil {\n\t\tprepareConn = func(ctx context.Context, conn *pgx.Conn) (bool, error) {\n\t\t\treturn config.BeforeAcquire(ctx, conn), nil\n\t\t}\n\t}\n\n\tp := &Pool{\n\t\tconfig:                config,\n\t\tbeforeConnect:         config.BeforeConnect,\n\t\tafterConnect:          config.AfterConnect,\n\t\tprepareConn:           prepareConn,\n\t\tafterRelease:          config.AfterRelease,\n\t\tbeforeClose:           config.BeforeClose,\n\t\tminConns:              config.MinConns,\n\t\tminIdleConns:          config.MinIdleConns,\n\t\tmaxConns:              config.MaxConns,\n\t\tmaxConnLifetime:       config.MaxConnLifetime,\n\t\tmaxConnLifetimeJitter: config.MaxConnLifetimeJitter,\n\t\tmaxConnIdleTime:       config.MaxConnIdleTime,\n\t\tpingTimeout:           config.PingTimeout,\n\t\thealthCheckPeriod:     config.HealthCheckPeriod,\n\t\thealthCheckChan:       make(chan struct{}, 1),\n\t\tcloseChan:             make(chan struct{}),\n\t}\n\n\tif t, ok := config.ConnConfig.Tracer.(AcquireTracer); ok {\n\t\tp.acquireTracer = t\n\t}\n\n\tif t, ok := config.ConnConfig.Tracer.(ReleaseTracer); ok {\n\t\tp.releaseTracer = t\n\t}\n\n\tif config.ShouldPing != nil {\n\t\tp.shouldPing = config.ShouldPing\n\t} else {\n\t\tp.shouldPing = func(ctx context.Context, params ShouldPingParams) bool {\n\t\t\treturn params.IdleDuration > time.Second\n\t\t}\n\t}\n\n\tvar err error\n\tp.p, err = puddle.NewPool(\n\t\t&puddle.Config[*connResource]{\n\t\t\tConstructor: func(ctx context.Context) (*connResource, error) {\n\t\t\t\tatomic.AddInt64(&p.newConnsCount, 1)\n\t\t\t\tconnConfig := p.config.ConnConfig.Copy()\n\n\t\t\t\t// Connection will continue in background even if Acquire is canceled. Ensure that a connect won't hang forever.\n\t\t\t\tif connConfig.ConnectTimeout <= 0 {\n\t\t\t\t\tconnConfig.ConnectTimeout = 2 * time.Minute\n\t\t\t\t}\n\n\t\t\t\tif p.beforeConnect != nil {\n\t\t\t\t\tif err := p.beforeConnect(ctx, connConfig); err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconn, err := pgx.ConnectConfig(ctx, connConfig)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\tif p.afterConnect != nil {\n\t\t\t\t\terr = p.afterConnect(ctx, conn)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tconn.Close(ctx)\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tjitterSecs := rand.Float64() * config.MaxConnLifetimeJitter.Seconds()\n\t\t\t\tmaxAgeTime := time.Now().Add(config.MaxConnLifetime).Add(time.Duration(jitterSecs) * time.Second)\n\n\t\t\t\tcr := &connResource{\n\t\t\t\t\tconn:       conn,\n\t\t\t\t\tconns:      make([]Conn, 64),\n\t\t\t\t\tpoolRows:   make([]poolRow, 64),\n\t\t\t\t\tpoolRowss:  make([]poolRows, 64),\n\t\t\t\t\tmaxAgeTime: maxAgeTime,\n\t\t\t\t}\n\n\t\t\t\treturn cr, nil\n\t\t\t},\n\t\t\tDestructor: func(value *connResource) {\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\t\t\t\tconn := value.conn\n\t\t\t\tif p.beforeClose != nil {\n\t\t\t\t\tp.beforeClose(conn)\n\t\t\t\t}\n\t\t\t\tconn.Close(ctx)\n\t\t\t\tselect {\n\t\t\t\tcase <-conn.PgConn().CleanupDone():\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t}\n\t\t\t\tcancel()\n\t\t\t},\n\t\t\tMaxSize: config.MaxConns,\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo func() {\n\t\ttargetIdleResources := max(int(p.minConns), int(p.minIdleConns))\n\t\tp.createIdleResources(ctx, targetIdleResources)\n\t\tp.backgroundHealthCheck()\n\t}()\n\n\treturn p, nil\n}\n\n// ParseConfig builds a Config from connString. It parses connString with the same behavior as [pgx.ParseConfig] with the\n// addition of the following variables:\n//\n//   - pool_max_conns: integer greater than 0 (default 4)\n//   - pool_min_conns: integer 0 or greater (default 0)\n//   - pool_max_conn_lifetime: duration string (default 1 hour)\n//   - pool_max_conn_idle_time: duration string (default 30 minutes)\n//   - pool_health_check_period: duration string (default 1 minute)\n//   - pool_max_conn_lifetime_jitter: duration string (default 0)\n//\n// See Config for definitions of these arguments.\n//\n//\t# Example Keyword/Value\n//\tuser=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca pool_max_conns=10 pool_max_conn_lifetime=1h30m\n//\n//\t# Example URL\n//\tpostgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca&pool_max_conns=10&pool_max_conn_lifetime=1h30m\nfunc ParseConfig(connString string) (*Config, error) {\n\tconnConfig, err := pgx.ParseConfig(connString)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfig := &Config{\n\t\tConnConfig:           connConfig,\n\t\tcreatedByParseConfig: true,\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_max_conns\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_max_conns\")\n\t\tn, err := strconv.ParseInt(s, 10, 32)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_max_conns\", err)\n\t\t}\n\t\tif n < 1 {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"pool_max_conns too small\", err)\n\t\t}\n\t\tconfig.MaxConns = int32(n)\n\t} else {\n\t\tconfig.MaxConns = defaultMaxConns\n\t\tif numCPU := int32(runtime.NumCPU()); numCPU > config.MaxConns {\n\t\t\tconfig.MaxConns = numCPU\n\t\t}\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_min_conns\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_min_conns\")\n\t\tn, err := strconv.ParseInt(s, 10, 32)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_min_conns\", err)\n\t\t}\n\t\tconfig.MinConns = int32(n)\n\t} else {\n\t\tconfig.MinConns = defaultMinConns\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_min_idle_conns\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_min_idle_conns\")\n\t\tn, err := strconv.ParseInt(s, 10, 32)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_min_idle_conns\", err)\n\t\t}\n\t\tconfig.MinIdleConns = int32(n)\n\t} else {\n\t\tconfig.MinIdleConns = defaultMinIdleConns\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_max_conn_lifetime\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_max_conn_lifetime\")\n\t\td, err := time.ParseDuration(s)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_max_conn_lifetime\", err)\n\t\t}\n\t\tconfig.MaxConnLifetime = d\n\t} else {\n\t\tconfig.MaxConnLifetime = defaultMaxConnLifetime\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_max_conn_idle_time\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_max_conn_idle_time\")\n\t\td, err := time.ParseDuration(s)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_max_conn_idle_time\", err)\n\t\t}\n\t\tconfig.MaxConnIdleTime = d\n\t} else {\n\t\tconfig.MaxConnIdleTime = defaultMaxConnIdleTime\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_health_check_period\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_health_check_period\")\n\t\td, err := time.ParseDuration(s)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_health_check_period\", err)\n\t\t}\n\t\tconfig.HealthCheckPeriod = d\n\t} else {\n\t\tconfig.HealthCheckPeriod = defaultHealthCheckPeriod\n\t}\n\n\tif s, ok := config.ConnConfig.Config.RuntimeParams[\"pool_max_conn_lifetime_jitter\"]; ok {\n\t\tdelete(connConfig.Config.RuntimeParams, \"pool_max_conn_lifetime_jitter\")\n\t\td, err := time.ParseDuration(s)\n\t\tif err != nil {\n\t\t\treturn nil, pgconn.NewParseConfigError(connString, \"cannot parse pool_max_conn_lifetime_jitter\", err)\n\t\t}\n\t\tconfig.MaxConnLifetimeJitter = d\n\t}\n\n\treturn config, nil\n}\n\n// Close closes all connections in the pool and rejects future Acquire calls. Blocks until all connections are returned\n// to pool and closed.\nfunc (p *Pool) Close() {\n\tp.closeOnce.Do(func() {\n\t\tclose(p.closeChan)\n\t\tp.p.Close()\n\t})\n}\n\nfunc (p *Pool) isExpired(res *puddle.Resource[*connResource]) bool {\n\treturn time.Now().After(res.Value().maxAgeTime)\n}\n\nfunc (p *Pool) triggerHealthCheck() {\n\tconst healthCheckDelay = 500 * time.Millisecond\n\n\tp.healthCheckMu.Lock()\n\tdefer p.healthCheckMu.Unlock()\n\n\tif p.healthCheckTimer == nil {\n\t\t// Destroy is asynchronous so we give it time to actually remove itself from\n\t\t// the pool otherwise we might try to check the pool size too soon\n\t\tp.healthCheckTimer = time.AfterFunc(healthCheckDelay, func() {\n\t\t\tselect {\n\t\t\tcase <-p.closeChan:\n\t\t\tcase p.healthCheckChan <- struct{}{}:\n\t\t\tdefault:\n\t\t\t}\n\t\t})\n\t\treturn\n\t}\n\n\tp.healthCheckTimer.Reset(healthCheckDelay)\n}\n\nfunc (p *Pool) backgroundHealthCheck() {\n\tticker := time.NewTicker(p.healthCheckPeriod)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-p.closeChan:\n\t\t\treturn\n\t\tcase <-p.healthCheckChan:\n\t\t\tp.checkHealth()\n\t\tcase <-ticker.C:\n\t\t\tp.checkHealth()\n\t\t}\n\t}\n}\n\nfunc (p *Pool) checkHealth() {\n\tfor {\n\t\t// If checkMinConns failed we don't destroy any connections since we couldn't\n\t\t// even get to minConns\n\t\tif err := p.checkMinConns(); err != nil {\n\t\t\t// Should we log this error somewhere?\n\t\t\tbreak\n\t\t}\n\t\tif !p.checkConnsHealth() {\n\t\t\t// Since we didn't destroy any connections we can stop looping\n\t\t\tbreak\n\t\t}\n\t\t// Technically Destroy is asynchronous but 500ms should be enough for it to\n\t\t// remove it from the underlying pool\n\t\tselect {\n\t\tcase <-p.closeChan:\n\t\t\treturn\n\t\tcase <-time.After(500 * time.Millisecond):\n\t\t}\n\t}\n}\n\n// checkConnsHealth will check all idle connections, destroy a connection if\n// it's idle or too old, and returns true if any were destroyed\nfunc (p *Pool) checkConnsHealth() bool {\n\tvar destroyed bool\n\ttotalConns := p.Stat().TotalConns()\n\tresources := p.p.AcquireAllIdle()\n\tfor _, res := range resources {\n\t\t// We're okay going under minConns if the lifetime is up\n\t\tif p.isExpired(res) && totalConns >= p.minConns {\n\t\t\tatomic.AddInt64(&p.lifetimeDestroyCount, 1)\n\t\t\tres.Destroy()\n\t\t\tdestroyed = true\n\t\t\t// Since Destroy is async we manually decrement totalConns.\n\t\t\ttotalConns--\n\t\t} else if res.IdleDuration() > p.maxConnIdleTime && totalConns > p.minConns {\n\t\t\tatomic.AddInt64(&p.idleDestroyCount, 1)\n\t\t\tres.Destroy()\n\t\t\tdestroyed = true\n\t\t\t// Since Destroy is async we manually decrement totalConns.\n\t\t\ttotalConns--\n\t\t} else {\n\t\t\tres.ReleaseUnused()\n\t\t}\n\t}\n\treturn destroyed\n}\n\nfunc (p *Pool) checkMinConns() error {\n\t// TotalConns can include ones that are being destroyed but we should have\n\t// sleep(500ms) around all of the destroys to help prevent that from throwing\n\t// off this check\n\n\t// Create the number of connections needed to get to both minConns and minIdleConns\n\ttoCreate := max(p.minConns-p.Stat().TotalConns(), p.minIdleConns-p.Stat().IdleConns())\n\tif toCreate > 0 {\n\t\treturn p.createIdleResources(context.Background(), int(toCreate))\n\t}\n\treturn nil\n}\n\nfunc (p *Pool) createIdleResources(parentCtx context.Context, targetResources int) error {\n\tctx, cancel := context.WithCancel(parentCtx)\n\tdefer cancel()\n\n\terrs := make(chan error, targetResources)\n\n\tfor range targetResources {\n\t\tgo func() {\n\t\t\terr := p.p.CreateResource(ctx)\n\t\t\t// Ignore ErrNotAvailable since it means that the pool has become full since we started creating resource.\n\t\t\tif err == puddle.ErrNotAvailable {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\terrs <- err\n\t\t}()\n\t}\n\n\tvar firstError error\n\tfor range targetResources {\n\t\terr := <-errs\n\t\tif err != nil && firstError == nil {\n\t\t\tcancel()\n\t\t\tfirstError = err\n\t\t}\n\t}\n\n\treturn firstError\n}\n\n// Acquire returns a connection (*Conn) from the Pool\nfunc (p *Pool) Acquire(ctx context.Context) (c *Conn, err error) {\n\tif p.acquireTracer != nil {\n\t\tctx = p.acquireTracer.TraceAcquireStart(ctx, p, TraceAcquireStartData{})\n\t\tdefer func() {\n\t\t\tvar conn *pgx.Conn\n\t\t\tif c != nil {\n\t\t\t\tconn = c.Conn()\n\t\t\t}\n\t\t\tp.acquireTracer.TraceAcquireEnd(ctx, p, TraceAcquireEndData{Conn: conn, Err: err})\n\t\t}()\n\t}\n\n\t// Try to acquire from the connection pool up to maxConns + 1 times, so that\n\t// any that fatal errors would empty the pool and still at least try 1 fresh\n\t// connection.\n\tfor range int(p.maxConns) + 1 {\n\t\tres, err := p.p.Acquire(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcr := res.Value()\n\n\t\tshouldPingParams := ShouldPingParams{Conn: cr.conn, IdleDuration: res.IdleDuration()}\n\t\tif p.shouldPing(ctx, shouldPingParams) {\n\t\t\tpingCtx := ctx\n\t\t\tif p.pingTimeout > 0 {\n\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\tpingCtx, cancel = context.WithTimeout(ctx, p.pingTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\n\t\t\terr := cr.conn.Ping(pingCtx)\n\t\t\tif err != nil {\n\t\t\t\tres.Destroy()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif p.prepareConn != nil {\n\t\t\tok, err := p.prepareConn(ctx, cr.conn)\n\t\t\tif !ok {\n\t\t\t\tres.Destroy()\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tif ok {\n\t\t\t\t\tres.Release()\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\treturn cr.getConn(p, res), nil\n\t}\n\treturn nil, errors.New(\"pgxpool: too many failed attempts acquiring connection; likely bug in PrepareConn, BeforeAcquire, or ShouldPing hook\")\n}\n\n// AcquireFunc acquires a *Conn and calls f with that *Conn. ctx will only affect the Acquire. It has no effect on the\n// call of f. The return value is either an error acquiring the *Conn or the return value of f. The *Conn is\n// automatically released after the call of f.\nfunc (p *Pool) AcquireFunc(ctx context.Context, f func(*Conn) error) error {\n\tconn, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Release()\n\n\treturn f(conn)\n}\n\n// AcquireAllIdle atomically acquires all currently idle connections. Its intended use is for health check and\n// keep-alive functionality. It does not update pool statistics.\nfunc (p *Pool) AcquireAllIdle(ctx context.Context) []*Conn {\n\tresources := p.p.AcquireAllIdle()\n\tconns := make([]*Conn, 0, len(resources))\n\tfor _, res := range resources {\n\t\tcr := res.Value()\n\t\tif p.prepareConn != nil {\n\t\t\tok, err := p.prepareConn(ctx, cr.conn)\n\t\t\tif !ok || err != nil {\n\t\t\t\tres.Destroy()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tconns = append(conns, cr.getConn(p, res))\n\t}\n\n\treturn conns\n}\n\n// Reset closes all connections, but leaves the pool open. It is intended for use when an error is detected that would\n// disrupt all connections (such as a network interruption or a server state change).\n//\n// It is safe to reset a pool while connections are checked out. Those connections will be closed when they are returned\n// to the pool.\nfunc (p *Pool) Reset() {\n\tp.p.Reset()\n}\n\n// Config returns a copy of config that was used to initialize this pool.\nfunc (p *Pool) Config() *Config { return p.config.Copy() }\n\n// Stat returns a pgxpool.Stat struct with a snapshot of Pool statistics.\nfunc (p *Pool) Stat() *Stat {\n\treturn &Stat{\n\t\ts:                    p.p.Stat(),\n\t\tnewConnsCount:        atomic.LoadInt64(&p.newConnsCount),\n\t\tlifetimeDestroyCount: atomic.LoadInt64(&p.lifetimeDestroyCount),\n\t\tidleDestroyCount:     atomic.LoadInt64(&p.idleDestroyCount),\n\t}\n}\n\n// Exec acquires a connection from the Pool and executes the given SQL.\n// SQL can be either a prepared statement name or an SQL string.\n// Arguments should be referenced positionally from the SQL string as $1, $2, etc.\n// The acquired connection is returned to the pool when the Exec function returns.\nfunc (p *Pool) Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\tdefer c.Release()\n\n\treturn c.Exec(ctx, sql, arguments...)\n}\n\n// Query acquires a connection and executes a query that returns pgx.Rows.\n// Arguments should be referenced positionally from the SQL string as $1, $2, etc.\n// See pgx.Rows documentation to close the returned Rows and return the acquired connection to the Pool.\n//\n// If there is an error, the returned pgx.Rows will be returned in an error state.\n// If preferred, ignore the error returned from Query and handle errors using the returned pgx.Rows.\n//\n// For extra control over how the query is executed, the types QuerySimpleProtocol, QueryResultFormats, and\n// QueryResultFormatsByOID may be used as the first args to control exactly how the query is executed. This is rarely\n// needed. See the documentation for those types for details.\nfunc (p *Pool) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn errRows{err: err}, err\n\t}\n\n\trows, err := c.Query(ctx, sql, args...)\n\tif err != nil {\n\t\tc.Release()\n\t\treturn errRows{err: err}, err\n\t}\n\n\treturn c.getPoolRows(rows), nil\n}\n\n// QueryRow acquires a connection and executes a query that is expected\n// to return at most one row (pgx.Row). Errors are deferred until pgx.Row's\n// Scan method is called. If the query selects no rows, pgx.Row's Scan will\n// return ErrNoRows. Otherwise, pgx.Row's Scan scans the first selected row\n// and discards the rest. The acquired connection is returned to the Pool when\n// pgx.Row's Scan method is called.\n//\n// Arguments should be referenced positionally from the SQL string as $1, $2, etc.\n//\n// For extra control over how the query is executed, the types QuerySimpleProtocol, QueryResultFormats, and\n// QueryResultFormatsByOID may be used as the first args to control exactly how the query is executed. This is rarely\n// needed. See the documentation for those types for details.\nfunc (p *Pool) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn errRow{err: err}\n\t}\n\n\trow := c.QueryRow(ctx, sql, args...)\n\treturn c.getPoolRow(row)\n}\n\nfunc (p *Pool) SendBatch(ctx context.Context, b *pgx.Batch) pgx.BatchResults {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn errBatchResults{err: err}\n\t}\n\n\tbr := c.SendBatch(ctx, b)\n\treturn &poolBatchResults{br: br, c: c}\n}\n\n// Begin acquires a connection from the Pool and starts a transaction. Unlike database/sql, the context only affects the begin command. i.e. there is no\n// auto-rollback on context cancellation. Begin initiates a transaction block without explicitly setting a transaction mode for the block (see BeginTx with TxOptions if transaction mode is required).\n// *pgxpool.Tx is returned, which implements the pgx.Tx interface.\n// Commit or Rollback must be called on the returned transaction to finalize the transaction block.\nfunc (p *Pool) Begin(ctx context.Context) (pgx.Tx, error) {\n\treturn p.BeginTx(ctx, pgx.TxOptions{})\n}\n\n// BeginTx acquires a connection from the Pool and starts a transaction with pgx.TxOptions determining the transaction mode.\n// Unlike database/sql, the context only affects the begin command. i.e. there is no auto-rollback on context cancellation.\n// *pgxpool.Tx is returned, which implements the pgx.Tx interface.\n// Commit or Rollback must be called on the returned transaction to finalize the transaction block.\nfunc (p *Pool) BeginTx(ctx context.Context, txOptions pgx.TxOptions) (pgx.Tx, error) {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tt, err := c.BeginTx(ctx, txOptions)\n\tif err != nil {\n\t\tc.Release()\n\t\treturn nil, err\n\t}\n\n\treturn &Tx{t: t, c: c}, nil\n}\n\nfunc (p *Pool) CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer c.Release()\n\n\treturn c.Conn().CopyFrom(ctx, tableName, columnNames, rowSrc)\n}\n\n// Ping acquires a connection from the Pool and executes an empty sql statement against it.\n// If the sql returns without error, the database Ping is considered successful, otherwise, the error is returned.\nfunc (p *Pool) Ping(ctx context.Context) error {\n\tc, err := p.Acquire(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer c.Release()\n\treturn c.Ping(ctx)\n}\n"
  },
  {
    "path": "pgxpool/pool_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnect(t *testing.T) {\n\tt.Parallel()\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\tpool, err := pgxpool.New(ctx, connString)\n\trequire.NoError(t, err)\n\tassert.Equal(t, connString, pool.Config().ConnString())\n\tpool.Close()\n}\n\nfunc TestConnectConfig(t *testing.T) {\n\tt.Parallel()\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\tconfig, err := pgxpool.ParseConfig(connString)\n\trequire.NoError(t, err)\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tassertConfigsEqual(t, config, pool.Config(), \"Pool.Config() returns original config\")\n\tpool.Close()\n}\n\nfunc TestParseConfigExtractsPoolArguments(t *testing.T) {\n\tt.Parallel()\n\n\tconfig, err := pgxpool.ParseConfig(\"pool_max_conns=42 pool_min_conns=1 pool_min_idle_conns=2\")\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, 42, config.MaxConns)\n\tassert.EqualValues(t, 1, config.MinConns)\n\tassert.EqualValues(t, 2, config.MinIdleConns)\n\tassert.NotContains(t, config.ConnConfig.Config.RuntimeParams, \"pool_max_conns\")\n\tassert.NotContains(t, config.ConnConfig.Config.RuntimeParams, \"pool_min_conns\")\n}\n\nfunc TestConstructorIgnoresContext(t *testing.T) {\n\tt.Parallel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\tassert.NoError(t, err)\n\tvar cancel func()\n\tconfig.BeforeConnect = func(context.Context, *pgx.ConnConfig) error {\n\t\t// cancel the query's context before we actually Dial to ensure the Dial's\n\t\t// context isn't cancelled\n\t\tcancel()\n\t\treturn nil\n\t}\n\n\tpool, err := pgxpool.NewWithConfig(context.Background(), config)\n\trequire.NoError(t, err)\n\n\tassert.EqualValues(t, 0, pool.Stat().TotalConns())\n\n\tvar ctx context.Context\n\tctx, cancel = context.WithCancel(context.Background())\n\tdefer cancel()\n\t_, err = pool.Exec(ctx, \"SELECT 1\")\n\tassert.ErrorIs(t, err, context.Canceled)\n\tassert.EqualValues(t, 1, pool.Stat().TotalConns())\n}\n\nfunc TestConnectConfigRequiresConnConfigFromParseConfig(t *testing.T) {\n\tt.Parallel()\n\n\tconfig := &pgxpool.Config{}\n\n\trequire.PanicsWithValue(t, \"config must be created by ParseConfig\", func() { pgxpool.NewWithConfig(context.Background(), config) })\n}\n\nfunc TestConfigCopyReturnsEqualConfig(t *testing.T) {\n\tconnString := \"postgres://jack:secret@localhost:5432/mydb?application_name=pgxtest&search_path=myschema&connect_timeout=5\"\n\toriginal, err := pgxpool.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\n\tassertConfigsEqual(t, original, copied, t.Name())\n}\n\nfunc TestConfigCopyCanBeUsedToConnect(t *testing.T) {\n\tconnString := os.Getenv(\"PGX_TEST_DATABASE\")\n\toriginal, err := pgxpool.ParseConfig(connString)\n\trequire.NoError(t, err)\n\n\tcopied := original.Copy()\n\tassert.NotPanics(t, func() {\n\t\t_, err = pgxpool.NewWithConfig(context.Background(), copied)\n\t})\n\tassert.NoError(t, err)\n}\n\nfunc TestPoolAcquireAndConnRelease(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n}\n\nfunc TestPoolAcquireAndConnHijack(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tconnsBeforeHijack := pool.Stat().TotalConns()\n\n\tconn := c.Hijack()\n\tdefer conn.Close(ctx)\n\n\tconnsAfterHijack := pool.Stat().TotalConns()\n\trequire.Equal(t, connsBeforeHijack-1, connsAfterHijack)\n\n\tvar n int32\n\terr = conn.QueryRow(ctx, `select 1`).Scan(&n)\n\trequire.NoError(t, err)\n\trequire.Equal(t, int32(1), n)\n}\n\nfunc TestPoolAcquireChecksIdleConns(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tcontrollerConn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer controllerConn.Close(ctx)\n\tpgxtest.SkipCockroachDB(t, controllerConn, \"Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)\")\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tvar conns []*pgxpool.Conn\n\tfor range 3 {\n\t\tc, err := pool.Acquire(ctx)\n\t\trequire.NoError(t, err)\n\t\tconns = append(conns, c)\n\t}\n\n\trequire.EqualValues(t, 3, pool.Stat().TotalConns())\n\n\tvar pids []uint32\n\tfor _, c := range conns {\n\t\tpids = append(pids, c.Conn().PgConn().PID())\n\t\tc.Release()\n\t}\n\n\t_, err = controllerConn.Exec(ctx, `select pg_terminate_backend(n) from unnest($1::int[]) n`, pids)\n\trequire.NoError(t, err)\n\n\t// All conns are dead they don't know it and neither does the pool.\n\trequire.EqualValues(t, 3, pool.Stat().TotalConns())\n\n\t// Wait long enough so the pool will realize it needs to check the connections.\n\ttime.Sleep(time.Second)\n\n\t// Pool should try all existing connections and find them dead, then create a new connection which should successfully ping.\n\terr = pool.Ping(ctx)\n\trequire.NoError(t, err)\n\n\t// The original 3 conns should have been terminated and the a new conn established for the ping.\n\trequire.EqualValues(t, 1, pool.Stat().TotalConns())\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tcPID := c.Conn().PgConn().PID()\n\tc.Release()\n\n\trequire.NotContains(t, pids, cPID)\n}\n\nfunc TestPoolAcquireChecksIdleConnsWithShouldPing(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tcontrollerConn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer controllerConn.Close(ctx)\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\t// Replace the default ShouldPing func\n\tvar shouldPingLastCalledWith *pgxpool.ShouldPingParams\n\tconfig.ShouldPing = func(ctx context.Context, params pgxpool.ShouldPingParams) bool {\n\t\tshouldPingLastCalledWith = &params\n\t\treturn false\n\t}\n\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n\n\ttime.Sleep(time.Millisecond * 200)\n\n\tc, err = pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tconn := c.Conn()\n\n\trequire.NotNil(t, shouldPingLastCalledWith)\n\tassert.Equal(t, conn, shouldPingLastCalledWith.Conn)\n\tassert.InDelta(t, time.Millisecond*200, shouldPingLastCalledWith.IdleDuration, float64(time.Millisecond*100))\n\n\tc.Release()\n}\n\n// https://github.com/jackc/pgx/issues/2379\nfunc TestPoolAcquireWithMaxConnsEqualsMaxInt32(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.MaxConns = math.MaxInt32\n\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n}\n\nfunc TestPoolAcquireFunc(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tvar n int32\n\terr = pool.AcquireFunc(ctx, func(c *pgxpool.Conn) error {\n\t\treturn c.QueryRow(ctx, \"select 1\").Scan(&n)\n\t})\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 1, n)\n}\n\nfunc TestPoolAcquireFuncReturnsFnError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\terr = pool.AcquireFunc(ctx, func(c *pgxpool.Conn) error {\n\t\treturn fmt.Errorf(\"some error\")\n\t})\n\trequire.EqualError(t, err, \"some error\")\n}\n\nfunc TestPoolBeforeConnect(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.BeforeConnect = func(ctx context.Context, cfg *pgx.ConnConfig) error {\n\t\tcfg.Config.RuntimeParams[\"application_name\"] = \"pgx\"\n\t\treturn nil\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tvar str string\n\terr = db.QueryRow(ctx, \"SHOW application_name\").Scan(&str)\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, \"pgx\", str)\n}\n\nfunc TestPoolAfterConnect(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.AfterConnect = func(ctx context.Context, c *pgx.Conn) error {\n\t\t_, err := c.Prepare(ctx, \"ps1\", \"select 1\")\n\t\treturn err\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tvar n int32\n\terr = db.QueryRow(ctx, \"ps1\").Scan(&n)\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, 1, n)\n}\n\nfunc TestPoolBeforeAcquire(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tacquireAttempts := 0\n\n\tconfig.BeforeAcquire = func(ctx context.Context, c *pgx.Conn) bool {\n\t\tacquireAttempts++\n\t\treturn acquireAttempts%2 == 0\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tconns := make([]*pgxpool.Conn, 4)\n\tfor i := range conns {\n\t\tconns[i], err = db.Acquire(ctx)\n\t\tassert.NoError(t, err)\n\t}\n\n\tfor _, c := range conns {\n\t\tc.Release()\n\t}\n\twaitForReleaseToComplete()\n\n\tassert.EqualValues(t, 8, acquireAttempts)\n\n\tconns = db.AcquireAllIdle(ctx)\n\tassert.Len(t, conns, 2)\n\n\tfor _, c := range conns {\n\t\tc.Release()\n\t}\n\twaitForReleaseToComplete()\n\n\tassert.EqualValues(t, 12, acquireAttempts)\n}\n\nfunc TestPoolPrepareConn(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tacquireAttempts := 0\n\n\tconfig.PrepareConn = func(context.Context, *pgx.Conn) (bool, error) {\n\t\tacquireAttempts++\n\t\tvar err error\n\t\tif acquireAttempts%3 == 0 {\n\t\t\terr = errors.New(\"PrepareConn error\")\n\t\t}\n\t\treturn acquireAttempts%2 == 0, err\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tt.Cleanup(db.Close)\n\n\tvar errorCount int\n\tconns := make([]*pgxpool.Conn, 0, 4)\n\tfor {\n\t\tconn, err := db.Acquire(ctx)\n\t\tif err != nil {\n\t\t\terrorCount++\n\t\t\tcontinue\n\t\t}\n\t\tconns = append(conns, conn)\n\t\tif len(conns) == 4 {\n\t\t\tbreak\n\t\t}\n\t}\n\tconst wantErrorCount = 3\n\tassert.Equal(t, wantErrorCount, errorCount, \"Acquire() should have failed %d times\", wantErrorCount)\n\n\tfor _, c := range conns {\n\t\tc.Release()\n\t}\n\twaitForReleaseToComplete()\n\n\tassert.EqualValues(t, len(conns)*2+wantErrorCount-1, acquireAttempts)\n\n\tconns = db.AcquireAllIdle(ctx)\n\tassert.Len(t, conns, 1)\n\n\tfor _, c := range conns {\n\t\tc.Release()\n\t}\n\twaitForReleaseToComplete()\n\n\tassert.EqualValues(t, 14, acquireAttempts)\n}\n\nfunc TestPoolAfterRelease(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tfunc() {\n\t\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tdefer pool.Close()\n\t}()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tafterReleaseCount := 0\n\n\tconfig.AfterRelease = func(c *pgx.Conn) bool {\n\t\tafterReleaseCount++\n\t\treturn afterReleaseCount%2 == 1\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tconnPIDs := map[uint32]struct{}{}\n\n\tfor range 10 {\n\t\tconn, err := db.Acquire(ctx)\n\t\tassert.NoError(t, err)\n\t\tconnPIDs[conn.Conn().PgConn().PID()] = struct{}{}\n\t\tconn.Release()\n\t\twaitForReleaseToComplete()\n\t}\n\n\tassert.EqualValues(t, 5, len(connPIDs))\n}\n\nfunc TestPoolBeforeClose(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tfunc() {\n\t\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\tdefer pool.Close()\n\t}()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconnPIDs := make(chan uint32, 5)\n\tconfig.BeforeClose = func(c *pgx.Conn) {\n\t\tconnPIDs <- c.PgConn().PID()\n\t}\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tacquiredPIDs := make([]uint32, 0, 5)\n\tclosedPIDs := make([]uint32, 0, 5)\n\tfor range 5 {\n\t\tconn, err := db.Acquire(ctx)\n\t\tassert.NoError(t, err)\n\t\tacquiredPIDs = append(acquiredPIDs, conn.Conn().PgConn().PID())\n\t\tconn.Release()\n\t\tdb.Reset()\n\t\tclosedPIDs = append(closedPIDs, <-connPIDs)\n\t}\n\n\tassert.ElementsMatch(t, acquiredPIDs, closedPIDs)\n}\n\nfunc TestPoolAcquireAllIdle(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tdb, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tconns := make([]*pgxpool.Conn, 3)\n\tfor i := range conns {\n\t\tconns[i], err = db.Acquire(ctx)\n\t\tassert.NoError(t, err)\n\t}\n\n\tfor _, c := range conns {\n\t\tif c != nil {\n\t\t\tc.Release()\n\t\t}\n\t}\n\twaitForReleaseToComplete()\n\n\tconns = db.AcquireAllIdle(ctx)\n\tassert.Len(t, conns, 3)\n\n\tfor _, c := range conns {\n\t\tc.Release()\n\t}\n}\n\nfunc TestPoolReset(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tdb, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tconns := make([]*pgxpool.Conn, 3)\n\tfor i := range conns {\n\t\tconns[i], err = db.Acquire(ctx)\n\t\tassert.NoError(t, err)\n\t}\n\n\tdb.Reset()\n\n\tfor _, c := range conns {\n\t\tif c != nil {\n\t\t\tc.Release()\n\t\t}\n\t}\n\twaitForReleaseToComplete()\n\n\trequire.EqualValues(t, 0, db.Stat().TotalConns())\n}\n\nfunc TestConnReleaseChecksMaxConnLifetime(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.MaxConnLifetime = 250 * time.Millisecond\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tc, err := db.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\ttime.Sleep(config.MaxConnLifetime)\n\n\tc.Release()\n\twaitForReleaseToComplete()\n\n\tstats := db.Stat()\n\tassert.EqualValues(t, 0, stats.TotalConns())\n}\n\nfunc TestConnReleaseClosesBusyConn(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tdb, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tc, err := db.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\t_, err = c.Query(ctx, \"select generate_series(1,10)\")\n\trequire.NoError(t, err)\n\n\tc.Release()\n\twaitForReleaseToComplete()\n\n\t// wait for the connection to actually be destroyed\n\tfor range 1000 {\n\t\tif db.Stat().TotalConns() == 0 {\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(time.Millisecond)\n\t}\n\n\tstats := db.Stat()\n\tassert.EqualValues(t, 0, stats.TotalConns())\n}\n\nfunc TestPoolBackgroundChecksMaxConnLifetime(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.MaxConnLifetime = 100 * time.Millisecond\n\tconfig.HealthCheckPeriod = 100 * time.Millisecond\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tc, err := db.Acquire(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n\ttime.Sleep(config.MaxConnLifetime + 500*time.Millisecond)\n\n\tstats := db.Stat()\n\tassert.EqualValues(t, 0, stats.TotalConns())\n\tassert.EqualValues(t, 0, stats.MaxIdleDestroyCount())\n\tassert.EqualValues(t, 1, stats.MaxLifetimeDestroyCount())\n\tassert.EqualValues(t, 1, stats.NewConnsCount())\n}\n\nfunc TestPoolBackgroundChecksMaxConnIdleTime(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.MaxConnLifetime = 1 * time.Minute\n\tconfig.MaxConnIdleTime = 100 * time.Millisecond\n\tconfig.HealthCheckPeriod = 150 * time.Millisecond\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tc, err := db.Acquire(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n\ttime.Sleep(config.HealthCheckPeriod)\n\n\tfor range 1000 {\n\t\tif db.Stat().TotalConns() == 0 {\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(time.Millisecond)\n\t}\n\n\tstats := db.Stat()\n\tassert.EqualValues(t, 0, stats.TotalConns())\n\tassert.EqualValues(t, 1, stats.MaxIdleDestroyCount())\n\tassert.EqualValues(t, 0, stats.MaxLifetimeDestroyCount())\n\tassert.EqualValues(t, 1, stats.NewConnsCount())\n}\n\nfunc TestPoolBackgroundChecksMinConns(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.HealthCheckPeriod = 100 * time.Millisecond\n\tconfig.MinConns = 2\n\n\tdb, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tstats := db.Stat()\n\tfor !(stats.IdleConns() == 2 && stats.MaxLifetimeDestroyCount() == 0 && stats.NewConnsCount() == 2) && ctx.Err() == nil {\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\tstats = db.Stat()\n\t}\n\trequire.EqualValues(t, 2, stats.IdleConns())\n\trequire.EqualValues(t, 0, stats.MaxLifetimeDestroyCount())\n\trequire.EqualValues(t, 2, stats.NewConnsCount())\n\n\tc, err := db.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tstats = db.Stat()\n\trequire.EqualValues(t, 1, stats.IdleConns())\n\trequire.EqualValues(t, 0, stats.MaxLifetimeDestroyCount())\n\trequire.EqualValues(t, 2, stats.NewConnsCount())\n\n\terr = c.Conn().Close(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n\n\tstats = db.Stat()\n\tfor !(stats.IdleConns() == 2 && stats.MaxIdleDestroyCount() == 0 && stats.NewConnsCount() == 3) && ctx.Err() == nil {\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\tstats = db.Stat()\n\t}\n\trequire.EqualValues(t, 2, stats.TotalConns())\n\trequire.EqualValues(t, 0, stats.MaxIdleDestroyCount())\n\trequire.EqualValues(t, 3, stats.NewConnsCount())\n}\n\nfunc TestPoolExec(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttestExec(t, ctx, pool)\n}\n\nfunc TestPoolQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\t// Test common usage\n\ttestQuery(t, ctx, pool)\n\twaitForReleaseToComplete()\n\n\t// Test expected pool behavior\n\trows, err := pool.Query(ctx, \"select generate_series(1,$1)\", 10)\n\trequire.NoError(t, err)\n\n\tstats := pool.Stat()\n\tassert.EqualValues(t, 1, stats.AcquiredConns())\n\tassert.EqualValues(t, 1, stats.TotalConns())\n\n\trows.Close()\n\tassert.NoError(t, rows.Err())\n\twaitForReleaseToComplete()\n\n\tstats = pool.Stat()\n\tassert.EqualValues(t, 0, stats.AcquiredConns())\n\tassert.EqualValues(t, 1, stats.TotalConns())\n}\n\nfunc TestPoolQueryRow(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttestQueryRow(t, ctx, pool)\n\twaitForReleaseToComplete()\n\n\tstats := pool.Stat()\n\tassert.EqualValues(t, 0, stats.AcquiredConns())\n\tassert.EqualValues(t, 1, stats.TotalConns())\n}\n\n// https://github.com/jackc/pgx/issues/677\nfunc TestPoolQueryRowErrNoRows(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\terr = pool.QueryRow(ctx, \"select n from generate_series(1,10) n where n=0\").Scan(nil)\n\trequire.Equal(t, pgx.ErrNoRows, err)\n}\n\n// https://github.com/jackc/pgx/issues/1628\nfunc TestPoolQueryRowScanPanicReleasesConnection(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\trequire.Panics(t, func() {\n\t\tvar greeting *string\n\t\tpool.QueryRow(ctx, \"select 'Hello, world!'\").Scan(greeting) // Note lack of &. This means that a typed nil is passed to Scan.\n\t})\n\n\t// If the connection is not released this will block forever in the defer pool.Close().\n}\n\nfunc TestPoolSendBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttestSendBatch(t, ctx, pool)\n\twaitForReleaseToComplete()\n\n\tstats := pool.Stat()\n\tassert.EqualValues(t, 0, stats.AcquiredConns())\n\tassert.EqualValues(t, 1, stats.TotalConns())\n}\n\nfunc TestPoolCopyFrom(t *testing.T) {\n\t// Not able to use testCopyFrom because it relies on temporary tables and the pool may run subsequent calls under\n\t// different connections.\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\t_, err = pool.Exec(ctx, `drop table if exists poolcopyfromtest`)\n\trequire.NoError(t, err)\n\n\t_, err = pool.Exec(ctx, `create table poolcopyfromtest(a int2, b int4, c int8, d varchar, e text, f date, g timestamptz)`)\n\trequire.NoError(t, err)\n\tdefer pool.Exec(ctx, `drop table poolcopyfromtest`)\n\n\ttzedTime := time.Date(2010, 2, 3, 4, 5, 6, 0, time.Local)\n\n\tinputRows := [][]any{\n\t\t{int16(0), int32(1), int64(2), \"abc\", \"efg\", time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), tzedTime},\n\t\t{nil, nil, nil, nil, nil, nil, nil},\n\t}\n\n\tcopyCount, err := pool.CopyFrom(ctx, pgx.Identifier{\"poolcopyfromtest\"}, []string{\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\"}, pgx.CopyFromRows(inputRows))\n\tassert.NoError(t, err)\n\tassert.EqualValues(t, len(inputRows), copyCount)\n\n\trows, err := pool.Query(ctx, \"select * from poolcopyfromtest\")\n\tassert.NoError(t, err)\n\n\tvar outputRows [][]any\n\tfor rows.Next() {\n\t\trow, err := rows.Values()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected error for rows.Values(): %v\", err)\n\t\t}\n\t\toutputRows = append(outputRows, row)\n\t}\n\n\tassert.NoError(t, rows.Err())\n\tassert.Equal(t, inputRows, outputRows)\n}\n\nfunc TestConnReleaseClosesConnInFailedTransaction(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tpid := c.Conn().PgConn().PID()\n\n\tassert.Equal(t, byte('I'), c.Conn().PgConn().TxStatus())\n\n\t_, err = c.Exec(ctx, \"begin\")\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, byte('T'), c.Conn().PgConn().TxStatus())\n\n\t_, err = c.Exec(ctx, \"selct\")\n\tassert.Error(t, err)\n\n\tassert.Equal(t, byte('E'), c.Conn().PgConn().TxStatus())\n\n\tc.Release()\n\twaitForReleaseToComplete()\n\n\tc, err = pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tassert.NotEqual(t, pid, c.Conn().PgConn().PID())\n\tassert.Equal(t, byte('I'), c.Conn().PgConn().TxStatus())\n\n\tc.Release()\n}\n\nfunc TestConnReleaseClosesConnInTransaction(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tpid := c.Conn().PgConn().PID()\n\n\tassert.Equal(t, byte('I'), c.Conn().PgConn().TxStatus())\n\n\t_, err = c.Exec(ctx, \"begin\")\n\tassert.NoError(t, err)\n\n\tassert.Equal(t, byte('T'), c.Conn().PgConn().TxStatus())\n\n\tc.Release()\n\twaitForReleaseToComplete()\n\n\tc, err = pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tassert.NotEqual(t, pid, c.Conn().PgConn().PID())\n\tassert.Equal(t, byte('I'), c.Conn().PgConn().TxStatus())\n\n\tc.Release()\n}\n\nfunc TestConnReleaseDestroysClosedConn(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\terr = c.Conn().Close(ctx)\n\trequire.NoError(t, err)\n\n\tassert.EqualValues(t, 1, pool.Stat().TotalConns())\n\n\tc.Release()\n\twaitForReleaseToComplete()\n\n\t// wait for the connection to actually be destroyed\n\tfor range 1000 {\n\t\tif pool.Stat().TotalConns() == 0 {\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(time.Millisecond)\n\t}\n\n\tassert.EqualValues(t, 0, pool.Stat().TotalConns())\n}\n\nfunc TestConnPoolQueryConcurrentLoad(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tn := 100\n\tdone := make(chan bool)\n\n\tfor range n {\n\t\tgo func() {\n\t\t\tdefer func() { done <- true }()\n\t\t\ttestQuery(t, ctx, pool)\n\t\t\ttestQueryRow(t, ctx, pool)\n\t\t}()\n\t}\n\n\tfor range n {\n\t\t<-done\n\t}\n}\n\nfunc TestConnReleaseWhenBeginFail(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tdb, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\ttx, err := db.BeginTx(ctx, pgx.TxOptions{\n\t\tIsoLevel: pgx.TxIsoLevel(\"foo\"),\n\t})\n\tassert.Error(t, err)\n\tif !assert.Zero(t, tx) {\n\t\terr := tx.Rollback(ctx)\n\t\tassert.NoError(t, err)\n\t}\n\n\tfor range 1000 {\n\t\tif db.Stat().TotalConns() == 0 {\n\t\t\tbreak\n\t\t}\n\t\ttime.Sleep(time.Millisecond)\n\t}\n\n\tassert.EqualValues(t, 0, db.Stat().TotalConns())\n}\n\nfunc TestTxBeginFuncNestedTransactionCommit(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tdb, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tcreateSql := `\n\t\tdrop table if exists pgxpooltx;\n    create temporary table pgxpooltx(\n      id integer,\n      unique (id)\n    );\n  `\n\n\t_, err = db.Exec(ctx, createSql)\n\trequire.NoError(t, err)\n\n\tdefer func() {\n\t\tdb.Exec(ctx, \"drop table pgxpooltx\")\n\t}()\n\n\terr = pgx.BeginFunc(ctx, db, func(db pgx.Tx) error {\n\t\t_, err := db.Exec(ctx, \"insert into pgxpooltx(id) values (1)\")\n\t\trequire.NoError(t, err)\n\n\t\terr = pgx.BeginFunc(ctx, db, func(db pgx.Tx) error {\n\t\t\t_, err := db.Exec(ctx, \"insert into pgxpooltx(id) values (2)\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\terr = pgx.BeginFunc(ctx, db, func(db pgx.Tx) error {\n\t\t\t\t_, err := db.Exec(ctx, \"insert into pgxpooltx(id) values (3)\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\t\t\treturn nil\n\t\t})\n\t\trequire.NoError(t, err)\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\n\tvar n int64\n\terr = db.QueryRow(ctx, \"select count(*) from pgxpooltx\").Scan(&n)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 3, n)\n}\n\nfunc TestTxBeginFuncNestedTransactionRollback(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tdb, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer db.Close()\n\n\tcreateSql := `\n\t\tdrop table if exists pgxpooltx;\n    create temporary table pgxpooltx(\n      id integer,\n      unique (id)\n    );\n  `\n\n\t_, err = db.Exec(ctx, createSql)\n\trequire.NoError(t, err)\n\n\tdefer func() {\n\t\tdb.Exec(ctx, \"drop table pgxpooltx\")\n\t}()\n\n\terr = pgx.BeginFunc(ctx, db, func(db pgx.Tx) error {\n\t\t_, err := db.Exec(ctx, \"insert into pgxpooltx(id) values (1)\")\n\t\trequire.NoError(t, err)\n\n\t\terr = pgx.BeginFunc(ctx, db, func(db pgx.Tx) error {\n\t\t\t_, err := db.Exec(ctx, \"insert into pgxpooltx(id) values (2)\")\n\t\t\trequire.NoError(t, err)\n\t\t\treturn errors.New(\"do a rollback\")\n\t\t})\n\t\trequire.EqualError(t, err, \"do a rollback\")\n\n\t\t_, err = db.Exec(ctx, \"insert into pgxpooltx(id) values (3)\")\n\t\trequire.NoError(t, err)\n\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\n\tvar n int64\n\terr = db.QueryRow(ctx, \"select count(*) from pgxpooltx\").Scan(&n)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 2, n)\n}\n\nfunc TestIdempotentPoolClose(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\t// Close the open pool.\n\trequire.NotPanics(t, func() { pool.Close() })\n\n\t// Close the already closed pool.\n\trequire.NotPanics(t, func() { pool.Close() })\n}\n\nfunc TestConnectEagerlyReachesMinPoolSize(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.MinConns = int32(12)\n\tconfig.MaxConns = int32(15)\n\n\tacquireAttempts := int64(0)\n\tconnectAttempts := int64(0)\n\n\tconfig.PrepareConn = func(ctx context.Context, conn *pgx.Conn) (bool, error) {\n\t\tatomic.AddInt64(&acquireAttempts, 1)\n\t\treturn true, nil\n\t}\n\tconfig.BeforeConnect = func(ctx context.Context, cfg *pgx.ConnConfig) error {\n\t\tatomic.AddInt64(&connectAttempts, 1)\n\t\treturn nil\n\t}\n\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tfor range 500 {\n\t\ttime.Sleep(10 * time.Millisecond)\n\n\t\tstat := pool.Stat()\n\t\tif stat.IdleConns() == 12 && stat.AcquireCount() == 0 && stat.TotalConns() == 12 && atomic.LoadInt64(&acquireAttempts) == 0 && atomic.LoadInt64(&connectAttempts) == 12 {\n\t\t\treturn\n\t\t}\n\t}\n\n\tt.Fatal(\"did not reach min pool size\")\n}\n\nfunc TestPoolSendBatchBatchCloseTwice(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\terrChan := make(chan error)\n\ttestCount := 5000\n\n\tfor range testCount {\n\t\tgo func() {\n\t\t\tbatch := &pgx.Batch{}\n\t\t\tbatch.Queue(\"select 1\")\n\t\t\tbatch.Queue(\"select 2\")\n\n\t\t\tbr := pool.SendBatch(ctx, batch)\n\t\t\tdefer br.Close()\n\n\t\t\tvar err error\n\t\t\tvar n int32\n\t\t\terr = br.QueryRow().Scan(&n)\n\t\t\tif err != nil {\n\t\t\t\terrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n != 1 {\n\t\t\t\terrChan <- fmt.Errorf(\"expected 1 got %v\", n)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terr = br.QueryRow().Scan(&n)\n\t\t\tif err != nil {\n\t\t\t\terrChan <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif n != 2 {\n\t\t\t\terrChan <- fmt.Errorf(\"expected 2 got %v\", n)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terr = br.Close()\n\t\t\terrChan <- err\n\t\t}()\n\t}\n\n\tfor range testCount {\n\t\terr := <-errChan\n\t\tassert.NoError(t, err)\n\t}\n}\n\nfunc TestPoolAcquirePingTimeout(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tconfig.PingTimeout = 200 * time.Millisecond\n\tconfig.ConnConfig.DialFunc = newDelayProxyDialFunc(500 * time.Millisecond)\n\n\tvar conID *uint32\n\t// Only ping the connection with the original PID to force creation of a new connection\n\tconfig.ShouldPing = func(_ context.Context, params pgxpool.ShouldPingParams) bool {\n\t\tif conID != nil && params.Conn.PgConn().PID() == *conID {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\t// Limit to a single connection to ensure the same connection is reused\n\tconfig.MinConns = 1\n\tconfig.MaxConns = 1\n\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 1, pool.Stat().TotalConns())\n\toriginalPID := c.Conn().PgConn().PID()\n\tconID = &originalPID\n\n\tc.Release()\n\trequire.EqualValues(t, 1, pool.Stat().TotalConns())\n\n\tc, err = pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 1, pool.Stat().TotalConns())\n\tnewPID := c.Conn().PgConn().PID()\n\n\tc.Release()\n\n\trequire.EqualValues(t, 1, pool.Stat().TotalConns())\n\tassert.Nil(t, ctx.Err())\n\tassert.NotEqualValues(t, originalPID, newPID,\n\t\t\"Expected new connection due to ping timeout, but got same connection\")\n}\n"
  },
  {
    "path": "pgxpool/rows.go",
    "content": "package pgxpool\n\nimport (\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\ntype errRows struct {\n\terr error\n}\n\nfunc (errRows) Close()                                       {}\nfunc (e errRows) Err() error                                 { return e.err }\nfunc (errRows) CommandTag() pgconn.CommandTag                { return pgconn.CommandTag{} }\nfunc (errRows) FieldDescriptions() []pgconn.FieldDescription { return nil }\nfunc (errRows) Next() bool                                   { return false }\nfunc (e errRows) Scan(dest ...any) error                     { return e.err }\nfunc (e errRows) Values() ([]any, error)                     { return nil, e.err }\nfunc (e errRows) RawValues() [][]byte                        { return nil }\nfunc (e errRows) Conn() *pgx.Conn                            { return nil }\n\ntype errRow struct {\n\terr error\n}\n\nfunc (e errRow) Scan(dest ...any) error { return e.err }\n\ntype poolRows struct {\n\tr   pgx.Rows\n\tc   *Conn\n\terr error\n}\n\nfunc (rows *poolRows) Close() {\n\trows.r.Close()\n\tif rows.c != nil {\n\t\trows.c.Release()\n\t\trows.c = nil\n\t}\n}\n\nfunc (rows *poolRows) Err() error {\n\tif rows.err != nil {\n\t\treturn rows.err\n\t}\n\treturn rows.r.Err()\n}\n\nfunc (rows *poolRows) CommandTag() pgconn.CommandTag {\n\treturn rows.r.CommandTag()\n}\n\nfunc (rows *poolRows) FieldDescriptions() []pgconn.FieldDescription {\n\treturn rows.r.FieldDescriptions()\n}\n\nfunc (rows *poolRows) Next() bool {\n\tif rows.err != nil {\n\t\treturn false\n\t}\n\n\tn := rows.r.Next()\n\tif !n {\n\t\trows.Close()\n\t}\n\treturn n\n}\n\nfunc (rows *poolRows) Scan(dest ...any) error {\n\terr := rows.r.Scan(dest...)\n\tif err != nil {\n\t\trows.Close()\n\t}\n\treturn err\n}\n\nfunc (rows *poolRows) Values() ([]any, error) {\n\tvalues, err := rows.r.Values()\n\tif err != nil {\n\t\trows.Close()\n\t}\n\treturn values, err\n}\n\nfunc (rows *poolRows) RawValues() [][]byte {\n\treturn rows.r.RawValues()\n}\n\nfunc (rows *poolRows) Conn() *pgx.Conn {\n\treturn rows.r.Conn()\n}\n\ntype poolRow struct {\n\tr   pgx.Row\n\tc   *Conn\n\terr error\n}\n\nfunc (row *poolRow) Scan(dest ...any) error {\n\tif row.err != nil {\n\t\treturn row.err\n\t}\n\n\tpanicked := true\n\tdefer func() {\n\t\tif panicked && row.c != nil {\n\t\t\trow.c.Release()\n\t\t}\n\t}()\n\terr := row.r.Scan(dest...)\n\tpanicked = false\n\tif row.c != nil {\n\t\trow.c.Release()\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "pgxpool/stat.go",
    "content": "package pgxpool\n\nimport (\n\t\"time\"\n\n\t\"github.com/jackc/puddle/v2\"\n)\n\n// Stat is a snapshot of Pool statistics.\ntype Stat struct {\n\ts                    *puddle.Stat\n\tnewConnsCount        int64\n\tlifetimeDestroyCount int64\n\tidleDestroyCount     int64\n}\n\n// AcquireCount returns the cumulative count of successful acquires from the pool.\nfunc (s *Stat) AcquireCount() int64 {\n\treturn s.s.AcquireCount()\n}\n\n// AcquireDuration returns the total duration of all successful acquires from\n// the pool.\nfunc (s *Stat) AcquireDuration() time.Duration {\n\treturn s.s.AcquireDuration()\n}\n\n// AcquiredConns returns the number of currently acquired connections in the pool.\nfunc (s *Stat) AcquiredConns() int32 {\n\treturn s.s.AcquiredResources()\n}\n\n// CanceledAcquireCount returns the cumulative count of acquires from the pool\n// that were canceled by a context.\nfunc (s *Stat) CanceledAcquireCount() int64 {\n\treturn s.s.CanceledAcquireCount()\n}\n\n// ConstructingConns returns the number of conns with construction in progress in\n// the pool.\nfunc (s *Stat) ConstructingConns() int32 {\n\treturn s.s.ConstructingResources()\n}\n\n// EmptyAcquireCount returns the cumulative count of successful acquires from the pool\n// that waited for a resource to be released or constructed because the pool was\n// empty.\nfunc (s *Stat) EmptyAcquireCount() int64 {\n\treturn s.s.EmptyAcquireCount()\n}\n\n// IdleConns returns the number of currently idle conns in the pool.\nfunc (s *Stat) IdleConns() int32 {\n\treturn s.s.IdleResources()\n}\n\n// MaxConns returns the maximum size of the pool.\nfunc (s *Stat) MaxConns() int32 {\n\treturn s.s.MaxResources()\n}\n\n// TotalConns returns the total number of resources currently in the pool.\n// The value is the sum of ConstructingConns, AcquiredConns, and\n// IdleConns.\nfunc (s *Stat) TotalConns() int32 {\n\treturn s.s.TotalResources()\n}\n\n// NewConnsCount returns the cumulative count of new connections opened.\nfunc (s *Stat) NewConnsCount() int64 {\n\treturn s.newConnsCount\n}\n\n// MaxLifetimeDestroyCount returns the cumulative count of connections destroyed\n// because they exceeded MaxConnLifetime.\nfunc (s *Stat) MaxLifetimeDestroyCount() int64 {\n\treturn s.lifetimeDestroyCount\n}\n\n// MaxIdleDestroyCount returns the cumulative count of connections destroyed because\n// they exceeded MaxConnIdleTime.\nfunc (s *Stat) MaxIdleDestroyCount() int64 {\n\treturn s.idleDestroyCount\n}\n\n// EmptyAcquireWaitTime returns the cumulative time waited for successful acquires\n// from the pool for a resource to be released or constructed because the pool was\n// empty.\nfunc (s *Stat) EmptyAcquireWaitTime() time.Duration {\n\treturn s.s.EmptyAcquireWaitTime()\n}\n"
  },
  {
    "path": "pgxpool/tracer.go",
    "content": "package pgxpool\n\nimport (\n\t\"context\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\n// AcquireTracer traces Acquire.\ntype AcquireTracer interface {\n\t// TraceAcquireStart is called at the beginning of Acquire.\n\t// The returned context is used for the rest of the call and will be passed to the TraceAcquireEnd.\n\tTraceAcquireStart(ctx context.Context, pool *Pool, data TraceAcquireStartData) context.Context\n\t// TraceAcquireEnd is called when a connection has been acquired.\n\tTraceAcquireEnd(ctx context.Context, pool *Pool, data TraceAcquireEndData)\n}\n\ntype TraceAcquireStartData struct{}\n\ntype TraceAcquireEndData struct {\n\tConn *pgx.Conn\n\tErr  error\n}\n\n// ReleaseTracer traces Release.\ntype ReleaseTracer interface {\n\t// TraceRelease is called at the beginning of Release.\n\tTraceRelease(pool *Pool, data TraceReleaseData)\n}\n\ntype TraceReleaseData struct {\n\tConn *pgx.Conn\n}\n"
  },
  {
    "path": "pgxpool/tracer_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype testTracer struct {\n\ttraceAcquireStart func(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context\n\ttraceAcquireEnd   func(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData)\n\ttraceRelease      func(pool *pgxpool.Pool, data pgxpool.TraceReleaseData)\n}\n\ntype ctxKey string\n\nfunc (tt *testTracer) TraceAcquireStart(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {\n\tif tt.traceAcquireStart != nil {\n\t\treturn tt.traceAcquireStart(ctx, pool, data)\n\t}\n\treturn ctx\n}\n\nfunc (tt *testTracer) TraceAcquireEnd(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {\n\tif tt.traceAcquireEnd != nil {\n\t\ttt.traceAcquireEnd(ctx, pool, data)\n\t}\n}\n\nfunc (tt *testTracer) TraceRelease(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {\n\tif tt.traceRelease != nil {\n\t\ttt.traceRelease(pool, data)\n\t}\n}\n\nfunc (tt *testTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\treturn ctx\n}\n\nfunc (tt *testTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n}\n\nfunc TestTraceAcquire(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tconfig.ConnConfig.Tracer = tracer\n\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttraceAcquireStartCalled := false\n\ttracer.traceAcquireStart = func(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {\n\t\ttraceAcquireStartCalled = true\n\t\trequire.NotNil(t, pool)\n\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceAcquireStart\"), \"foo\")\n\t}\n\n\ttraceAcquireEndCalled := false\n\ttracer.traceAcquireEnd = func(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {\n\t\ttraceAcquireEndCalled = true\n\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceAcquireStart\")))\n\t\trequire.NotNil(t, pool)\n\t\trequire.NotNil(t, data.Conn)\n\t\trequire.NoError(t, data.Err)\n\t}\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer c.Release()\n\trequire.True(t, traceAcquireStartCalled)\n\trequire.True(t, traceAcquireEndCalled)\n\n\ttraceAcquireStartCalled = false\n\ttraceAcquireEndCalled = false\n\ttracer.traceAcquireEnd = func(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {\n\t\ttraceAcquireEndCalled = true\n\t\trequire.NotNil(t, pool)\n\t\trequire.Nil(t, data.Conn)\n\t\trequire.Error(t, data.Err)\n\t}\n\n\tctx, cancel = context.WithCancel(ctx)\n\tcancel()\n\t_, err = pool.Acquire(ctx)\n\trequire.ErrorIs(t, err, context.Canceled)\n\trequire.True(t, traceAcquireStartCalled)\n\trequire.True(t, traceAcquireEndCalled)\n}\n\nfunc TestTraceRelease(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tconfig.ConnConfig.Tracer = tracer\n\n\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttraceReleaseCalled := false\n\ttracer.traceRelease = func(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {\n\t\ttraceReleaseCalled = true\n\t\trequire.NotNil(t, pool)\n\t\trequire.NotNil(t, data.Conn)\n\t}\n\n\tc, err := pool.Acquire(ctx)\n\trequire.NoError(t, err)\n\tc.Release()\n\trequire.True(t, traceReleaseCalled)\n}\n"
  },
  {
    "path": "pgxpool/tx.go",
    "content": "package pgxpool\n\nimport (\n\t\"context\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// Tx represents a database transaction acquired from a Pool.\ntype Tx struct {\n\tt pgx.Tx\n\tc *Conn\n}\n\n// Begin starts a pseudo nested transaction implemented with a savepoint.\nfunc (tx *Tx) Begin(ctx context.Context) (pgx.Tx, error) {\n\treturn tx.t.Begin(ctx)\n}\n\n// Commit commits the transaction and returns the associated connection back to the Pool. Commit will return an error\n// where errors.Is(ErrTxClosed) is true if the Tx is already closed, but is otherwise safe to call multiple times. If\n// the commit fails with a rollback status (e.g. the transaction was already in a broken state) then ErrTxCommitRollback\n// will be returned.\nfunc (tx *Tx) Commit(ctx context.Context) error {\n\terr := tx.t.Commit(ctx)\n\tif tx.c != nil {\n\t\ttx.c.Release()\n\t\ttx.c = nil\n\t}\n\treturn err\n}\n\n// Rollback rolls back the transaction and returns the associated connection back to the Pool. Rollback will return\n// where an error where errors.Is(ErrTxClosed) is true if the Tx is already closed, but is otherwise safe to call\n// multiple times. Hence, defer tx.Rollback() is safe even if tx.Commit() will be called first in a non-error condition.\nfunc (tx *Tx) Rollback(ctx context.Context) error {\n\terr := tx.t.Rollback(ctx)\n\tif tx.c != nil {\n\t\ttx.c.Release()\n\t\ttx.c = nil\n\t}\n\treturn err\n}\n\nfunc (tx *Tx) CopyFrom(ctx context.Context, tableName pgx.Identifier, columnNames []string, rowSrc pgx.CopyFromSource) (int64, error) {\n\treturn tx.t.CopyFrom(ctx, tableName, columnNames, rowSrc)\n}\n\nfunc (tx *Tx) SendBatch(ctx context.Context, b *pgx.Batch) pgx.BatchResults {\n\treturn tx.t.SendBatch(ctx, b)\n}\n\nfunc (tx *Tx) LargeObjects() pgx.LargeObjects {\n\treturn tx.t.LargeObjects()\n}\n\n// Prepare creates a prepared statement with name and sql. If the name is empty,\n// an anonymous prepared statement will be used. sql can contain placeholders\n// for bound parameters. These placeholders are referenced positionally as $1, $2, etc.\n//\n// Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same\n// name and sql arguments. This allows a code path to Prepare and Query/Exec without\n// needing to first check whether the statement has already been prepared.\nfunc (tx *Tx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) {\n\treturn tx.t.Prepare(ctx, name, sql)\n}\n\nfunc (tx *Tx) Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error) {\n\treturn tx.t.Exec(ctx, sql, arguments...)\n}\n\nfunc (tx *Tx) Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error) {\n\treturn tx.t.Query(ctx, sql, args...)\n}\n\nfunc (tx *Tx) QueryRow(ctx context.Context, sql string, args ...any) pgx.Row {\n\treturn tx.t.QueryRow(ctx, sql, args...)\n}\n\nfunc (tx *Tx) Conn() *pgx.Conn {\n\treturn tx.t.Conn()\n}\n"
  },
  {
    "path": "pgxpool/tx_test.go",
    "content": "package pgxpool_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTxExec(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttx, err := pool.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\ttestExec(t, ctx, tx)\n}\n\nfunc TestTxQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttx, err := pool.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\ttestQuery(t, ctx, tx)\n}\n\nfunc TestTxQueryRow(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttx, err := pool.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\ttestQueryRow(t, ctx, tx)\n}\n\nfunc TestTxSendBatch(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttx, err := pool.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\ttestSendBatch(t, ctx, tx)\n}\n\nfunc TestTxCopyFrom(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpool, err := pgxpool.New(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer pool.Close()\n\n\ttx, err := pool.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\ttestCopyFrom(t, ctx, tx)\n}\n"
  },
  {
    "path": "pgxtest/pgxtest.go",
    "content": "// Package pgxtest provides utilities for testing pgx and packages that integrate with pgx.\npackage pgxtest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n)\n\nvar AllQueryExecModes = []pgx.QueryExecMode{\n\tpgx.QueryExecModeCacheStatement,\n\tpgx.QueryExecModeCacheDescribe,\n\tpgx.QueryExecModeDescribeExec,\n\tpgx.QueryExecModeExec,\n\tpgx.QueryExecModeSimpleProtocol,\n}\n\n// KnownOIDQueryExecModes is a slice of all query exec modes where the param and result OIDs are known before sending the query.\nvar KnownOIDQueryExecModes = []pgx.QueryExecMode{\n\tpgx.QueryExecModeCacheStatement,\n\tpgx.QueryExecModeCacheDescribe,\n\tpgx.QueryExecModeDescribeExec,\n}\n\n// ConnTestRunner controls how a *pgx.Conn is created and closed by tests. All fields are required. Use DefaultConnTestRunner to get a\n// ConnTestRunner with reasonable default values.\ntype ConnTestRunner struct {\n\t// CreateConfig returns a *pgx.ConnConfig suitable for use with pgx.ConnectConfig.\n\tCreateConfig func(ctx context.Context, t testing.TB) *pgx.ConnConfig\n\n\t// AfterConnect is called after conn is established. It allows for arbitrary connection setup before a test begins.\n\tAfterConnect func(ctx context.Context, t testing.TB, conn *pgx.Conn)\n\n\t// AfterTest is called after the test is run. It allows for validating the state of the connection before it is closed.\n\tAfterTest func(ctx context.Context, t testing.TB, conn *pgx.Conn)\n\n\t// CloseConn closes conn.\n\tCloseConn func(ctx context.Context, t testing.TB, conn *pgx.Conn)\n}\n\n// DefaultConnTestRunner returns a new ConnTestRunner with all fields set to reasonable default values.\nfunc DefaultConnTestRunner() ConnTestRunner {\n\treturn ConnTestRunner{\n\t\tCreateConfig: func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\t\tconfig, err := pgx.ParseConfig(\"\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ParseConfig failed: %v\", err)\n\t\t\t}\n\t\t\treturn config\n\t\t},\n\t\tAfterConnect: func(ctx context.Context, t testing.TB, conn *pgx.Conn) {},\n\t\tAfterTest:    func(ctx context.Context, t testing.TB, conn *pgx.Conn) {},\n\t\tCloseConn: func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t\terr := conn.Close(ctx)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Close failed: %v\", err)\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunc (ctr *ConnTestRunner) RunTest(ctx context.Context, t testing.TB, f func(ctx context.Context, t testing.TB, conn *pgx.Conn)) {\n\tt.Helper()\n\n\tconfig := ctr.CreateConfig(ctx, t)\n\tconn, err := pgx.ConnectConfig(ctx, config)\n\tif err != nil {\n\t\tt.Fatalf(\"ConnectConfig failed: %v\", err)\n\t}\n\tdefer ctr.CloseConn(ctx, t, conn)\n\n\tctr.AfterConnect(ctx, t, conn)\n\tf(ctx, t, conn)\n\tctr.AfterTest(ctx, t, conn)\n}\n\n// RunWithQueryExecModes runs a f in a new test for each element of modes with a new connection created using connector.\n// If modes is nil all pgx.QueryExecModes are tested.\nfunc RunWithQueryExecModes(ctx context.Context, t *testing.T, ctr ConnTestRunner, modes []pgx.QueryExecMode, f func(ctx context.Context, t testing.TB, conn *pgx.Conn)) {\n\tif modes == nil {\n\t\tmodes = AllQueryExecModes\n\t}\n\n\tfor _, mode := range modes {\n\t\tctrWithMode := ctr\n\t\tctrWithMode.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\t\tconfig := ctr.CreateConfig(ctx, t)\n\t\t\tconfig.DefaultQueryExecMode = mode\n\t\t\treturn config\n\t\t}\n\n\t\tt.Run(mode.String(),\n\t\t\tfunc(t *testing.T) {\n\t\t\t\tctrWithMode.RunTest(ctx, t, f)\n\t\t\t},\n\t\t)\n\t}\n}\n\ntype ValueRoundTripTest struct {\n\tParam  any\n\tResult any\n\tTest   func(any) bool\n}\n\nfunc RunValueRoundTripTests(\n\tctx context.Context,\n\tt testing.TB,\n\tctr ConnTestRunner,\n\tmodes []pgx.QueryExecMode,\n\tpgTypeName string,\n\ttests []ValueRoundTripTest,\n) {\n\tt.Helper()\n\n\tif modes == nil {\n\t\tmodes = AllQueryExecModes\n\t}\n\n\tctr.RunTest(ctx, t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tt.Helper()\n\n\t\tsql := fmt.Sprintf(\"select $1::%s\", pgTypeName)\n\n\t\tfor i, tt := range tests {\n\t\t\tfor _, mode := range modes {\n\t\t\t\terr := conn.QueryRow(ctx, sql, mode, tt.Param).Scan(tt.Result)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"%d. %v: %v\", i, mode, err)\n\t\t\t\t}\n\n\t\t\t\tresult := reflect.ValueOf(tt.Result)\n\t\t\t\tif result.Kind() == reflect.Ptr {\n\t\t\t\t\tresult = result.Elem()\n\t\t\t\t}\n\n\t\t\t\tif !tt.Test(result.Interface()) {\n\t\t\t\t\tt.Errorf(\"%d. %v: unexpected result for %v: %v\", i, mode, tt.Param, result.Interface())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n}\n\n// SkipCockroachDB calls Skip on t with msg if the connection is to a CockroachDB server.\nfunc SkipCockroachDB(t testing.TB, conn *pgx.Conn, msg string) {\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(msg)\n\t}\n}\n\nfunc SkipPostgreSQLVersionLessThan(t testing.TB, conn *pgx.Conn, minVersion int64) {\n\tserverVersionStr := conn.PgConn().ParameterStatus(\"server_version\")\n\tserverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)\n\t// if not PostgreSQL do nothing\n\tif serverVersionStr == \"\" {\n\t\treturn\n\t}\n\n\tserverVersion, err := strconv.ParseInt(serverVersionStr, 10, 64)\n\tif err != nil {\n\t\tt.Fatalf(\"postgres version parsed failed: %s\", err)\n\t}\n\n\tif serverVersion < minVersion {\n\t\tt.Skipf(\"Test requires PostgreSQL v%d+\", minVersion)\n\t}\n}\n\nfunc SkipPostgreSQLVersionGreaterThan(t testing.TB, conn *pgx.Conn, maxVersion int64) {\n\tserverVersionStr := conn.PgConn().ParameterStatus(\"server_version\")\n\tserverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)\n\t// if not PostgreSQL do nothing\n\tif serverVersionStr == \"\" {\n\t\treturn\n\t}\n\n\tserverVersion, err := strconv.ParseInt(serverVersionStr, 10, 64)\n\tif err != nil {\n\t\tt.Fatalf(\"postgres version parsed failed: %s\", err)\n\t}\n\n\tif serverVersion > maxVersion {\n\t\tt.Skipf(\"Test requires PostgreSQL v%d or lower\", maxVersion)\n\t}\n}\n"
  },
  {
    "path": "pipeline_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPipelineWithoutPreparedOrDescribedStatements(t *testing.T) {\n\tt.Parallel()\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpipeline := conn.PgConn().StartPipeline(ctx)\n\n\t\teqb := pgx.ExtendedQueryBuilder{}\n\n\t\terr := eqb.Build(conn.TypeMap(), nil, []any{1, 2})\n\t\trequire.NoError(t, err)\n\t\tpipeline.SendQueryParams(`select $1::bigint + $2::bigint`, eqb.ParamValues, nil, eqb.ParamFormats, eqb.ResultFormats)\n\n\t\terr = eqb.Build(conn.TypeMap(), nil, []any{3, 4, 5})\n\t\trequire.NoError(t, err)\n\t\tpipeline.SendQueryParams(`select $1::bigint + $2::bigint + $3::bigint`, eqb.ParamValues, nil, eqb.ParamFormats, eqb.ResultFormats)\n\n\t\terr = pipeline.Sync()\n\t\trequire.NoError(t, err)\n\n\t\tresults, err := pipeline.GetResults()\n\t\trequire.NoError(t, err)\n\t\trr, ok := results.(*pgconn.ResultReader)\n\t\trequire.True(t, ok)\n\t\trows := pgx.RowsFromResultReader(conn.TypeMap(), rr)\n\n\t\trowCount := 0\n\t\tvar n int64\n\t\tfor rows.Next() {\n\t\t\terr = rows.Scan(&n)\n\t\t\trequire.NoError(t, err)\n\t\t\trowCount++\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\t\trequire.Equal(t, 1, rowCount)\n\t\trequire.Equal(t, \"SELECT 1\", rows.CommandTag().String())\n\t\trequire.EqualValues(t, 3, n)\n\n\t\tresults, err = pipeline.GetResults()\n\t\trequire.NoError(t, err)\n\t\trr, ok = results.(*pgconn.ResultReader)\n\t\trequire.True(t, ok)\n\t\trows = pgx.RowsFromResultReader(conn.TypeMap(), rr)\n\n\t\trowCount = 0\n\t\tn = 0\n\t\tfor rows.Next() {\n\t\t\terr = rows.Scan(&n)\n\t\t\trequire.NoError(t, err)\n\t\t\trowCount++\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\t\trequire.Equal(t, 1, rowCount)\n\t\trequire.Equal(t, \"SELECT 1\", rows.CommandTag().String())\n\t\trequire.EqualValues(t, 12, n)\n\n\t\tresults, err = pipeline.GetResults()\n\t\trequire.NoError(t, err)\n\t\t_, ok = results.(*pgconn.PipelineSync)\n\t\trequire.True(t, ok)\n\n\t\tresults, err = pipeline.GetResults()\n\t\trequire.NoError(t, err)\n\t\trequire.Nil(t, results)\n\n\t\terr = pipeline.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n"
  },
  {
    "path": "query_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestConnQueryScan(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar sum, rowCount int32\n\n\trows, err := conn.Query(context.Background(), \"select generate_series(1,$1)\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar n int32\n\t\trows.Scan(&n)\n\t\tsum += n\n\t\trowCount++\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", rows.Err())\n\t}\n\n\tassert.Equal(t, \"SELECT 10\", rows.CommandTag().String())\n\n\tif rowCount != 10 {\n\t\tt.Error(\"Select called onDataRow wrong number of times\")\n\t}\n\tif sum != 55 {\n\t\tt.Error(\"Wrong values returned\")\n\t}\n}\n\nfunc TestConnQueryRowsFieldDescriptionsBeforeNext(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\trows, err := conn.Query(context.Background(), \"select 'hello' as msg\")\n\trequire.NoError(t, err)\n\tdefer rows.Close()\n\n\trequire.Len(t, rows.FieldDescriptions(), 1)\n\tassert.Equal(t, \"msg\", rows.FieldDescriptions()[0].Name)\n}\n\nfunc TestConnQueryWithoutResultSetCommandTag(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\trows, err := conn.Query(context.Background(), \"create temporary table t (id serial);\")\n\tassert.NoError(t, err)\n\trows.Close()\n\tassert.NoError(t, rows.Err())\n\tassert.Equal(t, \"CREATE TABLE\", rows.CommandTag().String())\n}\n\nfunc TestConnQueryScanWithManyColumns(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcolumnCount := 1000\n\tsql := \"select \"\n\tfor i := range columnCount {\n\t\tif i > 0 {\n\t\t\tsql += \",\"\n\t\t}\n\t\tsql += fmt.Sprintf(\" %d\", i)\n\t}\n\tsql += \" from generate_series(1,5)\"\n\n\tdest := make([]int, columnCount)\n\n\tvar rowCount int\n\n\trows, err := conn.Query(context.Background(), sql)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tdestPtrs := make([]any, columnCount)\n\t\tfor i := range destPtrs {\n\t\t\tdestPtrs[i] = &dest[i]\n\t\t}\n\t\tif err := rows.Scan(destPtrs...); err != nil {\n\t\t\tt.Fatalf(\"rows.Scan failed: %v\", err)\n\t\t}\n\t\trowCount++\n\n\t\tfor i := range dest {\n\t\t\tif dest[i] != i {\n\t\t\t\tt.Errorf(\"dest[%d] => %d, want %d\", i, dest[i], i)\n\t\t\t}\n\t\t}\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", rows.Err())\n\t}\n\n\tif rowCount != 5 {\n\t\tt.Errorf(\"rowCount => %d, want %d\", rowCount, 5)\n\t}\n}\n\nfunc TestConnQueryValues(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar rowCount int32\n\n\trows, err := conn.Query(context.Background(), \"select 'foo'::text, 'bar'::varchar, n, null, n from generate_series(1,$1) n\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\trowCount++\n\n\t\tvalues, err := rows.Values()\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, values, 5)\n\t\tassert.Equal(t, \"foo\", values[0])\n\t\tassert.Equal(t, \"bar\", values[1])\n\t\tassert.EqualValues(t, rowCount, values[2])\n\t\tassert.Nil(t, values[3])\n\t\tassert.EqualValues(t, rowCount, values[4])\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", rows.Err())\n\t}\n\n\tif rowCount != 10 {\n\t\tt.Error(\"Select called onDataRow wrong number of times\")\n\t}\n}\n\n// https://github.com/jackc/pgx/issues/666\nfunc TestConnQueryValuesWhenUnableToDecode(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// Note that this relies on pgtype.Record not supporting the text protocol. This seems safe as it is impossible to\n\t// decode the text protocol because unlike the binary protocol there is no way to determine the OIDs of the elements.\n\trows, err := conn.Query(context.Background(), \"select (array[1::oid], null)\", pgx.QueryResultFormats{pgx.TextFormatCode})\n\trequire.NoError(t, err)\n\tdefer rows.Close()\n\n\trequire.True(t, rows.Next())\n\n\tvalues, err := rows.Values()\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"({1},)\", values[0])\n}\n\nfunc TestConnQueryValuesWithUnregisteredOID(t *testing.T) {\n\tt.Parallel()\n\tskipCockroachDB(t, \"CockroachDB auto commits DDL by default\")\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(ctx)\n\n\t_, err = tx.Exec(ctx, \"create type fruit as enum('orange', 'apple', 'pear')\")\n\trequire.NoError(t, err)\n\n\trows, err := conn.Query(context.Background(), \"select 'orange'::fruit\")\n\trequire.NoError(t, err)\n\tdefer rows.Close()\n\n\trequire.True(t, rows.Next())\n\n\tvalues, err := rows.Values()\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"orange\", values[0])\n}\n\nfunc TestConnQueryArgsAndScanWithUnregisteredOID(t *testing.T) {\n\tt.Parallel()\n\tskipCockroachDB(t, \"CockroachDB auto commits DDL by default\")\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\n\t\t_, err = tx.Exec(ctx, \"create type fruit as enum('orange', 'apple', 'pear')\")\n\t\trequire.NoError(t, err)\n\n\t\tvar result string\n\t\terr = conn.QueryRow(ctx, \"select $1::fruit\", \"orange\").Scan(&result)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"orange\", result)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/478\nfunc TestConnQueryReadRowMultipleTimes(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar rowCount int32\n\n\trows, err := conn.Query(context.Background(), \"select 'foo'::text, 'bar'::varchar, n, null, n from generate_series(1,$1) n\", 10)\n\trequire.NoError(t, err)\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\trowCount++\n\n\t\tfor range 2 {\n\t\t\tvalues, err := rows.Values()\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Len(t, values, 5)\n\t\t\trequire.Equal(t, \"foo\", values[0])\n\t\t\trequire.Equal(t, \"bar\", values[1])\n\t\t\trequire.EqualValues(t, rowCount, values[2])\n\t\t\trequire.Nil(t, values[3])\n\t\t\trequire.EqualValues(t, rowCount, values[4])\n\n\t\t\tvar a, b string\n\t\t\tvar c int32\n\t\t\tvar d pgtype.Text\n\t\t\tvar e int32\n\n\t\t\terr = rows.Scan(&a, &b, &c, &d, &e)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, \"foo\", a)\n\t\t\trequire.Equal(t, \"bar\", b)\n\t\t\trequire.Equal(t, rowCount, c)\n\t\t\trequire.False(t, d.Valid)\n\t\t\trequire.Equal(t, rowCount, e)\n\t\t}\n\t}\n\n\trequire.NoError(t, rows.Err())\n\trequire.Equal(t, int32(10), rowCount)\n}\n\n// https://github.com/jackc/pgx/issues/228\nfunc TestRowsScanDoesNotAllowScanningBinaryFormatValuesIntoString(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support point type\")\n\n\tvar s string\n\n\terr := conn.QueryRow(context.Background(), \"select point(1,2)\").Scan(&s)\n\tif err == nil || !(strings.Contains(err.Error(), \"cannot scan point (OID 600) in binary format into *string\")) {\n\t\tt.Fatalf(\"Expected Scan to fail to scan binary value into string but: %v\", err)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryRawValues(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar rowCount int32\n\n\trows, err := conn.Query(\n\t\tcontext.Background(),\n\t\t\"select 'foo'::text, 'bar'::varchar, n, null, n from generate_series(1,$1) n\",\n\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t10,\n\t)\n\trequire.NoError(t, err)\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\trowCount++\n\n\t\trawValues := rows.RawValues()\n\t\tassert.Len(t, rawValues, 5)\n\t\tassert.Equal(t, \"foo\", string(rawValues[0]))\n\t\tassert.Equal(t, \"bar\", string(rawValues[1]))\n\t\tassert.Equal(t, strconv.FormatInt(int64(rowCount), 10), string(rawValues[2]))\n\t\tassert.Nil(t, rawValues[3])\n\t\tassert.Equal(t, strconv.FormatInt(int64(rowCount), 10), string(rawValues[4]))\n\t}\n\n\trequire.NoError(t, rows.Err())\n\tassert.EqualValues(t, 10, rowCount)\n}\n\n// Test that a connection stays valid when query results are closed early\nfunc TestConnQueryCloseEarly(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// Immediately close query without reading any rows\n\trows, err := conn.Query(context.Background(), \"select generate_series(1,$1)\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\trows.Close()\n\n\tensureConnValid(t, conn)\n\n\t// Read partial response then close\n\trows, err = conn.Query(context.Background(), \"select generate_series(1,$1)\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\n\tok := rows.Next()\n\tif !ok {\n\t\tt.Fatal(\"rows.Next terminated early\")\n\t}\n\n\tvar n int32\n\trows.Scan(&n)\n\tif n != 1 {\n\t\tt.Fatalf(\"Expected 1 from first row, but got %v\", n)\n\t}\n\n\trows.Close()\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryCloseEarlyWithErrorOnWire(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\trows, err := conn.Query(context.Background(), \"select 1/(10-n) from generate_series(1,10) n\")\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\tassert.False(t, pgconn.SafeToRetry(err))\n\trows.Close()\n\n\tensureConnValid(t, conn)\n}\n\n// Test that a connection stays valid when query results read incorrectly\nfunc TestConnQueryReadWrongTypeError(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// Read a single value incorrectly\n\trows, err := conn.Query(context.Background(), \"select n::int4 from generate_series(1,$1) n\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\n\trowsRead := 0\n\n\tfor rows.Next() {\n\t\tvar t time.Time\n\t\trows.Scan(&t)\n\t\trowsRead++\n\t}\n\n\tif rowsRead != 1 {\n\t\tt.Fatalf(\"Expected error to cause only 1 row to be read, but %d were read\", rowsRead)\n\t}\n\n\tif rows.Err() == nil {\n\t\tt.Fatal(\"Expected Rows to have an error after an improper read but it didn't\")\n\t}\n\n\tif rows.Err().Error() != \"can't scan into dest[0] (col: n): cannot scan int4 (OID 23) in binary format into *time.Time\" {\n\t\tt.Fatalf(\"Expected different Rows.Err(): %v\", rows.Err())\n\t}\n\n\tensureConnValid(t, conn)\n}\n\n// Test that a connection stays valid when query results read incorrectly\nfunc TestConnQueryReadTooManyValues(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// Read too many values\n\trows, err := conn.Query(context.Background(), \"select generate_series(1,$1)\", 10)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\n\trowsRead := 0\n\n\tfor rows.Next() {\n\t\tvar n, m int32\n\t\trows.Scan(&n, &m)\n\t\trowsRead++\n\t}\n\n\tif rowsRead != 1 {\n\t\tt.Fatalf(\"Expected error to cause only 1 row to be read, but %d were read\", rowsRead)\n\t}\n\n\tif rows.Err() == nil {\n\t\tt.Fatal(\"Expected Rows to have an error after an improper read but it didn't\")\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryScanIgnoreColumn(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\trows, err := conn.Query(context.Background(), \"select 1::int8, 2::int8, 3::int8\")\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\n\tok := rows.Next()\n\tif !ok {\n\t\tt.Fatal(\"rows.Next terminated early\")\n\t}\n\n\tvar n, m int64\n\terr = rows.Scan(&n, nil, &m)\n\tif err != nil {\n\t\tt.Fatalf(\"rows.Scan failed: %v\", err)\n\t}\n\trows.Close()\n\n\tif n != 1 {\n\t\tt.Errorf(\"Expected n to equal 1, but it was %d\", n)\n\t}\n\n\tif m != 3 {\n\t\tt.Errorf(\"Expected n to equal 3, but it was %d\", m)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/issues/570\nfunc TestConnQueryDeferredError(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\n\tmustExec(t, conn, `create temporary table t (\n\tid text primary key,\n\tn int not null,\n\tunique (n) deferrable initially deferred\n);\n\ninsert into t (id, n) values ('a', 1), ('b', 2), ('c', 3);`)\n\n\trows, err := conn.Query(context.Background(), `update t set n=n+1 where id='b' returning *`)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar id string\n\t\tvar n int32\n\t\terr = rows.Scan(&id, &n)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\tif rows.Err() == nil {\n\t\tt.Fatal(\"expected error 23505 but got none\")\n\t}\n\n\tif err, ok := rows.Err().(*pgconn.PgError); !ok || err.Code != \"23505\" {\n\t\tt.Fatalf(\"expected error 23505, got %v\", err)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryErrorWhileReturningRows(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server uses numeric instead of int\")\n\n\tfor range 100 {\n\t\tfunc() {\n\t\t\tsql := `select 42 / (random() * 20)::integer from generate_series(1,100000)`\n\n\t\t\trows, err := conn.Query(context.Background(), sql)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer rows.Close()\n\n\t\t\tfor rows.Next() {\n\t\t\t\tvar n int32\n\t\t\t\tif err := rows.Scan(&n); err != nil {\n\t\t\t\t\tt.Fatalf(\"Row scan failed: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif _, ok := rows.Err().(*pgconn.PgError); !ok {\n\t\t\t\tt.Fatalf(\"Expected pgconn.PgError, got %v\", rows.Err())\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}()\n\t}\n}\n\nfunc TestQueryEncodeError(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\trows, err := conn.Query(context.Background(), \"select $1::integer\", \"wrong\")\n\tif err != nil {\n\t\tt.Errorf(\"conn.Query failure: %v\", err)\n\t}\n\tassert.False(t, pgconn.SafeToRetry(err))\n\tdefer rows.Close()\n\n\trows.Next()\n\n\tif rows.Err() == nil {\n\t\tt.Error(\"Expected rows.Err() to return error, but it didn't\")\n\t}\n\tif !strings.Contains(rows.Err().Error(), \"SQLSTATE 22P02\") {\n\t\tt.Error(\"Expected rows.Err() to return different error:\", rows.Err())\n\t}\n}\n\nfunc TestQueryRowCoreTypes(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttype allTypes struct {\n\t\ts   string\n\t\tf32 float32\n\t\tf64 float64\n\t\tb   bool\n\t\tt   time.Time\n\t\toid uint32\n\t}\n\n\tvar actual, zero allTypes\n\n\ttests := []struct {\n\t\tsql       string\n\t\tqueryArgs []any\n\t\tscanArgs  []any\n\t\texpected  allTypes\n\t}{\n\t\t{\"select $1::text\", []any{\"Jack\"}, []any{&actual.s}, allTypes{s: \"Jack\"}},\n\t\t{\"select $1::float4\", []any{float32(1.23)}, []any{&actual.f32}, allTypes{f32: 1.23}},\n\t\t{\"select $1::float8\", []any{float64(1.23)}, []any{&actual.f64}, allTypes{f64: 1.23}},\n\t\t{\"select $1::bool\", []any{true}, []any{&actual.b}, allTypes{b: true}},\n\t\t{\"select $1::timestamptz\", []any{time.Unix(123, 5000)}, []any{&actual.t}, allTypes{t: time.Unix(123, 5000)}},\n\t\t{\"select $1::timestamp\", []any{time.Date(2010, 1, 2, 3, 4, 5, 0, time.UTC)}, []any{&actual.t}, allTypes{t: time.Date(2010, 1, 2, 3, 4, 5, 0, time.UTC)}},\n\t\t{\"select $1::date\", []any{time.Date(1987, 1, 2, 0, 0, 0, 0, time.UTC)}, []any{&actual.t}, allTypes{t: time.Date(1987, 1, 2, 0, 0, 0, 0, time.UTC)}},\n\t\t{\"select $1::oid\", []any{uint32(42)}, []any{&actual.oid}, allTypes{oid: 42}},\n\t}\n\n\tfor i, tt := range tests {\n\t\tactual = zero\n\n\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.queryArgs...).Scan(tt.scanArgs...)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, queryArgs -> %v)\", i, err, tt.sql, tt.queryArgs)\n\t\t}\n\n\t\tif actual.s != tt.expected.s || actual.f32 != tt.expected.f32 || actual.b != tt.expected.b || !actual.t.Equal(tt.expected.t) || actual.oid != tt.expected.oid {\n\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v, queryArgs -> %v)\", i, tt.expected, actual, tt.sql, tt.queryArgs)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\n\t\t// Check that Scan errors when a core type is null\n\t\terr = conn.QueryRow(context.Background(), tt.sql, nil).Scan(tt.scanArgs...)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"%d. Expected null to cause error, but it didn't (sql -> %v)\", i, tt.sql)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n}\n\nfunc TestQueryRowCoreIntegerEncoding(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttype allTypes struct {\n\t\ti16 int16\n\t\ti32 int32\n\t\ti64 int64\n\t}\n\n\tvar actual, zero allTypes\n\n\tsuccessfulEncodeTests := []struct {\n\t\tsql      string\n\t\tqueryArg any\n\t\tscanArg  any\n\t\texpected allTypes\n\t}{\n\t\t// Check any integer type where value is within int2 range can be encoded\n\t\t{\"select $1::int2\", int(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", int8(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", int16(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", int32(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", int64(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", uint(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", uint8(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", uint16(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", uint32(42), &actual.i16, allTypes{i16: 42}},\n\t\t{\"select $1::int2\", uint64(42), &actual.i16, allTypes{i16: 42}},\n\n\t\t// Check any integer type where value is within int4 range can be encoded\n\t\t{\"select $1::int4\", int(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", int8(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", int16(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", int32(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", int64(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", uint(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", uint8(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", uint16(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", uint32(42), &actual.i32, allTypes{i32: 42}},\n\t\t{\"select $1::int4\", uint64(42), &actual.i32, allTypes{i32: 42}},\n\n\t\t// Check any integer type where value is within int8 range can be encoded\n\t\t{\"select $1::int8\", int(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", int8(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", int16(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", int32(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", int64(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", uint(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", uint8(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", uint16(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", uint32(42), &actual.i64, allTypes{i64: 42}},\n\t\t{\"select $1::int8\", uint64(42), &actual.i64, allTypes{i64: 42}},\n\t}\n\n\tfor i, tt := range successfulEncodeTests {\n\t\tactual = zero\n\n\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.queryArg).Scan(tt.scanArg)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, queryArg -> %v)\", i, err, tt.sql, tt.queryArg)\n\t\t\tcontinue\n\t\t}\n\n\t\tif actual != tt.expected {\n\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v, queryArg -> %v)\", i, tt.expected, actual, tt.sql, tt.queryArg)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n\n\tfailedEncodeTests := []struct {\n\t\tsql      string\n\t\tqueryArg any\n\t}{\n\t\t// Check any integer type where value is outside pg:int2 range cannot be encoded\n\t\t{\"select $1::int2\", int(32769)},\n\t\t{\"select $1::int2\", int32(32769)},\n\t\t{\"select $1::int2\", int32(32769)},\n\t\t{\"select $1::int2\", int64(32769)},\n\t\t{\"select $1::int2\", uint(32769)},\n\t\t{\"select $1::int2\", uint16(32769)},\n\t\t{\"select $1::int2\", uint32(32769)},\n\t\t{\"select $1::int2\", uint64(32769)},\n\n\t\t// Check any integer type where value is outside pg:int4 range cannot be encoded\n\t\t{\"select $1::int4\", int64(2147483649)},\n\t\t{\"select $1::int4\", uint32(2147483649)},\n\t\t{\"select $1::int4\", uint64(2147483649)},\n\n\t\t// Check any integer type where value is outside pg:int8 range cannot be encoded\n\t\t{\"select $1::int8\", uint64(9223372036854775809)},\n\t}\n\n\tfor i, tt := range failedEncodeTests {\n\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.queryArg).Scan(nil)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"%d. Expected failure to encode, but unexpectedly succeeded: %v (sql -> %v, queryArg -> %v)\", i, err, tt.sql, tt.queryArg)\n\t\t} else if !strings.Contains(err.Error(), \"is greater than\") {\n\t\t\tt.Errorf(\"%d. Expected failure to encode, but got: %v (sql -> %v, queryArg -> %v)\", i, err, tt.sql, tt.queryArg)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n}\n\nfunc TestQueryRowCoreIntegerDecoding(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttype allTypes struct {\n\t\tui   uint\n\t\tui8  uint8\n\t\tui16 uint16\n\t\tui32 uint32\n\t\tui64 uint64\n\t\ti    int\n\t\ti8   int8\n\t\ti16  int16\n\t\ti32  int32\n\t\ti64  int64\n\t}\n\n\tvar actual, zero allTypes\n\n\tsuccessfulDecodeTests := []struct {\n\t\tsql      string\n\t\tscanArg  any\n\t\texpected allTypes\n\t}{\n\t\t// Check any integer type where value is within Go:int range can be decoded\n\t\t{\"select 42::int2\", &actual.i, allTypes{i: 42}},\n\t\t{\"select 42::int4\", &actual.i, allTypes{i: 42}},\n\t\t{\"select 42::int8\", &actual.i, allTypes{i: 42}},\n\t\t{\"select -42::int2\", &actual.i, allTypes{i: -42}},\n\t\t{\"select -42::int4\", &actual.i, allTypes{i: -42}},\n\t\t{\"select -42::int8\", &actual.i, allTypes{i: -42}},\n\n\t\t// Check any integer type where value is within Go:int8 range can be decoded\n\t\t{\"select 42::int2\", &actual.i8, allTypes{i8: 42}},\n\t\t{\"select 42::int4\", &actual.i8, allTypes{i8: 42}},\n\t\t{\"select 42::int8\", &actual.i8, allTypes{i8: 42}},\n\t\t{\"select -42::int2\", &actual.i8, allTypes{i8: -42}},\n\t\t{\"select -42::int4\", &actual.i8, allTypes{i8: -42}},\n\t\t{\"select -42::int8\", &actual.i8, allTypes{i8: -42}},\n\n\t\t// Check any integer type where value is within Go:int16 range can be decoded\n\t\t{\"select 42::int2\", &actual.i16, allTypes{i16: 42}},\n\t\t{\"select 42::int4\", &actual.i16, allTypes{i16: 42}},\n\t\t{\"select 42::int8\", &actual.i16, allTypes{i16: 42}},\n\t\t{\"select -42::int2\", &actual.i16, allTypes{i16: -42}},\n\t\t{\"select -42::int4\", &actual.i16, allTypes{i16: -42}},\n\t\t{\"select -42::int8\", &actual.i16, allTypes{i16: -42}},\n\n\t\t// Check any integer type where value is within Go:int32 range can be decoded\n\t\t{\"select 42::int2\", &actual.i32, allTypes{i32: 42}},\n\t\t{\"select 42::int4\", &actual.i32, allTypes{i32: 42}},\n\t\t{\"select 42::int8\", &actual.i32, allTypes{i32: 42}},\n\t\t{\"select -42::int2\", &actual.i32, allTypes{i32: -42}},\n\t\t{\"select -42::int4\", &actual.i32, allTypes{i32: -42}},\n\t\t{\"select -42::int8\", &actual.i32, allTypes{i32: -42}},\n\n\t\t// Check any integer type where value is within Go:int64 range can be decoded\n\t\t{\"select 42::int2\", &actual.i64, allTypes{i64: 42}},\n\t\t{\"select 42::int4\", &actual.i64, allTypes{i64: 42}},\n\t\t{\"select 42::int8\", &actual.i64, allTypes{i64: 42}},\n\t\t{\"select -42::int2\", &actual.i64, allTypes{i64: -42}},\n\t\t{\"select -42::int4\", &actual.i64, allTypes{i64: -42}},\n\t\t{\"select -42::int8\", &actual.i64, allTypes{i64: -42}},\n\n\t\t// Check any integer type where value is within Go:uint range can be decoded\n\t\t{\"select 128::int2\", &actual.ui, allTypes{ui: 128}},\n\t\t{\"select 128::int4\", &actual.ui, allTypes{ui: 128}},\n\t\t{\"select 128::int8\", &actual.ui, allTypes{ui: 128}},\n\n\t\t// Check any integer type where value is within Go:uint8 range can be decoded\n\t\t{\"select 128::int2\", &actual.ui8, allTypes{ui8: 128}},\n\t\t{\"select 128::int4\", &actual.ui8, allTypes{ui8: 128}},\n\t\t{\"select 128::int8\", &actual.ui8, allTypes{ui8: 128}},\n\n\t\t// Check any integer type where value is within Go:uint16 range can be decoded\n\t\t{\"select 42::int2\", &actual.ui16, allTypes{ui16: 42}},\n\t\t{\"select 32768::int4\", &actual.ui16, allTypes{ui16: 32768}},\n\t\t{\"select 32768::int8\", &actual.ui16, allTypes{ui16: 32768}},\n\n\t\t// Check any integer type where value is within Go:uint32 range can be decoded\n\t\t{\"select 42::int2\", &actual.ui32, allTypes{ui32: 42}},\n\t\t{\"select 42::int4\", &actual.ui32, allTypes{ui32: 42}},\n\t\t{\"select 2147483648::int8\", &actual.ui32, allTypes{ui32: 2147483648}},\n\n\t\t// Check any integer type where value is within Go:uint64 range can be decoded\n\t\t{\"select 42::int2\", &actual.ui64, allTypes{ui64: 42}},\n\t\t{\"select 42::int4\", &actual.ui64, allTypes{ui64: 42}},\n\t\t{\"select 42::int8\", &actual.ui64, allTypes{ui64: 42}},\n\t}\n\n\tfor i, tt := range successfulDecodeTests {\n\t\tactual = zero\n\n\t\terr := conn.QueryRow(context.Background(), tt.sql).Scan(tt.scanArg)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v)\", i, err, tt.sql)\n\t\t\tcontinue\n\t\t}\n\n\t\tif actual != tt.expected {\n\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v)\", i, tt.expected, actual, tt.sql)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n\n\tfailedDecodeTests := []struct {\n\t\tsql     string\n\t\tscanArg any\n\t}{\n\t\t// Check any integer type where value is outside Go:int8 range cannot be decoded\n\t\t{\"select 128::int2\", &actual.i8},\n\t\t{\"select 128::int4\", &actual.i8},\n\t\t{\"select 128::int8\", &actual.i8},\n\t\t{\"select -129::int2\", &actual.i8},\n\t\t{\"select -129::int4\", &actual.i8},\n\t\t{\"select -129::int8\", &actual.i8},\n\n\t\t// Check any integer type where value is outside Go:int16 range cannot be decoded\n\t\t{\"select 32768::int4\", &actual.i16},\n\t\t{\"select 32768::int8\", &actual.i16},\n\t\t{\"select -32769::int4\", &actual.i16},\n\t\t{\"select -32769::int8\", &actual.i16},\n\n\t\t// Check any integer type where value is outside Go:int32 range cannot be decoded\n\t\t{\"select 2147483648::int8\", &actual.i32},\n\t\t{\"select -2147483649::int8\", &actual.i32},\n\n\t\t// Check any integer type where value is outside Go:uint range cannot be decoded\n\t\t{\"select -1::int2\", &actual.ui},\n\t\t{\"select -1::int4\", &actual.ui},\n\t\t{\"select -1::int8\", &actual.ui},\n\n\t\t// Check any integer type where value is outside Go:uint8 range cannot be decoded\n\t\t{\"select 256::int2\", &actual.ui8},\n\t\t{\"select 256::int4\", &actual.ui8},\n\t\t{\"select 256::int8\", &actual.ui8},\n\t\t{\"select -1::int2\", &actual.ui8},\n\t\t{\"select -1::int4\", &actual.ui8},\n\t\t{\"select -1::int8\", &actual.ui8},\n\n\t\t// Check any integer type where value is outside Go:uint16 cannot be decoded\n\t\t{\"select 65536::int4\", &actual.ui16},\n\t\t{\"select 65536::int8\", &actual.ui16},\n\t\t{\"select -1::int2\", &actual.ui16},\n\t\t{\"select -1::int4\", &actual.ui16},\n\t\t{\"select -1::int8\", &actual.ui16},\n\n\t\t// Check any integer type where value is outside Go:uint32 range cannot be decoded\n\t\t{\"select 4294967296::int8\", &actual.ui32},\n\t\t{\"select -1::int2\", &actual.ui32},\n\t\t{\"select -1::int4\", &actual.ui32},\n\t\t{\"select -1::int8\", &actual.ui32},\n\n\t\t// Check any integer type where value is outside Go:uint64 range cannot be decoded\n\t\t{\"select -1::int2\", &actual.ui64},\n\t\t{\"select -1::int4\", &actual.ui64},\n\t\t{\"select -1::int8\", &actual.ui64},\n\t}\n\n\tfor i, tt := range failedDecodeTests {\n\t\terr := conn.QueryRow(context.Background(), tt.sql).Scan(tt.scanArg)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"%d. Expected failure to decode, but unexpectedly succeeded: %v (sql -> %v)\", i, err, tt.sql)\n\t\t} else if !strings.Contains(err.Error(), \"can't scan\") {\n\t\t\tt.Errorf(\"%d. Expected failure to decode, but got: %v (sql -> %v)\", i, err, tt.sql)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n}\n\nfunc TestQueryRowCoreByteSlice(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttests := []struct {\n\t\tsql      string\n\t\tqueryArg any\n\t\texpected []byte\n\t}{\n\t\t{\"select $1::text\", \"Jack\", []byte(\"Jack\")},\n\t\t{\"select $1::text\", []byte(\"Jack\"), []byte(\"Jack\")},\n\t\t{\"select $1::varchar\", []byte(\"Jack\"), []byte(\"Jack\")},\n\t\t{\"select $1::bytea\", []byte{0, 15, 255, 17}, []byte{0, 15, 255, 17}},\n\t}\n\n\tfor i, tt := range tests {\n\t\tvar actual []byte\n\n\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.queryArg).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v)\", i, err, tt.sql)\n\t\t}\n\n\t\tif !bytes.Equal(actual, tt.expected) {\n\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v)\", i, tt.expected, actual, tt.sql)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n}\n\nfunc TestQueryRowErrors(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Skipping due to known server missing point type\")\n\t}\n\n\ttype allTypes struct {\n\t\ti16 int16\n\t\ts   string\n\t}\n\n\tvar actual, zero allTypes\n\n\ttests := []struct {\n\t\tsql       string\n\t\tqueryArgs []any\n\t\tscanArgs  []any\n\t\terr       string\n\t}{\n\t\t{\"select $1::badtype\", []any{\"Jack\"}, []any{&actual.i16}, `type \"badtype\" does not exist`},\n\t\t{\"SYNTAX ERROR\", []any{}, []any{&actual.i16}, \"SQLSTATE 42601\"},\n\t\t{\"select $1::text\", []any{\"Jack\"}, []any{&actual.i16}, \"cannot scan text (OID 25) in text format into *int16\"},\n\t\t{\"select $1::point\", []any{int(705)}, []any{&actual.s}, \"unable to encode 705 into binary format for point (OID 600)\"},\n\t}\n\n\tfor i, tt := range tests {\n\t\tactual = zero\n\n\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.queryArgs...).Scan(tt.scanArgs...)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"%d. Unexpected success (sql -> %v, queryArgs -> %v)\", i, tt.sql, tt.queryArgs)\n\t\t}\n\t\tif err != nil && !strings.Contains(err.Error(), tt.err) {\n\t\t\tt.Errorf(\"%d. Expected error to contain %s, but got %v (sql -> %v, queryArgs -> %v)\", i, tt.err, err, tt.sql, tt.queryArgs)\n\t\t}\n\n\t\tensureConnValid(t, conn)\n\t}\n}\n\nfunc TestQueryRowNoResults(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar n int32\n\terr := conn.QueryRow(context.Background(), \"select 1 where 1=0\").Scan(&n)\n\tif err != pgx.ErrNoRows {\n\t\tt.Errorf(\"Expected pgx.ErrNoRows, got %v\", err)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestQueryRowEmptyQuery(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tvar n int32\n\terr := conn.QueryRow(ctx, \"\").Scan(&n)\n\trequire.Error(t, err)\n\trequire.False(t, pgconn.Timeout(err))\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestReadingValueAfterEmptyArray(t *testing.T) {\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar a []string\n\tvar b int32\n\terr := conn.QueryRow(context.Background(), \"select '{}'::text[], 42::integer\").Scan(&a, &b)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.QueryRow failed: %v\", err)\n\t}\n\n\tif len(a) != 0 {\n\t\tt.Errorf(\"Expected 'a' to have length 0, but it was: %d\", len(a))\n\t}\n\n\tif b != 42 {\n\t\tt.Errorf(\"Expected 'b' to 42, but it was: %d\", b)\n\t}\n}\n\nfunc TestReadingNullByteArray(t *testing.T) {\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar a []byte\n\terr := conn.QueryRow(context.Background(), \"select null::text\").Scan(&a)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.QueryRow failed: %v\", err)\n\t}\n\n\tif a != nil {\n\t\tt.Errorf(\"Expected 'a' to be nil, but it was: %v\", a)\n\t}\n}\n\nfunc TestReadingNullByteArrays(t *testing.T) {\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\trows, err := conn.Query(context.Background(), \"select null::text union all select null::text\")\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Query failed: %v\", err)\n\t}\n\n\tcount := 0\n\tfor rows.Next() {\n\t\tcount++\n\t\tvar a []byte\n\t\tif err := rows.Scan(&a); err != nil {\n\t\t\tt.Fatalf(\"failed to scan row: %v\", err)\n\t\t}\n\t\tif a != nil {\n\t\t\tt.Errorf(\"Expected 'a' to be nil, but it was: %v\", a)\n\t\t}\n\t}\n\tif count != 2 {\n\t\tt.Errorf(\"Expected to read 2 rows, read: %d\", count)\n\t}\n}\n\nfunc TestQueryNullSliceIsSet(t *testing.T) {\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ta := []int32{1, 2, 3}\n\terr := conn.QueryRow(context.Background(), \"select null::int[]\").Scan(&a)\n\tif err != nil {\n\t\tt.Fatalf(\"conn.QueryRow failed: %v\", err)\n\t}\n\n\tif a != nil {\n\t\tt.Errorf(\"Expected 'a' to be nil, but it was: %v\", a)\n\t}\n}\n\nfunc TestConnQueryDatabaseSQLScanner(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar num sql.NullFloat64\n\n\terr := conn.QueryRow(context.Background(), \"select '1234.567'::float8\").Scan(&num)\n\tif err != nil {\n\t\tt.Fatalf(\"Scan failed: %v\", err)\n\t}\n\n\trequire.True(t, num.Valid)\n\trequire.Equal(t, 1234.567, num.Float64)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryDatabaseSQLDriverValuer(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\texpected := sql.NullFloat64{Float64: 1234.567, Valid: true}\n\tvar actual sql.NullFloat64\n\n\terr := conn.QueryRow(context.Background(), \"select $1::float8\", &expected).Scan(&actual)\n\trequire.NoError(t, err)\n\trequire.Equal(t, expected, actual)\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/issues/339\nfunc TestConnQueryDatabaseSQLDriverValuerWithAutoGeneratedPointerReceiver(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, \"create temporary table t(n numeric)\")\n\n\tvar d *sql.NullInt64\n\tcommandTag, err := conn.Exec(context.Background(), `insert into t(n) values($1)`, d)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif commandTag.String() != \"INSERT 0 1\" {\n\t\tt.Fatalf(\"want %s, got %s\", \"INSERT 0 1\", commandTag)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\ntype nilPointerAsEmptyJSONObject struct {\n\tID   string\n\tName string\n}\n\nfunc (v *nilPointerAsEmptyJSONObject) Value() (driver.Value, error) {\n\tif v == nil {\n\t\treturn \"{}\", nil\n\t}\n\n\treturn json.Marshal(v)\n}\n\n// https://github.com/jackc/pgx/issues/1566\nfunc TestConnQueryDatabaseSQLDriverValuerCalledOnNilPointerImplementers(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, \"create temporary table t(v json not null)\")\n\n\tvar v *nilPointerAsEmptyJSONObject\n\tcommandTag, err := conn.Exec(context.Background(), `insert into t(v) values($1)`, v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"INSERT 0 1\", commandTag.String())\n\n\tvar s string\n\terr = conn.QueryRow(context.Background(), \"select v from t\").Scan(&s)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"{}\", s)\n\n\t_, err = conn.Exec(context.Background(), `delete from t`)\n\trequire.NoError(t, err)\n\n\tv = &nilPointerAsEmptyJSONObject{ID: \"1\", Name: \"foo\"}\n\tcommandTag, err = conn.Exec(context.Background(), `insert into t(v) values($1)`, v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"INSERT 0 1\", commandTag.String())\n\n\tvar v2 *nilPointerAsEmptyJSONObject\n\terr = conn.QueryRow(context.Background(), \"select v from t\").Scan(&v2)\n\trequire.NoError(t, err)\n\trequire.Equal(t, v, v2)\n\n\tensureConnValid(t, conn)\n}\n\ntype nilSliceAsEmptySlice []byte\n\nfunc (j nilSliceAsEmptySlice) Value() (driver.Value, error) {\n\tif len(j) == 0 {\n\t\treturn []byte(\"[]\"), nil\n\t}\n\n\treturn []byte(j), nil\n}\n\nfunc (j *nilSliceAsEmptySlice) UnmarshalJSON(data []byte) error {\n\t*j = bytes.Clone(data)\n\treturn nil\n}\n\n// https://github.com/jackc/pgx/issues/1860\nfunc TestConnQueryDatabaseSQLDriverValuerCalledOnNilSliceImplementers(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, \"create temporary table t(v json not null)\")\n\n\tvar v nilSliceAsEmptySlice\n\tcommandTag, err := conn.Exec(context.Background(), `insert into t(v) values($1)`, v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"INSERT 0 1\", commandTag.String())\n\n\tvar s string\n\terr = conn.QueryRow(context.Background(), \"select v from t\").Scan(&s)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"[]\", s)\n\n\t_, err = conn.Exec(context.Background(), `delete from t`)\n\trequire.NoError(t, err)\n\n\tv = nilSliceAsEmptySlice(`{\"name\": \"foo\"}`)\n\tcommandTag, err = conn.Exec(context.Background(), `insert into t(v) values($1)`, v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"INSERT 0 1\", commandTag.String())\n\n\tvar v2 nilSliceAsEmptySlice\n\terr = conn.QueryRow(context.Background(), \"select v from t\").Scan(&v2)\n\trequire.NoError(t, err)\n\trequire.Equal(t, v, v2)\n\n\tensureConnValid(t, conn)\n}\n\ntype nilMapAsEmptyObject map[string]any\n\nfunc (j nilMapAsEmptyObject) Value() (driver.Value, error) {\n\tif j == nil {\n\t\treturn []byte(\"{}\"), nil\n\t}\n\n\treturn json.Marshal(j)\n}\n\nfunc (j *nilMapAsEmptyObject) UnmarshalJSON(data []byte) error {\n\tvar m map[string]any\n\terr := json.Unmarshal(data, &m)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*j = m\n\n\treturn nil\n}\n\n// https://github.com/jackc/pgx/pull/2019#discussion_r1605806751\nfunc TestConnQueryDatabaseSQLDriverValuerCalledOnNilMapImplementers(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tmustExec(t, conn, \"create temporary table t(v json not null)\")\n\n\tvar v nilMapAsEmptyObject\n\tcommandTag, err := conn.Exec(context.Background(), `insert into t(v) values($1)`, v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"INSERT 0 1\", commandTag.String())\n\n\tvar s string\n\terr = conn.QueryRow(context.Background(), \"select v from t\").Scan(&s)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"{}\", s)\n\n\t_, err = conn.Exec(context.Background(), `delete from t`)\n\trequire.NoError(t, err)\n\n\tv = nilMapAsEmptyObject{\"name\": \"foo\"}\n\tcommandTag, err = conn.Exec(context.Background(), `insert into t(v) values($1)`, v)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"INSERT 0 1\", commandTag.String())\n\n\tvar v2 nilMapAsEmptyObject\n\terr = conn.QueryRow(context.Background(), \"select v from t\").Scan(&v2)\n\trequire.NoError(t, err)\n\trequire.Equal(t, v, v2)\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryDatabaseSQLDriverScannerWithBinaryPgTypeThatAcceptsSameType(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tvar actual sql.NullString\n\terr := conn.QueryRow(context.Background(), \"select '6ba7b810-9dad-11d1-80b4-00c04fd430c8'::uuid\").Scan(&actual)\n\trequire.NoError(t, err)\n\n\trequire.True(t, actual.Valid)\n\trequire.Equal(t, \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", actual.String)\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/issues/1273#issuecomment-1221672175\nfunc TestConnQueryDatabaseSQLDriverValuerTextWhenBinaryIsPreferred(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\targ := sql.NullString{String: \"1.234\", Valid: true}\n\tvar result pgtype.Numeric\n\terr := conn.QueryRow(context.Background(), \"select $1::numeric\", arg).Scan(&result)\n\trequire.NoError(t, err)\n\n\trequire.True(t, result.Valid)\n\tf64, err := result.Float64Value()\n\trequire.NoError(t, err)\n\trequire.Equal(t, pgtype.Float8{Float64: 1.234, Valid: true}, f64)\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/issues/1426\nfunc TestConnQueryDatabaseSQLNullFloat64NegativeZeroPointZero(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttests := []float64{\n\t\t-0.01,\n\t\t-0.001,\n\t\t-0.0001,\n\t}\n\n\tfor _, val := range tests {\n\t\tvar result sql.NullFloat64\n\t\terr := conn.QueryRow(context.Background(), \"select $1::numeric\", val).Scan(&result)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, sql.NullFloat64{Float64: val, Valid: true}, result)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryDatabaseSQLNullX(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttype row struct {\n\t\tboolValid    sql.NullBool\n\t\tboolNull     sql.NullBool\n\t\tint64Valid   sql.NullInt64\n\t\tint64Null    sql.NullInt64\n\t\tfloat64Valid sql.NullFloat64\n\t\tfloat64Null  sql.NullFloat64\n\t\tstringValid  sql.NullString\n\t\tstringNull   sql.NullString\n\t}\n\n\texpected := row{\n\t\tboolValid:    sql.NullBool{Bool: true, Valid: true},\n\t\tint64Valid:   sql.NullInt64{Int64: 123, Valid: true},\n\t\tfloat64Valid: sql.NullFloat64{Float64: 3.14, Valid: true},\n\t\tstringValid:  sql.NullString{String: \"pgx\", Valid: true},\n\t}\n\n\tvar actual row\n\n\terr := conn.QueryRow(\n\t\tcontext.Background(),\n\t\t\"select $1::bool, $2::bool, $3::int8, $4::int8, $5::float8, $6::float8, $7::text, $8::text\",\n\t\texpected.boolValid,\n\t\texpected.boolNull,\n\t\texpected.int64Valid,\n\t\texpected.int64Null,\n\t\texpected.float64Valid,\n\t\texpected.float64Null,\n\t\texpected.stringValid,\n\t\texpected.stringNull,\n\t).Scan(\n\t\t&actual.boolValid,\n\t\t&actual.boolNull,\n\t\t&actual.int64Valid,\n\t\t&actual.int64Null,\n\t\t&actual.float64Valid,\n\t\t&actual.float64Null,\n\t\t&actual.stringValid,\n\t\t&actual.stringNull,\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"Scan failed: %v\", err)\n\t}\n\n\tif expected != actual {\n\t\tt.Errorf(\"Expected %v, but got %v\", expected, actual)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestQueryContextSuccess(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tctx := t.Context()\n\n\trows, err := conn.Query(ctx, \"select 42::integer\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar result, rowCount int\n\tfor rows.Next() {\n\t\terr = rows.Scan(&result)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\trowCount++\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Fatal(rows.Err())\n\t}\n\n\tif rowCount != 1 {\n\t\tt.Fatalf(\"Expected 1 row, got %d\", rowCount)\n\t}\n\tif result != 42 {\n\t\tt.Fatalf(\"Expected result 42, got %d\", result)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestQueryContextErrorWhileReceivingRows(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server uses numeric instead of int\")\n\n\tctx := t.Context()\n\n\trows, err := conn.Query(ctx, \"select 10/(10-n) from generate_series(1, 100) n\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar result, rowCount int\n\tfor rows.Next() {\n\t\terr = rows.Scan(&result)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\trowCount++\n\t}\n\n\tif rows.Err() == nil || rows.Err().Error() != \"ERROR: division by zero (SQLSTATE 22012)\" {\n\t\tt.Fatalf(\"Expected division by zero error, but got %v\", rows.Err())\n\t}\n\n\tif rowCount != 9 {\n\t\tt.Fatalf(\"Expected 9 rows, got %d\", rowCount)\n\t}\n\tif result != 10 {\n\t\tt.Fatalf(\"Expected result 10, got %d\", result)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestQueryRowContextSuccess(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tctx := t.Context()\n\n\tvar result int\n\terr := conn.QueryRow(ctx, \"select 42::integer\").Scan(&result)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif result != 42 {\n\t\tt.Fatalf(\"Expected result 42, got %d\", result)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestQueryRowContextErrorWhileReceivingRow(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tctx := t.Context()\n\n\tvar result int\n\terr := conn.QueryRow(ctx, \"select 10/0\").Scan(&result)\n\tif err == nil || err.Error() != \"ERROR: division by zero (SQLSTATE 22012)\" {\n\t\tt.Fatalf(\"Expected division by zero error, but got %v\", err)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestQueryCloseBefore(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tcloseConn(t, conn)\n\n\t_, err := conn.Query(context.Background(), \"select 1\")\n\trequire.Error(t, err)\n\tassert.True(t, pgconn.SafeToRetry(err))\n}\n\nfunc TestScanRow(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tresultReader := conn.PgConn().ExecParams(context.Background(), \"select generate_series(1,$1)\", [][]byte{[]byte(\"10\")}, nil, nil, nil)\n\n\tvar sum, rowCount int32\n\n\tfor resultReader.NextRow() {\n\t\tvar n int32\n\t\terr := pgx.ScanRow(conn.TypeMap(), resultReader.FieldDescriptions(), resultReader.Values(), &n)\n\t\tassert.NoError(t, err)\n\t\tsum += n\n\t\trowCount++\n\t}\n\n\t_, err := resultReader.Close()\n\n\trequire.NoError(t, err)\n\tassert.EqualValues(t, 10, rowCount)\n\tassert.EqualValues(t, 55, sum)\n}\n\nfunc TestConnSimpleProtocol(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\t// Test all supported low-level types\n\n\t{\n\t\texpected := int64(42)\n\t\tvar actual int64\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1::int8\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpected,\n\t\t).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t}\n\t}\n\n\t{\n\t\texpected := float64(1.23)\n\t\tvar actual float64\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1::float8\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpected,\n\t\t).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t}\n\t}\n\n\t{\n\t\texpected := true\n\t\tvar actual bool\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1::boolean\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpected,\n\t\t).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t}\n\t}\n\n\t{\n\t\texpected := []byte{0, 1, 20, 35, 64, 80, 120, 3, 255, 240, 128, 95}\n\t\tvar actual []byte\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1::bytea\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpected,\n\t\t).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif !bytes.Equal(actual, expected) {\n\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t}\n\t}\n\n\t{\n\t\texpected := \"test\"\n\t\tvar actual string\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1::text\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpected,\n\t\t).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []string\n\t\t}{\n\t\t\t{[]string(nil)},\n\t\t\t{[]string{}},\n\t\t\t{[]string{\"test\", \"foo\", \"bar\"}},\n\t\t\t{[]string{`foo'bar\"\\baz;quz`, `foo'bar\"\\baz;quz`}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []string\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::text[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []int16\n\t\t}{\n\t\t\t{[]int16(nil)},\n\t\t\t{[]int16{}},\n\t\t\t{[]int16{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []int16\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []int32\n\t\t}{\n\t\t\t{[]int32(nil)},\n\t\t\t{[]int32{}},\n\t\t\t{[]int32{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []int32\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::int[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []int64\n\t\t}{\n\t\t\t{[]int64(nil)},\n\t\t\t{[]int64{}},\n\t\t\t{[]int64{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []int64\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []int\n\t\t}{\n\t\t\t{[]int(nil)},\n\t\t\t{[]int{}},\n\t\t\t{[]int{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []int\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []uint16\n\t\t}{\n\t\t\t{[]uint16(nil)},\n\t\t\t{[]uint16{}},\n\t\t\t{[]uint16{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []uint16\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []uint32\n\t\t}{\n\t\t\t{[]uint32(nil)},\n\t\t\t{[]uint32{}},\n\t\t\t{[]uint32{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []uint32\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []uint64\n\t\t}{\n\t\t\t{[]uint64(nil)},\n\t\t\t{[]uint64{}},\n\t\t\t{[]uint64{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []uint64\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []uint\n\t\t}{\n\t\t\t{[]uint(nil)},\n\t\t\t{[]uint{}},\n\t\t\t{[]uint{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []uint\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []float32\n\t\t}{\n\t\t\t{[]float32(nil)},\n\t\t\t{[]float32{}},\n\t\t\t{[]float32{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []float32\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::float4[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t{\n\t\ttests := []struct {\n\t\t\texpected []float64\n\t\t}{\n\t\t\t{[]float64(nil)},\n\t\t\t{[]float64{}},\n\t\t\t{[]float64{1, 2, 3}},\n\t\t}\n\t\tfor i, tt := range tests {\n\t\t\tvar actual []float64\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::float8[]\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\ttt.expected,\n\t\t\t).Scan(&actual)\n\t\t\tassert.NoErrorf(t, err, \"%d\", i)\n\t\t\tassert.Equalf(t, tt.expected, actual, \"%d\", i)\n\t\t}\n\t}\n\n\t// Test high-level type\n\n\t{\n\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") == \"\" {\n\t\t\t// CockroachDB doesn't support circle type.\n\t\t\texpected := pgtype.Circle{P: pgtype.Vec2{X: 1, Y: 2}, R: 1.5, Valid: true}\n\t\t\tactual := expected\n\t\t\terr := conn.QueryRow(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"select $1::circle\",\n\t\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\t\t&expected,\n\t\t\t).Scan(&actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif expected != actual {\n\t\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Test multiple args in single query\n\n\t{\n\t\texpectedInt64 := int64(234423)\n\t\texpectedFloat64 := float64(-0.2312)\n\t\texpectedBool := true\n\t\texpectedBytes := []byte{255, 0, 23, 16, 87, 45, 9, 23, 45, 223}\n\t\texpectedString := \"test\"\n\t\tvar actualInt64 int64\n\t\tvar actualFloat64 float64\n\t\tvar actualBool bool\n\t\tvar actualBytes []byte\n\t\tvar actualString string\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1::int8, $2::float8, $3::boolean, $4::bytea, $5::text\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpectedInt64, expectedFloat64, expectedBool, expectedBytes, expectedString,\n\t\t).Scan(&actualInt64, &actualFloat64, &actualBool, &actualBytes, &actualString)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif expectedInt64 != actualInt64 {\n\t\t\tt.Errorf(\"expected %v got %v\", expectedInt64, actualInt64)\n\t\t}\n\t\tif expectedFloat64 != actualFloat64 {\n\t\t\tt.Errorf(\"expected %v got %v\", expectedFloat64, actualFloat64)\n\t\t}\n\t\tif expectedBool != actualBool {\n\t\t\tt.Errorf(\"expected %v got %v\", expectedBool, actualBool)\n\t\t}\n\t\tif !bytes.Equal(expectedBytes, actualBytes) {\n\t\t\tt.Errorf(\"expected %v got %v\", expectedBytes, actualBytes)\n\t\t}\n\t\tif expectedString != actualString {\n\t\t\tt.Errorf(\"expected %v got %v\", expectedString, actualString)\n\t\t}\n\t}\n\n\t// Test dangerous cases\n\n\t{\n\t\texpected := \"foo';drop table users;\"\n\t\tvar actual string\n\t\terr := conn.QueryRow(\n\t\t\tcontext.Background(),\n\t\t\t\"select $1\",\n\t\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\texpected,\n\t\t).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"expected %v got %v\", expected, actual)\n\t\t}\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnSimpleProtocolRefusesNonUTF8ClientEncoding(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support changing client_encoding (https://www.cockroachlabs.com/docs/stable/set-vars.html)\")\n\n\tmustExec(t, conn, \"set client_encoding to 'SQL_ASCII'\")\n\n\tvar expected string\n\terr := conn.QueryRow(\n\t\tcontext.Background(),\n\t\t\"select $1\",\n\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t\"test\",\n\t).Scan(&expected)\n\tif err == nil {\n\t\tt.Error(\"expected error when client_encoding not UTF8, but no error occurred\")\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnSimpleProtocolRefusesNonStandardConformingStrings(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support standard_conforming_strings = off (https://github.com/cockroachdb/cockroach/issues/36215)\")\n\tpgxtest.SkipPostgreSQLVersionGreaterThan(t, conn, 18) // PG19 stopped supporting standard_conforming_strings = off\n\n\tmustExec(t, conn, \"set standard_conforming_strings to off\")\n\n\tvar expected string\n\terr := conn.QueryRow(\n\t\tcontext.Background(),\n\t\t\"select $1\",\n\t\tpgx.QueryExecModeSimpleProtocol,\n\t\t`\\'; drop table users; --`,\n\t).Scan(&expected)\n\tif err == nil {\n\t\tt.Error(\"expected error when standard_conforming_strings is off, but no error occurred\")\n\t}\n\n\tensureConnValid(t, conn)\n}\n\n// https://github.com/jackc/pgx/issues/895\nfunc TestQueryErrorWithDisabledStatementCache(t *testing.T) {\n\tt.Parallel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeDescribeExec\n\tconfig.StatementCacheCapacity = 0\n\tconfig.DescriptionCacheCapacity = 0\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\t_, err := conn.Exec(context.Background(), \"create temporary table t_unq(id text primary key);\")\n\trequire.NoError(t, err)\n\n\t_, err = conn.Exec(context.Background(), \"insert into t_unq (id) values ($1)\", \"abc\")\n\trequire.NoError(t, err)\n\n\trows, err := conn.Query(context.Background(), \"insert into t_unq (id) values ($1)\", \"abc\")\n\trequire.NoError(t, err)\n\trows.Close()\n\terr = rows.Err()\n\trequire.Error(t, err)\n\tvar pgErr *pgconn.PgError\n\tif errors.As(err, &pgErr) {\n\t\tassert.Equal(t, \"23505\", pgErr.Code)\n\t} else {\n\t\tt.Errorf(\"err is not a *pgconn.PgError: %T\", err)\n\t}\n\n\tensureConnValid(t, conn)\n}\n\nfunc TestConnQueryQueryExecModeCacheDescribeSafeEvenWhenTypesChange(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support alter column type from int to float4\")\n\n\t_, err := conn.Exec(ctx, `create temporary table to_change (\n\tname text primary key,\n\tage int\n);\n\ninsert into to_change (name, age) values ('John', 42);`)\n\trequire.NoError(t, err)\n\n\tvar name string\n\tvar ageInt32 int32\n\terr = conn.QueryRow(ctx, \"select * from to_change where age = $1\", pgx.QueryExecModeCacheDescribe, int32(42)).Scan(&name, &ageInt32)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"John\", name)\n\trequire.Equal(t, int32(42), ageInt32)\n\n\t_, err = conn.Exec(ctx, `alter table to_change alter column age type float4;`)\n\trequire.NoError(t, err)\n\n\terr = conn.QueryRow(ctx, \"select * from to_change where age = $1\", pgx.QueryExecModeCacheDescribe, int32(42)).Scan(&name, &ageInt32)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"John\", name)\n\trequire.Equal(t, int32(42), ageInt32)\n\n\tvar ageFloat32 float32\n\terr = conn.QueryRow(ctx, \"select * from to_change where age = $1\", pgx.QueryExecModeCacheDescribe, int32(42)).Scan(&name, &ageFloat32)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"John\", name)\n\trequire.Equal(t, float32(42), ageFloat32)\n\n\t_, err = conn.Exec(ctx, `alter table to_change drop column name;`)\n\trequire.NoError(t, err)\n\n\t// Number of result columns has changed, so just like with a prepared statement, this will fail the first time.\n\terr = conn.QueryRow(ctx, \"select * from to_change where age = $1\", pgx.QueryExecModeCacheDescribe, int32(42)).Scan(&ageFloat32)\n\trequire.EqualError(t, err, \"ERROR: bind message has 2 result formats but query has 1 columns (SQLSTATE 08P01)\")\n\n\t// But it will work the second time after the cache is invalidated.\n\terr = conn.QueryRow(ctx, \"select * from to_change where age = $1\", pgx.QueryExecModeCacheDescribe, int32(42)).Scan(&ageFloat32)\n\trequire.NoError(t, err)\n\trequire.Equal(t, float32(42), ageFloat32)\n\n\t_, err = conn.Exec(ctx, `alter table to_change alter column age type numeric;`)\n\trequire.NoError(t, err)\n\n\terr = conn.QueryRow(ctx, \"select * from to_change where age = $1\", pgx.QueryExecModeCacheDescribe, int32(42)).Scan(&ageFloat32)\n\trequire.NoError(t, err)\n\trequire.Equal(t, float32(42), ageFloat32)\n}\n\nfunc TestQueryWithQueryRewriter(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tqr := testQueryRewriter{sql: \"select $1::int\", args: []any{42}}\n\t\trows, err := conn.Query(ctx, \"should be replaced\", &qr)\n\t\trequire.NoError(t, err)\n\n\t\tvar n int32\n\t\tvar rowCount int\n\t\tfor rows.Next() {\n\t\t\trowCount++\n\t\t\terr = rows.Scan(&n)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\trequire.NoError(t, rows.Err())\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/2402\nfunc TestQueryWithEmptyQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\temptyQueryStrings := []string{\"\", \" \", \"/* ping */\", \"-- ping\"}\n\t\tfor _, eq := range emptyQueryStrings {\n\t\t\trows, err := conn.Query(ctx, eq)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, []pgconn.FieldDescription(nil), rows.FieldDescriptions())\n\t\t\trequire.False(t, rows.Next())\n\t\t\trequire.NoError(t, rows.Err())\n\t\t}\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/2456\nfunc TestQueryWithFunctionOutParameters(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support plpgsql\")\n\n\t\t_, err := conn.Exec(ctx, `drop function if exists out_param_func(out int, out int)`)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = conn.Exec(ctx, `\ncreate function out_param_func(out a int, out b int) language plpgsql as $$\nbegin\n\ta := 1;\n\tb := 2;\n\treturn;\nend;\n$$;`)\n\t\trequire.NoError(t, err)\n\t\tdefer func() {\n\t\t\t_, err := conn.Exec(ctx, `drop function if exists out_param_func(out int, out int)`)\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\n\t\tvar a, b int\n\t\terr = conn.QueryRow(ctx, `select * from out_param_func()`).Scan(&a, &b)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, 1, a)\n\t\trequire.Equal(t, 2, b)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/2456\nfunc TestQueryWithProcedureParametersInAndOut(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support plpgsql\")\n\n\t\t_, err := conn.Exec(ctx, `\n\tcreate procedure test_proc(in a int, inout b int, out c int) language plpgsql as $$\n\tbegin\n\t\tb := b + 1;\n\t\tc := a + b;\n\t\treturn;\n\tend;\n\t$$;`)\n\t\trequire.NoError(t, err)\n\t\tdefer func() {\n\t\t\t_, err := conn.Exec(ctx, `drop procedure if exists test_proc(in a int, inout b int, out c int)`)\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\n\t\tvar b, c int\n\t\terr = conn.QueryRow(ctx, `call test_proc(1, 2, null)`).Scan(&b, &c)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, 3, b)\n\t\trequire.Equal(t, 4, c)\n\t})\n}\n\ntype byteCounterConn struct {\n\tconn         net.Conn\n\tbytesRead    int\n\tbytesWritten int\n}\n\nfunc (cbn *byteCounterConn) Read(b []byte) (n int, err error) {\n\tn, err = cbn.conn.Read(b)\n\tcbn.bytesRead += n\n\treturn n, err\n}\n\nfunc (cbn *byteCounterConn) Write(b []byte) (n int, err error) {\n\tn, err = cbn.conn.Write(b)\n\tcbn.bytesWritten += n\n\treturn n, err\n}\n\nfunc (cbn *byteCounterConn) Close() error {\n\treturn cbn.conn.Close()\n}\n\nfunc (cbn *byteCounterConn) LocalAddr() net.Addr {\n\treturn cbn.conn.LocalAddr()\n}\n\nfunc (cbn *byteCounterConn) RemoteAddr() net.Addr {\n\treturn cbn.conn.RemoteAddr()\n}\n\nfunc (cbn *byteCounterConn) SetDeadline(t time.Time) error {\n\treturn cbn.conn.SetDeadline(t)\n}\n\nfunc (cbn *byteCounterConn) SetReadDeadline(t time.Time) error {\n\treturn cbn.conn.SetReadDeadline(t)\n}\n\nfunc (cbn *byteCounterConn) SetWriteDeadline(t time.Time) error {\n\treturn cbn.conn.SetWriteDeadline(t)\n}\n\nfunc TestQueryNetworkUsage(t *testing.T) {\n\tt.Parallel()\n\n\tconfig := mustParseConfig(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tconfig.DefaultQueryExecMode = pgx.QueryExecModeCacheStatement\n\tvar counterConn *byteCounterConn\n\tconfig.AfterNetConnect = func(ctx context.Context, config *pgconn.Config, conn net.Conn) (net.Conn, error) {\n\t\tcounterConn = &byteCounterConn{conn: conn}\n\t\treturn counterConn, nil\n\t}\n\n\tconn := mustConnect(t, config)\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server uses different number of bytes for same operations\")\n\n\tcounterConn.bytesWritten = 0\n\tcounterConn.bytesRead = 0\n\n\trows, _ := conn.Query(\n\t\tcontext.Background(),\n\t\t\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '{foo,bar,baz}'::text[], '2001-01-28 01:02:03-05'::timestamptz from generate_series(100001, 100000 + $1) n\",\n\t\t1,\n\t)\n\trows.Close()\n\trequire.NoError(t, rows.Err())\n\n\tassert.Equal(t, 413, counterConn.bytesRead)\n\tassert.Equal(t, 427, counterConn.bytesWritten)\n\tensureConnValid(t, conn)\n}\n\n// This example uses Query without using any helpers to read the results. Normally CollectRows, ForEachRow, or another\n// helper function should be used.\nfunc ExampleConn_Query() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// Skip test / example when running on CockroachDB. Since an example can't be skipped fake success instead.\n\t\tfmt.Println(`Cheeseburger: $10\nFries: $5\nSoft Drink: $3`)\n\t\treturn\n\t}\n\n\t// Setup example schema and data.\n\t_, err = conn.Exec(ctx, `\ncreate temporary table products (\n\tid int primary key generated by default as identity,\n\tname varchar(100) not null,\n\tprice int not null\n);\n\ninsert into products (name, price) values\n\t('Cheeseburger', 10),\n\t('Double Cheeseburger', 14),\n\t('Fries', 5),\n\t('Soft Drink', 3);\n`)\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to setup example schema and data: %v\", err)\n\t\treturn\n\t}\n\n\trows, err := conn.Query(ctx, \"select name, price from products where price < $1 order by price desc\", 12)\n\t// It is unnecessary to check err. If an error occurred it will be returned by rows.Err() later. But in rare\n\t// cases it may be useful to detect the error as early as possible.\n\tif err != nil {\n\t\tfmt.Printf(\"Query error: %v\", err)\n\t\treturn\n\t}\n\n\t// Ensure rows is closed. It is safe to close rows multiple times.\n\tdefer rows.Close()\n\n\t// Iterate through the result set\n\tfor rows.Next() {\n\t\tvar name string\n\t\tvar price int32\n\n\t\terr = rows.Scan(&name, &price)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Scan error: %v\", err)\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Printf(\"%s: $%d\\n\", name, price)\n\t}\n\n\t// rows is closed automatically when rows.Next() returns false so it is not necessary to manually close rows.\n\n\t// The first error encountered by the original Query call, rows.Next or rows.Scan will be returned here.\n\tif rows.Err() != nil {\n\t\tfmt.Printf(\"rows error: %v\", rows.Err())\n\t\treturn\n\t}\n\n\t// Output:\n\t// Cheeseburger: $10\n\t// Fries: $5\n\t// Soft Drink: $3\n}\n"
  },
  {
    "path": "rows.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// Rows is the result set returned from *Conn.Query. Rows must be closed before\n// the *Conn can be used again. Rows are closed by explicitly calling Close(),\n// calling Next() until it returns false, or when a fatal error occurs.\n//\n// Once a Rows is closed the only methods that may be called are Close(), Err(),\n// and CommandTag().\n//\n// Rows is an interface instead of a struct to allow tests to mock Query. However,\n// adding a method to an interface is technically a breaking change. Because of this\n// the Rows interface is partially excluded from semantic version requirements.\n// Methods will not be removed or changed, but new methods may be added.\ntype Rows interface {\n\t// Close closes the rows, making the connection ready for use again. It is safe\n\t// to call Close after rows is already closed.\n\tClose()\n\n\t// Err returns any error that occurred while executing a query or reading its results. Err must be called after the\n\t// Rows is closed (either by calling Close or by Next returning false) to check if the query was successful. If it is\n\t// called before the Rows is closed it may return nil even if the query failed on the server.\n\tErr() error\n\n\t// CommandTag returns the command tag from this query. It is only available after Rows is closed.\n\tCommandTag() pgconn.CommandTag\n\n\t// FieldDescriptions returns the field descriptions of the columns. It may return nil. In particular this can occur\n\t// when there was an error executing the query.\n\tFieldDescriptions() []pgconn.FieldDescription\n\n\t// Next prepares the next row for reading. It returns true if there is another row and false if no more rows are\n\t// available or a fatal error has occurred. It automatically closes rows upon returning false (whether due to all rows\n\t// having been read or due to an error).\n\t//\n\t// Callers should check rows.Err() after rows.Next() returns false to detect whether result-set reading ended\n\t// prematurely due to an error. See Conn.Query for details.\n\t//\n\t// For simpler error handling, consider using the higher-level pgx v5 CollectRows() and ForEachRow() helpers instead.\n\tNext() bool\n\n\t// Scan reads the values from the current row into dest values positionally. dest can include pointers to core types,\n\t// values implementing the Scanner interface, and nil. nil will skip the value entirely. It is an error to call Scan\n\t// without first calling Next() and checking that it returned true. Rows is automatically closed upon error.\n\tScan(dest ...any) error\n\n\t// Values returns the decoded row values. As with Scan(), it is an error to\n\t// call Values without first calling Next() and checking that it returned\n\t// true.\n\tValues() ([]any, error)\n\n\t// RawValues returns the unparsed bytes of the row values. The returned data is only valid until the next Next\n\t// call or the Rows is closed.\n\tRawValues() [][]byte\n\n\t// Conn returns the underlying *Conn on which the query was executed. This may return nil if Rows did not come from a\n\t// *Conn (e.g. if it was created by RowsFromResultReader)\n\tConn() *Conn\n}\n\n// Row is a convenience wrapper over Rows that is returned by QueryRow.\n//\n// Row is an interface instead of a struct to allow tests to mock QueryRow. However,\n// adding a method to an interface is technically a breaking change. Because of this\n// the Row interface is partially excluded from semantic version requirements.\n// Methods will not be removed or changed, but new methods may be added.\ntype Row interface {\n\t// Scan works the same as Rows. with the following exceptions. If no\n\t// rows were found it returns ErrNoRows. If multiple rows are returned it\n\t// ignores all but the first.\n\tScan(dest ...any) error\n}\n\n// RowScanner scans an entire row at a time into the RowScanner.\ntype RowScanner interface {\n\t// ScanRows scans the row.\n\tScanRow(rows Rows) error\n}\n\n// connRow implements the Row interface for Conn.QueryRow.\ntype connRow baseRows\n\nfunc (r *connRow) Scan(dest ...any) (err error) {\n\trows := (*baseRows)(r)\n\n\tif rows.Err() != nil {\n\t\treturn rows.Err()\n\t}\n\n\tfor _, d := range dest {\n\t\tif _, ok := d.(*pgtype.DriverBytes); ok {\n\t\t\trows.Close()\n\t\t\treturn fmt.Errorf(\"cannot scan into *pgtype.DriverBytes from QueryRow\")\n\t\t}\n\t}\n\n\tif !rows.Next() {\n\t\tif rows.Err() == nil {\n\t\t\treturn ErrNoRows\n\t\t}\n\t\treturn rows.Err()\n\t}\n\n\trows.Scan(dest...)\n\trows.Close()\n\treturn rows.Err()\n}\n\n// baseRows implements the Rows interface for Conn.Query.\ntype baseRows struct {\n\ttypeMap      *pgtype.Map\n\tresultReader *pgconn.ResultReader\n\n\tvalues [][]byte\n\n\tcommandTag pgconn.CommandTag\n\terr        error\n\tclosed     bool\n\n\tscanPlans []pgtype.ScanPlan\n\tscanTypes []reflect.Type\n\n\tconn              *Conn\n\tmultiResultReader *pgconn.MultiResultReader\n\n\tqueryTracer QueryTracer\n\tbatchTracer BatchTracer\n\tctx         context.Context\n\tstartTime   time.Time\n\tsql         string\n\targs        []any\n\trowCount    int\n}\n\nfunc (rows *baseRows) FieldDescriptions() []pgconn.FieldDescription {\n\treturn rows.resultReader.FieldDescriptions()\n}\n\nfunc (rows *baseRows) Close() {\n\tif rows.closed {\n\t\treturn\n\t}\n\n\trows.closed = true\n\n\tif rows.resultReader != nil {\n\t\tvar closeErr error\n\t\trows.commandTag, closeErr = rows.resultReader.Close()\n\t\tif rows.err == nil {\n\t\t\trows.err = closeErr\n\t\t}\n\t}\n\n\tif rows.multiResultReader != nil {\n\t\tcloseErr := rows.multiResultReader.Close()\n\t\tif rows.err == nil {\n\t\t\trows.err = closeErr\n\t\t}\n\t}\n\n\tif rows.err != nil && rows.conn != nil && rows.sql != \"\" {\n\t\tif sc := rows.conn.statementCache; sc != nil {\n\t\t\tsc.Invalidate(rows.sql)\n\t\t}\n\n\t\tif sc := rows.conn.descriptionCache; sc != nil {\n\t\t\tsc.Invalidate(rows.sql)\n\t\t}\n\t}\n\n\tif rows.batchTracer != nil {\n\t\trows.batchTracer.TraceBatchQuery(rows.ctx, rows.conn, TraceBatchQueryData{SQL: rows.sql, Args: rows.args, CommandTag: rows.commandTag, Err: rows.err})\n\t} else if rows.queryTracer != nil {\n\t\trows.queryTracer.TraceQueryEnd(rows.ctx, rows.conn, TraceQueryEndData{rows.commandTag, rows.err})\n\t}\n\n\t// Zero references to other memory allocations. This allows them to be GC'd even when the Rows still referenced. In\n\t// particular, when using pgxpool GC could be delayed as pgxpool.poolRows are allocated in large slices.\n\t//\n\t// https://github.com/jackc/pgx/pull/2269\n\trows.values = nil\n\trows.scanPlans = nil\n\trows.scanTypes = nil\n\trows.ctx = nil\n\trows.sql = \"\"\n\trows.args = nil\n}\n\nfunc (rows *baseRows) CommandTag() pgconn.CommandTag {\n\treturn rows.commandTag\n}\n\nfunc (rows *baseRows) Err() error {\n\treturn rows.err\n}\n\n// fatal signals an error occurred after the query was sent to the server. It\n// closes the rows automatically.\nfunc (rows *baseRows) fatal(err error) {\n\tif rows.err != nil {\n\t\treturn\n\t}\n\n\trows.err = err\n\trows.Close()\n}\n\nfunc (rows *baseRows) Next() bool {\n\tif rows.closed {\n\t\treturn false\n\t}\n\n\tif rows.resultReader.NextRow() {\n\t\trows.rowCount++\n\t\trows.values = rows.resultReader.Values()\n\t\treturn true\n\t} else {\n\t\trows.Close()\n\t\treturn false\n\t}\n}\n\nfunc (rows *baseRows) Scan(dest ...any) error {\n\tm := rows.typeMap\n\tfieldDescriptions := rows.FieldDescriptions()\n\tvalues := rows.values\n\n\tif len(fieldDescriptions) != len(values) {\n\t\terr := fmt.Errorf(\"number of field descriptions must equal number of values, got %d and %d\", len(fieldDescriptions), len(values))\n\t\trows.fatal(err)\n\t\treturn err\n\t}\n\n\tif len(dest) == 1 {\n\t\tif rc, ok := dest[0].(RowScanner); ok {\n\t\t\terr := rc.ScanRow(rows)\n\t\t\tif err != nil {\n\t\t\t\trows.fatal(err)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif len(fieldDescriptions) != len(dest) {\n\t\terr := fmt.Errorf(\"number of field descriptions must equal number of destinations, got %d and %d\", len(fieldDescriptions), len(dest))\n\t\trows.fatal(err)\n\t\treturn err\n\t}\n\n\tif rows.scanPlans == nil {\n\t\trows.scanPlans = make([]pgtype.ScanPlan, len(values))\n\t\trows.scanTypes = make([]reflect.Type, len(values))\n\t\tfor i := range dest {\n\t\t\trows.scanPlans[i] = m.PlanScan(fieldDescriptions[i].DataTypeOID, fieldDescriptions[i].Format, dest[i])\n\t\t\trows.scanTypes[i] = reflect.TypeOf(dest[i])\n\t\t}\n\t}\n\n\tfor i, dst := range dest {\n\t\tif dst == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif rows.scanTypes[i] != reflect.TypeOf(dst) {\n\t\t\trows.scanPlans[i] = m.PlanScan(fieldDescriptions[i].DataTypeOID, fieldDescriptions[i].Format, dest[i])\n\t\t\trows.scanTypes[i] = reflect.TypeOf(dest[i])\n\t\t}\n\n\t\terr := rows.scanPlans[i].Scan(values[i], dst)\n\t\tif err != nil {\n\t\t\terr = ScanArgError{ColumnIndex: i, FieldName: fieldDescriptions[i].Name, Err: err}\n\t\t\trows.fatal(err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (rows *baseRows) Values() ([]any, error) {\n\tif rows.closed {\n\t\treturn nil, errors.New(\"rows is closed\")\n\t}\n\n\tvalues := make([]any, 0, len(rows.FieldDescriptions()))\n\n\tfor i := range rows.FieldDescriptions() {\n\t\tbuf := rows.values[i]\n\t\tfd := &rows.FieldDescriptions()[i]\n\n\t\tif buf == nil {\n\t\t\tvalues = append(values, nil)\n\t\t\tcontinue\n\t\t}\n\n\t\tif dt, ok := rows.typeMap.TypeForOID(fd.DataTypeOID); ok {\n\t\t\tvalue, err := dt.Codec.DecodeValue(rows.typeMap, fd.DataTypeOID, fd.Format, buf)\n\t\t\tif err != nil {\n\t\t\t\trows.fatal(err)\n\t\t\t}\n\t\t\tvalues = append(values, value)\n\t\t} else {\n\t\t\tswitch fd.Format {\n\t\t\tcase TextFormatCode:\n\t\t\t\tvalues = append(values, string(buf))\n\t\t\tcase BinaryFormatCode:\n\t\t\t\tnewBuf := make([]byte, len(buf))\n\t\t\t\tcopy(newBuf, buf)\n\t\t\t\tvalues = append(values, newBuf)\n\t\t\tdefault:\n\t\t\t\trows.fatal(errors.New(\"unknown format code\"))\n\t\t\t}\n\t\t}\n\n\t\tif rows.Err() != nil {\n\t\t\treturn nil, rows.Err()\n\t\t}\n\t}\n\n\treturn values, rows.Err()\n}\n\nfunc (rows *baseRows) RawValues() [][]byte {\n\treturn rows.values\n}\n\nfunc (rows *baseRows) Conn() *Conn {\n\treturn rows.conn\n}\n\ntype ScanArgError struct {\n\tColumnIndex int\n\tFieldName   string\n\tErr         error\n}\n\nfunc (e ScanArgError) Error() string {\n\tif e.FieldName == \"?column?\" { // Don't include the fieldname if it's unknown\n\t\treturn fmt.Sprintf(\"can't scan into dest[%d]: %v\", e.ColumnIndex, e.Err)\n\t}\n\n\treturn fmt.Sprintf(\"can't scan into dest[%d] (col: %s): %v\", e.ColumnIndex, e.FieldName, e.Err)\n}\n\nfunc (e ScanArgError) Unwrap() error {\n\treturn e.Err\n}\n\n// ScanRow decodes raw row data into dest. It can be used to scan rows read from the lower level pgconn interface.\n//\n// typeMap - OID to Go type mapping.\n// fieldDescriptions - OID and format of values\n// values - the raw data as returned from the PostgreSQL server\n// dest - the destination that values will be decoded into\nfunc ScanRow(typeMap *pgtype.Map, fieldDescriptions []pgconn.FieldDescription, values [][]byte, dest ...any) error {\n\tif len(fieldDescriptions) != len(values) {\n\t\treturn fmt.Errorf(\"number of field descriptions must equal number of values, got %d and %d\", len(fieldDescriptions), len(values))\n\t}\n\tif len(fieldDescriptions) != len(dest) {\n\t\treturn fmt.Errorf(\"number of field descriptions must equal number of destinations, got %d and %d\", len(fieldDescriptions), len(dest))\n\t}\n\n\tfor i, d := range dest {\n\t\tif d == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\terr := typeMap.Scan(fieldDescriptions[i].DataTypeOID, fieldDescriptions[i].Format, values[i], d)\n\t\tif err != nil {\n\t\t\treturn ScanArgError{ColumnIndex: i, FieldName: fieldDescriptions[i].Name, Err: err}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// RowsFromResultReader returns a Rows that will read from values resultReader and decode with typeMap. It can be used\n// to read from the lower level pgconn interface.\nfunc RowsFromResultReader(typeMap *pgtype.Map, resultReader *pgconn.ResultReader) Rows {\n\treturn &baseRows{\n\t\ttypeMap:      typeMap,\n\t\tresultReader: resultReader,\n\t}\n}\n\n// ForEachRow iterates through rows. For each row it scans into the elements of scans and calls fn. If any row\n// fails to scan or fn returns an error the query will be aborted and the error will be returned. Rows will be closed\n// when ForEachRow returns.\nfunc ForEachRow(rows Rows, scans []any, fn func() error) (pgconn.CommandTag, error) {\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\terr := rows.Scan(scans...)\n\t\tif err != nil {\n\t\t\treturn pgconn.CommandTag{}, err\n\t\t}\n\n\t\terr = fn()\n\t\tif err != nil {\n\t\t\treturn pgconn.CommandTag{}, err\n\t\t}\n\t}\n\n\tif err := rows.Err(); err != nil {\n\t\treturn pgconn.CommandTag{}, err\n\t}\n\n\treturn rows.CommandTag(), nil\n}\n\n// CollectableRow is the subset of Rows methods that a RowToFunc is allowed to call.\ntype CollectableRow interface {\n\tFieldDescriptions() []pgconn.FieldDescription\n\tScan(dest ...any) error\n\tValues() ([]any, error)\n\tRawValues() [][]byte\n}\n\n// RowToFunc is a function that scans or otherwise converts row to a T.\ntype RowToFunc[T any] func(row CollectableRow) (T, error)\n\n// AppendRows iterates through rows, calling fn for each row, and appending the results into a slice of T.\n//\n// This function closes the rows automatically on return.\nfunc AppendRows[T any, S ~[]T](slice S, rows Rows, fn RowToFunc[T]) (S, error) {\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvalue, err := fn(rows)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tslice = append(slice, value)\n\t}\n\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn slice, nil\n}\n\n// CollectRows iterates through rows, calling fn for each row, and collecting the results into a slice of T.\n//\n// This function closes the rows automatically on return.\nfunc CollectRows[T any](rows Rows, fn RowToFunc[T]) ([]T, error) {\n\treturn AppendRows([]T{}, rows, fn)\n}\n\n// CollectOneRow calls fn for the first row in rows and returns the result. If no rows are found returns an error where errors.Is(ErrNoRows) is true.\n// CollectOneRow is to CollectRows as QueryRow is to Query.\n//\n// This function closes the rows automatically on return.\nfunc CollectOneRow[T any](rows Rows, fn RowToFunc[T]) (T, error) {\n\tdefer rows.Close()\n\n\tvar value T\n\tvar err error\n\n\tif !rows.Next() {\n\t\tif err = rows.Err(); err != nil {\n\t\t\treturn value, err\n\t\t}\n\t\treturn value, ErrNoRows\n\t}\n\n\tvalue, err = fn(rows)\n\tif err != nil {\n\t\treturn value, err\n\t}\n\n\t// The defer rows.Close() won't have executed yet. If the query returned more than one row, rows would still be open.\n\t// rows.Close() must be called before rows.Err() so we explicitly call it here.\n\trows.Close()\n\treturn value, rows.Err()\n}\n\n// CollectExactlyOneRow calls fn for the first row in rows and returns the result.\n//   - If no rows are found returns an error where errors.Is(ErrNoRows) is true.\n//   - If more than 1 row is found returns an error where errors.Is(ErrTooManyRows) is true.\n//\n// This function closes the rows automatically on return.\nfunc CollectExactlyOneRow[T any](rows Rows, fn RowToFunc[T]) (T, error) {\n\tdefer rows.Close()\n\n\tvar (\n\t\terr   error\n\t\tvalue T\n\t)\n\n\tif !rows.Next() {\n\t\tif err = rows.Err(); err != nil {\n\t\t\treturn value, err\n\t\t}\n\n\t\treturn value, ErrNoRows\n\t}\n\n\tvalue, err = fn(rows)\n\tif err != nil {\n\t\treturn value, err\n\t}\n\n\tif rows.Next() {\n\t\tvar zero T\n\n\t\treturn zero, ErrTooManyRows\n\t}\n\n\treturn value, rows.Err()\n}\n\n// RowTo returns a T scanned from row.\nfunc RowTo[T any](row CollectableRow) (T, error) {\n\tvar value T\n\terr := row.Scan(&value)\n\treturn value, err\n}\n\n// RowToAddrOf returns the address of a T scanned from row.\nfunc RowToAddrOf[T any](row CollectableRow) (*T, error) {\n\tvar value T\n\terr := row.Scan(&value)\n\treturn &value, err\n}\n\n// RowToMap returns a map scanned from row.\nfunc RowToMap(row CollectableRow) (map[string]any, error) {\n\tvar value map[string]any\n\terr := row.Scan((*mapRowScanner)(&value))\n\treturn value, err\n}\n\ntype mapRowScanner map[string]any\n\nfunc (rs *mapRowScanner) ScanRow(rows Rows) error {\n\tvalues, err := rows.Values()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*rs = make(mapRowScanner, len(values))\n\n\tfor i := range values {\n\t\t(*rs)[string(rows.FieldDescriptions()[i].Name)] = values[i]\n\t}\n\n\treturn nil\n}\n\n// RowToStructByPos returns a T scanned from row. T must be a struct. T must have the same number of public fields as row\n// has fields. The row and T fields will be matched by position. If the \"db\" struct tag is \"-\" then the field will be\n// ignored.\nfunc RowToStructByPos[T any](row CollectableRow) (T, error) {\n\tvar value T\n\terr := (&positionalStructRowScanner{ptrToStruct: &value}).ScanRow(row)\n\treturn value, err\n}\n\n// RowToAddrOfStructByPos returns the address of a T scanned from row. T must be a struct. T must have the same number a\n// public fields as row has fields. The row and T fields will be matched by position. If the \"db\" struct tag is \"-\" then\n// the field will be ignored.\nfunc RowToAddrOfStructByPos[T any](row CollectableRow) (*T, error) {\n\tvar value T\n\terr := (&positionalStructRowScanner{ptrToStruct: &value}).ScanRow(row)\n\treturn &value, err\n}\n\ntype positionalStructRowScanner struct {\n\tptrToStruct any\n}\n\nfunc (rs *positionalStructRowScanner) ScanRow(rows CollectableRow) error {\n\ttyp := reflect.TypeOf(rs.ptrToStruct).Elem()\n\tfields := lookupStructFields(typ)\n\tif len(rows.RawValues()) > len(fields) {\n\t\treturn fmt.Errorf(\n\t\t\t\"got %d values, but dst struct has only %d fields\",\n\t\t\tlen(rows.RawValues()),\n\t\t\tlen(fields),\n\t\t)\n\t}\n\tscanTargets := setupStructScanTargets(rs.ptrToStruct, fields)\n\treturn rows.Scan(scanTargets...)\n}\n\n// Map from reflect.Type -> []structRowField\nvar positionalStructFieldMap sync.Map\n\nfunc lookupStructFields(t reflect.Type) []structRowField {\n\tif cached, ok := positionalStructFieldMap.Load(t); ok {\n\t\treturn cached.([]structRowField)\n\t}\n\n\tfieldStack := make([]int, 0, 1)\n\tfields := computeStructFields(t, make([]structRowField, 0, t.NumField()), &fieldStack)\n\tfieldsIface, _ := positionalStructFieldMap.LoadOrStore(t, fields)\n\treturn fieldsIface.([]structRowField)\n}\n\nfunc computeStructFields(\n\tt reflect.Type,\n\tfields []structRowField,\n\tfieldStack *[]int,\n) []structRowField {\n\ttail := len(*fieldStack)\n\t*fieldStack = append(*fieldStack, 0)\n\tfor i := 0; i < t.NumField(); i++ {\n\t\tsf := t.Field(i)\n\t\t(*fieldStack)[tail] = i\n\t\t// Handle anonymous struct embedding, but do not try to handle embedded pointers.\n\t\tif sf.Anonymous && sf.Type.Kind() == reflect.Struct {\n\t\t\tfields = computeStructFields(sf.Type, fields, fieldStack)\n\t\t} else if sf.PkgPath == \"\" {\n\t\t\tdbTag, _ := sf.Tag.Lookup(structTagKey)\n\t\t\tif dbTag == \"-\" {\n\t\t\t\t// Field is ignored, skip it.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfields = append(fields, structRowField{\n\t\t\t\tpath: append([]int(nil), *fieldStack...),\n\t\t\t})\n\t\t}\n\t}\n\t*fieldStack = (*fieldStack)[:tail]\n\treturn fields\n}\n\n// RowToStructByName returns a T scanned from row. T must be a struct. T must have the same number of named public\n// fields as row has fields. The row and T fields will be matched by name. The match is case-insensitive. The database\n// column name can be overridden with a \"db\" struct tag. If the \"db\" struct tag is \"-\" then the field will be ignored.\nfunc RowToStructByName[T any](row CollectableRow) (T, error) {\n\tvar value T\n\terr := (&namedStructRowScanner{ptrToStruct: &value}).ScanRow(row)\n\treturn value, err\n}\n\n// RowToAddrOfStructByName returns the address of a T scanned from row. T must be a struct. T must have the same number\n// of named public fields as row has fields. The row and T fields will be matched by name. The match is\n// case-insensitive. The database column name can be overridden with a \"db\" struct tag. If the \"db\" struct tag is \"-\"\n// then the field will be ignored.\nfunc RowToAddrOfStructByName[T any](row CollectableRow) (*T, error) {\n\tvar value T\n\terr := (&namedStructRowScanner{ptrToStruct: &value}).ScanRow(row)\n\treturn &value, err\n}\n\n// RowToStructByNameLax returns a T scanned from row. T must be a struct. T must have greater than or equal number of named public\n// fields as row has fields. The row and T fields will be matched by name. The match is case-insensitive. The database\n// column name can be overridden with a \"db\" struct tag. If the \"db\" struct tag is \"-\" then the field will be ignored.\nfunc RowToStructByNameLax[T any](row CollectableRow) (T, error) {\n\tvar value T\n\terr := (&namedStructRowScanner{ptrToStruct: &value, lax: true}).ScanRow(row)\n\treturn value, err\n}\n\n// RowToAddrOfStructByNameLax returns the address of a T scanned from row. T must be a struct. T must have greater than or\n// equal number of named public fields as row has fields. The row and T fields will be matched by name. The match is\n// case-insensitive. The database column name can be overridden with a \"db\" struct tag. If the \"db\" struct tag is \"-\"\n// then the field will be ignored.\nfunc RowToAddrOfStructByNameLax[T any](row CollectableRow) (*T, error) {\n\tvar value T\n\terr := (&namedStructRowScanner{ptrToStruct: &value, lax: true}).ScanRow(row)\n\treturn &value, err\n}\n\ntype namedStructRowScanner struct {\n\tptrToStruct any\n\tlax         bool\n}\n\nfunc (rs *namedStructRowScanner) ScanRow(rows CollectableRow) error {\n\ttyp := reflect.TypeOf(rs.ptrToStruct).Elem()\n\tfldDescs := rows.FieldDescriptions()\n\tnamedStructFields, err := lookupNamedStructFields(typ, fldDescs)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !rs.lax && namedStructFields.missingField != \"\" {\n\t\treturn fmt.Errorf(\"cannot find field %s in returned row\", namedStructFields.missingField)\n\t}\n\tfields := namedStructFields.fields\n\tscanTargets := setupStructScanTargets(rs.ptrToStruct, fields)\n\treturn rows.Scan(scanTargets...)\n}\n\n// Map from namedStructFieldMap -> *namedStructFields\nvar namedStructFieldMap sync.Map\n\ntype namedStructFieldsKey struct {\n\tt        reflect.Type\n\tcolNames string\n}\n\ntype namedStructFields struct {\n\tfields []structRowField\n\t// missingField is the first field from the struct without a corresponding row field.\n\t// This is used to construct the correct error message for non-lax queries.\n\tmissingField string\n}\n\nfunc lookupNamedStructFields(\n\tt reflect.Type,\n\tfldDescs []pgconn.FieldDescription,\n) (*namedStructFields, error) {\n\tkey := namedStructFieldsKey{\n\t\tt:        t,\n\t\tcolNames: joinFieldNames(fldDescs),\n\t}\n\tif cached, ok := namedStructFieldMap.Load(key); ok {\n\t\treturn cached.(*namedStructFields), nil\n\t}\n\n\t// We could probably do two-levels of caching, where we compute the key -> fields mapping\n\t// for a type only once, cache it by type, then use that to compute the column -> fields\n\t// mapping for a given set of columns.\n\tfieldStack := make([]int, 0, 1)\n\tfields, missingField := computeNamedStructFields(\n\t\tfldDescs,\n\t\tt,\n\t\tmake([]structRowField, len(fldDescs)),\n\t\t&fieldStack,\n\t)\n\tfor i, f := range fields {\n\t\tif f.path == nil {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"struct doesn't have corresponding row field %s\",\n\t\t\t\tfldDescs[i].Name,\n\t\t\t)\n\t\t}\n\t}\n\n\tfieldsIface, _ := namedStructFieldMap.LoadOrStore(\n\t\tkey,\n\t\t&namedStructFields{fields: fields, missingField: missingField},\n\t)\n\treturn fieldsIface.(*namedStructFields), nil\n}\n\nfunc joinFieldNames(fldDescs []pgconn.FieldDescription) string {\n\tswitch len(fldDescs) {\n\tcase 0:\n\t\treturn \"\"\n\tcase 1:\n\t\treturn fldDescs[0].Name\n\t}\n\n\ttotalSize := len(fldDescs) - 1 // Space for separator bytes.\n\tfor _, d := range fldDescs {\n\t\ttotalSize += len(d.Name)\n\t}\n\tvar b strings.Builder\n\tb.Grow(totalSize)\n\tb.WriteString(fldDescs[0].Name)\n\tfor _, d := range fldDescs[1:] {\n\t\tb.WriteByte(0) // Join with NUL byte as it's (presumably) not a valid column character.\n\t\tb.WriteString(d.Name)\n\t}\n\treturn b.String()\n}\n\nfunc computeNamedStructFields(\n\tfldDescs []pgconn.FieldDescription,\n\tt reflect.Type,\n\tfields []structRowField,\n\tfieldStack *[]int,\n) ([]structRowField, string) {\n\tvar missingField string\n\ttail := len(*fieldStack)\n\t*fieldStack = append(*fieldStack, 0)\n\tfor i := 0; i < t.NumField(); i++ {\n\t\tsf := t.Field(i)\n\t\t(*fieldStack)[tail] = i\n\t\tif sf.PkgPath != \"\" && !sf.Anonymous {\n\t\t\t// Field is unexported, skip it.\n\t\t\tcontinue\n\t\t}\n\t\t// Handle anonymous struct embedding, but do not try to handle embedded pointers.\n\t\tif sf.Anonymous && sf.Type.Kind() == reflect.Struct {\n\t\t\tvar missingSubField string\n\t\t\tfields, missingSubField = computeNamedStructFields(\n\t\t\t\tfldDescs,\n\t\t\t\tsf.Type,\n\t\t\t\tfields,\n\t\t\t\tfieldStack,\n\t\t\t)\n\t\t\tif missingField == \"\" {\n\t\t\t\tmissingField = missingSubField\n\t\t\t}\n\t\t} else {\n\t\t\tdbTag, dbTagPresent := sf.Tag.Lookup(structTagKey)\n\t\t\tif dbTagPresent {\n\t\t\t\tdbTag, _, _ = strings.Cut(dbTag, \",\")\n\t\t\t}\n\t\t\tif dbTag == \"-\" {\n\t\t\t\t// Field is ignored, skip it.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcolName := dbTag\n\t\t\tif !dbTagPresent {\n\t\t\t\tcolName = sf.Name\n\t\t\t}\n\t\t\tfpos := fieldPosByName(fldDescs, colName, !dbTagPresent)\n\t\t\tif fpos == -1 {\n\t\t\t\tif missingField == \"\" {\n\t\t\t\t\tmissingField = colName\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfields[fpos] = structRowField{\n\t\t\t\tpath: append([]int(nil), *fieldStack...),\n\t\t\t}\n\t\t}\n\t}\n\t*fieldStack = (*fieldStack)[:tail]\n\n\treturn fields, missingField\n}\n\nconst structTagKey = \"db\"\n\nfunc fieldPosByName(fldDescs []pgconn.FieldDescription, field string, normalize bool) (i int) {\n\ti = -1\n\n\tif normalize {\n\t\tfield = strings.ReplaceAll(field, \"_\", \"\")\n\t}\n\tfor i, desc := range fldDescs {\n\t\tif normalize {\n\t\t\tif strings.EqualFold(strings.ReplaceAll(desc.Name, \"_\", \"\"), field) {\n\t\t\t\treturn i\n\t\t\t}\n\t\t} else {\n\t\t\tif desc.Name == field {\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\t}\n\treturn i\n}\n\n// structRowField describes a field of a struct.\n//\n// TODO: It would be a bit more efficient to track the path using the pointer\n// offset within the (outermost) struct and use unsafe.Pointer arithmetic to\n// construct references when scanning rows. However, it's not clear it's worth\n// using unsafe for this.\ntype structRowField struct {\n\tpath []int\n}\n\nfunc setupStructScanTargets(receiver any, fields []structRowField) []any {\n\tscanTargets := make([]any, len(fields))\n\tv := reflect.ValueOf(receiver).Elem()\n\tfor i, f := range fields {\n\t\tscanTargets[i] = v.FieldByIndex(f.path).Addr().Interface()\n\t}\n\treturn scanTargets\n}\n"
  },
  {
    "path": "rows_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n)\n\ntype testRowScanner struct {\n\tname string\n\tage  int32\n}\n\nfunc (rs *testRowScanner) ScanRow(rows pgx.Rows) error {\n\treturn rows.Scan(&rs.name, &rs.age)\n}\n\nfunc TestRowScanner(t *testing.T) {\n\tt.Parallel()\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar s testRowScanner\n\t\terr := conn.QueryRow(ctx, \"select 'Adam' as name, 72 as height\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"Adam\", s.name)\n\t\trequire.Equal(t, int32(72), s.age)\n\t})\n}\n\ntype testErrRowScanner string\n\nfunc (ers *testErrRowScanner) ScanRow(rows pgx.Rows) error {\n\treturn errors.New(string(*ers))\n}\n\n// https://github.com/jackc/pgx/issues/1654\nfunc TestRowScannerErrorIsFatalToRows(t *testing.T) {\n\tt.Parallel()\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ts := testErrRowScanner(\"foo\")\n\t\terr := conn.QueryRow(ctx, \"select 'Adam' as name, 72 as height\").Scan(&s)\n\t\trequire.EqualError(t, err, \"foo\")\n\t})\n}\n\nfunc TestForEachRow(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar actualResults []any\n\n\t\trows, _ := conn.Query(\n\t\t\tcontext.Background(),\n\t\t\t\"select n, n * 2 from generate_series(1, $1) n\",\n\t\t\t3,\n\t\t)\n\t\tvar a, b int\n\t\tct, err := pgx.ForEachRow(rows, []any{&a, &b}, func() error {\n\t\t\tactualResults = append(actualResults, []any{a, b})\n\t\t\treturn nil\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\texpectedResults := []any{\n\t\t\t[]any{1, 2},\n\t\t\t[]any{2, 4},\n\t\t\t[]any{3, 6},\n\t\t}\n\t\trequire.Equal(t, expectedResults, actualResults)\n\t\trequire.EqualValues(t, 3, ct.RowsAffected())\n\t})\n}\n\nfunc TestForEachRowScanError(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar actualResults []any\n\n\t\trows, _ := conn.Query(\n\t\t\tcontext.Background(),\n\t\t\t\"select 'foo', 'bar' from generate_series(1, $1) n\",\n\t\t\t3,\n\t\t)\n\t\tvar a, b int\n\t\tct, err := pgx.ForEachRow(rows, []any{&a, &b}, func() error {\n\t\t\tactualResults = append(actualResults, []any{a, b})\n\t\t\treturn nil\n\t\t})\n\t\trequire.EqualError(t, err, \"can't scan into dest[0]: cannot scan text (OID 25) in text format into *int\")\n\t\trequire.Equal(t, pgconn.CommandTag{}, ct)\n\t})\n}\n\nfunc TestForEachRowAbort(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(\n\t\t\tcontext.Background(),\n\t\t\t\"select n, n * 2 from generate_series(1, $1) n\",\n\t\t\t3,\n\t\t)\n\t\tvar a, b int\n\t\tct, err := pgx.ForEachRow(rows, []any{&a, &b}, func() error {\n\t\t\treturn errors.New(\"abort\")\n\t\t})\n\t\trequire.EqualError(t, err, \"abort\")\n\t\trequire.Equal(t, pgconn.CommandTag{}, ct)\n\t})\n}\n\nfunc ExampleForEachRow() {\n\tconn, err := pgx.Connect(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\trows, _ := conn.Query(\n\t\tcontext.Background(),\n\t\t\"select n, n * 2 from generate_series(1, $1) n\",\n\t\t3,\n\t)\n\tvar a, b int\n\t_, err = pgx.ForEachRow(rows, []any{&a, &b}, func() error {\n\t\tfmt.Printf(\"%v, %v\\n\", a, b)\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tfmt.Printf(\"ForEachRow error: %v\", err)\n\t\treturn\n\t}\n\n\t// Output:\n\t// 1, 2\n\t// 2, 4\n\t// 3, 6\n}\n\nfunc TestCollectRows(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select n from generate_series(0, 99) n`)\n\t\tnumbers, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, numbers, 100)\n\t\tfor i := range numbers {\n\t\t\tassert.Equal(t, int32(i), numbers[i])\n\t\t}\n\t})\n}\n\nfunc TestCollectRowsEmpty(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select n from generate_series(1, 0) n`)\n\t\tnumbers, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\trequire.NoError(t, err)\n\t\trequire.NotNil(t, numbers)\n\n\t\tassert.Empty(t, numbers)\n\t})\n}\n\n// This example uses CollectRows with a manually written collector function. In most cases RowTo, RowToAddrOf,\n// RowToStructByPos, RowToAddrOfStructByPos, or another generic function would be used.\nfunc ExampleCollectRows() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\trows, _ := conn.Query(ctx, `select n from generate_series(1, 5) n`)\n\tnumbers, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\tvar n int32\n\t\terr := row.Scan(&n)\n\t\treturn n, err\n\t})\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(numbers)\n\n\t// Output:\n\t// [1 2 3 4 5]\n}\n\nfunc TestCollectOneRow(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 42`)\n\t\tn, err := pgx.CollectOneRow(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int32(42), n)\n\t})\n}\n\nfunc TestCollectOneRowNotFound(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 42 where false`)\n\t\tn, err := pgx.CollectOneRow(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\tassert.ErrorIs(t, err, pgx.ErrNoRows)\n\t\tassert.Equal(t, int32(0), n)\n\t})\n}\n\nfunc TestCollectOneRowIgnoresExtraRows(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select n from generate_series(42, 99) n`)\n\t\tn, err := pgx.CollectOneRow(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int32(42), n)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1334\nfunc TestCollectOneRowPrefersPostgreSQLErrorOverErrNoRows(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `create temporary table t (name text not null unique)`)\n\t\trequire.NoError(t, err)\n\n\t\tvar name string\n\t\trows, _ := conn.Query(ctx, `insert into t (name) values ('foo') returning name`)\n\t\tname, err = pgx.CollectOneRow(rows, func(row pgx.CollectableRow) (string, error) {\n\t\t\tvar n string\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"foo\", name)\n\n\t\trows, _ = conn.Query(ctx, `insert into t (name) values ('foo') returning name`)\n\t\tname, err = pgx.CollectOneRow(rows, func(row pgx.CollectableRow) (string, error) {\n\t\t\tvar n string\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\trequire.Error(t, err)\n\t\tvar pgErr *pgconn.PgError\n\t\trequire.ErrorAs(t, err, &pgErr)\n\t\trequire.Equal(t, \"23505\", pgErr.Code)\n\t\trequire.Equal(t, \"\", name)\n\t})\n}\n\nfunc TestCollectExactlyOneRow(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 42`)\n\t\tn, err := pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int32(42), n)\n\t})\n}\n\nfunc TestCollectExactlyOneRowNotFound(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 42 where false`)\n\t\tn, err := pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\tassert.ErrorIs(t, err, pgx.ErrNoRows)\n\t\tassert.Equal(t, int32(0), n)\n\t})\n}\n\nfunc TestCollectExactlyOneRowExtraRows(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select n from generate_series(42, 99) n`)\n\t\tn, err := pgx.CollectExactlyOneRow(rows, func(row pgx.CollectableRow) (int32, error) {\n\t\t\tvar n int32\n\t\t\terr := row.Scan(&n)\n\t\t\treturn n, err\n\t\t})\n\t\tassert.ErrorIs(t, err, pgx.ErrTooManyRows)\n\t\tassert.Equal(t, int32(0), n)\n\t})\n}\n\nfunc TestRowTo(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select n from generate_series(0, 99) n`)\n\t\tnumbers, err := pgx.CollectRows(rows, pgx.RowTo[int32])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, numbers, 100)\n\t\tfor i := range numbers {\n\t\t\tassert.Equal(t, int32(i), numbers[i])\n\t\t}\n\t})\n}\n\nfunc ExampleRowTo() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\trows, _ := conn.Query(ctx, `select n from generate_series(1, 5) n`)\n\tnumbers, err := pgx.CollectRows(rows, pgx.RowTo[int32])\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfmt.Println(numbers)\n\n\t// Output:\n\t// [1 2 3 4 5]\n}\n\nfunc TestRowToAddrOf(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select n from generate_series(0, 99) n`)\n\t\tnumbers, err := pgx.CollectRows(rows, pgx.RowToAddrOf[int32])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, numbers, 100)\n\t\tfor i := range numbers {\n\t\t\tassert.Equal(t, int32(i), *numbers[i])\n\t\t}\n\t})\n}\n\nfunc ExampleRowToAddrOf() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\trows, _ := conn.Query(ctx, `select n from generate_series(1, 5) n`)\n\tpNumbers, err := pgx.CollectRows(rows, pgx.RowToAddrOf[int32])\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfor _, p := range pNumbers {\n\t\tfmt.Println(*p)\n\t}\n\n\t// Output:\n\t// 1\n\t// 2\n\t// 3\n\t// 4\n\t// 5\n}\n\nfunc TestRowToMap(t *testing.T) {\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'Joe' as name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToMap)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Joe\", slice[i][\"name\"])\n\t\t\tassert.EqualValues(t, i, slice[i][\"age\"])\n\t\t}\n\t})\n}\n\nfunc TestRowToStructByPos(t *testing.T) {\n\ttype person struct {\n\t\tName string\n\t\tAge  int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'Joe' as name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByPos[person])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Joe\", slice[i].Name)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\t})\n}\n\nfunc TestRowToStructByPosIgnoredField(t *testing.T) {\n\ttype person struct {\n\t\tName string\n\t\tAge  int32 `db:\"-\"`\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'Joe' as name from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByPos[person])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Joe\", slice[i].Name)\n\t\t}\n\t})\n}\n\nfunc TestRowToStructByPosEmbeddedStruct(t *testing.T) {\n\ttype Name struct {\n\t\tFirst string\n\t\tLast  string\n\t}\n\n\ttype person struct {\n\t\tName\n\t\tAge int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByPos[person])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"John\", slice[i].Name.First)\n\t\t\tassert.Equal(t, \"Smith\", slice[i].Name.Last)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\t})\n}\n\nfunc TestRowToStructByPosMultipleEmbeddedStruct(t *testing.T) {\n\ttype Sandwich struct {\n\t\tBread string\n\t\tSalad string\n\t}\n\ttype Drink struct {\n\t\tMl int\n\t}\n\n\ttype meal struct {\n\t\tSandwich\n\t\tDrink\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'Baguette' as bread, 'Lettuce' as salad, drink_ml from generate_series(0, 9) drink_ml`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByPos[meal])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Baguette\", slice[i].Sandwich.Bread)\n\t\t\tassert.Equal(t, \"Lettuce\", slice[i].Sandwich.Salad)\n\t\t\tassert.EqualValues(t, i, slice[i].Drink.Ml)\n\t\t}\n\t})\n}\n\nfunc TestRowToStructByPosEmbeddedUnexportedStruct(t *testing.T) {\n\ttype name struct {\n\t\tFirst string\n\t\tLast  string\n\t}\n\n\ttype person struct {\n\t\tname\n\t\tAge int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByPos[person])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"John\", slice[i].name.First)\n\t\t\tassert.Equal(t, \"Smith\", slice[i].name.Last)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\t})\n}\n\n// Pointer to struct is not supported. But check that we don't panic.\nfunc TestRowToStructByPosEmbeddedPointerToStruct(t *testing.T) {\n\ttype Name struct {\n\t\tFirst string\n\t\tLast  string\n\t}\n\n\ttype person struct {\n\t\t*Name\n\t\tAge int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\t_, err := pgx.CollectRows(rows, pgx.RowToStructByPos[person])\n\t\trequire.EqualError(t, err, \"got 3 values, but dst struct has only 2 fields\")\n\t})\n}\n\nfunc ExampleRowToStructByPos() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// Skip test / example when running on CockroachDB. Since an example can't be skipped fake success instead.\n\t\tfmt.Println(`Cheeseburger: $10\nFries: $5\nSoft Drink: $3`)\n\t\treturn\n\t}\n\n\t// Setup example schema and data.\n\t_, err = conn.Exec(ctx, `\ncreate temporary table products (\n\tid int primary key generated by default as identity,\n\tname varchar(100) not null,\n\tprice int not null\n);\n\ninsert into products (name, price) values\n\t('Cheeseburger', 10),\n\t('Double Cheeseburger', 14),\n\t('Fries', 5),\n\t('Soft Drink', 3);\n`)\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to setup example schema and data: %v\", err)\n\t\treturn\n\t}\n\n\ttype product struct {\n\t\tID    int32\n\t\tName  string\n\t\tPrice int32\n\t}\n\n\trows, _ := conn.Query(ctx, \"select * from products where price < $1 order by price desc\", 12)\n\tproducts, err := pgx.CollectRows(rows, pgx.RowToStructByPos[product])\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfor _, p := range products {\n\t\tfmt.Printf(\"%s: $%d\\n\", p.Name, p.Price)\n\t}\n\n\t// Output:\n\t// Cheeseburger: $10\n\t// Fries: $5\n\t// Soft Drink: $3\n}\n\nfunc TestRowToAddrOfStructPos(t *testing.T) {\n\ttype person struct {\n\t\tName string\n\t\tAge  int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'Joe' as name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByPos[person])\n\t\trequire.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Joe\", slice[i].Name)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\t})\n}\n\nfunc TestRowToStructByName(t *testing.T) {\n\ttype person struct {\n\t\tLast      string\n\t\tFirst     string\n\t\tAge       int32\n\t\tAccountID string\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first, 'Smith' as last, n as age, 'd5e49d3f' as account_id from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByName[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Smith\", slice[i].Last)\n\t\t\tassert.Equal(t, \"John\", slice[i].First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t\tassert.Equal(t, \"d5e49d3f\", slice[i].AccountID)\n\t\t}\n\n\t\t// check missing fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last, n as age from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToStructByName[person])\n\t\tassert.ErrorContains(t, err, \"cannot find field First in returned row\")\n\n\t\t// check missing field in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'John' as first, 'Smith' as last, n as age, 'd5e49d3f' as account_id, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\t})\n}\n\nfunc TestRowToStructByNameDbTags(t *testing.T) {\n\ttype person struct {\n\t\tLast             string `db:\"last_name\"`\n\t\tFirst            string `db:\"first_name\"`\n\t\tAge              int32  `db:\"age\"`\n\t\tAccountID        string `db:\"account_id\"`\n\t\tAnotherAccountID string `db:\"account__id\"`\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, 'd5e49d3f' as account_id, '5e49d321' as account__id from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByName[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Smith\", slice[i].Last)\n\t\t\tassert.Equal(t, \"John\", slice[i].First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t\tassert.Equal(t, \"d5e49d3f\", slice[i].AccountID)\n\t\t\tassert.Equal(t, \"5e49d321\", slice[i].AnotherAccountID)\n\t\t}\n\n\t\t// check missing fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToStructByName[person])\n\t\tassert.ErrorContains(t, err, \"cannot find field first_name in returned row\")\n\n\t\t// check missing field in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, 'd5e49d3f' as account_id, '5e49d321' as account__id, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\t})\n}\n\nfunc TestRowToStructByNameEmbeddedStruct(t *testing.T) {\n\ttype Name struct {\n\t\tLast  string `db:\"last_name\"`\n\t\tFirst string `db:\"first_name\"`\n\t}\n\n\ttype person struct {\n\t\tIgnore bool `db:\"-\"`\n\t\tName\n\t\tAge int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByName[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Smith\", slice[i].Name.Last)\n\t\t\tassert.Equal(t, \"John\", slice[i].Name.First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\n\t\t// check missing fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToStructByName[person])\n\t\tassert.ErrorContains(t, err, \"cannot find field first_name in returned row\")\n\n\t\t// check missing field in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\t})\n}\n\nfunc ExampleRowToStructByName() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// Skip test / example when running on CockroachDB. Since an example can't be skipped fake success instead.\n\t\tfmt.Println(`Cheeseburger: $10\nFries: $5\nSoft Drink: $3`)\n\t\treturn\n\t}\n\n\t// Setup example schema and data.\n\t_, err = conn.Exec(ctx, `\ncreate temporary table products (\n\tid int primary key generated by default as identity,\n\tname varchar(100) not null,\n\tprice int not null\n);\n\ninsert into products (name, price) values\n\t('Cheeseburger', 10),\n\t('Double Cheeseburger', 14),\n\t('Fries', 5),\n\t('Soft Drink', 3);\n`)\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to setup example schema and data: %v\", err)\n\t\treturn\n\t}\n\n\ttype product struct {\n\t\tID    int32\n\t\tName  string\n\t\tPrice int32\n\t}\n\n\trows, _ := conn.Query(ctx, \"select * from products where price < $1 order by price desc\", 12)\n\tproducts, err := pgx.CollectRows(rows, pgx.RowToStructByName[product])\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfor _, p := range products {\n\t\tfmt.Printf(\"%s: $%d\\n\", p.Name, p.Price)\n\t}\n\n\t// Output:\n\t// Cheeseburger: $10\n\t// Fries: $5\n\t// Soft Drink: $3\n}\n\nfunc TestRowToStructByNameLax(t *testing.T) {\n\ttype person struct {\n\t\tLast   string\n\t\tFirst  string\n\t\tAge    int32\n\t\tIgnore bool `db:\"-\"`\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first, 'Smith' as last, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Smith\", slice[i].Last)\n\t\t\tassert.Equal(t, \"John\", slice[i].First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\n\t\t// check missing fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'John' as first, n as age from generate_series(0, 9) n`)\n\t\tslice, err = pgx.CollectRows(rows, pgx.RowToStructByNameLax[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"John\", slice[i].First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\n\t\t// check extra fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'John' as first, 'Smith' as last, n as age, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByNameLax[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\n\t\t// check missing fields in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last, 'D.' as middle, n as age from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByNameLax[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field middle\")\n\n\t\t// check ignored fields in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last, n as age, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByNameLax[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\t})\n}\n\nfunc TestRowToStructByNameLaxEmbeddedStruct(t *testing.T) {\n\ttype Name struct {\n\t\tLast  string `db:\"last_name\"`\n\t\tFirst string `db:\"first_name\"`\n\t}\n\n\ttype person struct {\n\t\tIgnore bool `db:\"-\"`\n\t\tName\n\t\tAge int32\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\trows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age from generate_series(0, 9) n`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"Smith\", slice[i].Name.Last)\n\t\t\tassert.Equal(t, \"John\", slice[i].Name.First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\n\t\t// check missing fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'John' as first_name, n as age from generate_series(0, 9) n`)\n\t\tslice, err = pgx.CollectRows(rows, pgx.RowToStructByNameLax[person])\n\t\tassert.NoError(t, err)\n\n\t\tassert.Len(t, slice, 10)\n\t\tfor i := range slice {\n\t\t\tassert.Equal(t, \"John\", slice[i].Name.First)\n\t\t\tassert.EqualValues(t, i, slice[i].Age)\n\t\t}\n\n\t\t// check extra fields in a returned row\n\t\trows, _ = conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByNameLax[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\n\t\t// check missing fields in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last_name, 'D.' as middle_name, n as age from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByNameLax[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field middle_name\")\n\n\t\t// check ignored fields in a destination struct\n\t\trows, _ = conn.Query(ctx, `select 'Smith' as last_name, n as age, null as ignore from generate_series(0, 9) n`)\n\t\t_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByNameLax[person])\n\t\tassert.ErrorContains(t, err, \"struct doesn't have corresponding row field ignore\")\n\t})\n}\n\nfunc TestRowToStructByNameLaxRowValue(t *testing.T) {\n\ttype AnotherTable struct{}\n\ttype User struct {\n\t\tUserID int    `json:\"userId\" db:\"user_id\"`\n\t\tName   string `json:\"name\" db:\"name\"`\n\t}\n\ttype UserAPIKey struct {\n\t\tUserAPIKeyID int `json:\"userApiKeyId\" db:\"user_api_key_id\"`\n\t\tUserID       int `json:\"userId\" db:\"user_id\"`\n\n\t\tUser         *User         `json:\"user\" db:\"user\"`\n\t\tAnotherTable *AnotherTable `json:\"anotherTable\" db:\"another_table\"`\n\t}\n\n\tdefaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"\")\n\n\t\trows, _ := conn.Query(ctx, `\n\t\tWITH user_api_keys AS (\n\t\t\tSELECT 1 AS user_id, 101 AS user_api_key_id, 'abc123' AS api_key\n\t\t), users AS (\n\t\t\tSELECT 1 AS user_id, 'John Doe' AS name\n\t\t)\n\t\tSELECT user_api_keys.user_api_key_id, user_api_keys.user_id, row(users.*) AS user\n\t\tFROM user_api_keys\n\t\tLEFT JOIN users ON users.user_id = user_api_keys.user_id\n\t\tWHERE user_api_keys.api_key = 'abc123';\n\t\t`)\n\t\tslice, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[UserAPIKey])\n\n\t\tassert.NoError(t, err)\n\t\tassert.ElementsMatch(t, slice, []UserAPIKey{{UserAPIKeyID: 101, UserID: 1, User: &User{UserID: 1, Name: \"John Doe\"}, AnotherTable: nil}})\n\t})\n}\n\nfunc ExampleRowToStructByNameLax() {\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconn, err := pgx.Connect(ctx, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to establish connection: %v\", err)\n\t\treturn\n\t}\n\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t// Skip test / example when running on CockroachDB. Since an example can't be skipped fake success instead.\n\t\tfmt.Println(`Cheeseburger: $10\nFries: $5\nSoft Drink: $3`)\n\t\treturn\n\t}\n\n\t// Setup example schema and data.\n\t_, err = conn.Exec(ctx, `\ncreate temporary table products (\n\tid int primary key generated by default as identity,\n\tname varchar(100) not null,\n\tprice int not null\n);\n\ninsert into products (name, price) values\n\t('Cheeseburger', 10),\n\t('Double Cheeseburger', 14),\n\t('Fries', 5),\n\t('Soft Drink', 3);\n`)\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to setup example schema and data: %v\", err)\n\t\treturn\n\t}\n\n\ttype product struct {\n\t\tID    int32\n\t\tName  string\n\t\tType  string\n\t\tPrice int32\n\t}\n\n\trows, _ := conn.Query(ctx, \"select * from products where price < $1 order by price desc\", 12)\n\tproducts, err := pgx.CollectRows(rows, pgx.RowToStructByNameLax[product])\n\tif err != nil {\n\t\tfmt.Printf(\"CollectRows error: %v\", err)\n\t\treturn\n\t}\n\n\tfor _, p := range products {\n\t\tfmt.Printf(\"%s: $%d\\n\", p.Name, p.Price)\n\t}\n\n\t// Output:\n\t// Cheeseburger: $10\n\t// Fries: $5\n\t// Soft Drink: $3\n}\n"
  },
  {
    "path": "stdlib/bench_test.go",
    "content": "package stdlib_test\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc getSelectRowsCounts(b *testing.B) []int64 {\n\tvar rowCounts []int64\n\t{\n\t\ts := os.Getenv(\"PGX_BENCH_SELECT_ROWS_COUNTS\")\n\t\tif s != \"\" {\n\t\t\tfor p := range strings.SplitSeq(s, \" \") {\n\t\t\t\tn, err := strconv.ParseInt(p, 10, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatalf(\"Bad PGX_BENCH_SELECT_ROWS_COUNTS value: %v\", err)\n\t\t\t\t}\n\t\t\t\trowCounts = append(rowCounts, n)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(rowCounts) == 0 {\n\t\trowCounts = []int64{1, 10, 100, 1000}\n\t}\n\n\treturn rowCounts\n}\n\ntype BenchRowSimple struct {\n\tID         int32\n\tFirstName  string\n\tLastName   string\n\tSex        string\n\tBirthDate  time.Time\n\tWeight     int32\n\tHeight     int32\n\tUpdateTime time.Time\n}\n\nfunc BenchmarkSelectRowsScanSimple(b *testing.B) {\n\tdb := openDB(b)\n\tdefer closeDB(b, db)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tbr := &BenchRowSimple{}\n\t\t\tfor b.Loop() {\n\t\t\t\trows, err := db.Query(\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '2001-01-28 01:02:03-05'::timestamptz from generate_series(1, $1) n\", rowCount)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\trows.Scan(&br.ID, &br.FirstName, &br.LastName, &br.Sex, &br.BirthDate, &br.Weight, &br.Height, &br.UpdateTime)\n\t\t\t\t}\n\n\t\t\t\tif rows.Err() != nil {\n\t\t\t\t\tb.Fatal(rows.Err())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype BenchRowNull struct {\n\tID         sql.NullInt32\n\tFirstName  sql.NullString\n\tLastName   sql.NullString\n\tSex        sql.NullString\n\tBirthDate  sql.NullTime\n\tWeight     sql.NullInt32\n\tHeight     sql.NullInt32\n\tUpdateTime sql.NullTime\n}\n\nfunc BenchmarkSelectRowsScanNull(b *testing.B) {\n\tdb := openDB(b)\n\tdefer closeDB(b, db)\n\n\trowCounts := getSelectRowsCounts(b)\n\n\tfor _, rowCount := range rowCounts {\n\t\tb.Run(fmt.Sprintf(\"%d rows\", rowCount), func(b *testing.B) {\n\t\t\tbr := &BenchRowSimple{}\n\t\t\tfor b.Loop() {\n\t\t\t\trows, err := db.Query(\"select n, 'Adam', 'Smith ' || n, 'male', '1952-06-16'::date, 258, 72, '2001-01-28 01:02:03-05'::timestamptz from generate_series(100000, 100000 +  $1) n\", rowCount)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tfor rows.Next() {\n\t\t\t\t\trows.Scan(&br.ID, &br.FirstName, &br.LastName, &br.Sex, &br.BirthDate, &br.Weight, &br.Height, &br.UpdateTime)\n\t\t\t\t}\n\n\t\t\t\tif rows.Err() != nil {\n\t\t\t\t\tb.Fatal(rows.Err())\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "stdlib/sql.go",
    "content": "// Package stdlib is the compatibility layer from pgx to database/sql.\n//\n// A database/sql connection can be established through sql.Open.\n//\n//\tdb, err := sql.Open(\"pgx\", \"postgres://pgx_md5:secret@localhost:5432/pgx_test?sslmode=disable\")\n//\tif err != nil {\n//\t  return err\n//\t}\n//\n// Or from a keyword/value string.\n//\n//\tdb, err := sql.Open(\"pgx\", \"user=postgres password=secret host=localhost port=5432 database=pgx_test sslmode=disable\")\n//\tif err != nil {\n//\t  return err\n//\t}\n//\n// Or from a *pgxpool.Pool.\n//\n//\tpool, err := pgxpool.New(context.Background(), os.Getenv(\"DATABASE_URL\"))\n//\tif err != nil {\n//\t  return err\n//\t}\n//\n//\tdb := stdlib.OpenDBFromPool(pool)\n//\n// Or a pgx.ConnConfig can be used to set configuration not accessible via connection string. In this case the\n// pgx.ConnConfig must first be registered with the driver. This registration returns a connection string which is used\n// with sql.Open.\n//\n//\tconnConfig, _ := pgx.ParseConfig(os.Getenv(\"DATABASE_URL\"))\n//\tconnConfig.Tracer = &tracelog.TraceLog{Logger: myLogger, LogLevel: tracelog.LogLevelInfo}\n//\tconnStr := stdlib.RegisterConnConfig(connConfig)\n//\tdb, _ := sql.Open(\"pgx\", connStr)\n//\n// pgx uses standard PostgreSQL positional parameters in queries. e.g. $1, $2. It does not support named parameters.\n//\n//\tdb.QueryRow(\"select * from users where id=$1\", userID)\n//\n// (*sql.Conn) Raw() can be used to get a *pgx.Conn from the standard database/sql.DB connection pool. This allows\n// operations that use pgx specific functionality.\n//\n//\t// Given db is a *sql.DB\n//\tconn, err := db.Conn(context.Background())\n//\tif err != nil {\n//\t  // handle error from acquiring connection from DB pool\n//\t}\n//\n//\terr = conn.Raw(func(driverConn any) error {\n//\t  conn := driverConn.(*stdlib.Conn).Conn() // conn is a *pgx.Conn\n//\t  // Do pgx specific stuff with conn\n//\t  conn.CopyFrom(...)\n//\t  return nil\n//\t})\n//\tif err != nil {\n//\t  // handle error that occurred while using *pgx.Conn\n//\t}\n//\n// # PostgreSQL Specific Data Types\n//\n// The pgtype package provides support for PostgreSQL specific types. *pgtype.Map.SQLScanner is an adapter that makes\n// these types usable as a sql.Scanner.\n//\n//\tm := pgtype.NewMap()\n//\tvar a []int64\n//\terr := db.QueryRow(\"select '{1,2,3}'::bigint[]\").Scan(m.SQLScanner(&a))\npackage stdlib\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"math/rand/v2\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n)\n\n// Only intrinsic types should be binary format with database/sql.\nvar databaseSQLResultFormats pgx.QueryResultFormatsByOID\n\nvar pgxDriver *Driver\n\nfunc init() {\n\tpgxDriver = &Driver{\n\t\tconfigs: make(map[string]*pgx.ConnConfig),\n\t}\n\n\t// if pgx driver was already registered by different pgx major version then we\n\t// skip registration under the default name.\n\tif !slices.Contains(sql.Drivers(), \"pgx\") {\n\t\tsql.Register(\"pgx\", pgxDriver)\n\t}\n\tsql.Register(\"pgx/v5\", pgxDriver)\n\n\tdatabaseSQLResultFormats = pgx.QueryResultFormatsByOID{\n\t\tpgtype.BoolOID:        1,\n\t\tpgtype.ByteaOID:       1,\n\t\tpgtype.CIDOID:         1,\n\t\tpgtype.DateOID:        1,\n\t\tpgtype.Float4OID:      1,\n\t\tpgtype.Float8OID:      1,\n\t\tpgtype.Int2OID:        1,\n\t\tpgtype.Int4OID:        1,\n\t\tpgtype.Int8OID:        1,\n\t\tpgtype.OIDOID:         1,\n\t\tpgtype.TimestampOID:   1,\n\t\tpgtype.TimestamptzOID: 1,\n\t\tpgtype.XIDOID:         1,\n\t}\n}\n\n// OptionOpenDB options for configuring the driver when opening a new db pool.\ntype OptionOpenDB func(*connector)\n\n// ShouldPingParams are passed to OptionShouldPing to decide whether to ping before reusing a connection.\ntype ShouldPingParams struct {\n\t// Conn is the underlying pgx connection.\n\tConn *pgx.Conn\n\t// IdleDuration is how long it has been since ResetSession last ran.\n\tIdleDuration time.Duration\n}\n\n// OptionShouldPing controls whether stdlib should issue a liveness ping before reusing a connection.\n// If the function returns true, stdlib will ping.\n// If it returns false, stdlib will skip the ping.\n// If not provided, default is ping only when IdleDuration > 1s.\nfunc OptionShouldPing(f func(context.Context, ShouldPingParams) bool) OptionOpenDB {\n\treturn func(dc *connector) { dc.ShouldPing = f }\n}\n\n// OptionBeforeConnect provides a callback for before connect. It is passed a shallow copy of the ConnConfig that will\n// be used to connect, so only its immediate members should be modified. Used only if db is opened with *pgx.ConnConfig.\nfunc OptionBeforeConnect(bc func(context.Context, *pgx.ConnConfig) error) OptionOpenDB {\n\treturn func(dc *connector) {\n\t\tdc.BeforeConnect = bc\n\t}\n}\n\n// OptionAfterConnect provides a callback for after connect. Used only if db is opened with *pgx.ConnConfig.\nfunc OptionAfterConnect(ac func(context.Context, *pgx.Conn) error) OptionOpenDB {\n\treturn func(dc *connector) {\n\t\tdc.AfterConnect = ac\n\t}\n}\n\n// OptionResetSession provides a callback that can be used to add custom logic prior to executing a query on the\n// connection if the connection has been used before.\n// If ResetSessionFunc returns ErrBadConn error the connection will be discarded.\nfunc OptionResetSession(rs func(context.Context, *pgx.Conn) error) OptionOpenDB {\n\treturn func(dc *connector) {\n\t\tdc.ResetSession = rs\n\t}\n}\n\n// RandomizeHostOrderFunc is a BeforeConnect hook that randomizes the host order in the provided connConfig, so that a\n// new host becomes primary each time. This is useful to distribute connections for multi-master databases like\n// CockroachDB. If you use this you likely should set https://golang.org/pkg/database/sql/#DB.SetConnMaxLifetime as well\n// to ensure that connections are periodically rebalanced across your nodes.\nfunc RandomizeHostOrderFunc(ctx context.Context, connConfig *pgx.ConnConfig) error {\n\tif len(connConfig.Fallbacks) == 0 {\n\t\treturn nil\n\t}\n\n\tnewFallbacks := append([]*pgconn.FallbackConfig{{\n\t\tHost:      connConfig.Host,\n\t\tPort:      connConfig.Port,\n\t\tTLSConfig: connConfig.TLSConfig,\n\t}}, connConfig.Fallbacks...)\n\n\trand.Shuffle(len(newFallbacks), func(i, j int) {\n\t\tnewFallbacks[i], newFallbacks[j] = newFallbacks[j], newFallbacks[i]\n\t})\n\n\t// Use the one that sorted last as the primary and keep the rest as the fallbacks\n\tnewPrimary := newFallbacks[len(newFallbacks)-1]\n\tconnConfig.Host = newPrimary.Host\n\tconnConfig.Port = newPrimary.Port\n\tconnConfig.TLSConfig = newPrimary.TLSConfig\n\tconnConfig.Fallbacks = newFallbacks[:len(newFallbacks)-1]\n\treturn nil\n}\n\nfunc GetConnector(config pgx.ConnConfig, opts ...OptionOpenDB) driver.Connector {\n\tc := connector{\n\t\tConnConfig:    config,\n\t\tBeforeConnect: func(context.Context, *pgx.ConnConfig) error { return nil }, // noop before connect by default\n\t\tAfterConnect:  func(context.Context, *pgx.Conn) error { return nil },       // noop after connect by default\n\t\tResetSession:  func(context.Context, *pgx.Conn) error { return nil },       // noop reset session by default\n\t\tdriver:        pgxDriver,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(&c)\n\t}\n\treturn c\n}\n\n// GetPoolConnector creates a new driver.Connector from the given *pgxpool.Pool. By using this be sure to set the\n// maximum idle connections of the *sql.DB created with this connector to zero since they must be managed from the\n// *pgxpool.Pool. This is required to avoid acquiring all the connections from the pgxpool and starving any direct\n// users of the pgxpool.\nfunc GetPoolConnector(pool *pgxpool.Pool, opts ...OptionOpenDB) driver.Connector {\n\tc := connector{\n\t\tpool:         pool,\n\t\tResetSession: func(context.Context, *pgx.Conn) error { return nil }, // noop reset session by default\n\t\tdriver:       pgxDriver,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(&c)\n\t}\n\n\treturn c\n}\n\nfunc OpenDB(config pgx.ConnConfig, opts ...OptionOpenDB) *sql.DB {\n\tc := GetConnector(config, opts...)\n\treturn sql.OpenDB(c)\n}\n\n// OpenDBFromPool creates a new *sql.DB from the given *pgxpool.Pool. Note that this method automatically sets the\n// maximum number of idle connections in *sql.DB to zero, since they must be managed from the *pgxpool.Pool. This is\n// required to avoid acquiring all the connections from the pgxpool and starving any direct users of the pgxpool. Note\n// that closing the returned *sql.DB will not close the *pgxpool.Pool.\nfunc OpenDBFromPool(pool *pgxpool.Pool, opts ...OptionOpenDB) *sql.DB {\n\tc := GetPoolConnector(pool, opts...)\n\tdb := sql.OpenDB(c)\n\tdb.SetMaxIdleConns(0)\n\treturn db\n}\n\ntype connector struct {\n\tpgx.ConnConfig\n\tpool          *pgxpool.Pool\n\tBeforeConnect func(context.Context, *pgx.ConnConfig) error // function to call before creation of every new connection\n\tAfterConnect  func(context.Context, *pgx.Conn) error       // function to call after creation of every new connection\n\tResetSession  func(context.Context, *pgx.Conn) error       // function is called before a connection is reused\n\tShouldPing    func(context.Context, ShouldPingParams) bool // function to decide if stdlib should ping before reusing a connection\n\tdriver        *Driver\n}\n\n// Connect implement driver.Connector interface\nfunc (c connector) Connect(ctx context.Context) (driver.Conn, error) {\n\tvar (\n\t\tconnConfig pgx.ConnConfig\n\t\tconn       *pgx.Conn\n\t\tclose      func(context.Context) error\n\t\terr        error\n\t)\n\n\tif c.pool == nil {\n\t\t// Create a shallow copy of the config, so that BeforeConnect can safely modify it\n\t\tconnConfig = c.ConnConfig\n\n\t\tif err = c.BeforeConnect(ctx, &connConfig); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif conn, err = pgx.ConnectConfig(ctx, &connConfig); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err = c.AfterConnect(ctx, conn); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tclose = conn.Close\n\t} else {\n\t\tvar pconn *pgxpool.Conn\n\n\t\tpconn, err = c.pool.Acquire(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tconn = pconn.Conn()\n\n\t\tclose = func(_ context.Context) error {\n\t\t\tpconn.Release()\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn &Conn{\n\t\tconn:             conn,\n\t\tclose:            close,\n\t\tdriver:           c.driver,\n\t\tconnConfig:       connConfig,\n\t\tresetSessionFunc: c.ResetSession,\n\t\tshouldPing:       c.ShouldPing,\n\t\tpsRefCounts:      make(map[*pgconn.StatementDescription]int),\n\t}, nil\n}\n\n// Driver implement driver.Connector interface\nfunc (c connector) Driver() driver.Driver {\n\treturn c.driver\n}\n\n// GetDefaultDriver returns the driver initialized in the init function\n// and used when the pgx driver is registered.\nfunc GetDefaultDriver() driver.Driver {\n\treturn pgxDriver\n}\n\ntype Driver struct {\n\tconfigMutex sync.Mutex\n\tconfigs     map[string]*pgx.ConnConfig\n\tsequence    int\n}\n\nfunc (d *Driver) Open(name string) (driver.Conn, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) // Ensure eventual timeout\n\tdefer cancel()\n\n\tconnector, err := d.OpenConnector(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn connector.Connect(ctx)\n}\n\nfunc (d *Driver) OpenConnector(name string) (driver.Connector, error) {\n\treturn &driverConnector{driver: d, name: name}, nil\n}\n\nfunc (d *Driver) registerConnConfig(c *pgx.ConnConfig) string {\n\td.configMutex.Lock()\n\tconnStr := fmt.Sprintf(\"registeredConnConfig%d\", d.sequence)\n\td.sequence++\n\td.configs[connStr] = c\n\td.configMutex.Unlock()\n\treturn connStr\n}\n\nfunc (d *Driver) unregisterConnConfig(connStr string) {\n\td.configMutex.Lock()\n\tdelete(d.configs, connStr)\n\td.configMutex.Unlock()\n}\n\ntype driverConnector struct {\n\tdriver *Driver\n\tname   string\n}\n\nfunc (dc *driverConnector) Connect(ctx context.Context) (driver.Conn, error) {\n\tvar connConfig *pgx.ConnConfig\n\n\tdc.driver.configMutex.Lock()\n\tconnConfig = dc.driver.configs[dc.name]\n\tdc.driver.configMutex.Unlock()\n\n\tif connConfig == nil {\n\t\tvar err error\n\t\tconnConfig, err = pgx.ParseConfig(dc.name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tconn, err := pgx.ConnectConfig(ctx, connConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc := &Conn{\n\t\tconn:             conn,\n\t\tclose:            conn.Close,\n\t\tdriver:           dc.driver,\n\t\tconnConfig:       *connConfig,\n\t\tresetSessionFunc: func(context.Context, *pgx.Conn) error { return nil },\n\t\tpsRefCounts:      make(map[*pgconn.StatementDescription]int),\n\t}\n\n\treturn c, nil\n}\n\nfunc (dc *driverConnector) Driver() driver.Driver {\n\treturn dc.driver\n}\n\n// RegisterConnConfig registers a ConnConfig and returns the connection string to use with Open.\nfunc RegisterConnConfig(c *pgx.ConnConfig) string {\n\treturn pgxDriver.registerConnConfig(c)\n}\n\n// UnregisterConnConfig removes the ConnConfig registration for connStr.\nfunc UnregisterConnConfig(connStr string) {\n\tpgxDriver.unregisterConnConfig(connStr)\n}\n\ntype Conn struct {\n\tconn                 *pgx.Conn\n\tclose                func(context.Context) error\n\tdriver               *Driver\n\tconnConfig           pgx.ConnConfig\n\tresetSessionFunc     func(context.Context, *pgx.Conn) error       // Function is called before a connection is reused\n\tshouldPing           func(context.Context, ShouldPingParams) bool // Function to decide if stdlib should ping before reusing a connection\n\tlastResetSessionTime time.Time\n\n\t// psRefCounts contains reference counts for prepared statements. Prepare uses the underlying pgx logic to generate\n\t// deterministic statement names from the statement text. If this query has already been prepared then the existing\n\t// *pgconn.StatementDescription will be returned. However, this means that if Close is called on the returned Stmt\n\t// then the underlying prepared statement will be closed even when the underlying prepared statement is still in use\n\t// by another database/sql Stmt. To prevent this psRefCounts keeps track of how many database/sql statements are using\n\t// the same underlying statement and only closes the underlying statement when the reference count reaches 0.\n\tpsRefCounts map[*pgconn.StatementDescription]int\n}\n\n// Conn returns the underlying *pgx.Conn\nfunc (c *Conn) Conn() *pgx.Conn {\n\treturn c.conn\n}\n\nfunc (c *Conn) Prepare(query string) (driver.Stmt, error) {\n\treturn c.PrepareContext(context.Background(), query)\n}\n\nfunc (c *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {\n\tif c.conn.IsClosed() {\n\t\treturn nil, driver.ErrBadConn\n\t}\n\n\tsd, err := c.conn.Prepare(ctx, query, query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc.psRefCounts[sd]++\n\n\treturn &Stmt{sd: sd, conn: c}, nil\n}\n\nfunc (c *Conn) Close() error {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\tdefer cancel()\n\treturn c.close(ctx)\n}\n\nfunc (c *Conn) Begin() (driver.Tx, error) {\n\treturn c.BeginTx(context.Background(), driver.TxOptions{})\n}\n\nfunc (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {\n\tif c.conn.IsClosed() {\n\t\treturn nil, driver.ErrBadConn\n\t}\n\n\tvar pgxOpts pgx.TxOptions\n\tswitch sql.IsolationLevel(opts.Isolation) {\n\tcase sql.LevelDefault:\n\tcase sql.LevelReadUncommitted:\n\t\tpgxOpts.IsoLevel = pgx.ReadUncommitted\n\tcase sql.LevelReadCommitted:\n\t\tpgxOpts.IsoLevel = pgx.ReadCommitted\n\tcase sql.LevelRepeatableRead, sql.LevelSnapshot:\n\t\tpgxOpts.IsoLevel = pgx.RepeatableRead\n\tcase sql.LevelSerializable:\n\t\tpgxOpts.IsoLevel = pgx.Serializable\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported isolation: %v\", opts.Isolation)\n\t}\n\n\tif opts.ReadOnly {\n\t\tpgxOpts.AccessMode = pgx.ReadOnly\n\t}\n\n\ttx, err := c.conn.BeginTx(ctx, pgxOpts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn wrapTx{ctx: ctx, tx: tx}, nil\n}\n\nfunc (c *Conn) ExecContext(ctx context.Context, query string, argsV []driver.NamedValue) (driver.Result, error) {\n\tif c.conn.IsClosed() {\n\t\treturn nil, driver.ErrBadConn\n\t}\n\n\targs := make([]any, len(argsV))\n\tconvertNamedArguments(args, argsV)\n\n\tcommandTag, err := c.conn.Exec(ctx, query, args...)\n\t// if we got a network error before we had a chance to send the query, retry\n\tif err != nil {\n\t\tif pgconn.SafeToRetry(err) {\n\t\t\treturn nil, driver.ErrBadConn\n\t\t}\n\t}\n\treturn driver.RowsAffected(commandTag.RowsAffected()), err\n}\n\nfunc (c *Conn) QueryContext(ctx context.Context, query string, argsV []driver.NamedValue) (driver.Rows, error) {\n\tif c.conn.IsClosed() {\n\t\treturn nil, driver.ErrBadConn\n\t}\n\n\targs := make([]any, 1+len(argsV))\n\targs[0] = databaseSQLResultFormats\n\tconvertNamedArguments(args[1:], argsV)\n\n\trows, err := c.conn.Query(ctx, query, args...)\n\tif err != nil {\n\t\tif pgconn.SafeToRetry(err) {\n\t\t\treturn nil, driver.ErrBadConn\n\t\t}\n\t\treturn nil, err\n\t}\n\n\t// Preload first row because otherwise we won't know what columns are available when database/sql asks.\n\tmore := rows.Next()\n\tif err = rows.Err(); err != nil {\n\t\trows.Close()\n\t\treturn nil, err\n\t}\n\treturn &Rows{conn: c, rows: rows, skipNext: true, skipNextMore: more}, nil\n}\n\nfunc (c *Conn) Ping(ctx context.Context) error {\n\tif c.conn.IsClosed() {\n\t\treturn driver.ErrBadConn\n\t}\n\n\terr := c.conn.Ping(ctx)\n\tif err != nil {\n\t\t// A Ping failure implies some sort of fatal state. The connection is almost certainly already closed by the\n\t\t// failure, but manually close it just to be sure.\n\t\tc.Close()\n\t\treturn driver.ErrBadConn\n\t}\n\n\treturn nil\n}\n\nfunc (c *Conn) CheckNamedValue(*driver.NamedValue) error {\n\t// Underlying pgx supports sql.Scanner and driver.Valuer interfaces natively. So everything can be passed through directly.\n\treturn nil\n}\n\nfunc (c *Conn) ResetSession(ctx context.Context) error {\n\tif c.conn.IsClosed() {\n\t\treturn driver.ErrBadConn\n\t}\n\n\t// Discard connection if it has an open transaction. This can happen if the\n\t// application did not properly commit or rollback a transaction.\n\tif c.conn.PgConn().TxStatus() != 'I' {\n\t\treturn driver.ErrBadConn\n\t}\n\n\tnow := time.Now()\n\tidle := now.Sub(c.lastResetSessionTime)\n\n\tdoPing := idle > time.Second // default behavior: ping only if idle > 1s\n\n\tif c.shouldPing != nil {\n\t\tdoPing = c.shouldPing(ctx, ShouldPingParams{\n\t\t\tConn:         c.conn,\n\t\t\tIdleDuration: idle,\n\t\t})\n\t}\n\n\tif doPing {\n\t\tif err := c.conn.PgConn().Ping(ctx); err != nil {\n\t\t\treturn driver.ErrBadConn\n\t\t}\n\t}\n\n\tc.lastResetSessionTime = now\n\n\treturn c.resetSessionFunc(ctx, c.conn)\n}\n\ntype Stmt struct {\n\tsd   *pgconn.StatementDescription\n\tconn *Conn\n}\n\nfunc (s *Stmt) Close() error {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\tdefer cancel()\n\n\trefCount := s.conn.psRefCounts[s.sd]\n\tif refCount == 1 {\n\t\tdelete(s.conn.psRefCounts, s.sd)\n\t} else {\n\t\ts.conn.psRefCounts[s.sd]--\n\t\treturn nil\n\t}\n\n\treturn s.conn.conn.Deallocate(ctx, s.sd.SQL)\n}\n\nfunc (s *Stmt) NumInput() int {\n\treturn len(s.sd.ParamOIDs)\n}\n\nfunc (s *Stmt) Exec(argsV []driver.Value) (driver.Result, error) {\n\treturn nil, errors.New(\"Stmt.Exec deprecated and not implemented\")\n}\n\nfunc (s *Stmt) ExecContext(ctx context.Context, argsV []driver.NamedValue) (driver.Result, error) {\n\treturn s.conn.ExecContext(ctx, s.sd.SQL, argsV)\n}\n\nfunc (s *Stmt) Query(argsV []driver.Value) (driver.Rows, error) {\n\treturn nil, errors.New(\"Stmt.Query deprecated and not implemented\")\n}\n\nfunc (s *Stmt) QueryContext(ctx context.Context, argsV []driver.NamedValue) (driver.Rows, error) {\n\treturn s.conn.QueryContext(ctx, s.sd.SQL, argsV)\n}\n\ntype rowValueFunc func(src []byte) (driver.Value, error)\n\ntype Rows struct {\n\tconn         *Conn\n\trows         pgx.Rows\n\tvalueFuncs   []rowValueFunc\n\tskipNext     bool\n\tskipNextMore bool\n\n\tcolumnNames []string\n}\n\nfunc (r *Rows) Columns() []string {\n\tif r.columnNames == nil {\n\t\tfields := r.rows.FieldDescriptions()\n\t\tr.columnNames = make([]string, len(fields))\n\t\tfor i, fd := range fields {\n\t\t\tr.columnNames[i] = string(fd.Name)\n\t\t}\n\t}\n\n\treturn r.columnNames\n}\n\n// ColumnTypeDatabaseTypeName returns the database system type name. If the name is unknown the OID is returned.\nfunc (r *Rows) ColumnTypeDatabaseTypeName(index int) string {\n\tif dt, ok := r.conn.conn.TypeMap().TypeForOID(r.rows.FieldDescriptions()[index].DataTypeOID); ok {\n\t\treturn strings.ToUpper(dt.Name)\n\t}\n\n\treturn strconv.FormatInt(int64(r.rows.FieldDescriptions()[index].DataTypeOID), 10)\n}\n\nconst varHeaderSize = 4\n\n// ColumnTypeLength returns the length of the column type if the column is a\n// variable length type. If the column is not a variable length type ok\n// should return false.\nfunc (r *Rows) ColumnTypeLength(index int) (int64, bool) {\n\tfd := r.rows.FieldDescriptions()[index]\n\n\tswitch fd.DataTypeOID {\n\tcase pgtype.TextOID, pgtype.ByteaOID:\n\t\treturn math.MaxInt64, true\n\tcase pgtype.VarcharOID, pgtype.BPCharArrayOID:\n\t\treturn int64(fd.TypeModifier - varHeaderSize), true\n\tcase pgtype.VarbitOID:\n\t\treturn int64(fd.TypeModifier), true\n\tdefault:\n\t\treturn 0, false\n\t}\n}\n\n// ColumnTypePrecisionScale should return the precision and scale for decimal\n// types. If not applicable, ok should be false.\nfunc (r *Rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {\n\tfd := r.rows.FieldDescriptions()[index]\n\n\tswitch fd.DataTypeOID {\n\tcase pgtype.NumericOID:\n\t\tmod := fd.TypeModifier - varHeaderSize\n\t\tprecision = int64((mod >> 16) & 0xffff)\n\t\tscale = int64(mod & 0xffff)\n\t\treturn precision, scale, true\n\tdefault:\n\t\treturn 0, 0, false\n\t}\n}\n\n// ColumnTypeScanType returns the value type that can be used to scan types into.\nfunc (r *Rows) ColumnTypeScanType(index int) reflect.Type {\n\tfd := r.rows.FieldDescriptions()[index]\n\n\tswitch fd.DataTypeOID {\n\tcase pgtype.Float8OID:\n\t\treturn reflect.TypeOf(float64(0))\n\tcase pgtype.Float4OID:\n\t\treturn reflect.TypeOf(float32(0))\n\tcase pgtype.Int8OID:\n\t\treturn reflect.TypeOf(int64(0))\n\tcase pgtype.Int4OID:\n\t\treturn reflect.TypeOf(int32(0))\n\tcase pgtype.Int2OID:\n\t\treturn reflect.TypeOf(int16(0))\n\tcase pgtype.BoolOID:\n\t\treturn reflect.TypeOf(false)\n\tcase pgtype.NumericOID:\n\t\treturn reflect.TypeOf(float64(0))\n\tcase pgtype.DateOID, pgtype.TimestampOID, pgtype.TimestamptzOID:\n\t\treturn reflect.TypeOf(time.Time{})\n\tcase pgtype.ByteaOID:\n\t\treturn reflect.TypeOf([]byte(nil))\n\tdefault:\n\t\treturn reflect.TypeOf(\"\")\n\t}\n}\n\nfunc (r *Rows) Close() error {\n\tr.rows.Close()\n\treturn r.rows.Err()\n}\n\nfunc (r *Rows) Next(dest []driver.Value) error {\n\tm := r.conn.conn.TypeMap()\n\tfieldDescriptions := r.rows.FieldDescriptions()\n\n\tif r.valueFuncs == nil {\n\t\tr.valueFuncs = make([]rowValueFunc, len(fieldDescriptions))\n\n\t\tfor i, fd := range fieldDescriptions {\n\t\t\tdataTypeOID := fd.DataTypeOID\n\t\t\tformat := fd.Format\n\n\t\t\tswitch fd.DataTypeOID {\n\t\t\tcase pgtype.BoolOID:\n\t\t\t\tvar d bool\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn d, err\n\t\t\t\t}\n\t\t\tcase pgtype.ByteaOID:\n\t\t\t\tvar d []byte\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn d, err\n\t\t\t\t}\n\t\t\tcase pgtype.CIDOID, pgtype.OIDOID, pgtype.XIDOID:\n\t\t\t\tvar d pgtype.Uint32\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn d.Value()\n\t\t\t\t}\n\t\t\tcase pgtype.DateOID:\n\t\t\t\tvar d pgtype.Date\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn d.Value()\n\t\t\t\t}\n\t\t\tcase pgtype.Float4OID:\n\t\t\t\tvar d float32\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn float64(d), err\n\t\t\t\t}\n\t\t\tcase pgtype.Float8OID:\n\t\t\t\tvar d float64\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn d, err\n\t\t\t\t}\n\t\t\tcase pgtype.Int2OID:\n\t\t\t\tvar d int16\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn int64(d), err\n\t\t\t\t}\n\t\t\tcase pgtype.Int4OID:\n\t\t\t\tvar d int32\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn int64(d), err\n\t\t\t\t}\n\t\t\tcase pgtype.Int8OID:\n\t\t\t\tvar d int64\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn d, err\n\t\t\t\t}\n\t\t\tcase pgtype.JSONOID, pgtype.JSONBOID:\n\t\t\t\tvar d []byte\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn d, nil\n\t\t\t\t}\n\t\t\tcase pgtype.TimestampOID:\n\t\t\t\tvar d pgtype.Timestamp\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn d.Value()\n\t\t\t\t}\n\t\t\tcase pgtype.TimestamptzOID:\n\t\t\t\tvar d pgtype.Timestamptz\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn d.Value()\n\t\t\t\t}\n\t\t\tcase pgtype.XMLOID:\n\t\t\t\tvar d []byte\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn d, nil\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tvar d string\n\t\t\t\tscanPlan := m.PlanScan(dataTypeOID, format, &d)\n\t\t\t\tr.valueFuncs[i] = func(src []byte) (driver.Value, error) {\n\t\t\t\t\terr := scanPlan.Scan(src, &d)\n\t\t\t\t\treturn d, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tvar more bool\n\tif r.skipNext {\n\t\tmore = r.skipNextMore\n\t\tr.skipNext = false\n\t} else {\n\t\tmore = r.rows.Next()\n\t}\n\n\tif !more {\n\t\tif r.rows.Err() == nil {\n\t\t\treturn io.EOF\n\t\t} else {\n\t\t\treturn r.rows.Err()\n\t\t}\n\t}\n\n\tfor i, rv := range r.rows.RawValues() {\n\t\tif rv != nil {\n\t\t\tvar err error\n\t\t\tdest[i], err = r.valueFuncs[i](rv)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"convert field %d failed: %w\", i, err)\n\t\t\t}\n\t\t} else {\n\t\t\tdest[i] = nil\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc convertNamedArguments(args []any, argsV []driver.NamedValue) {\n\tfor i, v := range argsV {\n\t\tif v.Value != nil {\n\t\t\targs[i] = v.Value.(any)\n\t\t} else {\n\t\t\targs[i] = nil\n\t\t}\n\t}\n}\n\ntype wrapTx struct {\n\tctx context.Context\n\ttx  pgx.Tx\n}\n\nfunc (wtx wrapTx) Commit() error { return wtx.tx.Commit(wtx.ctx) }\n\nfunc (wtx wrapTx) Rollback() error { return wtx.tx.Rollback(wtx.ctx) }\n"
  },
  {
    "path": "stdlib/sql_test.go",
    "content": "package stdlib_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/jackc/pgx/v5/stdlib\"\n\t\"github.com/jackc/pgx/v5/tracelog\"\n)\n\nfunc openDB(t testing.TB, opts ...stdlib.OptionOpenDB) *sql.DB {\n\tt.Helper()\n\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\treturn stdlib.OpenDB(*config, opts...)\n}\n\nfunc closeDB(t testing.TB, db *sql.DB) {\n\terr := db.Close()\n\trequire.NoError(t, err)\n}\n\nfunc skipCockroachDB(t testing.TB, db *sql.DB, msg string) {\n\tconn, err := db.Conn(context.Background())\n\trequire.NoError(t, err)\n\tdefer conn.Close()\n\n\terr = conn.Raw(func(driverConn any) error {\n\t\tconn := driverConn.(*stdlib.Conn).Conn()\n\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\t\tt.Skip(msg)\n\t\t}\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n}\n\nfunc skipPostgreSQLVersionLessThan(t testing.TB, db *sql.DB, minVersion int64) {\n\tconn, err := db.Conn(context.Background())\n\trequire.NoError(t, err)\n\tdefer conn.Close()\n\n\terr = conn.Raw(func(driverConn any) error {\n\t\tconn := driverConn.(*stdlib.Conn).Conn()\n\t\tserverVersionStr := conn.PgConn().ParameterStatus(\"server_version\")\n\t\tserverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)\n\t\t// if not PostgreSQL do nothing\n\t\tif serverVersionStr == \"\" {\n\t\t\treturn nil\n\t\t}\n\n\t\tserverVersion, err := strconv.ParseInt(serverVersionStr, 10, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif serverVersion < minVersion {\n\t\t\tt.Skipf(\"Test requires PostgreSQL v%d+\", minVersion)\n\t\t}\n\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n}\n\nfunc testWithAllQueryExecModes(t *testing.T, f func(t *testing.T, db *sql.DB)) {\n\tfor _, mode := range []pgx.QueryExecMode{\n\t\tpgx.QueryExecModeCacheStatement,\n\t\tpgx.QueryExecModeCacheDescribe,\n\t\tpgx.QueryExecModeDescribeExec,\n\t\tpgx.QueryExecModeExec,\n\t\tpgx.QueryExecModeSimpleProtocol,\n\t} {\n\t\tt.Run(mode.String(),\n\t\t\tfunc(t *testing.T) {\n\t\t\t\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tconfig.DefaultQueryExecMode = mode\n\t\t\t\tdb := stdlib.OpenDB(*config)\n\t\t\t\tdefer func() {\n\t\t\t\t\terr := db.Close()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t}()\n\n\t\t\t\tf(t, db)\n\n\t\t\t\tensureDBValid(t, db)\n\t\t\t},\n\t\t)\n\t}\n}\n\n// Do a simple query to ensure the DB is still usable. This is of less use in stdlib as the connection pool should\n// cover broken connections.\nfunc ensureDBValid(t testing.TB, db *sql.DB) {\n\tvar sum, rowCount int32\n\n\trows, err := db.Query(\"select generate_series(1,$1)\", 10)\n\trequire.NoError(t, err)\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar n int32\n\t\trows.Scan(&n)\n\t\tsum += n\n\t\trowCount++\n\t}\n\n\trequire.NoError(t, rows.Err())\n\n\tif rowCount != 10 {\n\t\tt.Error(\"Select called onDataRow wrong number of times\")\n\t}\n\tif sum != 55 {\n\t\tt.Error(\"Wrong values returned\")\n\t}\n}\n\ntype preparer interface {\n\tPrepare(query string) (*sql.Stmt, error)\n}\n\nfunc prepareStmt(t *testing.T, p preparer, sql string) *sql.Stmt {\n\tstmt, err := p.Prepare(sql)\n\trequire.NoError(t, err)\n\treturn stmt\n}\n\nfunc closeStmt(t *testing.T, stmt *sql.Stmt) {\n\terr := stmt.Close()\n\trequire.NoError(t, err)\n}\n\nfunc TestSQLOpen(t *testing.T) {\n\ttests := []struct {\n\t\tdriverName string\n\t}{\n\t\t{driverName: \"pgx\"},\n\t\t{driverName: \"pgx/v5\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.driverName, func(t *testing.T) {\n\t\t\tdb, err := sql.Open(tt.driverName, os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\t\trequire.NoError(t, err)\n\t\t\tcloseDB(t, db)\n\t\t})\n\t}\n}\n\nfunc TestSQLOpenFromPool(t *testing.T) {\n\tpool, err := pgxpool.New(context.Background(), os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tt.Cleanup(pool.Close)\n\n\tdb := stdlib.OpenDBFromPool(pool)\n\tensureDBValid(t, db)\n\n\tdb.Close()\n}\n\nfunc TestNormalLifeCycle(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\tskipCockroachDB(t, db, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\n\tstmt := prepareStmt(t, db, \"select 'foo', n from generate_series($1::int, $2::int) n\")\n\tdefer closeStmt(t, stmt)\n\n\trows, err := stmt.Query(int32(1), int32(10))\n\trequire.NoError(t, err)\n\n\trowCount := int64(0)\n\n\tfor rows.Next() {\n\t\trowCount++\n\n\t\tvar s string\n\t\tvar n int64\n\t\terr := rows.Scan(&s, &n)\n\t\trequire.NoError(t, err)\n\n\t\tif s != \"foo\" {\n\t\t\tt.Errorf(`Expected \"foo\", received \"%v\"`, s)\n\t\t}\n\t\tif n != rowCount {\n\t\t\tt.Errorf(\"Expected %d, received %d\", rowCount, n)\n\t\t}\n\t}\n\trequire.NoError(t, rows.Err())\n\n\trequire.EqualValues(t, 10, rowCount)\n\n\terr = rows.Close()\n\trequire.NoError(t, err)\n\n\tensureDBValid(t, db)\n}\n\nfunc TestStmtExec(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\ttx, err := db.Begin()\n\trequire.NoError(t, err)\n\n\tcreateStmt := prepareStmt(t, tx, \"create temporary table t(a varchar not null)\")\n\t_, err = createStmt.Exec()\n\trequire.NoError(t, err)\n\tcloseStmt(t, createStmt)\n\n\tinsertStmt := prepareStmt(t, tx, \"insert into t values($1::text)\")\n\tresult, err := insertStmt.Exec(\"foo\")\n\trequire.NoError(t, err)\n\n\tn, err := result.RowsAffected()\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 1, n)\n\tcloseStmt(t, insertStmt)\n\n\tensureDBValid(t, db)\n}\n\nfunc TestQueryCloseRowsEarly(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\tskipCockroachDB(t, db, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\n\tstmt := prepareStmt(t, db, \"select 'foo', n from generate_series($1::int, $2::int) n\")\n\tdefer closeStmt(t, stmt)\n\n\trows, err := stmt.Query(int32(1), int32(10))\n\trequire.NoError(t, err)\n\n\t// Close rows immediately without having read them\n\terr = rows.Close()\n\trequire.NoError(t, err)\n\n\t// Run the query again to ensure the connection and statement are still ok\n\trows, err = stmt.Query(int32(1), int32(10))\n\trequire.NoError(t, err)\n\n\trowCount := int64(0)\n\n\tfor rows.Next() {\n\t\trowCount++\n\n\t\tvar s string\n\t\tvar n int64\n\t\terr := rows.Scan(&s, &n)\n\t\trequire.NoError(t, err)\n\t\tif s != \"foo\" {\n\t\t\tt.Errorf(`Expected \"foo\", received \"%v\"`, s)\n\t\t}\n\t\tif n != rowCount {\n\t\t\tt.Errorf(\"Expected %d, received %d\", rowCount, n)\n\t\t}\n\t}\n\trequire.NoError(t, rows.Err())\n\trequire.EqualValues(t, 10, rowCount)\n\n\terr = rows.Close()\n\trequire.NoError(t, err)\n\n\tensureDBValid(t, db)\n}\n\nfunc TestConnExec(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\t_, err := db.Exec(\"create temporary table t(a varchar not null)\")\n\t\trequire.NoError(t, err)\n\n\t\tresult, err := db.Exec(\"insert into t values('hey')\")\n\t\trequire.NoError(t, err)\n\n\t\tn, err := result.RowsAffected()\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, n)\n\t})\n}\n\nfunc TestConnQuery(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\n\t\trows, err := db.Query(\"select 'foo', n from generate_series($1::int, $2::int) n\", int32(1), int32(10))\n\t\trequire.NoError(t, err)\n\n\t\trowCount := int64(0)\n\n\t\tfor rows.Next() {\n\t\t\trowCount++\n\n\t\t\tvar s string\n\t\t\tvar n int64\n\t\t\terr := rows.Scan(&s, &n)\n\t\t\trequire.NoError(t, err)\n\t\t\tif s != \"foo\" {\n\t\t\t\tt.Errorf(`Expected \"foo\", received \"%v\"`, s)\n\t\t\t}\n\t\t\tif n != rowCount {\n\t\t\t\tt.Errorf(\"Expected %d, received %d\", rowCount, n)\n\t\t\t}\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\t\trequire.EqualValues(t, 10, rowCount)\n\n\t\terr = rows.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnConcurrency(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\t_, err := db.Exec(\"create table t (id integer primary key, str text, dur_str interval)\")\n\t\trequire.NoError(t, err)\n\n\t\tdefer func() {\n\t\t\t_, err := db.Exec(\"drop table t\")\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\n\t\tvar wg sync.WaitGroup\n\n\t\tconcurrency := 50\n\t\terrChan := make(chan error, concurrency)\n\n\t\tfor i := 1; i <= concurrency; i++ {\n\t\t\twg.Add(1)\n\n\t\t\tgo func(idx int) {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tstr := strconv.Itoa(idx)\n\t\t\t\tduration := time.Duration(idx) * time.Second\n\t\t\t\t_, err := db.ExecContext(ctx, \"insert into t values($1)\", idx)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrChan <- fmt.Errorf(\"insert failed: %d %w\", idx, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t_, err = db.ExecContext(ctx, \"update t set str = $1 where id = $2\", str, idx)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrChan <- fmt.Errorf(\"update 1 failed: %d %w\", idx, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t_, err = db.ExecContext(ctx, \"update t set dur_str = $1 where id = $2\", duration, idx)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrChan <- fmt.Errorf(\"update 2 failed: %d %w\", idx, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\terrChan <- nil\n\t\t\t}(i)\n\t\t}\n\t\twg.Wait()\n\t\tfor i := 1; i <= concurrency; i++ {\n\t\t\terr := <-errChan\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\tfor i := 1; i <= concurrency; i++ {\n\t\t\twg.Add(1)\n\n\t\t\tgo func(idx int) {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tvar id int\n\t\t\t\tvar str string\n\t\t\t\tvar duration pgtype.Interval\n\t\t\t\terr := db.QueryRowContext(ctx, \"select id,str,dur_str from t where id = $1\", idx).Scan(&id, &str, &duration)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrChan <- fmt.Errorf(\"select failed: %d %w\", idx, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif id != idx {\n\t\t\t\t\terrChan <- fmt.Errorf(\"id mismatch: %d %d\", idx, id)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif str != strconv.Itoa(idx) {\n\t\t\t\t\terrChan <- fmt.Errorf(\"str mismatch: %d %s\", idx, str)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\texpectedDuration := pgtype.Interval{\n\t\t\t\t\tMicroseconds: int64(idx) * time.Second.Microseconds(),\n\t\t\t\t\tValid:        true,\n\t\t\t\t}\n\t\t\t\tif duration != expectedDuration {\n\t\t\t\t\terrChan <- fmt.Errorf(\"duration mismatch: %d %v\", idx, duration)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\terrChan <- nil\n\t\t\t}(i)\n\t\t}\n\t\twg.Wait()\n\t\tfor i := 1; i <= concurrency; i++ {\n\t\t\terr := <-errChan\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/781\nfunc TestConnQueryDifferentScanPlansIssue781(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tvar s string\n\t\tvar b bool\n\n\t\trows, err := db.Query(\"select true, 'foo'\")\n\t\trequire.NoError(t, err)\n\n\t\trequire.True(t, rows.Next())\n\t\trequire.NoError(t, rows.Scan(&b, &s))\n\t\tassert.Equal(t, true, b)\n\t\tassert.Equal(t, \"foo\", s)\n\t})\n}\n\nfunc TestConnQueryNull(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\trows, err := db.Query(\"select $1::int\", nil)\n\t\trequire.NoError(t, err)\n\n\t\trowCount := int64(0)\n\n\t\tfor rows.Next() {\n\t\t\trowCount++\n\n\t\t\tvar n sql.NullInt64\n\t\t\terr := rows.Scan(&n)\n\t\t\trequire.NoError(t, err)\n\t\t\tif n.Valid != false {\n\t\t\t\tt.Errorf(\"Expected n to be null, but it was %v\", n)\n\t\t\t}\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\t\trequire.EqualValues(t, 1, rowCount)\n\n\t\terr = rows.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnQueryRowByteSlice(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\texpected := []byte{222, 173, 190, 239}\n\t\tvar actual []byte\n\n\t\terr := db.QueryRow(`select E'\\\\xdeadbeef'::bytea`).Scan(&actual)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, expected, actual)\n\t})\n}\n\nfunc TestConnQueryFailure(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\t_, err := db.Query(\"select 'foo\")\n\t\trequire.Error(t, err)\n\t\trequire.ErrorAs(t, err, new(*pgconn.PgError))\n\t})\n}\n\nfunc TestConnSimpleSlicePassThrough(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server does not support cardinality function\")\n\n\t\tvar n int64\n\t\terr := db.QueryRow(\"select cardinality($1::text[])\", []string{\"a\", \"b\", \"c\"}).Scan(&n)\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, 3, n)\n\t})\n}\n\nfunc TestConnQueryScanGoArray(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tm := pgtype.NewMap()\n\n\t\tvar a []int64\n\t\terr := db.QueryRow(\"select '{1,2,3}'::bigint[]\").Scan(m.SQLScanner(&a))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, []int64{1, 2, 3}, a)\n\t})\n}\n\nfunc TestConnQueryScanArray(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tm := pgtype.NewMap()\n\n\t\tvar a pgtype.Array[int64]\n\t\terr := db.QueryRow(\"select '{1,2,3}'::bigint[]\").Scan(m.SQLScanner(&a))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, pgtype.Array[int64]{Elements: []int64{1, 2, 3}, Dims: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}}, Valid: true}, a)\n\n\t\terr = db.QueryRow(\"select null::bigint[]\").Scan(m.SQLScanner(&a))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(t, pgtype.Array[int64]{Elements: nil, Dims: nil, Valid: false}, a)\n\t})\n}\n\nfunc TestConnQueryScanRange(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server does not support int4range\")\n\n\t\tm := pgtype.NewMap()\n\n\t\tvar r pgtype.Range[pgtype.Int4]\n\t\terr := db.QueryRow(\"select int4range(1, 5)\").Scan(m.SQLScanner(&r))\n\t\trequire.NoError(t, err)\n\t\tassert.Equal(\n\t\t\tt,\n\t\t\tpgtype.Range[pgtype.Int4]{\n\t\t\t\tLower:     pgtype.Int4{Int32: 1, Valid: true},\n\t\t\t\tUpper:     pgtype.Int4{Int32: 5, Valid: true},\n\t\t\t\tLowerType: pgtype.Inclusive,\n\t\t\t\tUpperType: pgtype.Exclusive,\n\t\t\t\tValid:     true,\n\t\t\t},\n\t\t\tr)\n\t})\n}\n\n// Test type that pgx would handle natively in binary, but since it is not a\n// database/sql native type should be passed through as a string\nfunc TestConnQueryRowPgxBinary(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tsql := \"select $1::int4[]\"\n\t\texpected := \"{1,2,3}\"\n\t\tvar actual string\n\n\t\terr := db.QueryRow(sql, expected).Scan(&actual)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, expected, actual)\n\t})\n}\n\nfunc TestConnQueryRowUnknownType(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server does not support point type\")\n\n\t\tsql := \"select $1::point\"\n\t\texpected := \"(1,2)\"\n\t\tvar actual string\n\n\t\terr := db.QueryRow(sql, expected).Scan(&actual)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, expected, actual)\n\t})\n}\n\nfunc TestConnQueryJSONIntoByteSlice(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\t_, err := db.Exec(`\n\t\tcreate temporary table docs(\n\t\t\tbody json not null\n\t\t);\n\n\t\tinsert into docs(body) values('{\"foo\": \"bar\"}');\n`)\n\t\trequire.NoError(t, err)\n\n\t\tsql := `select * from docs`\n\t\texpected := []byte(`{\"foo\": \"bar\"}`)\n\t\tvar actual []byte\n\n\t\terr = db.QueryRow(sql).Scan(&actual)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected failure: %v (sql -> %v)\", err, sql)\n\t\t}\n\n\t\tif !bytes.Equal(actual, expected) {\n\t\t\tt.Errorf(`Expected \"%v\", got \"%v\" (sql -> %v)`, string(expected), string(actual), sql)\n\t\t}\n\n\t\t_, err = db.Exec(`drop table docs`)\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnExecInsertByteSliceIntoJSON(t *testing.T) {\n\t// Not testing with simple protocol because there is no way for that to work. A []byte will be considered binary data\n\t// that needs to escape. No way to know whether the destination is really a text compatible or a bytea.\n\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\t_, err := db.Exec(`\n\t\tcreate temporary table docs(\n\t\t\tbody json not null\n\t\t);\n`)\n\trequire.NoError(t, err)\n\n\texpected := []byte(`{\"foo\": \"bar\"}`)\n\n\t_, err = db.Exec(`insert into docs(body) values($1)`, expected)\n\trequire.NoError(t, err)\n\n\tvar actual []byte\n\terr = db.QueryRow(`select body from docs`).Scan(&actual)\n\trequire.NoError(t, err)\n\n\tif !bytes.Equal(actual, expected) {\n\t\tt.Errorf(`Expected \"%v\", got \"%v\"`, string(expected), string(actual))\n\t}\n\n\t_, err = db.Exec(`drop table docs`)\n\trequire.NoError(t, err)\n}\n\nfunc TestTransactionLifeCycle(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\t_, err := db.Exec(\"create temporary table t(a varchar not null)\")\n\t\trequire.NoError(t, err)\n\n\t\ttx, err := db.Begin()\n\t\trequire.NoError(t, err)\n\n\t\t_, err = tx.Exec(\"insert into t values('hi')\")\n\t\trequire.NoError(t, err)\n\n\t\terr = tx.Rollback()\n\t\trequire.NoError(t, err)\n\n\t\tvar n int64\n\t\terr = db.QueryRow(\"select count(*) from t\").Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 0, n)\n\n\t\ttx, err = db.Begin()\n\t\trequire.NoError(t, err)\n\n\t\t_, err = tx.Exec(\"insert into t values('hi')\")\n\t\trequire.NoError(t, err)\n\n\t\terr = tx.Commit()\n\t\trequire.NoError(t, err)\n\n\t\terr = db.QueryRow(\"select count(*) from t\").Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, n)\n\t})\n}\n\nfunc TestConnBeginTxIsolation(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server always uses serializable isolation level\")\n\n\t\tvar defaultIsoLevel string\n\t\terr := db.QueryRow(\"show transaction_isolation\").Scan(&defaultIsoLevel)\n\t\trequire.NoError(t, err)\n\n\t\tsupportedTests := []struct {\n\t\t\tsqlIso sql.IsolationLevel\n\t\t\tpgIso  string\n\t\t}{\n\t\t\t{sqlIso: sql.LevelDefault, pgIso: defaultIsoLevel},\n\t\t\t{sqlIso: sql.LevelReadUncommitted, pgIso: \"read uncommitted\"},\n\t\t\t{sqlIso: sql.LevelReadCommitted, pgIso: \"read committed\"},\n\t\t\t{sqlIso: sql.LevelRepeatableRead, pgIso: \"repeatable read\"},\n\t\t\t{sqlIso: sql.LevelSnapshot, pgIso: \"repeatable read\"},\n\t\t\t{sqlIso: sql.LevelSerializable, pgIso: \"serializable\"},\n\t\t}\n\t\tfor i, tt := range supportedTests {\n\t\t\tfunc() {\n\t\t\t\ttx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: tt.sqlIso})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"%d. BeginTx failed: %v\", i, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer tx.Rollback()\n\n\t\t\t\tvar pgIso string\n\t\t\t\terr = tx.QueryRow(\"show transaction_isolation\").Scan(&pgIso)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"%d. QueryRow failed: %v\", i, err)\n\t\t\t\t}\n\n\t\t\t\tif pgIso != tt.pgIso {\n\t\t\t\t\tt.Errorf(\"%d. pgIso => %s, want %s\", i, pgIso, tt.pgIso)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tunsupportedTests := []struct {\n\t\t\tsqlIso sql.IsolationLevel\n\t\t}{\n\t\t\t{sqlIso: sql.LevelWriteCommitted},\n\t\t\t{sqlIso: sql.LevelLinearizable},\n\t\t}\n\t\tfor i, tt := range unsupportedTests {\n\t\t\ttx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: tt.sqlIso})\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"%d. BeginTx should have failed\", i)\n\t\t\t\ttx.Rollback()\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestConnBeginTxReadOnly(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\ttx, err := db.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true})\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback()\n\n\t\tvar pgReadOnly string\n\t\terr = tx.QueryRow(\"show transaction_read_only\").Scan(&pgReadOnly)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"QueryRow failed: %v\", err)\n\t\t}\n\n\t\tif pgReadOnly != \"on\" {\n\t\t\tt.Errorf(\"pgReadOnly => %s, want %s\", pgReadOnly, \"on\")\n\t\t}\n\t})\n}\n\nfunc TestBeginTxContextCancel(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"CockroachDB auto commits DDL by default\")\n\n\t\t_, err := db.Exec(\"drop table if exists t\")\n\t\trequire.NoError(t, err)\n\n\t\tctx, cancelFn := context.WithCancel(context.Background())\n\n\t\ttx, err := db.BeginTx(ctx, nil)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = tx.Exec(\"create table t(id serial)\")\n\t\trequire.NoError(t, err)\n\n\t\tcancelFn()\n\n\t\terr = tx.Commit()\n\t\tif err != context.Canceled && err != sql.ErrTxDone {\n\t\t\tt.Fatalf(\"err => %v, want %v or %v\", err, context.Canceled, sql.ErrTxDone)\n\t\t}\n\n\t\tvar n int\n\t\terr = db.QueryRow(\"select count(*) from t\").Scan(&n)\n\t\tvar pgErr *pgconn.PgError\n\t\tif !errors.As(err, &pgErr) || pgErr.Code != \"42P01\" {\n\t\t\tt.Fatalf(`err => %v, want PgError{Code: \"42P01\"}`, err)\n\t\t}\n\t})\n}\n\nfunc TestConnRaw(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tconn, err := db.Conn(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tvar n int\n\t\terr = conn.Raw(func(driverConn any) error {\n\t\t\tconn := driverConn.(*stdlib.Conn).Conn()\n\t\t\treturn conn.QueryRow(context.Background(), \"select 42\").Scan(&n)\n\t\t})\n\t\trequire.NoError(t, err)\n\t\tassert.EqualValues(t, 42, n)\n\t})\n}\n\nfunc TestConnPingContextSuccess(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\terr := db.PingContext(context.Background())\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnPrepareContextSuccess(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tstmt, err := db.PrepareContext(context.Background(), \"select now()\")\n\t\trequire.NoError(t, err)\n\t\terr = stmt.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1753#issuecomment-1746033281\n// https://github.com/jackc/pgx/issues/1754#issuecomment-1752004634\nfunc TestConnMultiplePrepareAndDeallocate(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server does not support pg_prepared_statements\")\n\n\t\tsql := \"select 42\"\n\t\tstmt1, err := db.PrepareContext(context.Background(), sql)\n\t\trequire.NoError(t, err)\n\t\tstmt2, err := db.PrepareContext(context.Background(), sql)\n\t\trequire.NoError(t, err)\n\t\terr = stmt1.Close()\n\t\trequire.NoError(t, err)\n\n\t\tvar preparedStmtCount int64\n\t\terr = db.QueryRowContext(context.Background(), \"select count(*) from pg_prepared_statements where statement = $1\", sql).Scan(&preparedStmtCount)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, preparedStmtCount)\n\n\t\terr = stmt2.Close() // err isn't as useful as it should be as database/sql will ignore errors from Deallocate.\n\t\trequire.NoError(t, err)\n\n\t\terr = db.QueryRowContext(context.Background(), \"select count(*) from pg_prepared_statements where statement = $1\", sql).Scan(&preparedStmtCount)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 0, preparedStmtCount)\n\t})\n}\n\nfunc TestConnExecContextSuccess(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\t_, err := db.ExecContext(context.Background(), \"create temporary table exec_context_test(id serial primary key)\")\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestConnQueryContextSuccess(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\trows, err := db.QueryContext(context.Background(), \"select * from generate_series(1,10) n\")\n\t\trequire.NoError(t, err)\n\n\t\tfor rows.Next() {\n\t\t\tvar n int64\n\t\t\terr := rows.Scan(&n)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\t})\n}\n\nfunc TestRowsColumnTypeDatabaseTypeName(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\trows, err := db.Query(\"select 42::bigint\")\n\t\trequire.NoError(t, err)\n\n\t\tcolumnTypes, err := rows.ColumnTypes()\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, columnTypes, 1)\n\n\t\tif columnTypes[0].DatabaseTypeName() != \"INT8\" {\n\t\t\tt.Errorf(\"columnTypes[0].DatabaseTypeName() => %v, want %v\", columnTypes[0].DatabaseTypeName(), \"INT8\")\n\t\t}\n\n\t\terr = rows.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestStmtExecContextSuccess(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\t_, err := db.Exec(\"create temporary table t(id int primary key)\")\n\trequire.NoError(t, err)\n\n\tstmt, err := db.Prepare(\"insert into t(id) values ($1::int4)\")\n\trequire.NoError(t, err)\n\tdefer stmt.Close()\n\n\t_, err = stmt.ExecContext(context.Background(), 42)\n\trequire.NoError(t, err)\n\n\tensureDBValid(t, db)\n}\n\nfunc TestStmtExecContextCancel(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\t_, err := db.Exec(\"create temporary table t(id int primary key)\")\n\trequire.NoError(t, err)\n\n\tstmt, err := db.Prepare(\"insert into t(id) select $1::int4 from pg_sleep(5)\")\n\trequire.NoError(t, err)\n\tdefer stmt.Close()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\tdefer cancel()\n\n\t_, err = stmt.ExecContext(ctx, 42)\n\tif !pgconn.Timeout(err) {\n\t\tt.Errorf(\"expected timeout error, got %v\", err)\n\t}\n\n\tensureDBValid(t, db)\n}\n\nfunc TestStmtQueryContextSuccess(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\tskipCockroachDB(t, db, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\n\tstmt, err := db.Prepare(\"select * from generate_series(1,$1::int4) n\")\n\trequire.NoError(t, err)\n\tdefer stmt.Close()\n\n\trows, err := stmt.QueryContext(context.Background(), 5)\n\trequire.NoError(t, err)\n\n\tfor rows.Next() {\n\t\tvar n int64\n\t\tif err := rows.Scan(&n); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t}\n\n\tif rows.Err() != nil {\n\t\tt.Error(rows.Err())\n\t}\n\n\tensureDBValid(t, db)\n}\n\nfunc TestRowsColumnTypes(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tcolumnTypesTests := []struct {\n\t\t\tName     string\n\t\t\tTypeName string\n\t\t\tLength   struct {\n\t\t\t\tLen int64\n\t\t\t\tOK  bool\n\t\t\t}\n\t\t\tDecimalSize struct {\n\t\t\t\tPrecision int64\n\t\t\t\tScale     int64\n\t\t\t\tOK        bool\n\t\t\t}\n\t\t\tScanType reflect.Type\n\t\t}{\n\t\t\t{\n\t\t\t\tName:     \"a\",\n\t\t\t\tTypeName: \"INT8\",\n\t\t\t\tLength: struct {\n\t\t\t\t\tLen int64\n\t\t\t\t\tOK  bool\n\t\t\t\t}{\n\t\t\t\t\tLen: 0,\n\t\t\t\t\tOK:  false,\n\t\t\t\t},\n\t\t\t\tDecimalSize: struct {\n\t\t\t\t\tPrecision int64\n\t\t\t\t\tScale     int64\n\t\t\t\t\tOK        bool\n\t\t\t\t}{\n\t\t\t\t\tPrecision: 0,\n\t\t\t\t\tScale:     0,\n\t\t\t\t\tOK:        false,\n\t\t\t\t},\n\t\t\t\tScanType: reflect.TypeOf(int64(0)),\n\t\t\t}, {\n\t\t\t\tName:     \"bar\",\n\t\t\t\tTypeName: \"TEXT\",\n\t\t\t\tLength: struct {\n\t\t\t\t\tLen int64\n\t\t\t\t\tOK  bool\n\t\t\t\t}{\n\t\t\t\t\tLen: math.MaxInt64,\n\t\t\t\t\tOK:  true,\n\t\t\t\t},\n\t\t\t\tDecimalSize: struct {\n\t\t\t\t\tPrecision int64\n\t\t\t\t\tScale     int64\n\t\t\t\t\tOK        bool\n\t\t\t\t}{\n\t\t\t\t\tPrecision: 0,\n\t\t\t\t\tScale:     0,\n\t\t\t\t\tOK:        false,\n\t\t\t\t},\n\t\t\t\tScanType: reflect.TypeOf(\"\"),\n\t\t\t}, {\n\t\t\t\tName:     \"dec\",\n\t\t\t\tTypeName: \"NUMERIC\",\n\t\t\t\tLength: struct {\n\t\t\t\t\tLen int64\n\t\t\t\t\tOK  bool\n\t\t\t\t}{\n\t\t\t\t\tLen: 0,\n\t\t\t\t\tOK:  false,\n\t\t\t\t},\n\t\t\t\tDecimalSize: struct {\n\t\t\t\t\tPrecision int64\n\t\t\t\t\tScale     int64\n\t\t\t\t\tOK        bool\n\t\t\t\t}{\n\t\t\t\t\tPrecision: 9,\n\t\t\t\t\tScale:     2,\n\t\t\t\t\tOK:        true,\n\t\t\t\t},\n\t\t\t\tScanType: reflect.TypeOf(float64(0)),\n\t\t\t}, {\n\t\t\t\tName:     \"d\",\n\t\t\t\tTypeName: \"1266\",\n\t\t\t\tLength: struct {\n\t\t\t\t\tLen int64\n\t\t\t\t\tOK  bool\n\t\t\t\t}{\n\t\t\t\t\tLen: 0,\n\t\t\t\t\tOK:  false,\n\t\t\t\t},\n\t\t\t\tDecimalSize: struct {\n\t\t\t\t\tPrecision int64\n\t\t\t\t\tScale     int64\n\t\t\t\t\tOK        bool\n\t\t\t\t}{\n\t\t\t\t\tPrecision: 0,\n\t\t\t\t\tScale:     0,\n\t\t\t\t\tOK:        false,\n\t\t\t\t},\n\t\t\t\tScanType: reflect.TypeOf(\"\"),\n\t\t\t},\n\t\t}\n\n\t\trows, err := db.Query(\"SELECT 1::bigint AS a, text 'bar' AS bar, 1.28::numeric(9, 2) AS dec, '12:00:00'::timetz as d\")\n\t\trequire.NoError(t, err)\n\n\t\tcolumns, err := rows.ColumnTypes()\n\t\trequire.NoError(t, err)\n\t\tassert.Len(t, columns, 4)\n\n\t\tfor i, tt := range columnTypesTests {\n\t\t\tc := columns[i]\n\t\t\tif c.Name() != tt.Name {\n\t\t\t\tt.Errorf(\"(%d) got: %s, want: %s\", i, c.Name(), tt.Name)\n\t\t\t}\n\t\t\tif c.DatabaseTypeName() != tt.TypeName {\n\t\t\t\tt.Errorf(\"(%d) got: %s, want: %s\", i, c.DatabaseTypeName(), tt.TypeName)\n\t\t\t}\n\t\t\tl, ok := c.Length()\n\t\t\tif l != tt.Length.Len {\n\t\t\t\tt.Errorf(\"(%d) got: %d, want: %d\", i, l, tt.Length.Len)\n\t\t\t}\n\t\t\tif ok != tt.Length.OK {\n\t\t\t\tt.Errorf(\"(%d) got: %t, want: %t\", i, ok, tt.Length.OK)\n\t\t\t}\n\t\t\tp, s, ok := c.DecimalSize()\n\t\t\tif p != tt.DecimalSize.Precision {\n\t\t\t\tt.Errorf(\"(%d) got: %d, want: %d\", i, p, tt.DecimalSize.Precision)\n\t\t\t}\n\t\t\tif s != tt.DecimalSize.Scale {\n\t\t\t\tt.Errorf(\"(%d) got: %d, want: %d\", i, s, tt.DecimalSize.Scale)\n\t\t\t}\n\t\t\tif ok != tt.DecimalSize.OK {\n\t\t\t\tt.Errorf(\"(%d) got: %t, want: %t\", i, ok, tt.DecimalSize.OK)\n\t\t\t}\n\t\t\tif c.ScanType() != tt.ScanType {\n\t\t\t\tt.Errorf(\"(%d) got: %v, want: %v\", i, c.ScanType(), tt.ScanType)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestQueryLifeCycle(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server issues incorrect ParameterDescription (https://github.com/cockroachdb/cockroach/issues/60907)\")\n\n\t\trows, err := db.Query(\"SELECT 'foo', n FROM generate_series($1::int, $2::int) n WHERE 3 = $3\", 1, 10, 3)\n\t\trequire.NoError(t, err)\n\n\t\trowCount := int64(0)\n\n\t\tfor rows.Next() {\n\t\t\trowCount++\n\t\t\tvar (\n\t\t\t\ts string\n\t\t\t\tn int64\n\t\t\t)\n\n\t\t\terr := rows.Scan(&s, &n)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tif s != \"foo\" {\n\t\t\t\tt.Errorf(`Expected \"foo\", received \"%v\"`, s)\n\t\t\t}\n\n\t\t\tif n != rowCount {\n\t\t\t\tt.Errorf(\"Expected %d, received %d\", rowCount, n)\n\t\t\t}\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\n\t\terr = rows.Close()\n\t\trequire.NoError(t, err)\n\n\t\trows, err = db.Query(\"select 1 where false\")\n\t\trequire.NoError(t, err)\n\n\t\trowCount = int64(0)\n\n\t\tfor rows.Next() {\n\t\t\trowCount++\n\t\t}\n\t\trequire.NoError(t, rows.Err())\n\t\trequire.EqualValues(t, 0, rowCount)\n\n\t\terr = rows.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/409\nfunc TestScanJSONIntoJSONRawMessage(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tvar msg json.RawMessage\n\n\t\terr := db.QueryRow(\"select '{}'::json\").Scan(&msg)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, []byte(\"{}\"), []byte(msg))\n\t})\n}\n\ntype testLog struct {\n\tlvl  tracelog.LogLevel\n\tmsg  string\n\tdata map[string]any\n}\n\ntype testLogger struct {\n\tlogs []testLog\n}\n\nfunc (l *testLogger) Log(ctx context.Context, lvl tracelog.LogLevel, msg string, data map[string]any) {\n\tl.logs = append(l.logs, testLog{lvl: lvl, msg: msg, data: data})\n}\n\nfunc TestRegisterConnConfig(t *testing.T) {\n\tconnConfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tlogger := &testLogger{}\n\tconnConfig.Tracer = &tracelog.TraceLog{Logger: logger, LogLevel: tracelog.LogLevelInfo}\n\n\t// Issue 947: Register and unregister a ConnConfig and ensure that the\n\t// returned connection string is not reused.\n\tconnStr := stdlib.RegisterConnConfig(connConfig)\n\trequire.Equal(t, \"registeredConnConfig0\", connStr)\n\tstdlib.UnregisterConnConfig(connStr)\n\n\tconnStr = stdlib.RegisterConnConfig(connConfig)\n\tdefer stdlib.UnregisterConnConfig(connStr)\n\trequire.Equal(t, \"registeredConnConfig1\", connStr)\n\n\tdb, err := sql.Open(\"pgx\", connStr)\n\trequire.NoError(t, err)\n\tdefer closeDB(t, db)\n\n\tvar n int64\n\terr = db.QueryRow(\"select 1\").Scan(&n)\n\trequire.NoError(t, err)\n\n\tl := logger.logs[len(logger.logs)-1]\n\tassert.Equal(t, \"Query\", l.msg)\n\tassert.Equal(t, \"select 1\", l.data[\"sql\"])\n}\n\n// https://github.com/jackc/pgx/issues/958\nfunc TestConnQueryRowConstraintErrors(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipPostgreSQLVersionLessThan(t, db, 11)\n\t\tskipCockroachDB(t, db, \"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\n\t\t_, err := db.Exec(`create temporary table defer_test (\n\t\t\tid text primary key,\n\t\t\tn int not null, unique (n),\n\t\t\tunique (n) deferrable initially deferred )`)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = db.Exec(`drop function if exists test_trigger cascade`)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = db.Exec(`create function test_trigger() returns trigger language plpgsql as $$\n\t\tbegin\n\t\tif new.n = 4 then\n\t\t\traise exception 'n cant be 4!';\n\t\tend if;\n\t\treturn new;\n\tend$$`)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = db.Exec(`create constraint trigger test\n\t\t\tafter insert or update on defer_test\n\t\t\tdeferrable initially deferred\n\t\t\tfor each row\n\t\t\texecute function test_trigger()`)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = db.Exec(`insert into defer_test (id, n) values ('a', 1), ('b', 2), ('c', 3)`)\n\t\trequire.NoError(t, err)\n\n\t\tvar id string\n\t\terr = db.QueryRow(`insert into defer_test (id, n) values ('e', 4) returning id`).Scan(&id)\n\t\tassert.Error(t, err)\n\t})\n}\n\nfunc TestOptionBeforeAfterConnect(t *testing.T) {\n\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tvar beforeConnConfigs []*pgx.ConnConfig\n\tvar afterConns []*pgx.Conn\n\tdb := stdlib.OpenDB(*config,\n\t\tstdlib.OptionBeforeConnect(func(ctx context.Context, connConfig *pgx.ConnConfig) error {\n\t\t\tbeforeConnConfigs = append(beforeConnConfigs, connConfig)\n\t\t\treturn nil\n\t\t}),\n\t\tstdlib.OptionAfterConnect(func(ctx context.Context, conn *pgx.Conn) error {\n\t\t\tafterConns = append(afterConns, conn)\n\t\t\treturn nil\n\t\t}))\n\tdefer closeDB(t, db)\n\n\t// Force it to close and reopen a new connection after each query\n\tdb.SetMaxIdleConns(0)\n\n\t_, err = db.Exec(\"select 1\")\n\trequire.NoError(t, err)\n\n\t_, err = db.Exec(\"select 1\")\n\trequire.NoError(t, err)\n\n\trequire.Len(t, beforeConnConfigs, 2)\n\trequire.Len(t, afterConns, 2)\n\n\t// Note: BeforeConnect creates a shallow copy, so the config contents will be the same but we wean to ensure they\n\t// are different objects, so can't use require.NotEqual\n\trequire.False(t, config == beforeConnConfigs[0])\n\trequire.False(t, beforeConnConfigs[0] == beforeConnConfigs[1])\n}\n\nfunc TestRandomizeHostOrderFunc(t *testing.T) {\n\tconfig, err := pgx.ParseConfig(\"postgres://host1,host2,host3\")\n\trequire.NoError(t, err)\n\n\t// Test that at some point we connect to all 3 hosts\n\thostsNotSeenYet := map[string]struct{}{\n\t\t\"host1\": {},\n\t\t\"host2\": {},\n\t\t\"host3\": {},\n\t}\n\n\t// If we don't succeed within this many iterations, something is certainly wrong\n\tfor range 100_000 {\n\t\tconnCopy := *config\n\t\tstdlib.RandomizeHostOrderFunc(context.Background(), &connCopy)\n\n\t\tdelete(hostsNotSeenYet, connCopy.Host)\n\t\tif len(hostsNotSeenYet) == 0 {\n\t\t\treturn\n\t\t}\n\n\thostCheckLoop:\n\t\tfor _, h := range []string{\"host1\", \"host2\", \"host3\"} {\n\t\t\tif connCopy.Host == h {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, f := range connCopy.Fallbacks {\n\t\t\t\tif f.Host == h {\n\t\t\t\t\tcontinue hostCheckLoop\n\t\t\t\t}\n\t\t\t}\n\t\t\trequire.Failf(t, \"got configuration from RandomizeHostOrderFunc that did not have all the hosts\", \"%+v\", connCopy)\n\t\t}\n\t}\n\n\trequire.Fail(t, \"did not get all hosts as primaries after many randomizations\")\n}\n\nfunc TestResetSessionHookCalled(t *testing.T) {\n\tvar mockCalled bool\n\n\tconnConfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\n\tdb := stdlib.OpenDB(*connConfig, stdlib.OptionResetSession(func(ctx context.Context, conn *pgx.Conn) error {\n\t\tmockCalled = true\n\n\t\treturn nil\n\t}))\n\n\tdefer closeDB(t, db)\n\n\terr = db.Ping()\n\trequire.NoError(t, err)\n\n\terr = db.Ping()\n\trequire.NoError(t, err)\n\n\trequire.True(t, mockCalled)\n}\n\nfunc TestCheckIdleConn(t *testing.T) {\n\tcontrollerConn, err := sql.Open(\"pgx\", os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeDB(t, controllerConn)\n\n\tskipCockroachDB(t, controllerConn, \"Server does not support pg_terminate_backend() (https://github.com/cockroachdb/cockroach/issues/35897)\")\n\n\tdb, err := sql.Open(\"pgx\", os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tdefer closeDB(t, db)\n\n\tvar conns []*sql.Conn\n\tfor range 3 {\n\t\tc, err := db.Conn(context.Background())\n\t\trequire.NoError(t, err)\n\t\tconns = append(conns, c)\n\t}\n\n\trequire.EqualValues(t, 3, db.Stats().OpenConnections)\n\n\tvar pids []uint32\n\tfor _, c := range conns {\n\t\terr := c.Raw(func(driverConn any) error {\n\t\t\tpids = append(pids, driverConn.(*stdlib.Conn).Conn().PgConn().PID())\n\t\t\treturn nil\n\t\t})\n\t\trequire.NoError(t, err)\n\t\terr = c.Close()\n\t\trequire.NoError(t, err)\n\t}\n\n\t// The database/sql connection pool seems to automatically close idle connections to only keep 2 alive.\n\t// require.EqualValues(t, 3, db.Stats().OpenConnections)\n\n\t_, err = controllerConn.ExecContext(context.Background(), `select pg_terminate_backend(n) from unnest($1::int[]) n`, pids)\n\trequire.NoError(t, err)\n\n\t// All conns are dead they don't know it and neither does the pool. But because of database/sql automatically closing\n\t// idle connections we can't be sure how many we should have. require.EqualValues(t, 3, db.Stats().OpenConnections)\n\n\t// Wait long enough so the pool will realize it needs to check the connections.\n\ttime.Sleep(time.Second)\n\n\t// Pool should try all existing connections and find them dead, then create a new connection which should successfully ping.\n\terr = db.PingContext(context.Background())\n\trequire.NoError(t, err)\n\n\t// The original 3 conns should have been terminated and the a new conn established for the ping.\n\trequire.EqualValues(t, 1, db.Stats().OpenConnections)\n\tc, err := db.Conn(context.Background())\n\trequire.NoError(t, err)\n\n\tvar cPID uint32\n\terr = c.Raw(func(driverConn any) error {\n\t\tcPID = driverConn.(*stdlib.Conn).Conn().PgConn().PID()\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\terr = c.Close()\n\trequire.NoError(t, err)\n\n\trequire.NotContains(t, pids, cPID)\n}\n\nfunc TestOptionShouldPing_HookCalledOnReuse(t *testing.T) {\n\thookCalled := false\n\n\tdb := openDB(t,\n\t\tstdlib.OptionShouldPing(func(context.Context, stdlib.ShouldPingParams) bool {\n\t\t\thookCalled = true\n\t\t\t// Return false to avoid relying on actual ping behavior.\n\t\t\treturn false\n\t\t}),\n\t)\n\tdefer closeDB(t, db)\n\n\t// Ensure reuse (so ResetSession runs)\n\tdb.SetMaxOpenConns(1)\n\tdb.SetMaxIdleConns(1)\n\n\t// Establish the connection\n\trequire.NoError(t, db.Ping())\n\n\t// Reuse the connection -> should trigger ResetSession -> ShouldPing\n\t_, err := db.Exec(\"select 1\")\n\trequire.NoError(t, err)\n\n\trequire.True(t, hookCalled, \"hook should be called on reuse\")\n}\n\n// https://github.com/jackc/pgx/pull/2481\nfunc TestOpenTransactionsDiscarded(t *testing.T) {\n\tdb := openDB(t)\n\tdefer closeDB(t, db)\n\n\tskipCockroachDB(t, db, \"CockroachDB auto commits DDL by default\")\n\n\tdb.SetMaxOpenConns(1)\n\tctx := context.Background()\n\n\tfor range 3 {\n\t\tfunc() {\n\t\t\tconn, err := db.Conn(ctx)\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer conn.Close()\n\n\t\t\t_, err = conn.ExecContext(ctx, \"begin;\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\t// If the open transaction is not discarded, the second time this is run it will fail.\n\t\t\t_, err = conn.ExecContext(ctx, \"create temporary table in_tx_discard_test(id int);\")\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\t}\n\n\tensureDBValid(t, db)\n}\n\nfunc TestRowsColumnTypeLength(t *testing.T) {\n\ttestWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {\n\t\tskipCockroachDB(t, db, \"Server does not support type\")\n\n\t\tcolumnTypeLengthTests := []struct {\n\t\t\tLen int64\n\t\t\tOK  bool\n\t\t}{\n\t\t\t{\n\t\t\t\tmath.MaxInt64,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\tmath.MaxInt64,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\t255,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\t10,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\t50,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\t0,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t}\n\n\t\t_, err := db.Exec(`CREATE TEMPORARY TABLE temp_column_type_length (\n\t\t\t\t\t\t   text_column TEXT,\n\t\t\t\t\t\t   bytea_column BYTEA,\n\t\t\t\t\t\t   varchar_column VARCHAR(255),\n\t\t\t\t\t\t   bpcharA_column BPCHAR(10)[],\n\t\t\t\t\t\t   varbit_column VARBIT(50),\n\t\t\t\t\t\t   int_column INT\n\t\t\t\t\t);`)\n\t\trequire.NoError(t, err)\n\n\t\trows, err := db.Query(\"SELECT * FROM temp_column_type_length\")\n\t\trequire.NoError(t, err)\n\n\t\tcolumns, err := rows.ColumnTypes()\n\t\trequire.NoError(t, err)\n\t\tassert.Len(t, columns, 6)\n\n\t\tfor i, tt := range columnTypeLengthTests {\n\t\t\tc := columns[i]\n\n\t\t\tl, ok := c.Length()\n\t\t\tif l != tt.Len {\n\t\t\t\tt.Errorf(\"(%d) got: %d, want: %d\", i, l, tt.Len)\n\t\t\t}\n\t\t\tif ok != tt.OK {\n\t\t\t\tt.Errorf(\"(%d) got: %t, want: %t\", i, ok, tt.OK)\n\t\t\t}\n\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "test.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\n# test.sh - Run pgx tests against specific database targets\n#\n# Usage:\n#   ./test.sh [target] [go test flags...]\n#\n# Targets:\n#   pg14    - PostgreSQL 14 (port 5414)\n#   pg15    - PostgreSQL 15 (port 5415)\n#   pg16    - PostgreSQL 16 (port 5416)\n#   pg17    - PostgreSQL 17 (port 5417)\n#   pg18    - PostgreSQL 18 (port 5432) [default]\n#   crdb    - CockroachDB (port 26257)\n#   all     - Run against all targets sequentially\n#\n# Examples:\n#   ./test.sh                          # Test against PG18\n#   ./test.sh pg14                     # Test against PG14\n#   ./test.sh crdb                     # Test against CockroachDB\n#   ./test.sh all                      # Test against all targets\n#   ./test.sh pg16 -run TestConnect    # Test specific test against PG16\n#   ./test.sh pg18 -count=1 -v         # Verbose, no cache, PG18\n\n# Color output (disabled if not a terminal)\nif [ -t 1 ]; then\n    GREEN='\\033[0;32m'\n    RED='\\033[0;31m'\n    BLUE='\\033[0;34m'\n    NC='\\033[0m'\nelse\n    GREEN=''\n    RED=''\n    BLUE=''\n    NC=''\nfi\n\nlog_info()  { echo -e \"${BLUE}==> $*${NC}\"; }\nlog_ok()    { echo -e \"${GREEN}==> $*${NC}\"; }\nlog_err()   { echo -e \"${RED}==> $*${NC}\" >&2; }\n\n# Wait for a database to accept connections\nwait_for_ready() {\n    local connstr=\"$1\"\n    local label=\"$2\"\n    local max_attempts=30\n    local attempt=0\n\n    log_info \"Waiting for $label to be ready...\"\n    while ! psql \"$connstr\" -c \"SELECT 1\" > /dev/null 2>&1; do\n        attempt=$((attempt + 1))\n        if [ \"$attempt\" -ge \"$max_attempts\" ]; then\n            log_err \"$label did not become ready after $max_attempts attempts\"\n            return 1\n        fi\n        sleep 1\n    done\n    log_ok \"$label is ready\"\n}\n\n# Directory containing this script (used to locate testsetup/)\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nCERTS_DIR=\"$SCRIPT_DIR/testsetup/certs\"\n\n# Copy client certificates to /tmp for TLS tests\nsetup_client_certs() {\n    if [ -d \"$CERTS_DIR\" ]; then\n        base64 -d \"$CERTS_DIR/ca.pem.b64\" > /tmp/ca.pem\n        base64 -d \"$CERTS_DIR/pgx_sslcert.crt.b64\" > /tmp/pgx_sslcert.crt\n        base64 -d \"$CERTS_DIR/pgx_sslcert.key.b64\" > /tmp/pgx_sslcert.key\n    fi\n}\n\n# Initialize CockroachDB (create database if not exists)\ninit_crdb() {\n    local connstr=\"postgresql://root@localhost:26257/?sslmode=disable\"\n    wait_for_ready \"$connstr\" \"CockroachDB\"\n    log_info \"Ensuring pgx_test database exists on CockroachDB...\"\n    psql \"$connstr\" -c \"CREATE DATABASE IF NOT EXISTS pgx_test\" 2>/dev/null || true\n}\n\n# Run tests against a single target\nrun_tests() {\n    local target=\"$1\"\n    shift\n    local extra_args=(\"$@\")\n\n    local label=\"\"\n    local port=\"\"\n\n    case \"$target\" in\n        pg14) label=\"PostgreSQL 14\"; port=5414 ;;\n        pg15) label=\"PostgreSQL 15\"; port=5415 ;;\n        pg16) label=\"PostgreSQL 16\"; port=5416 ;;\n        pg17) label=\"PostgreSQL 17\"; port=5417 ;;\n        pg18) label=\"PostgreSQL 18\"; port=5432 ;;\n        crdb)\n            label=\"CockroachDB (port 26257)\"\n            init_crdb\n            log_info \"Testing against $label\"\n            if ! PGX_TEST_DATABASE=\"postgresql://root@localhost:26257/pgx_test?sslmode=disable&experimental_enable_temp_tables=on\" \\\n                go test -count=1 \"${extra_args[@]}\" ./...; then\n                log_err \"Tests FAILED against $label\"\n                return 1\n            fi\n            log_ok \"Tests passed against $label\"\n            return 0\n            ;;\n        *)\n            log_err \"Unknown target: $target\"\n            log_err \"Valid targets: pg14, pg15, pg16, pg17, pg18, crdb, all\"\n            return 1\n            ;;\n    esac\n\n    setup_client_certs\n\n    log_info \"Testing against $label (port $port)\"\n    if ! PGX_TEST_DATABASE=\"host=localhost port=$port user=postgres password=postgres dbname=pgx_test\" \\\n         PGX_TEST_UNIX_SOCKET_CONN_STRING=\"host=/var/run/postgresql port=$port user=postgres dbname=pgx_test\" \\\n         PGX_TEST_TCP_CONN_STRING=\"host=127.0.0.1 port=$port user=pgx_md5 password=secret dbname=pgx_test\" \\\n         PGX_TEST_MD5_PASSWORD_CONN_STRING=\"host=127.0.0.1 port=$port user=pgx_md5 password=secret dbname=pgx_test\" \\\n         PGX_TEST_SCRAM_PASSWORD_CONN_STRING=\"host=127.0.0.1 port=$port user=pgx_scram password=secret dbname=pgx_test channel_binding=disable\" \\\n         PGX_TEST_SCRAM_PLUS_CONN_STRING=\"host=localhost port=$port user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require\" \\\n         PGX_TEST_PLAIN_PASSWORD_CONN_STRING=\"host=127.0.0.1 port=$port user=pgx_pw password=secret dbname=pgx_test\" \\\n         PGX_TEST_TLS_CONN_STRING=\"host=localhost port=$port user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable\" \\\n         PGX_TEST_TLS_CLIENT_CONN_STRING=\"host=localhost port=$port user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test\" \\\n         PGX_SSL_PASSWORD=certpw \\\n         go test -count=1 \"${extra_args[@]}\" ./...; then\n        log_err \"Tests FAILED against $label\"\n        return 1\n    fi\n    log_ok \"Tests passed against $label\"\n}\n\n# Main\nmain() {\n    local target=\"${1:-pg18}\"\n\n    if [ \"$target\" = \"all\" ]; then\n        shift || true\n        local targets=(pg14 pg15 pg16 pg17 pg18 crdb)\n        local failed=()\n\n        for t in \"${targets[@]}\"; do\n            echo \"\"\n            log_info \"==========================================\"\n            log_info \"Target: $t\"\n            log_info \"==========================================\"\n            if ! run_tests \"$t\" \"$@\"; then\n                failed+=(\"$t\")\n                log_err \"FAILED: $t\"\n            fi\n        done\n\n        echo \"\"\n        if [ ${#failed[@]} -gt 0 ]; then\n            log_err \"Failed targets: ${failed[*]}\"\n            return 1\n        else\n            log_ok \"All targets passed\"\n        fi\n    else\n        shift || true\n        run_tests \"$target\" \"$@\"\n    fi\n}\n\nmain \"$@\"\n"
  },
  {
    "path": "testsetup/README.md",
    "content": "# Test Setup\n\nThis directory contains miscellaneous files used to setup a test database.\n"
  },
  {
    "path": "testsetup/certs/ca.key.b64",
    "content": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBdlFIZUUzb0hn\nSzU4QUZwTm5Kd0dRR2pSSnNTMXlNYzJQUk5tV0k3OFgvRkZSc25rCmxIQ0FrR2VieElNdDJoc3Fq\nT0F6VnNJdW43eHJUZnlwREdpZ2hzUi9HVjF2U2NsL0pBcmY3YWo1Z2w0bXYzN0EKazhSaDVMTHpU\ndFd5TjZodkVmKzBpTUZRdlY0UnJPOE9MNUVqTytsN0ZwTndVQVJlcGtUS2hnNDVZbXJuelc2bgpP\nUzdyaU04RG1WMVhrWEc2QzNtNDZvVzcwaStWZkxEWWt3VFNHdkZHNDl5U25iOVZzTkZqTDZsS3Ri\nTlNKUzY4CjJ4alNIZzhLWmhnSnZ1NXhkdnMyS2xYVFRxTjViM1Rwa0RpYzVlbUt1cGdDRnZoQnRI\nYjc0akEzK29KcW1oZmMKYjJDZWFxRHdFVjU4aTJjRjNSb2l5cDdkWC9tT2d0SWdWd3FmdWpWa0hG\nOXY4dW1EaVlKdHlMKy9Jd2ZWaHFpYQpGWklqU3Z2aUZxZUJuSHR1WjNNZGNNK0d4T2V2cnIxaTFm\nVVd2QVRKQSswVk5qb2tMQTlNbGU2WldRZ3VYL2ViClNvK21CZGt6T1lpNktmaCtaWHhRNUxudk1D\naFNWYXRHTDU5NkZxVHJGZHUvVy9oM2VJdWErZzVSTWttK1VubTEKeWRMdTJTQzFzOFFaNGNtdjRn\nVjdFbXpvcHZjVVJTQlpVWHdGWHc2WHkwL0NUeDY3UmI1S0RncEM3SDNkS24ycgpWTWV0V051YXlQ\nRnVjYWRsK3JRVS9GbElFSUZlcno1dGVPTDRYN1h5Lzl1ZEZBelc5YzBGN2NhUmNRWWlydldUCmhi\nRG5laXlqclRONHp0K2tYRDF4MHpQYkRjRUtraUo2a01Ld3BWLy9IdEhTb2M1Y1dwaGU0OE53TFow\nQ0F3RUEKQVFLQ0FnQUNFanJxOVJRMENsVjBtL1p3c2wrNTRPVlEvMVkxRVJhb0NTOUg1anM3SWxw\nbXZvSnR4OXV3UGY1cgpZeCtlNXR1cXd3czArMHFQU3VzZCtETzduWXJuWGdjODlsQXhwS0QwT28x\nSGNHbkhSVVpjTnNreVFNYWp0Yk1ICmhCakZETjlMQWlvOGFQeDhSUXlGNVJaUnJIdFFpcm5hd3lp\nQXZrTHhzZ0R1M2hLdCsrSkl4TFg2Vi9xYlNTaWMKZVZCc0dTUDhHN1ZWaEU1TTdRZkhscUlhcDhU\nVk9vbHVoQVdySmwrblJaRklsS3k5cWphVDZoYzlGR21vd3dZMworRVAzWEd0K0cxVGFUVVZtYTNZ\nZUNIUllFUUI0QTg4eUlPb1pPOWNtZkxyaFFlcGZJRG1ydzBNL2lSREVmeWNSCkwvSVA4eWgyUVpD\nSGlmaCt0c2wrNTIrQ2dUOGU1eGxCTGVneTFyYXU2c3BPOFEvMVNReUx2T0M5OTNnNFlrTUIKd2lB\nM0hKWlB1R1NURHVWd1cwK0NzN096ei8zREtxMlZCUGttRkE0b1ZTTGhkUDhWa000UndkRWI5RzBw\nbllVdQpiQ0tVTU1TYmtnT3BCOHVVcjhpZE9nbktDUEtMM1pxR1NRQVlkaG1YeXo4WUNGSThLVk01\nMHFLTzNGSUplQnRGCmJZTUI0cTVldk1tdE9ORGdnejhiWlFrdU1XbTcvazR6OHJnbmxqYURtUGtU\nTXcwUkFnU0VWN3FQWFlacFZ0VGQKREdjUWtxRTZISUsvZ2xJS3c5UkQ5Uk1VS0ZSSnVhVDJOWG9N\nbUtCTGxCV1BSNVE1NXVxcTR6b05aQ2pwV0RtUAp2dm1FYlpPMmZMU0tubjNBNy93MVo1NEpzM0tw\nN3VqZHBoTlh5ZTd3Rmp1MStYNDZPUUtDQVFFQTgzaGFKUGRzCkx4aFNub1ZDVmo2SDBQVGhnS1J6\nNkIydlExOFZCSXRMTytjVERuelBSVXJaWEQwK2ZKUUVNSmg4OFk4TndJYmkKbWttRFhESDBLaEZI\naktycm95bEJKQWpQdVBEWWVqQTY5eE82NzIvV0dQdHhUYzA2Uno0Ymxmd1hYbHJ5WE9RcgpiemRD\neEU3N0JSemw3VTFTQU9wTmVsLzRxbksrRnQzWjJQOXk4bXNiVnlTdjNDcnVFUFpkZkZUY1pXUU95\nd3dxCjJLWi9Mc1RpRmJUQlA2NWE4a3pDSTY3TnFJSER0QUxXTm4rcURSSnBhV0NhMVFhem1sZ0JW\ndmVPblljSUZ6OGsKNmVhdUJjUElPWUhWMmUwYXFDajJ6TjNCUGM4dWpRZFdQNmppNHhmZVk4YlZI\nWjBBc1lmeGE5Y0FQM2dHRFU1WgpnYi9aakpKUXFweDlTUUtDQVFFQXhydjd3NXcvK2kyNjF2N1Y1\nYk1tZFpYR3B1M2JpZGxCa2FWYjg0Rm5ZWGh0ClZmOHNYWFltSGxSTTl1cFArVGpmTWxQbVhreFds\nT0tuMU1CZE1ZZHlwTk8vOEhzN3UxZWk3dCtTWUlMRHlldGgKSStqTFRMTU9FdGo0L1QwbXpvaDlm\nQVVWWk5PUW02cFlTT2k1cSs3OGV3SjF5SDRwV1BBMnRpOTRONm8yaEpxeApqR1ByamNFbU05dm83\nWElGWGtRRDVwVXpZdTBONXYrWWhNUFZnZlllYm0rT3VMeml5YWVYVWxmQzlXYXRMb3JzCjBzQnJL\ndTQyOGNkQzB1NnlKenY3WmR2dlhINnp3eFBhYWhXdEh2Qmx2Z2JHWWxkaHlYYU9DN2xNdnZoc3lq\nK3AKYlcvbklPTXJqaWRzWi80NzNnVnF4T25YRDNPK0ptTythUXZ2NjJ2UnRRS0NBUUVBcXE2Y3cz\nMlJJaEx6R0o1agpPd0xyUTlub25MNnRZdlFzZ2hhT0xYbFpiTTA1MXRZNG1HTTRyaWxoUFFTblMw\nb2JqVnkwVDF4QVQ5bHBkYUN2Cmd5NUo4cHVBUmdvTjJLMDBzbnBseUlNM1V5OUp4Y0dwWGlvbjdG\nc0xtQlk2VUpOQjdxdmpiS3VvcDNjZUgzRWYKR1I2Qk1qTWFZZm5nQndKdzJVMUhaZEJ4Z0diVXRv\nM1B4Tys3T2FYKzczVkhaSG81Wi9zVlE5TnIxaVlpMHk3RApNR1JmZGIxTmxMK3Era2o1ZEVCRWFs\nWHl4cElKTE5EVGhVUnNSeHREbmRoRVdDdUVsM3FxZVdHVEIwYm9pTElRCkZBQ3BZdldOMG9XbFAw\nbld4dllQdEY2SVlHaU5acGE5R1VXeThlZTNMTDRZeVhrWWUyWXhGL0x0WE1DYVNxbm8KVXpNUndR\nS0NBUUJ0RnkyNWorRFNyUjJkL1I5ZlNyczIyclBFV0ZjbDBWdWIzcUNlQjQrL1FvTUtMTnVtcGor\nQQpjSmdiQmo1R09VRFQrSmhCZFRnbDNGRnlpUEhmU1ZIV1J1SmEyTnJPR09vNHBBTk85UmtTa3lH\nc3U1STFsU2lKCmtnOER1L2k4Y05JcExRVTBacW8vdVA5amJsVFZtNGQrNHJmVENlWWFpeW9OVWZ0\nMDJ2dTRTTTVqbVVucGMzaE0KQ1ZCUVgwR3hJRGJmOFZEVzRmZTZzTjJCSzVyZEQwUmZMTiswYXdN\nMjNNZEZEU1hZM2JiOUVJZTFuS2gzVlhKVgp3Q0xmZE9YWWdhV2JvV0pBYk5DeXFnaEFLWXZ4cXRh\nK3ZDdFVPbEZVYkw3R2tyNGRLbk5WaGlLMER6Ulpxam56CmU1S1hkMGhYdExPS1NpNmlyYzlZZFhK\nRmFhY1duUVh0QW9JQkFRQ1k1QnhzaFBrNUJldnpxbE9JQmhqdGxnRDQKS0xFc3EwTzNSMGx2eGh1\nVmVsOGNWS0cxZzA2d0padnZPNWlwR1hTaVc4ZVJ5ODhBTDhQSnA3TldtRWkwQjhrYwpoOUdUNkRH\nUEo5Ykh6ak1WOHNRd2UvOTNUV1pPQzRiVm5Mcis3dDN4U1dmb2o4aCtva2FDblZ0S3BFTyt2YUl4\nCkNRaHJ4TXFoWWF4b25sV1BDcVRRK3hoejc3RHFqUmI3dzkzWUhjVFF3a3hQZXNHbzhEQnBUOHZm\nKzdVUVZKWmgKMG5rUGQ3SVE5cDFrcmJyczlDZ0k2bjlhOTB1V0lDcm9BcFYzbEp1cG90SU5QcUlH\nNVJUOU5zbmJXNlRESjdpSQpYaU5DUHlYZVZYTk5HQ2JlT1hDazkxV1lEamtiMk5wOWhtdVdlV2JF\nQkdsbk5rY1ZMTFVJS2dkN2owNVYKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\n"
  },
  {
    "path": "testsetup/certs/ca.pem.b64",
    "content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZDRENDQXZDZ0F3SUJBZ0lCQVRBTkJna3Fo\na2lHOXcwQkFRc0ZBREFXTVJRd0VnWURWUVFERXd0d1ozZ3QKY205dmRDMWpZVEFlRncweU5qQXlN\namd4TlRJMk1qUmFGdzAwTmpBeU1qZ3hOVEkyTWpSYU1CWXhGREFTQmdOVgpCQU1UQzNCbmVDMXli\nMjkwTFdOaE1JSUNJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBZzhBTUlJQ0NnS0NBZ0VBCnZRSGVF\nM29IZ0s1OEFGcE5uSndHUUdqUkpzUzF5TWMyUFJObVdJNzhYL0ZGUnNua2xIQ0FrR2VieElNdDJo\nc3EKak9BelZzSXVuN3hyVGZ5cERHaWdoc1IvR1YxdlNjbC9KQXJmN2FqNWdsNG12MzdBazhSaDVM\nTHpUdFd5TjZodgpFZiswaU1GUXZWNFJyTzhPTDVFak8rbDdGcE53VUFSZXBrVEtoZzQ1WW1ybnpX\nNm5PUzdyaU04RG1WMVhrWEc2CkMzbTQ2b1c3MGkrVmZMRFlrd1RTR3ZGRzQ5eVNuYjlWc05Gakw2\nbEt0Yk5TSlM2ODJ4alNIZzhLWmhnSnZ1NXgKZHZzMktsWFRUcU41YjNUcGtEaWM1ZW1LdXBnQ0Z2\naEJ0SGI3NGpBMytvSnFtaGZjYjJDZWFxRHdFVjU4aTJjRgozUm9peXA3ZFgvbU9ndElnVndxZnVq\nVmtIRjl2OHVtRGlZSnR5TCsvSXdmVmhxaWFGWklqU3Z2aUZxZUJuSHR1ClozTWRjTStHeE9ldnJy\nMWkxZlVXdkFUSkErMFZOam9rTEE5TWxlNlpXUWd1WC9lYlNvK21CZGt6T1lpNktmaCsKWlh4UTVM\nbnZNQ2hTVmF0R0w1OTZGcVRyRmR1L1cvaDNlSXVhK2c1Uk1rbStVbm0xeWRMdTJTQzFzOFFaNGNt\ndgo0Z1Y3RW16b3B2Y1VSU0JaVVh3Rlh3Nlh5MC9DVHg2N1JiNUtEZ3BDN0gzZEtuMnJWTWV0V051\nYXlQRnVjYWRsCityUVUvRmxJRUlGZXJ6NXRlT0w0WDdYeS85dWRGQXpXOWMwRjdjYVJjUVlpcnZX\nVGhiRG5laXlqclRONHp0K2sKWEQxeDB6UGJEY0VLa2lKNmtNS3dwVi8vSHRIU29jNWNXcGhlNDhO\nd0xaMENBd0VBQWFOaE1GOHdEZ1lEVlIwUApBUUgvQkFRREFnS0VNQjBHQTFVZEpRUVdNQlFHQ0Nz\nR0FRVUZCd01DQmdnckJnRUZCUWNEQVRBUEJnTlZIUk1CCkFmOEVCVEFEQVFIL01CMEdBMVVkRGdR\nV0JCU2J0S0drYVpUM2ZuRjZtQmNFcmkwcko1SjVaakFOQmdrcWhraUcKOXcwQkFRc0ZBQU9DQWdF\nQVJCNXk4bFVkdEo2NzRnRVdQdFR4YXpvVFFWQWVPT2xIVkNYTWdUWE9Ca21qUnFodQpsdno2aksv\nTTdXUTh1TU5DTm5DUnRuYno0cGpZcFRBeUZNdkVPOGUzZ085dmhoUnNsRFFOU202UlYvOFFnbGRt\nCk1PeE5mU09veS9Id2Q4SXpPTzIvWXN5dTdHaFVXQjlLQUE5bXd1MWQ1WnBVelM2ZEFlS3c0WGxs\nallmMlgxTlkKbTluK0ZPUTNvQnY3UkRTYTY1dGp3T2ZxdVdJajlkTG5IM3JpWENPRENCNWpVb25I\nL29uelN6UjNvUkdERHVFaApHUm1vVFNONUgwSlJDc2VmK2svR3krOVViSWRrZUkybkw5ekJCdEEr\nYkQrKzA2WXdkUE43NExRS0VXV09wbDVCCmlYaFVCcG90anJ5UU92cXFoSFJGSVZRa1YwTGtJMHlp\ndFpHNDVOd2J0V3RiTWp2Y04vTXpWVWo4bWdlNnFWUlcKdmcyazlVY1BUbmtuVVhZR1MyWVFvNXc0\nTVIweWhlbzVqMTFQQktwNGJlZW0rMHV6YitlS0xBN3JNVWNnQmJ5OAorRGpLQ0lub0V1TDRYdEJa\nTnFiYzgrd1N4ZkE1anExVUlqYXhJTzhzQllCdjJSSXNGWDBFMUlnN3E5R2Qyc2VDCkRFVnVKWFJJ\ncVpQM1QxdU1FdFlCZDRCdUo4dDkxOS9LYlBpbkZRQnFUa0RhMUxxSzlQaTl0b0xDTVRFekZpTnIK\nWmxuTWV4eDZoQ0tOUVhiZHVFdXUvRERyekFoVzk1MEtzSmxXMHFQWjBkTnFpSGpJakE0SzZ0QTVH\nMFRyWEM1bwpPRUlwcVdwU2h6VjVLK1plNWJDdWl4QlVmOEZTOTRYMllsTnJKbE82ZEowdG5YUUFS\nNHZibGp2cHlsVT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\n"
  },
  {
    "path": "testsetup/certs/localhost.crt.b64",
    "content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVCRENDQWV5Z0F3SUJBZ0lCQWpBTkJna3Fo\na2lHOXcwQkFRc0ZBREFXTVJRd0VnWURWUVFERXd0d1ozZ3QKY205dmRDMWpZVEFlRncweU5qQXlN\namd4TlRJMk1qUmFGdzAwTmpBeU1qZ3hOVEkyTWpSYU1CUXhFakFRQmdOVgpCQU1UQ1d4dlkyRnNh\nRzl6ZERDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTkIyCkxXdjdr\nYnViTmJhVkpNWlVybHo0Nisvekhrb1ZWcmFZTFVBbzhmY2NBcE90YWtCSzVBOXdyYmdhYlRZcjVI\nYmwKSlZqZEN1V2psZ1p3TUlheTRvZnk1OWNJejI0VmRReGVBQ1RkUzhlZU9oQ2M5Ti8vRVZTcUk1\nWlpiQ29hY05aUgoyaUJPTisvek1WejRzeCtGRW45UXo1UkpNSGFMbnBQajAyZlgvcGRHdnJrbUJT\nRkZOMXoyS3EvNDdTMCs1SFpJCnZ0UXhRYW1zaG40YmZxM1BEbm9abEhVazFLVXA5Z0xaMFdGaHlU\nMlB3TFVKZSt5d2JhRjhOcDczN0VFcnB5UG0KaWFWRXFqb2ZKVms2ektkUlkyZENGRW9hRThVaXhO\neGJ4TG1Wa1ZhZDJnbTllck15S0ZZTzZ2akxPYzJVVys3dgpHK3JVVFh1MlVBUkc4SFo0V2M4Q0F3\nRUFBYU5mTUYwd0RnWURWUjBQQVFIL0JBUURBZ2VBTUIwR0ExVWRKUVFXCk1CUUdDQ3NHQVFVRkJ3\nTUNCZ2dyQmdFRkJRY0RBVEFzQmdOVkhSRUVKVEFqZ2dsc2IyTmhiR2h2YzNTSEJIOEEKQUFHSEVB\nQUFBQUFBQUFBQUFBQUFBQUFBQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFGaEgwNGJiWVp2\nQQpKQ1IxZms5aE9XeHFQSkRaWkRvRkpUNmRmckVGbGRHcXNjNUErMlJURGd5L3I5ZFlJdCt4Ujk2\nSlM2Z2dic2V0ClQ4SWcvTm1ZOS8rV2JkWGhGUkpoQVJHWUx6Y1RBa01yVU1CQlhaR2p0bE5jbmFr\nbVhyek5rakJqWVl2dHd3VXoKVXo2M09RZnhrdmxJWFdNQkR3dUJndzlCcnp1VlZyT2VYd1BNeDRu\nQXR4blRCWjMyV3BIdzVKOGJXUUlpNEZSRgpJWE9OUXl3ajNRY3BxeVdpcCtnUm82cXFHNVk5dVRk\nci9GQzFJN0dCYnhtK2lOMEdPaTRUbENDeGxkNy80NlpsClBVcWEzY1hrbnNWeU9LVDg0NlN2Y1Rh\nYWI3dmM1dWF0YTZsQkMzU3gzUzVLbGJCNWt3NmFkd3JWek00N3lOaCsKeUJ3eTR0WThlLys1MUgv\nb1R3MlY3WmRhb3MremRHcVc2dlFXVm4wZjlPbkpMdkQvZ3BVbmp1eFQ1Znh2UThkSApHU0Iya3M0\ncllmcGZVdHZKNFNueVBsS3lQRFNYNnBXMUN5K2dIaUl2MEtQQ3JBdU85YXo4Qnpia0FIU29HWUNv\nCjJTaXhJQ1R1M09mY2lyRk5XTzNTTS9oeUJ6QXBGdXkxQmovbk85ZU1sRk9ReXpFZjVFNDhHRUFa\ncTNEQXFVVWwKRFNoMVR5eDBmd2VyQW5CNjJkbVUvc2tNZlhDRitUbEVTT3V2cnNUSUNxTnJ1MUx4\nRjRVUlN3aTJHeXp3RmtJSwpNaVd6WWcxMThCSVlySWJWSitTU2QveS9zN2xIdlZPeEdxMFBwZHcz\nSWRSWEhQRFVwNUo1eHA2V3lWTWlMK1hICjJpY2w5Z3lpOGZOd1pRbFJab0dINDd4cWVabzR4VkNj\nCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\n"
  },
  {
    "path": "testsetup/certs/localhost.key.b64",
    "content": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBMEhZdGEvdVJ1\nNXMxdHBVa3hsU3VYUGpyNy9NZVNoVld0cGd0UUNqeDl4d0NrNjFxClFFcmtEM0N0dUJwdE5pdmtk\ndVVsV04wSzVhT1dCbkF3aHJMaWgvTG4xd2pQYmhWMURGNEFKTjFMeDU0NkVKejAKMy84UlZLb2ps\nbGxzS2hwdzFsSGFJRTQzNy9NeFhQaXpINFVTZjFEUGxFa3dkb3VlaytQVFo5ZitsMGErdVNZRgpJ\nVVUzWFBZcXIvanRMVDdrZGtpKzFERkJxYXlHZmh0K3JjOE9laG1VZFNUVXBTbjJBdG5SWVdISlBZ\nL0F0UWw3CjdMQnRvWHcybnZmc1FTdW5JK2FKcFVTcU9oOGxXVHJNcDFGalowSVVTaG9UeFNMRTNG\ndkV1WldSVnAzYUNiMTYKc3pJb1ZnN3ErTXM1elpSYjd1OGI2dFJOZTdaUUJFYndkbmhaendJREFR\nQUJBb0lCQUVGWCtlN3lRWUU4S3I4Ygp5eFk4THhmSGt6VjQwTkxLRTQ5UzdrUkJVVzl6SVQrblNU\nbnovNzd6UkJFRWY1MU9idVB4K2gzNzA5R2QwSVllCkhxZnFaT1hXZDJQSlNEUXFsN0VUdWhuVUlF\nL25RaTQ0OU8xTjUrSFJIeXlleFRBQmI4Tk9qd0EzWklOYmlPcXEKcm9NLzFFZUNUajhlSGduYXhB\nOTVoNmZ5WmNTSEFKbnBtaWxQTjNIZ3A3ajd4WmVWTTR2TzlPV0pYWDdYVlB3Sgp1ajlyWVQ3bmpP\nY1RXSUtmVU1rNithdGwvQm1ZbmFBa05nWlJ0MmJUa0ZMbk1SNSs0QzB4UVJOaHZ0MkVaSkpqCnVU\nYk8vbTRaeElRSDd6Y2dBTzRaaHBmSk85TzBKUFk2UDY2bUJLbGVLU1RRUENVM2pDWklYUVEzdldz\nN29lM1MKc0tXZENtRUNnWUVBNEtveVBjTjlmMEt1dGdINWVYcWZpRUdWMXM2SW5OR2w0M1Q3T3o4\ncnJ0NWxlSHNnbmRIUApqOVRZdXFpcit5dTJCZ2gyY040SWhKdXZwUWlvREZIelU2Uy9vODZhZ2Vq\nMFpzSnVWU2trUnlOWnJXeHFFbFJyCmgwTHFYWXkvWXhPUzZrcDNpTm9xcFRNcXhRU1lYbFJtZml4\nejAxTXdXVkRoZlhPSVlGSkZHVEVDZ1lFQTdZbHYKZU92V280b3IwaDhySmRobGVXRGs1TTBFYVVa\nR3hFcU5xU3JaOG9DMjBOekVEZUNBbWYwT3pENzJ5VTFZbGhDVQp6NGlpUi9Mb1d6azZ1aDRBVFhi\naWJobFRDTzl3ZXBhbFBnem8xNm1oUTFDNlh1Tzc5b0VaZzhWUjJ1Z1YyMnJrCmhVSVNqS2IyVDUw\nVERqKy9SdUEvQUxUS2s3YmYrVVFDbkVBWDR2OENnWUJ1dURIdWNtdGt6azRmOExVc0F4eWcKSUFK\naVRJTFRGaGIxRSsvRWNRdHNNTG85T0NFSTVoK3V1S3pld01XUkVTZXhyWFlCaGNzWTMwa2V3amRm\nYXZ2SwpHd1UvQzFEbCtSS0ptZ1NaLzU2YldIYVhtemhqTkN1U0M0NUk4RlpaMDRteDRXVXJrcWsx\ncFJQTTZNaldwQzJtCnZPMlFGZzFiMXhwcG93aHd2OXJCNFFLQmdBUXlkM3ZGZndkb3U1bDgzNEJO\nc0kxU2Z2amovbjMxdGU1bCt0YlEKWVFWOTdMMElDWmJOTHJpSjFpY1p2YmFVM1VuWmhTZWRuSEIx\nQkZJSVV0ZFRER0pTQlhJNGxGVUdGaEMwZExzQgo4Mk5NSmdOUWU5cU5YNWJkZFJ2MDhwdFJtb0ZF\nQkVHdWh6RmhDZDBpUTJIdkVtd29MalRPL1AxYkw5M3hxN0lqCkY4ajFBb0dBTWk1a05yOEluSmZ4\nSVp4SklaNFlJekplakNKdGh6K0NMUzVQOVMyN2w1L1ZPUEl5WjhnNWNtQmIKeEtLbXREb2VBTXBj\nQVlUVDVJU0l5T3NyYUo3aE9NdWFLYVRqbGRpb25sQVpXVllOaUtHbnhNdzlza1k3U0tOUwphbTRI\nQS9aUi9uUGtwTjlZaFArclhpd29EaVUxU0xacGtaRWYvSlBuSlVEZU9xeENUT0E9Ci0tLS0tRU5E\nIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==\n"
  },
  {
    "path": "testsetup/certs/pgx_sslcert.crt.b64",
    "content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR6akNDQWJhZ0F3SUJBZ0lCQXpBTkJna3Fo\na2lHOXcwQkFRc0ZBREFXTVJRd0VnWURWUVFERXd0d1ozZ3QKY205dmRDMWpZVEFlRncweU5qQXlN\namd4TlRJMk1qUmFGdzAwTmpBeU1qZ3hOVEkyTWpSYU1CWXhGREFTQmdOVgpCQU1NQzNCbmVGOXpj\nMnhqWlhKME1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCm9tMW1R\nS3owbzlTR0c3VWhlUjBtb243bEdlbVJPOS92VXdIeHlZdjIvT1IxcGlEbi8yYjdVRmdOQUNsVTE3\nNTkKbFl3cWdwMVdDT2xYV05oWXJyTTJVVUE1Z0cwWlpOTHlnc2ZFNFMvWHdjbzVkZVNMT3BUalJi\nTms5dEx3MUZVcwpnQXlFSTJhRUxCb0xRS04rQlRUWVYrYmdDMWxvWUJOdW1ZeTFZZ3UwUGpSU3Zx\nZmRLMGpnOTdwN0poRFpZQm5ICjFhUmcyK3ZQZEtlaHFLUnQzaFB0V1BQdWZERE9vOUVkNGdFTG56\nVERURFZnQVFLVjkxUDE2SCtOMmhWSng2OEMKaFJSUXFFRC9RMlZxNGFwbDBzcHEzcWU1RHdvd3dT\nenRpc0JWU0EwMGR0Z0d3cDFLam52L2JIR2pOZWNuUmdXbAoyNFlWb1lhWW52SXI4Sm1CS0V3bkF3\nSURBUUFCb3ljd0pUQU9CZ05WSFE4QkFmOEVCQU1DQjRBd0V3WURWUjBsCkJBd3dDZ1lJS3dZQkJR\nVUhBd0l3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCQUJiRWM1WlpaVGJBNDRWZXhYYUwKUXF1RW05\nbEE0b2VMYjlGYm9zaWQ1aHF1Rno1bnM3ME9tMlNrV1ZFb2xGL1VhYTR0SjRSSk9HQW9YVzhXeGVq\nUApRckd3MjRWbk0yVS8wZCtnTjk4RUtLeVhyQnVuYkU3aUYwRVp4WFd3M3FzbFEwL1BLdHZ1Z1hs\nRWlWelVOY1VICkVVOXJJNlFUdFVvRFU1TnlrSGpMNVFaUTQyZERUQ3RXU3N3bEVvUFp2bFRTQ2Ni\nRVlyc21iRkl3Sm1tV1BUOHAKUzMrSEtyazBuVUM2NGppeFdmdEZXZ2FEc0krYlo5OGc3NTZTWTA1\ndXVheWdjdThnRmh6V0MxWjVtVE9rNXlQdgo4VHZsZDZvNUxGcGZkdVNCdmF5cWNyODI1bkUvRlRx\nZ1J3d3lKVTkrNlV0N05hOXhNVjFXT1Z6M2hRenpOUko0Cm5KT0FyRGJyaUoyNGFBUWpLUzJMNkt4\na1ZUb2JyaGFHOEhZb1k1Z3U1NXg3c0JobmJTa2ZqeWR0cTF5MlN2QlAKMFFkYnRNVHcwaW9naUFx\nbVJlemd4a3gySTNRMU1OZzNZY3NEd1FDclV4V0VDY3VYbERTSXgxVzZpUG5kNjR0VwpyS2lVY0NU\nR1JoeWV4QTZ6ZVcxc1ZnQzJ1MVByQzA0ZE1MejM1VWI5V0VsQ0JMK211Wi9zNFhscmNUZ1VlaFM5\nClIxa3cycjBmZ3dZOTJQUlpmTFJhSGJKQStOQWtFNndYN01lNE9HQmNRQWNYZkVNUThOVzBmZkV6\nU0pKQytTQ0EKYW43RCs4RWREdHdRZ2xuY1B3bTJFbU5YSlowd3lTSzRYMUoxQTU3ZjBLSk9icmJ2\ndXpWZ29ENnYxOFRaSEx4egpzWnh1NGFxOWpHeGMvWG9SNXBqY0s1NjgKLS0tLS1FTkQgQ0VSVElG\nSUNBVEUtLS0tLQo=\n"
  },
  {
    "path": "testsetup/certs/pgx_sslcert.key.b64",
    "content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tClByb2MtVHlwZTogNCxFTkNSWVBURUQKREVLLUlu\nZm86IERFUy1FREUzLUNCQyw5NzBiMjEzNzg4ZDNjNTIwCgpvZXJRQ0ZTaEs5WU5hMDQ1Q0hFZ0w1\ndHhVQW9yOUZOQ2NDRjlicFFEd28wTmtTODV4VytxVHd4WGc0WjhXYi85CjN1NE9PZEtmQm8vNlhD\nL3NoUTlyUVU0K081RkhyY3BvRExXNXY1ZjdUd2JmdHR5ckFFVEZ5VjBuWlcrNG92ZnYKMEwyVXQx\nTDZkUDRuZ25qdkFnZTFXVHBYSjhmRkVKazUvUzJIZUVIRUQ2ZldiOUdKTFFmYlVmWFlaVW12STNU\nVApDMkk2RjR6RjhuZzFQWjkwZVd2Zm5xNVZ3ZHBkSnY0ckh3cEMzb29EUURkK1hqa2FydkgybGtN\nWXFEbGRrR3N3Cm9INVdPL21lTjlYcVpGT2ZpKzBwaHg0UG1DTHY5M0ZVcHhYS01jalphM0N0RzJv\neFhLQW1iZEpBamVzUmNkMlQKSEFDd0VpZ2Z2SGJIMHNiVXk4U1NBSTlzMW1lVk00TnBveW54aTBs\nUTBsMm8vYWdWWDVLOENuVDZCNGkwRVV0RgpSb2dNZ0toTC9HZllJTlJaUUpXWXp5QzZ4QkJZb1Nm\nTHBialk2MTNYOXNaUWpuekZWdHpKa2hudEQybVlIeUMxCiszTU1BRGFrTjdOK1VsWDBWbFpnTmlZ\nOW9nZnFuSEYrWWNGWFByUVhNNHEyTSticTZEaksrNFB2ZW8yVHFHc08KVDVRVnZsY28veXRmdFhi\nYXVYVFByeHVMcEZsWmJrRVdQWk9WZFlISXV2cFhIU0NiYVZ4WnlTKzhwWnJ2M0QwdwpRKys2K2ZY\ndFc3T1pBOWNCZWtJVUxXWURMYytNTzNYcEU2dnNha21zWUJQVFNRcUJmb2JIV1hOVklIRUhtT0RI\nCjdZanB5OVlmL1prTTF1em50WVRSMFE4ZkFxVjU2VjR6NW4xTmttaWUzN09oZkxDQWMxa2NPNHVi\nMkZZMnpsUnUKSWlxWGJIZTRIZEJkUnBJRk1RK3Z2eU91Q3U4QUpZc0tnMEJFVW91MmhFaHRJL3U1\nWS9DL3ZGa0I2UkczeksrWgpYR1g0THRuWG1iTGhOMWVBMnlPc1luanVRNDFValIwSHZpVWVUak9p\nNTYzSXZxWWRVeXNJVUpKQ3hteTdJdlVCCkxrMTRteW1abDlldVR4M0JNVVBCcTVmZk4xQzlJeGo1\neWZWR1RheCt3UjNvNVBuYTdXQ2tubVp1WStjU0g5M0cKQWVDbE5vVVgzQ2Via0U4RzJGYVRsS2RV\nMERuS0Z6VmxFRy8velozblRGMldUWWZxU0lKeWtMUDlMQ3luU0oxLwpKcjQ0VzN2c3BzUzZLaE9U\nWE1oNE9oT3gwbXdOc3VrU2FlOGRBd1o3d096Zks2ZENUenVYK0htZDNxSkRRT2xjCjJ0YW5LQkdw\nQ3VZODFUbUZaSXpRTnBva1UzOTVENmJTVXg0OHZaaU9aQzhNKzNQdUIwQUU2TFFiWkpYRFVibmsK\nTk4zclJkbi9HNHMwclRrSjBIVWsvbHZqYU9oTy9MWXdMdFZ3Yy9pQUdlTVB0N0xnYWZWTnlGQlFN\nY3RIYWVCUwpablllQzFrZ1RWUlc5K2pLZXhhOER3Vk12Rmcwc0VqbXFhVGZCUW9aWDJqTDZIbHIy\nTGpCN0lLVmVGSWxNd3lBCmVFMTBHWjNEdTQ2TjlWVTRKVmtZMC9MS01KMTFxZTNndUNmangreEJh\naElOMzNxOWFGWWs2ZDhUQk5kNTZpMTMKR0w1Rk9lMjY0SzNsWmR6YXU4UzErbXpiRWYzV0hUR3Vk\na0FIS3h2cWJTRWFWYjBlbC9icW4wNCtvWWtFcUs1RQpsdUs3S1FMWFViZldBbkJIcUJSNkYwVDg5\nZVVLYnpXbngzYS9sekY4Tm5yNUJYTlNqazBEL0xuRVZtRVFCRkFDCjdwSW5jcTdaY3g4dVduRFIx\naVYybkJOVDd0Q2grN2tva1dnMHJZV0Z5djhPMktFVkd3SzVtNW9aUEE5VDBrSzgKcDhhUUZXSjJ1\ncVRzVm5QRTVoZSsyU2R5T0kybWp2allYMDdUQm5xSWMzM3JKUDVuKy9PaHBMVWxBQzlvWGw1ZgpX\nemRaa2UrMGhvL1lmaEUxSDRlTURWNFFXVWkzYjBXY3lpbnFiaW5kN1FvZ2NlZjZSd0xVUys4YUJO\nL3RNTWtOCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\n"
  },
  {
    "path": "testsetup/generate_certs.go",
    "content": "// Generates a CA, server certificate, and encrypted client certificate for testing pgx.\n\npackage main\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n)\n\nfunc main() {\n\t// Create the CA\n\tca := &x509.Certificate{\n\t\tSerialNumber: big.NewInt(1),\n\t\tSubject: pkix.Name{\n\t\t\tCommonName: \"pgx-root-ca\",\n\t\t},\n\t\tNotBefore:             time.Now(),\n\t\tNotAfter:              time.Now().AddDate(20, 0, 0),\n\t\tIsCA:                  true,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},\n\t\tKeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,\n\t\tBasicConstraintsValid: true,\n\t}\n\n\tcaKey, err := rsa.GenerateKey(rand.Reader, 4096)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcaBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = writePrivateKey(\"ca.key\", caKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = writeCertificate(\"ca.pem\", caBytes)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a server certificate signed by the CA for localhost.\n\tserverCert := &x509.Certificate{\n\t\tSerialNumber: big.NewInt(2),\n\t\tSubject: pkix.Name{\n\t\t\tCommonName: \"localhost\",\n\t\t},\n\t\tDNSNames:    []string{\"localhost\"},\n\t\tIPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},\n\t\tNotBefore:   time.Now(),\n\t\tNotAfter:    time.Now().AddDate(20, 0, 0),\n\t\tExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},\n\t\tKeyUsage:    x509.KeyUsageDigitalSignature,\n\t}\n\n\tserverCertPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tserverBytes, err := x509.CreateCertificate(rand.Reader, serverCert, ca, &serverCertPrivKey.PublicKey, caKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = writePrivateKey(\"localhost.key\", serverCertPrivKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = writeCertificate(\"localhost.crt\", serverBytes)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a client certificate signed by the CA and encrypted.\n\tclientCert := &x509.Certificate{\n\t\tSerialNumber: big.NewInt(3),\n\t\tSubject: pkix.Name{\n\t\t\tCommonName: \"pgx_sslcert\",\n\t\t},\n\t\tNotBefore:   time.Now(),\n\t\tNotAfter:    time.Now().AddDate(20, 0, 0),\n\t\tExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},\n\t\tKeyUsage:    x509.KeyUsageDigitalSignature,\n\t}\n\n\tclientCertPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tclientBytes, err := x509.CreateCertificate(rand.Reader, clientCert, ca, &clientCertPrivKey.PublicKey, caKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = writeEncryptedPrivateKey(\"pgx_sslcert.key\", clientCertPrivKey, \"certpw\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = writeCertificate(\"pgx_sslcert.crt\", clientBytes)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc writePrivateKey(path string, privateKey *rsa.PrivateKey) error {\n\tfile, err := os.Create(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writePrivateKey: %w\", err)\n\t}\n\n\terr = pem.Encode(file, &pem.Block{\n\t\tType:  \"RSA PRIVATE KEY\",\n\t\tBytes: x509.MarshalPKCS1PrivateKey(privateKey),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writePrivateKey: %w\", err)\n\t}\n\n\terr = file.Close()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writePrivateKey: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc writeEncryptedPrivateKey(path string, privateKey *rsa.PrivateKey, password string) error {\n\tfile, err := os.Create(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeEncryptedPrivateKey: %w\", err)\n\t}\n\n\tblock, err := x509.EncryptPEMBlock(rand.Reader, \"CERTIFICATE\", x509.MarshalPKCS1PrivateKey(privateKey), []byte(password), x509.PEMCipher3DES)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeEncryptedPrivateKey: %w\", err)\n\t}\n\n\terr = pem.Encode(file, block)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeEncryptedPrivateKey: %w\", err)\n\t}\n\n\terr = file.Close()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeEncryptedPrivateKey: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc writeCertificate(path string, certBytes []byte) error {\n\tfile, err := os.Create(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeCertificate: %w\", err)\n\t}\n\n\terr = pem.Encode(file, &pem.Block{\n\t\tType:  \"CERTIFICATE\",\n\t\tBytes: certBytes,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeCertificate: %w\", err)\n\t}\n\n\terr = file.Close()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"writeCertificate: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "testsetup/oauth_validator_module/Makefile",
    "content": ".PHONY = install clean\n\nPG_CONFIG = pg_config\nPKGLIBDIR = $(shell $(PG_CONFIG) --pkglibdir)\nCPPFLAGS += -I$(shell $(PG_CONFIG) --includedir-server)\nCFLAGS += -fPIC\n\ndummy_validator.so: dummy_validator.o\n\t$(CC) -shared $(CFLAGS) $(LDFLAGS) -o $@ $^\n\ndummy_validator.o: dummy_validator.c\n\ninstall:\n\tinstall -D -m 755 dummy_validator.so $(PKGLIBDIR)/\n\nclean:\n\trm -f dummy_validator.o dummy_validator.so\n"
  },
  {
    "path": "testsetup/oauth_validator_module/dummy_validator.c",
    "content": "#include \"postgres.h\"\n#include \"fmgr.h\"\n#include \"libpq/oauth.h\"\n\nPG_MODULE_MAGIC;\n\nbool validate(const ValidatorModuleState *state, const char *token,\n              const char *role, ValidatorModuleResult *result) {\n\n  elog(LOG, \"accept token '%s' for role '%s'\", token, role);\n  char *authn_id = pstrdup(token);\n  result->authn_id = authn_id;\n  result->authorized = true;\n  return true;\n}\n\nconst OAuthValidatorCallbacks callbacks = {\n    .magic = PG_OAUTH_VALIDATOR_MAGIC,\n    .startup_cb = NULL,\n    .shutdown_cb = NULL,\n    .validate_cb = validate,\n};\n\nconst OAuthValidatorCallbacks *_PG_oauth_validator_module_init() {\n  return &callbacks;\n}\n"
  },
  {
    "path": "testsetup/oauth_validator_module/pg_hba.conf",
    "content": "host      all         pgx_oauth   127.0.0.1/32          oauth validator=dummy_validator issuer=https://example.com scope=\n"
  },
  {
    "path": "testsetup/oauth_validator_module/postgresql.conf",
    "content": "oauth_validator_libraries = 'dummy_validator'\n"
  },
  {
    "path": "testsetup/pg_hba.conf",
    "content": "local     all         postgres                          trust\nlocal     all         all                               trust\nhost      all         pgx_md5     127.0.0.1/32          md5\nhost      all         pgx_scram   127.0.0.1/32          scram-sha-256\nhost      all         pgx_pw      127.0.0.1/32          password\nhostssl   all         pgx_ssl     127.0.0.1/32          scram-sha-256\nhostssl   all         pgx_sslcert 127.0.0.1/32          cert\n"
  },
  {
    "path": "testsetup/pg_hba_devcontainer.conf",
    "content": "local     all         postgres                          trust\nlocal     all         all                               trust\nhost      all         postgres    127.0.0.1/32          trust\nhost      all         postgres    ::1/128               trust\nhost      all         pgx_md5     127.0.0.1/32          md5\nhost      all         pgx_scram   127.0.0.1/32          scram-sha-256\nhost      all         pgx_pw      127.0.0.1/32          password\nhostssl   all         pgx_ssl     127.0.0.1/32          scram-sha-256\nhostssl   all         pgx_sslcert 127.0.0.1/32          cert\nhost      all         all         127.0.0.1/32          trust\nhost      all         all         ::1/128               trust\n"
  },
  {
    "path": "testsetup/pg_ssl_init.sh",
    "content": "#!/bin/bash\n# Docker initdb script: copies SSL certificates to PGDATA with correct\n# permissions and enables SSL. Runs as the postgres user during container\n# initialization.\nbase64 -d /etc/postgresql/ssl/localhost.crt.b64 > \"$PGDATA/server.crt\"\nbase64 -d /etc/postgresql/ssl/localhost.key.b64 > \"$PGDATA/server.key\"\nbase64 -d /etc/postgresql/ssl/ca.pem.b64 > \"$PGDATA/root.crt\"\nchmod 600 \"$PGDATA/server.key\"\n\n# Append SSL config to postgresql.conf rather than using command-line flags,\n# because the docker entrypoint passes command-line args to the temporary server\n# it starts before initdb scripts run. That temp server would fail with ssl=on\n# since the cert files don't exist yet.\ncat /etc/postgresql/postgresql_ssl.conf >> \"$PGDATA/postgresql.conf\"\n"
  },
  {
    "path": "testsetup/postgresql_setup.sql",
    "content": "-- Create extensions and types.\ncreate extension hstore;\ncreate extension ltree;\ncreate domain uint64 as numeric(20,0);\n\n-- Create users for different types of connections and authentication.\ncreate user pgx_ssl with superuser PASSWORD 'secret';\ncreate user pgx_sslcert with superuser PASSWORD 'secret';\nset password_encryption = md5;\ncreate user pgx_md5 with superuser PASSWORD 'secret';\nset password_encryption = 'scram-sha-256';\ncreate user pgx_pw with superuser PASSWORD 'secret';\ncreate user pgx_scram with superuser PASSWORD 'secret';\ncreate user pgx_oauth with superuser;\n\n-- When running in devcontainers, `whoami` will be `postgres`. Since the\n-- `postgres` user already exists, attempting to recreate it will fail.\n-- Therefore, we'll guard against that by no-op'ing if/when the user already\n-- exists and thereby not aborting the remaining setup.\n\\set whoami `whoami`\nselect format('create user %I with superuser', :'whoami')\nwhere not exists (select from pg_roles where rolname = :'whoami') \\gexec\n\n-- The tricky test user, below, has to actually exist so that it can be used in a test\n-- of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.\ncreate user \" tricky, ' } \"\" \\\\ test user \" superuser password 'secret';\n"
  },
  {
    "path": "testsetup/postgresql_ssl.conf",
    "content": "ssl = on\nssl_cert_file = 'server.crt'\nssl_key_file = 'server.key'\nssl_ca_file = 'root.crt'\n"
  },
  {
    "path": "tracelog/tracelog.go",
    "content": "// Package tracelog provides a tracer that acts as a traditional logger.\npackage tracelog\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n)\n\n// LogLevel represents the pgx logging level. See LogLevel* constants for\n// possible values.\ntype LogLevel int\n\n// The values for log levels are chosen such that the zero value means that no\n// log level was specified.\nconst (\n\tLogLevelTrace = LogLevel(6)\n\tLogLevelDebug = LogLevel(5)\n\tLogLevelInfo  = LogLevel(4)\n\tLogLevelWarn  = LogLevel(3)\n\tLogLevelError = LogLevel(2)\n\tLogLevelNone  = LogLevel(1)\n)\n\nfunc (ll LogLevel) String() string {\n\tswitch ll {\n\tcase LogLevelTrace:\n\t\treturn \"trace\"\n\tcase LogLevelDebug:\n\t\treturn \"debug\"\n\tcase LogLevelInfo:\n\t\treturn \"info\"\n\tcase LogLevelWarn:\n\t\treturn \"warn\"\n\tcase LogLevelError:\n\t\treturn \"error\"\n\tcase LogLevelNone:\n\t\treturn \"none\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"invalid level %d\", ll)\n\t}\n}\n\n// Logger is the interface used to get log output from pgx.\ntype Logger interface {\n\t// Log a message at the given level with data key/value pairs. data may be nil.\n\tLog(ctx context.Context, level LogLevel, msg string, data map[string]any)\n}\n\n// LoggerFunc is a wrapper around a function to satisfy the pgx.Logger interface\ntype LoggerFunc func(ctx context.Context, level LogLevel, msg string, data map[string]any)\n\n// Log delegates the logging request to the wrapped function\nfunc (f LoggerFunc) Log(ctx context.Context, level LogLevel, msg string, data map[string]any) {\n\tf(ctx, level, msg, data)\n}\n\n// LogLevelFromString converts log level string to constant\n//\n// Valid levels:\n//\n//\ttrace\n//\tdebug\n//\tinfo\n//\twarn\n//\terror\n//\tnone\nfunc LogLevelFromString(s string) (LogLevel, error) {\n\tswitch s {\n\tcase \"trace\":\n\t\treturn LogLevelTrace, nil\n\tcase \"debug\":\n\t\treturn LogLevelDebug, nil\n\tcase \"info\":\n\t\treturn LogLevelInfo, nil\n\tcase \"warn\":\n\t\treturn LogLevelWarn, nil\n\tcase \"error\":\n\t\treturn LogLevelError, nil\n\tcase \"none\":\n\t\treturn LogLevelNone, nil\n\tdefault:\n\t\treturn 0, errors.New(\"invalid log level\")\n\t}\n}\n\nfunc logQueryArgs(args []any) []any {\n\tlogArgs := make([]any, 0, len(args))\n\n\tfor _, a := range args {\n\t\tswitch v := a.(type) {\n\t\tcase []byte:\n\t\t\tif len(v) < 64 {\n\t\t\t\ta = hex.EncodeToString(v)\n\t\t\t} else {\n\t\t\t\ta = fmt.Sprintf(\"%x (truncated %d bytes)\", v[:64], len(v)-64)\n\t\t\t}\n\t\tcase string:\n\t\t\tif len(v) > 64 {\n\t\t\t\tl := 0\n\t\t\t\tfor w := 0; l < 64; l += w {\n\t\t\t\t\t_, w = utf8.DecodeRuneInString(v[l:])\n\t\t\t\t}\n\t\t\t\tif len(v) > l {\n\t\t\t\t\ta = fmt.Sprintf(\"%s (truncated %d bytes)\", v[:l], len(v)-l)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlogArgs = append(logArgs, a)\n\t}\n\n\treturn logArgs\n}\n\n// TraceLogConfig holds the configuration for key names\ntype TraceLogConfig struct {\n\tTimeKey string\n}\n\n// DefaultTraceLogConfig returns the default configuration for TraceLog\nfunc DefaultTraceLogConfig() *TraceLogConfig {\n\treturn &TraceLogConfig{\n\t\tTimeKey: \"time\",\n\t}\n}\n\n// TraceLog implements pgx.QueryTracer, pgx.BatchTracer, pgx.ConnectTracer, pgx.CopyFromTracer, pgxpool.AcquireTracer,\n// and pgxpool.ReleaseTracer. Logger and LogLevel are required. Config will be automatically initialized on the\n// first use if nil.\ntype TraceLog struct {\n\tLogger   Logger\n\tLogLevel LogLevel\n\n\tConfig           *TraceLogConfig\n\tensureConfigOnce sync.Once\n}\n\n// ensureConfig initializes the Config field with default values if it is nil.\nfunc (tl *TraceLog) ensureConfig() {\n\ttl.ensureConfigOnce.Do(\n\t\tfunc() {\n\t\t\tif tl.Config == nil {\n\t\t\t\ttl.Config = DefaultTraceLogConfig()\n\t\t\t}\n\t\t},\n\t)\n}\n\ntype ctxKey int\n\nconst (\n\t_ ctxKey = iota\n\ttracelogQueryCtxKey\n\ttracelogBatchCtxKey\n\ttracelogCopyFromCtxKey\n\ttracelogConnectCtxKey\n\ttracelogPrepareCtxKey\n\ttracelogAcquireCtxKey\n)\n\ntype traceQueryData struct {\n\tstartTime time.Time\n\tsql       string\n\targs      []any\n}\n\nfunc (tl *TraceLog) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\treturn context.WithValue(ctx, tracelogQueryCtxKey, &traceQueryData{\n\t\tstartTime: time.Now(),\n\t\tsql:       data.SQL,\n\t\targs:      data.Args,\n\t})\n}\n\nfunc (tl *TraceLog) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n\ttl.ensureConfig()\n\tqueryData := ctx.Value(tracelogQueryCtxKey).(*traceQueryData)\n\n\tendTime := time.Now()\n\tinterval := endTime.Sub(queryData.startTime)\n\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.log(ctx, conn, LogLevelError, \"Query\", map[string]any{\"sql\": queryData.sql, \"args\": logQueryArgs(queryData.args), \"err\": data.Err, tl.Config.TimeKey: interval})\n\t\t}\n\t\treturn\n\t}\n\n\tif tl.shouldLog(LogLevelInfo) {\n\t\ttl.log(ctx, conn, LogLevelInfo, \"Query\", map[string]any{\"sql\": queryData.sql, \"args\": logQueryArgs(queryData.args), tl.Config.TimeKey: interval, \"commandTag\": data.CommandTag.String()})\n\t}\n}\n\ntype traceBatchData struct {\n\tstartTime time.Time\n}\n\nfunc (tl *TraceLog) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\treturn context.WithValue(ctx, tracelogBatchCtxKey, &traceBatchData{\n\t\tstartTime: time.Now(),\n\t})\n}\n\nfunc (tl *TraceLog) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.log(ctx, conn, LogLevelError, \"BatchQuery\", map[string]any{\"sql\": data.SQL, \"args\": logQueryArgs(data.Args), \"err\": data.Err})\n\t\t}\n\t\treturn\n\t}\n\n\tif tl.shouldLog(LogLevelInfo) {\n\t\ttl.log(ctx, conn, LogLevelInfo, \"BatchQuery\", map[string]any{\"sql\": data.SQL, \"args\": logQueryArgs(data.Args), \"commandTag\": data.CommandTag.String()})\n\t}\n}\n\nfunc (tl *TraceLog) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\ttl.ensureConfig()\n\tqueryData := ctx.Value(tracelogBatchCtxKey).(*traceBatchData)\n\n\tendTime := time.Now()\n\tinterval := endTime.Sub(queryData.startTime)\n\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.log(ctx, conn, LogLevelError, \"BatchClose\", map[string]any{\"err\": data.Err, tl.Config.TimeKey: interval})\n\t\t}\n\t\treturn\n\t}\n\n\tif tl.shouldLog(LogLevelInfo) {\n\t\ttl.log(ctx, conn, LogLevelInfo, \"BatchClose\", map[string]any{tl.Config.TimeKey: interval})\n\t}\n}\n\ntype traceCopyFromData struct {\n\tstartTime   time.Time\n\tTableName   pgx.Identifier\n\tColumnNames []string\n}\n\nfunc (tl *TraceLog) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {\n\treturn context.WithValue(ctx, tracelogCopyFromCtxKey, &traceCopyFromData{\n\t\tstartTime:   time.Now(),\n\t\tTableName:   data.TableName,\n\t\tColumnNames: data.ColumnNames,\n\t})\n}\n\nfunc (tl *TraceLog) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {\n\ttl.ensureConfig()\n\tcopyFromData := ctx.Value(tracelogCopyFromCtxKey).(*traceCopyFromData)\n\n\tendTime := time.Now()\n\tinterval := endTime.Sub(copyFromData.startTime)\n\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.log(ctx, conn, LogLevelError, \"CopyFrom\", map[string]any{\"tableName\": copyFromData.TableName, \"columnNames\": copyFromData.ColumnNames, \"err\": data.Err, tl.Config.TimeKey: interval})\n\t\t}\n\t\treturn\n\t}\n\n\tif tl.shouldLog(LogLevelInfo) {\n\t\ttl.log(ctx, conn, LogLevelInfo, \"CopyFrom\", map[string]any{\"tableName\": copyFromData.TableName, \"columnNames\": copyFromData.ColumnNames, \"err\": data.Err, tl.Config.TimeKey: interval, \"rowCount\": data.CommandTag.RowsAffected()})\n\t}\n}\n\ntype traceConnectData struct {\n\tstartTime  time.Time\n\tconnConfig *pgx.ConnConfig\n}\n\nfunc (tl *TraceLog) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {\n\treturn context.WithValue(ctx, tracelogConnectCtxKey, &traceConnectData{\n\t\tstartTime:  time.Now(),\n\t\tconnConfig: data.ConnConfig,\n\t})\n}\n\nfunc (tl *TraceLog) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {\n\ttl.ensureConfig()\n\tconnectData := ctx.Value(tracelogConnectCtxKey).(*traceConnectData)\n\n\tendTime := time.Now()\n\tinterval := endTime.Sub(connectData.startTime)\n\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.Logger.Log(ctx, LogLevelError, \"Connect\", map[string]any{\n\t\t\t\t\"host\":            connectData.connConfig.Host,\n\t\t\t\t\"port\":            connectData.connConfig.Port,\n\t\t\t\t\"database\":        connectData.connConfig.Database,\n\t\t\t\ttl.Config.TimeKey: interval,\n\t\t\t\t\"err\":             data.Err,\n\t\t\t})\n\t\t}\n\t\treturn\n\t}\n\n\tif data.Conn != nil {\n\t\tif tl.shouldLog(LogLevelInfo) {\n\t\t\ttl.log(ctx, data.Conn, LogLevelInfo, \"Connect\", map[string]any{\n\t\t\t\t\"host\":            connectData.connConfig.Host,\n\t\t\t\t\"port\":            connectData.connConfig.Port,\n\t\t\t\t\"database\":        connectData.connConfig.Database,\n\t\t\t\ttl.Config.TimeKey: interval,\n\t\t\t})\n\t\t}\n\t}\n}\n\ntype tracePrepareData struct {\n\tstartTime time.Time\n\tname      string\n\tsql       string\n}\n\nfunc (tl *TraceLog) TracePrepareStart(ctx context.Context, _ *pgx.Conn, data pgx.TracePrepareStartData) context.Context {\n\treturn context.WithValue(ctx, tracelogPrepareCtxKey, &tracePrepareData{\n\t\tstartTime: time.Now(),\n\t\tname:      data.Name,\n\t\tsql:       data.SQL,\n\t})\n}\n\nfunc (tl *TraceLog) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {\n\ttl.ensureConfig()\n\tprepareData := ctx.Value(tracelogPrepareCtxKey).(*tracePrepareData)\n\n\tendTime := time.Now()\n\tinterval := endTime.Sub(prepareData.startTime)\n\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.log(ctx, conn, LogLevelError, \"Prepare\", map[string]any{\"name\": prepareData.name, \"sql\": prepareData.sql, \"err\": data.Err, tl.Config.TimeKey: interval})\n\t\t}\n\t\treturn\n\t}\n\n\tif tl.shouldLog(LogLevelInfo) {\n\t\ttl.log(ctx, conn, LogLevelInfo, \"Prepare\", map[string]any{\"name\": prepareData.name, \"sql\": prepareData.sql, tl.Config.TimeKey: interval, \"alreadyPrepared\": data.AlreadyPrepared})\n\t}\n}\n\ntype traceAcquireData struct {\n\tstartTime time.Time\n}\n\nfunc (tl *TraceLog) TraceAcquireStart(ctx context.Context, _ *pgxpool.Pool, _ pgxpool.TraceAcquireStartData) context.Context {\n\treturn context.WithValue(ctx, tracelogAcquireCtxKey, &traceAcquireData{\n\t\tstartTime: time.Now(),\n\t})\n}\n\nfunc (tl *TraceLog) TraceAcquireEnd(ctx context.Context, _ *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {\n\ttl.ensureConfig()\n\tacquireData := ctx.Value(tracelogAcquireCtxKey).(*traceAcquireData)\n\n\tendTime := time.Now()\n\tinterval := endTime.Sub(acquireData.startTime)\n\n\tif data.Err != nil {\n\t\tif tl.shouldLog(LogLevelError) {\n\t\t\ttl.Logger.Log(ctx, LogLevelError, \"Acquire\", map[string]any{\"err\": data.Err, tl.Config.TimeKey: interval})\n\t\t}\n\t\treturn\n\t}\n\n\tif data.Conn != nil {\n\t\tif tl.shouldLog(LogLevelDebug) {\n\t\t\ttl.log(ctx, data.Conn, LogLevelDebug, \"Acquire\", map[string]any{tl.Config.TimeKey: interval})\n\t\t}\n\t}\n}\n\nfunc (tl *TraceLog) TraceRelease(_ *pgxpool.Pool, data pgxpool.TraceReleaseData) {\n\tif tl.shouldLog(LogLevelDebug) {\n\t\t// there is no context on the TraceRelease callback\n\t\ttl.log(context.Background(), data.Conn, LogLevelDebug, \"Release\", map[string]any{})\n\t}\n}\n\nfunc (tl *TraceLog) shouldLog(lvl LogLevel) bool {\n\treturn tl.LogLevel >= lvl\n}\n\nfunc (tl *TraceLog) log(ctx context.Context, conn *pgx.Conn, lvl LogLevel, msg string, data map[string]any) {\n\tif data == nil {\n\t\tdata = map[string]any{}\n\t}\n\n\tpgConn := conn.PgConn()\n\tif pgConn != nil {\n\t\tpid := pgConn.PID()\n\t\tif pid != 0 {\n\t\t\tdata[\"pid\"] = pid\n\t\t}\n\t}\n\n\ttl.Logger.Log(ctx, lvl, msg, data)\n}\n"
  },
  {
    "path": "tracelog/tracelog_test.go",
    "content": "package tracelog_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/jackc/pgx/v5/tracelog\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar defaultConnTestRunner pgxtest.ConnTestRunner\n\nfunc init() {\n\tdefaultConnTestRunner = pgxtest.DefaultConnTestRunner()\n\tdefaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig, err := pgx.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\t\trequire.NoError(t, err)\n\t\treturn config\n\t}\n}\n\ntype testLog struct {\n\tlvl  tracelog.LogLevel\n\tmsg  string\n\tdata map[string]any\n}\n\ntype testLogger struct {\n\tlogs []testLog\n\n\tmux sync.Mutex\n}\n\nfunc (l *testLogger) Log(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) {\n\tl.mux.Lock()\n\tdefer l.mux.Unlock()\n\n\tdata[\"ctxdata\"] = ctx.Value(\"ctxdata\")\n\tl.logs = append(l.logs, testLog{lvl: level, msg: msg, data: data})\n}\n\nfunc (l *testLogger) Clear() {\n\tl.mux.Lock()\n\tdefer l.mux.Unlock()\n\n\tl.logs = l.logs[0:0]\n}\n\nfunc (l *testLogger) FilterByMsg(msg string) (res []testLog) {\n\tl.mux.Lock()\n\tdefer l.mux.Unlock()\n\n\tfor _, log := range l.logs {\n\t\tif log.msg == msg {\n\t\t\tres = append(res, log)\n\t\t}\n\t}\n\n\treturn res\n}\n\nfunc TestContextGetsPassedToLogMethod(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\tctx = context.WithValue(ctx, \"ctxdata\", \"foo\")\n\t\t_, err := conn.Exec(ctx, `;`)\n\t\trequire.NoError(t, err)\n\t\trequire.Len(t, logger.logs, 1)\n\t\trequire.Equal(t, \"foo\", logger.logs[0].data[\"ctxdata\"])\n\t})\n}\n\nfunc TestLoggerFunc(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tconst testMsg = \"foo\"\n\n\tbuf := bytes.Buffer{}\n\tlogger := log.New(&buf, \"\", 0)\n\n\tcreateAdapterFn := func(logger *log.Logger) tracelog.LoggerFunc {\n\t\treturn func(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) {\n\t\t\tlogger.Printf(\"%s\", testMsg)\n\t\t}\n\t}\n\n\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\tconfig.Tracer = &tracelog.TraceLog{\n\t\tLogger:   createAdapterFn(logger),\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tconn, err := pgx.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer conn.Close(ctx)\n\n\tbuf.Reset() // Clear logs written when establishing connection\n\n\tif _, err := conn.Exec(context.TODO(), \";\"); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif strings.TrimSpace(buf.String()) != testMsg {\n\t\tt.Errorf(\"Expected logger function to return '%s', but it was '%s'\", testMsg, buf.String())\n\t}\n}\n\nfunc TestLogQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\t_, err := conn.Exec(ctx, `select $1::text`, \"testing\")\n\t\trequire.NoError(t, err)\n\n\t\tlogs := logger.FilterByMsg(\"Query\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelInfo, logs[0].lvl)\n\n\t\tlogger.Clear()\n\n\t\t_, err = conn.Exec(ctx, `foo`, \"testing\")\n\t\trequire.Error(t, err)\n\n\t\tlogs = logger.FilterByMsg(\"Query\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelError, logs[0].lvl)\n\t\trequire.Equal(t, err, logs[0].data[\"err\"])\n\t})\n}\n\n// https://github.com/jackc/pgx/issues/1365\nfunc TestLogQueryArgsHandlesUTF8(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\tvar s string\n\t\tfor range 63 {\n\t\t\ts += \"0\"\n\t\t}\n\t\ts += \"😊\"\n\n\t\t_, err := conn.Exec(ctx, `select $1::text`, s)\n\t\trequire.NoError(t, err)\n\n\t\tlogs := logger.FilterByMsg(\"Query\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelInfo, logs[0].lvl)\n\t\trequire.Equal(t, s, logs[0].data[\"args\"].([]any)[0])\n\n\t\tlogger.Clear()\n\n\t\t_, err = conn.Exec(ctx, `select $1::text`, s+\"000\")\n\t\trequire.NoError(t, err)\n\n\t\tlogs = logger.FilterByMsg(\"Query\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelInfo, logs[0].lvl)\n\t\trequire.Equal(t, s+\" (truncated 3 bytes)\", logs[0].data[\"args\"].([]any)[0])\n\t})\n}\n\nfunc TestLogCopyFrom(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, pgxtest.KnownOIDQueryExecModes, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\t_, err := conn.Exec(ctx, `create temporary table foo(a int4)`)\n\t\trequire.NoError(t, err)\n\n\t\tlogger.Clear()\n\n\t\tinputRows := [][]any{\n\t\t\t{int32(1)},\n\t\t\t{nil},\n\t\t}\n\n\t\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, pgx.CopyFromRows(inputRows))\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, len(inputRows), copyCount)\n\n\t\tlogs := logger.FilterByMsg(\"CopyFrom\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelInfo, logs[0].lvl)\n\n\t\tlogger.Clear()\n\n\t\tinputRows = [][]any{\n\t\t\t{\"not an integer\"},\n\t\t\t{nil},\n\t\t}\n\n\t\tcopyCount, err = conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, pgx.CopyFromRows(inputRows))\n\t\trequire.Error(t, err)\n\t\trequire.EqualValues(t, 0, copyCount)\n\n\t\tlogs = logger.FilterByMsg(\"CopyFrom\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelError, logs[0].lvl)\n\t})\n}\n\nfunc TestLogConnect(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\tconfig.Tracer = tracer\n\n\tconn1, err := pgx.ConnectConfig(ctx, config)\n\trequire.NoError(t, err)\n\tdefer conn1.Close(ctx)\n\trequire.Len(t, logger.logs, 1)\n\trequire.Equal(t, \"Connect\", logger.logs[0].msg)\n\trequire.Equal(t, tracelog.LogLevelInfo, logger.logs[0].lvl)\n\n\tlogger.Clear()\n\n\tconfig, err = pgx.ParseConfig(\"host=/invalid\")\n\trequire.NoError(t, err)\n\tconfig.Tracer = tracer\n\n\tconn2, err := pgx.ConnectConfig(ctx, config)\n\trequire.Nil(t, conn2)\n\trequire.Error(t, err)\n\trequire.Len(t, logger.logs, 1)\n\trequire.Equal(t, \"Connect\", logger.logs[0].msg)\n\trequire.Equal(t, tracelog.LogLevelError, logger.logs[0].lvl)\n}\n\nfunc TestLogBatchStatementsOnExec(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"CockroachDB auto commits DDL by default\")\n\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"create table foo (id bigint)\")\n\t\tbatch.Queue(\"drop table foo\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\n\t\t_, err := br.Exec()\n\t\trequire.NoError(t, err)\n\n\t\t_, err = br.Exec()\n\t\trequire.NoError(t, err)\n\n\t\terr = br.Close()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, logger.logs, 3)\n\t\tassert.Equal(t, \"BatchQuery\", logger.logs[0].msg)\n\t\tassert.Equal(t, \"create table foo (id bigint)\", logger.logs[0].data[\"sql\"])\n\t\tassert.Equal(t, \"BatchQuery\", logger.logs[1].msg)\n\t\tassert.Equal(t, \"drop table foo\", logger.logs[1].data[\"sql\"])\n\t\tassert.Equal(t, \"BatchClose\", logger.logs[2].msg)\n\t})\n}\n\nfunc TestLogBatchStatementsOnBatchResultClose(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(\"select generate_series(1,$1)\", 100)\n\t\tbatch.Queue(\"select 1 = 1;\")\n\n\t\tbr := conn.SendBatch(ctx, batch)\n\t\terr := br.Close()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, logger.logs, 3)\n\t\tassert.Equal(t, \"BatchQuery\", logger.logs[0].msg)\n\t\tassert.Equal(t, \"select generate_series(1,$1)\", logger.logs[0].data[\"sql\"])\n\t\tassert.Equal(t, \"BatchQuery\", logger.logs[1].msg)\n\t\tassert.Equal(t, \"select 1 = 1;\", logger.logs[1].data[\"sql\"])\n\t\tassert.Equal(t, \"BatchClose\", logger.logs[2].msg)\n\t})\n}\n\nfunc TestLogAcquire(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\tconfig.Tracer = tracer\n\n\tpoolConfig, err := pgxpool.ParseConfig(config.ConnString())\n\trequire.NoError(t, err)\n\n\tpoolConfig.ConnConfig = config\n\tpool1, err := pgxpool.NewWithConfig(ctx, poolConfig)\n\trequire.NoError(t, err)\n\tdefer pool1.Close()\n\n\tconn1, err := pool1.Acquire(ctx)\n\trequire.NoError(t, err)\n\tdefer conn1.Release()\n\trequire.Len(t, logger.logs, 2) // Has both the Connect and Acquire logs\n\trequire.Equal(t, \"Acquire\", logger.logs[1].msg)\n\trequire.Equal(t, tracelog.LogLevelDebug, logger.logs[1].lvl)\n\n\tlogger.Clear()\n\n\t// create a 2nd pool with a bad host to verify the error handling\n\tpoolConfig, err = pgxpool.ParseConfig(\"host=/invalid\")\n\trequire.NoError(t, err)\n\tpoolConfig.ConnConfig.Tracer = tracer\n\n\tpool2, err := pgxpool.NewWithConfig(ctx, poolConfig)\n\trequire.NoError(t, err)\n\tdefer pool2.Close()\n\n\tconn2, err := pool2.Acquire(ctx)\n\trequire.Error(t, err)\n\trequire.Nil(t, conn2)\n\trequire.Len(t, logger.logs, 2)\n\trequire.Equal(t, \"Acquire\", logger.logs[1].msg)\n\trequire.Equal(t, tracelog.LogLevelError, logger.logs[1].lvl)\n}\n\nfunc TestLogRelease(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\tconfig.Tracer = tracer\n\n\tpoolConfig, err := pgxpool.ParseConfig(config.ConnString())\n\trequire.NoError(t, err)\n\n\tpoolConfig.ConnConfig = config\n\tpool1, err := pgxpool.NewWithConfig(ctx, poolConfig)\n\trequire.NoError(t, err)\n\tdefer pool1.Close()\n\n\tconn1, err := pool1.Acquire(ctx)\n\trequire.NoError(t, err)\n\n\tlogger.Clear()\n\tconn1.Release()\n\trequire.Len(t, logger.logs, 1)\n\trequire.Equal(t, \"Release\", logger.logs[0].msg)\n\trequire.Equal(t, tracelog.LogLevelDebug, logger.logs[0].lvl)\n}\n\nfunc TestLogPrepare(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, []pgx.QueryExecMode{\n\t\tpgx.QueryExecModeCacheStatement,\n\t\tpgx.QueryExecModeCacheDescribe,\n\t\tpgx.QueryExecModeDescribeExec,\n\t}, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\t_, err := conn.Exec(ctx, `select $1::text`, \"testing\")\n\t\trequire.NoError(t, err)\n\n\t\tlogs := logger.FilterByMsg(\"Prepare\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, tracelog.LogLevelInfo, logs[0].lvl)\n\n\t\tlogger.Clear()\n\n\t\t_, err = conn.Exec(ctx, `foo aaaa`, \"testing\")\n\t\trequire.Error(t, err)\n\n\t\tlogs = logger.FilterByMsg(\"Prepare\")\n\t\trequire.Len(t, logs, 1)\n\t\trequire.Equal(t, err, logs[0].data[\"err\"])\n\t})\n\n\tctx, cancel = context.WithTimeout(ctx, 30*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tlogger.Clear() // Clear any logs written when establishing connection\n\n\t\t_, err := conn.Prepare(ctx, \"test_query_1\", `select $1::int`)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, logger.logs, 1)\n\t\trequire.Equal(t, \"Prepare\", logger.logs[0].msg)\n\t\trequire.Equal(t, tracelog.LogLevelInfo, logger.logs[0].lvl)\n\n\t\tlogger.Clear()\n\n\t\t_, err = conn.Prepare(ctx, `test_query_2`, \"foo aaaa\")\n\t\trequire.Error(t, err)\n\n\t\trequire.Len(t, logger.logs, 1)\n\t\trequire.Equal(t, \"Prepare\", logger.logs[0].msg)\n\t\trequire.Equal(t, err, logger.logs[0].data[\"err\"])\n\t})\n}\n\n// https://github.com/jackc/pgx/pull/2120\nfunc TestConcurrentUsage(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tlogger := &testLogger{}\n\ttracer := &tracelog.TraceLog{\n\t\tLogger:   logger,\n\t\tLogLevel: tracelog.LogLevelTrace,\n\t}\n\n\tconfig, err := pgxpool.ParseConfig(os.Getenv(\"PGX_TEST_DATABASE\"))\n\trequire.NoError(t, err)\n\tconfig.ConnConfig.Tracer = tracer\n\n\tfor range 50 {\n\t\tfunc() {\n\t\t\tpool, err := pgxpool.NewWithConfig(ctx, config)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tdefer pool.Close()\n\n\t\t\teg := errgroup.Group{}\n\n\t\t\tfor range 5 {\n\t\t\t\teg.Go(func() error {\n\t\t\t\t\t_, err := pool.Exec(ctx, `select 1`)\n\t\t\t\t\treturn err\n\t\t\t\t})\n\t\t\t}\n\n\t\t\terr = eg.Wait()\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\t}\n}\n"
  },
  {
    "path": "tracer.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// QueryTracer traces Query, QueryRow, and Exec.\ntype QueryTracer interface {\n\t// TraceQueryStart is called at the beginning of Query, QueryRow, and Exec calls. The returned context is used for the\n\t// rest of the call and will be passed to TraceQueryEnd.\n\tTraceQueryStart(ctx context.Context, conn *Conn, data TraceQueryStartData) context.Context\n\n\tTraceQueryEnd(ctx context.Context, conn *Conn, data TraceQueryEndData)\n}\n\ntype TraceQueryStartData struct {\n\tSQL  string\n\tArgs []any\n}\n\ntype TraceQueryEndData struct {\n\tCommandTag pgconn.CommandTag\n\tErr        error\n}\n\n// BatchTracer traces SendBatch.\ntype BatchTracer interface {\n\t// TraceBatchStart is called at the beginning of SendBatch calls. The returned context is used for the\n\t// rest of the call and will be passed to TraceBatchQuery and TraceBatchEnd.\n\tTraceBatchStart(ctx context.Context, conn *Conn, data TraceBatchStartData) context.Context\n\n\tTraceBatchQuery(ctx context.Context, conn *Conn, data TraceBatchQueryData)\n\tTraceBatchEnd(ctx context.Context, conn *Conn, data TraceBatchEndData)\n}\n\ntype TraceBatchStartData struct {\n\tBatch *Batch\n}\n\ntype TraceBatchQueryData struct {\n\tSQL        string\n\tArgs       []any\n\tCommandTag pgconn.CommandTag\n\tErr        error\n}\n\ntype TraceBatchEndData struct {\n\tErr error\n}\n\n// CopyFromTracer traces CopyFrom.\ntype CopyFromTracer interface {\n\t// TraceCopyFromStart is called at the beginning of CopyFrom calls. The returned context is used for the\n\t// rest of the call and will be passed to TraceCopyFromEnd.\n\tTraceCopyFromStart(ctx context.Context, conn *Conn, data TraceCopyFromStartData) context.Context\n\n\tTraceCopyFromEnd(ctx context.Context, conn *Conn, data TraceCopyFromEndData)\n}\n\ntype TraceCopyFromStartData struct {\n\tTableName   Identifier\n\tColumnNames []string\n}\n\ntype TraceCopyFromEndData struct {\n\tCommandTag pgconn.CommandTag\n\tErr        error\n}\n\n// PrepareTracer traces Prepare.\ntype PrepareTracer interface {\n\t// TracePrepareStart is called at the beginning of Prepare calls. The returned context is used for the\n\t// rest of the call and will be passed to TracePrepareEnd.\n\tTracePrepareStart(ctx context.Context, conn *Conn, data TracePrepareStartData) context.Context\n\n\tTracePrepareEnd(ctx context.Context, conn *Conn, data TracePrepareEndData)\n}\n\ntype TracePrepareStartData struct {\n\tName string\n\tSQL  string\n}\n\ntype TracePrepareEndData struct {\n\tAlreadyPrepared bool\n\tErr             error\n}\n\n// ConnectTracer traces Connect and ConnectConfig.\ntype ConnectTracer interface {\n\t// TraceConnectStart is called at the beginning of Connect and ConnectConfig calls. The returned context is used for\n\t// the rest of the call and will be passed to TraceConnectEnd.\n\tTraceConnectStart(ctx context.Context, data TraceConnectStartData) context.Context\n\n\tTraceConnectEnd(ctx context.Context, data TraceConnectEndData)\n}\n\ntype TraceConnectStartData struct {\n\tConnConfig *ConnConfig\n}\n\ntype TraceConnectEndData struct {\n\tConn *Conn\n\tErr  error\n}\n"
  },
  {
    "path": "tracer_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype testTracer struct {\n\ttraceQueryStart    func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context\n\ttraceQueryEnd      func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData)\n\ttraceBatchStart    func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context\n\ttraceBatchQuery    func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData)\n\ttraceBatchEnd      func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData)\n\ttraceCopyFromStart func(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context\n\ttraceCopyFromEnd   func(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData)\n\ttracePrepareStart  func(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context\n\ttracePrepareEnd    func(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData)\n\ttraceConnectStart  func(ctx context.Context, data pgx.TraceConnectStartData) context.Context\n\ttraceConnectEnd    func(ctx context.Context, data pgx.TraceConnectEndData)\n}\n\ntype ctxKey string\n\nfunc (tt *testTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\tif tt.traceQueryStart != nil {\n\t\treturn tt.traceQueryStart(ctx, conn, data)\n\t}\n\treturn ctx\n}\n\nfunc (tt *testTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n\tif tt.traceQueryEnd != nil {\n\t\ttt.traceQueryEnd(ctx, conn, data)\n\t}\n}\n\nfunc (tt *testTracer) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\tif tt.traceBatchStart != nil {\n\t\treturn tt.traceBatchStart(ctx, conn, data)\n\t}\n\treturn ctx\n}\n\nfunc (tt *testTracer) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\tif tt.traceBatchQuery != nil {\n\t\ttt.traceBatchQuery(ctx, conn, data)\n\t}\n}\n\nfunc (tt *testTracer) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\tif tt.traceBatchEnd != nil {\n\t\ttt.traceBatchEnd(ctx, conn, data)\n\t}\n}\n\nfunc (tt *testTracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {\n\tif tt.traceCopyFromStart != nil {\n\t\treturn tt.traceCopyFromStart(ctx, conn, data)\n\t}\n\treturn ctx\n}\n\nfunc (tt *testTracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {\n\tif tt.traceCopyFromEnd != nil {\n\t\ttt.traceCopyFromEnd(ctx, conn, data)\n\t}\n}\n\nfunc (tt *testTracer) TracePrepareStart(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {\n\tif tt.tracePrepareStart != nil {\n\t\treturn tt.tracePrepareStart(ctx, conn, data)\n\t}\n\treturn ctx\n}\n\nfunc (tt *testTracer) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {\n\tif tt.tracePrepareEnd != nil {\n\t\ttt.tracePrepareEnd(ctx, conn, data)\n\t}\n}\n\nfunc (tt *testTracer) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {\n\tif tt.traceConnectStart != nil {\n\t\treturn tt.traceConnectStart(ctx, data)\n\t}\n\treturn ctx\n}\n\nfunc (tt *testTracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {\n\tif tt.traceConnectEnd != nil {\n\t\ttt.traceConnectEnd(ctx, data)\n\t}\n}\n\nfunc TestTraceExec(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttraceQueryStartCalled := false\n\t\ttracer.traceQueryStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\t\t\ttraceQueryStartCalled = true\n\t\t\trequire.Equal(t, `select $1::text`, data.SQL)\n\t\t\trequire.Len(t, data.Args, 1)\n\t\t\trequire.Equal(t, `testing`, data.Args[0])\n\t\t\treturn context.WithValue(ctx, ctxKey(ctxKey(\"fromTraceQueryStart\")), \"foo\")\n\t\t}\n\n\t\ttraceQueryEndCalled := false\n\t\ttracer.traceQueryEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n\t\t\ttraceQueryEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(ctxKey(\"fromTraceQueryStart\"))))\n\t\t\trequire.Equal(t, `SELECT 1`, data.CommandTag.String())\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\t_, err := conn.Exec(ctx, `select $1::text`, \"testing\")\n\t\trequire.NoError(t, err)\n\t\trequire.True(t, traceQueryStartCalled)\n\t\trequire.True(t, traceQueryEndCalled)\n\t})\n}\n\nfunc TestTraceQuery(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttraceQueryStartCalled := false\n\t\ttracer.traceQueryStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\t\t\ttraceQueryStartCalled = true\n\t\t\trequire.Equal(t, `select $1::text`, data.SQL)\n\t\t\trequire.Len(t, data.Args, 1)\n\t\t\trequire.Equal(t, `testing`, data.Args[0])\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceQueryStart\"), \"foo\")\n\t\t}\n\n\t\ttraceQueryEndCalled := false\n\t\ttracer.traceQueryEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {\n\t\t\ttraceQueryEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceQueryStart\")))\n\t\t\trequire.Equal(t, `SELECT 1`, data.CommandTag.String())\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\tvar s string\n\t\terr := conn.QueryRow(ctx, `select $1::text`, \"testing\").Scan(&s)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"testing\", s)\n\t\trequire.True(t, traceQueryStartCalled)\n\t\trequire.True(t, traceQueryEndCalled)\n\t})\n}\n\nfunc TestTraceBatchNormal(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttraceBatchStartCalled := false\n\t\ttracer.traceBatchStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\t\t\ttraceBatchStartCalled = true\n\t\t\trequire.NotNil(t, data.Batch)\n\t\t\trequire.Equal(t, 2, data.Batch.Len())\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceBatchStart\"), \"foo\")\n\t\t}\n\n\t\ttraceBatchQueryCalledCount := 0\n\t\ttracer.traceBatchQuery = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\t\t\ttraceBatchQueryCalledCount++\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\ttraceBatchEndCalled := false\n\t\ttracer.traceBatchEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\t\t\ttraceBatchEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(`select 1`)\n\t\tbatch.Queue(`select 2`)\n\n\t\tbr := conn.SendBatch(context.Background(), batch)\n\t\trequire.True(t, traceBatchStartCalled)\n\n\t\tvar n int32\n\t\terr := br.QueryRow().Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 1, n)\n\t\trequire.EqualValues(t, 1, traceBatchQueryCalledCount)\n\n\t\terr = br.QueryRow().Scan(&n)\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 2, n)\n\t\trequire.EqualValues(t, 2, traceBatchQueryCalledCount)\n\n\t\terr = br.Close()\n\t\trequire.NoError(t, err)\n\n\t\trequire.True(t, traceBatchEndCalled)\n\t})\n}\n\nfunc TestTraceBatchClose(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttraceBatchStartCalled := false\n\t\ttracer.traceBatchStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\t\t\ttraceBatchStartCalled = true\n\t\t\trequire.NotNil(t, data.Batch)\n\t\t\trequire.Equal(t, 2, data.Batch.Len())\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceBatchStart\"), \"foo\")\n\t\t}\n\n\t\ttraceBatchQueryCalledCount := 0\n\t\ttracer.traceBatchQuery = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\t\t\ttraceBatchQueryCalledCount++\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\ttraceBatchEndCalled := false\n\t\ttracer.traceBatchEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\t\t\ttraceBatchEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(`select 1`)\n\t\tbatch.Queue(`select 2`)\n\n\t\tbr := conn.SendBatch(context.Background(), batch)\n\t\trequire.True(t, traceBatchStartCalled)\n\t\terr := br.Close()\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, 2, traceBatchQueryCalledCount)\n\t\trequire.True(t, traceBatchEndCalled)\n\t})\n}\n\nfunc TestTraceBatchErrorWhileReadingResults(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, []pgx.QueryExecMode{pgx.QueryExecModeSimpleProtocol}, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttraceBatchStartCalled := false\n\t\ttracer.traceBatchStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\t\t\ttraceBatchStartCalled = true\n\t\t\trequire.NotNil(t, data.Batch)\n\t\t\trequire.Equal(t, 3, data.Batch.Len())\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceBatchStart\"), \"foo\")\n\t\t}\n\n\t\ttraceBatchQueryCalledCount := 0\n\t\ttracer.traceBatchQuery = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\t\t\ttraceBatchQueryCalledCount++\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\tif traceBatchQueryCalledCount == 2 {\n\t\t\t\trequire.Error(t, data.Err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, data.Err)\n\t\t\t}\n\t\t}\n\n\t\ttraceBatchEndCalled := false\n\t\ttracer.traceBatchEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\t\t\ttraceBatchEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\trequire.Error(t, data.Err)\n\t\t}\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(`select 1`)\n\t\tbatch.Queue(`select 2/n-2 from generate_series(0,10) n`)\n\t\tbatch.Queue(`select 3`)\n\n\t\tbr := conn.SendBatch(context.Background(), batch)\n\t\trequire.True(t, traceBatchStartCalled)\n\n\t\tcommandTag, err := br.Exec()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, \"SELECT 1\", commandTag.String())\n\n\t\tcommandTag, err = br.Exec()\n\t\trequire.Error(t, err)\n\t\trequire.Equal(t, \"\", commandTag.String())\n\n\t\tcommandTag, err = br.Exec()\n\t\trequire.Error(t, err)\n\t\trequire.Equal(t, \"\", commandTag.String())\n\n\t\terr = br.Close()\n\t\trequire.Error(t, err)\n\t\trequire.EqualValues(t, 2, traceBatchQueryCalledCount)\n\t\trequire.True(t, traceBatchEndCalled)\n\t})\n}\n\nfunc TestTraceBatchErrorWhileReadingResultsWhileClosing(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, []pgx.QueryExecMode{pgx.QueryExecModeSimpleProtocol}, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttraceBatchStartCalled := false\n\t\ttracer.traceBatchStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {\n\t\t\ttraceBatchStartCalled = true\n\t\t\trequire.NotNil(t, data.Batch)\n\t\t\trequire.Equal(t, 3, data.Batch.Len())\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceBatchStart\"), \"foo\")\n\t\t}\n\n\t\ttraceBatchQueryCalledCount := 0\n\t\ttracer.traceBatchQuery = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {\n\t\t\ttraceBatchQueryCalledCount++\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\tif traceBatchQueryCalledCount == 2 {\n\t\t\t\trequire.Error(t, data.Err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, data.Err)\n\t\t\t}\n\t\t}\n\n\t\ttraceBatchEndCalled := false\n\t\ttracer.traceBatchEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {\n\t\t\ttraceBatchEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceBatchStart\")))\n\t\t\trequire.Error(t, data.Err)\n\t\t}\n\n\t\tbatch := &pgx.Batch{}\n\t\tbatch.Queue(`select 1`)\n\t\tbatch.Queue(`select 2/n-2 from generate_series(0,10) n`)\n\t\tbatch.Queue(`select 3`)\n\n\t\tbr := conn.SendBatch(context.Background(), batch)\n\t\trequire.True(t, traceBatchStartCalled)\n\t\terr := br.Close()\n\t\trequire.Error(t, err)\n\t\trequire.EqualValues(t, 2, traceBatchQueryCalledCount)\n\t\trequire.True(t, traceBatchEndCalled)\n\t})\n}\n\nfunc TestTraceCopyFrom(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tctx, cancel := context.WithTimeout(ctx, 30*time.Second)\n\t\tdefer cancel()\n\n\t\ttraceCopyFromStartCalled := false\n\t\ttracer.traceCopyFromStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {\n\t\t\ttraceCopyFromStartCalled = true\n\t\t\trequire.Equal(t, pgx.Identifier{\"foo\"}, data.TableName)\n\t\t\trequire.Equal(t, []string{\"a\"}, data.ColumnNames)\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceCopyFromStart\"), \"foo\")\n\t\t}\n\n\t\ttraceCopyFromEndCalled := false\n\t\ttracer.traceCopyFromEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {\n\t\t\ttraceCopyFromEndCalled = true\n\t\t\trequire.Equal(t, \"foo\", ctx.Value(ctxKey(\"fromTraceCopyFromStart\")))\n\t\t\trequire.Equal(t, `COPY 2`, data.CommandTag.String())\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\t_, err := conn.Exec(ctx, `create temporary table foo(a int4)`)\n\t\trequire.NoError(t, err)\n\n\t\tinputRows := [][]any{\n\t\t\t{int32(1)},\n\t\t\t{nil},\n\t\t}\n\n\t\tcopyCount, err := conn.CopyFrom(ctx, pgx.Identifier{\"foo\"}, []string{\"a\"}, pgx.CopyFromRows(inputRows))\n\t\trequire.NoError(t, err)\n\t\trequire.EqualValues(t, len(inputRows), copyCount)\n\t\trequire.True(t, traceCopyFromStartCalled)\n\t\trequire.True(t, traceCopyFromEndCalled)\n\t})\n}\n\nfunc TestTracePrepare(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttracePrepareStartCalled := false\n\t\ttracer.tracePrepareStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {\n\t\t\ttracePrepareStartCalled = true\n\t\t\trequire.Equal(t, `ps`, data.Name)\n\t\t\trequire.Equal(t, `select $1::text`, data.SQL)\n\t\t\treturn context.WithValue(ctx, ctxKey(\"fromTracePrepareStart\"), \"foo\")\n\t\t}\n\n\t\ttracePrepareEndCalled := false\n\t\ttracer.tracePrepareEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {\n\t\t\ttracePrepareEndCalled = true\n\t\t\trequire.False(t, data.AlreadyPrepared)\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\t_, err := conn.Prepare(ctx, \"ps\", `select $1::text`)\n\t\trequire.NoError(t, err)\n\t\trequire.True(t, tracePrepareStartCalled)\n\t\trequire.True(t, tracePrepareEndCalled)\n\n\t\ttracePrepareStartCalled = false\n\t\ttracePrepareEndCalled = false\n\t\ttracer.tracePrepareEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {\n\t\t\ttracePrepareEndCalled = true\n\t\t\trequire.True(t, data.AlreadyPrepared)\n\t\t\trequire.NoError(t, data.Err)\n\t\t}\n\n\t\t_, err = conn.Prepare(ctx, \"ps\", `select $1::text`)\n\t\trequire.NoError(t, err)\n\t\trequire.True(t, tracePrepareStartCalled)\n\t\trequire.True(t, tracePrepareEndCalled)\n\t})\n}\n\nfunc TestTraceConnect(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tconfig := defaultConnTestRunner.CreateConfig(context.Background(), t)\n\tconfig.Tracer = tracer\n\n\ttraceConnectStartCalled := false\n\ttracer.traceConnectStart = func(ctx context.Context, data pgx.TraceConnectStartData) context.Context {\n\t\ttraceConnectStartCalled = true\n\t\trequire.NotNil(t, data.ConnConfig)\n\t\treturn context.WithValue(ctx, ctxKey(\"fromTraceConnectStart\"), \"foo\")\n\t}\n\n\ttraceConnectEndCalled := false\n\ttracer.traceConnectEnd = func(ctx context.Context, data pgx.TraceConnectEndData) {\n\t\ttraceConnectEndCalled = true\n\t\trequire.NotNil(t, data.Conn)\n\t\trequire.NoError(t, data.Err)\n\t}\n\n\tconn1, err := pgx.ConnectConfig(context.Background(), config)\n\trequire.NoError(t, err)\n\tdefer conn1.Close(context.Background())\n\trequire.True(t, traceConnectStartCalled)\n\trequire.True(t, traceConnectEndCalled)\n\n\tconfig, err = pgx.ParseConfig(\"host=/invalid\")\n\trequire.NoError(t, err)\n\tconfig.Tracer = tracer\n\n\ttraceConnectStartCalled = false\n\ttraceConnectEndCalled = false\n\ttracer.traceConnectEnd = func(ctx context.Context, data pgx.TraceConnectEndData) {\n\t\ttraceConnectEndCalled = true\n\t\trequire.Nil(t, data.Conn)\n\t\trequire.Error(t, data.Err)\n\t}\n\n\tconn2, err := pgx.ConnectConfig(context.Background(), config)\n\trequire.Nil(t, conn2)\n\trequire.Error(t, err)\n\trequire.True(t, traceConnectStartCalled)\n\trequire.True(t, traceConnectEndCalled)\n}\n\n// Ensure tracer runs within a transaction.\n//\n// https://github.com/jackc/pgx/issues/2304\nfunc TestTraceWithinTx(t *testing.T) {\n\tt.Parallel()\n\n\ttracer := &testTracer{}\n\n\tctr := defaultConnTestRunner\n\tctr.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {\n\t\tconfig := defaultConnTestRunner.CreateConfig(ctx, t)\n\t\tconfig.Tracer = tracer\n\t\treturn config\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, ctr, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar queries []string\n\t\ttracer.traceQueryStart = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {\n\t\t\tqueries = append(queries, data.SQL)\n\t\t\treturn ctx\n\t\t}\n\n\t\ttx, err := conn.Begin(ctx)\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\t\t_, err = tx.Exec(ctx, `select $1::text`, \"testing\")\n\t\trequire.NoError(t, err)\n\t\terr = tx.Commit(ctx)\n\t\trequire.NoError(t, err)\n\n\t\trequire.Len(t, queries, 3)\n\t\trequire.Equal(t, `begin`, queries[0])\n\t\trequire.Equal(t, `select $1::text`, queries[1])\n\t\trequire.Equal(t, `commit`, queries[2])\n\t})\n}\n"
  },
  {
    "path": "tx.go",
    "content": "package pgx\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5/pgconn\"\n)\n\n// TxIsoLevel is the transaction isolation level (serializable, repeatable read, read committed or read uncommitted)\ntype TxIsoLevel string\n\n// Transaction isolation levels\nconst (\n\tSerializable    TxIsoLevel = \"serializable\"\n\tRepeatableRead  TxIsoLevel = \"repeatable read\"\n\tReadCommitted   TxIsoLevel = \"read committed\"\n\tReadUncommitted TxIsoLevel = \"read uncommitted\"\n)\n\n// TxAccessMode is the transaction access mode (read write or read only)\ntype TxAccessMode string\n\n// Transaction access modes\nconst (\n\tReadWrite TxAccessMode = \"read write\"\n\tReadOnly  TxAccessMode = \"read only\"\n)\n\n// TxDeferrableMode is the transaction deferrable mode (deferrable or not deferrable)\ntype TxDeferrableMode string\n\n// Transaction deferrable modes\nconst (\n\tDeferrable    TxDeferrableMode = \"deferrable\"\n\tNotDeferrable TxDeferrableMode = \"not deferrable\"\n)\n\n// TxOptions are transaction modes within a transaction block\ntype TxOptions struct {\n\tIsoLevel       TxIsoLevel\n\tAccessMode     TxAccessMode\n\tDeferrableMode TxDeferrableMode\n\n\t// BeginQuery is the SQL query that will be executed to begin the transaction. This allows using non-standard syntax\n\t// such as BEGIN PRIORITY HIGH with CockroachDB. If set this will override the other settings.\n\tBeginQuery string\n\t// CommitQuery is the SQL query that will be executed to commit the transaction.\n\tCommitQuery string\n}\n\nvar emptyTxOptions TxOptions\n\nfunc (txOptions TxOptions) beginSQL() string {\n\tif txOptions == emptyTxOptions {\n\t\treturn \"begin\"\n\t}\n\n\tif txOptions.BeginQuery != \"\" {\n\t\treturn txOptions.BeginQuery\n\t}\n\n\tvar buf strings.Builder\n\tbuf.Grow(64) // 64 - maximum length of string with available options\n\tbuf.WriteString(\"begin\")\n\n\tif txOptions.IsoLevel != \"\" {\n\t\tbuf.WriteString(\" isolation level \")\n\t\tbuf.WriteString(string(txOptions.IsoLevel))\n\t}\n\tif txOptions.AccessMode != \"\" {\n\t\tbuf.WriteByte(' ')\n\t\tbuf.WriteString(string(txOptions.AccessMode))\n\t}\n\tif txOptions.DeferrableMode != \"\" {\n\t\tbuf.WriteByte(' ')\n\t\tbuf.WriteString(string(txOptions.DeferrableMode))\n\t}\n\n\treturn buf.String()\n}\n\nvar ErrTxClosed = errors.New(\"tx is closed\")\n\n// ErrTxCommitRollback occurs when an error has occurred in a transaction and\n// Commit() is called. PostgreSQL accepts COMMIT on aborted transactions, but\n// it is treated as ROLLBACK.\nvar ErrTxCommitRollback = errors.New(\"commit unexpectedly resulted in rollback\")\n\n// Begin starts a transaction. Unlike database/sql, the context only affects the begin command. i.e. there is no\n// auto-rollback on context cancellation.\nfunc (c *Conn) Begin(ctx context.Context) (Tx, error) {\n\treturn c.BeginTx(ctx, TxOptions{})\n}\n\n// BeginTx starts a transaction with txOptions determining the transaction mode. Unlike database/sql, the context only\n// affects the begin command. i.e. there is no auto-rollback on context cancellation.\nfunc (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) {\n\t_, err := c.Exec(ctx, txOptions.beginSQL())\n\tif err != nil {\n\t\t// begin should never fail unless there is an underlying connection issue or\n\t\t// a context timeout. In either case, the connection is possibly broken.\n\t\tc.die()\n\t\treturn nil, err\n\t}\n\n\treturn &dbTx{\n\t\tconn:        c,\n\t\tcommitQuery: txOptions.CommitQuery,\n\t}, nil\n}\n\n// Tx represents a database transaction.\n//\n// Tx is an interface instead of a struct to enable connection pools to be implemented without relying on internal pgx\n// state, to support pseudo-nested transactions with savepoints, and to allow tests to mock transactions. However,\n// adding a method to an interface is technically a breaking change. If new methods are added to Conn it may be\n// desirable to add them to Tx as well. Because of this the Tx interface is partially excluded from semantic version\n// requirements. Methods will not be removed or changed, but new methods may be added.\ntype Tx interface {\n\t// Begin starts a pseudo nested transaction.\n\tBegin(ctx context.Context) (Tx, error)\n\n\t// Commit commits the transaction if this is a real transaction or releases the savepoint if this is a pseudo nested\n\t// transaction. Commit will return an error where errors.Is(ErrTxClosed) is true if the Tx is already closed, but is\n\t// otherwise safe to call multiple times. If the commit fails with a rollback status (e.g. the transaction was already\n\t// in a broken state) then an error where errors.Is(ErrTxCommitRollback) is true will be returned.\n\tCommit(ctx context.Context) error\n\n\t// Rollback rolls back the transaction if this is a real transaction or rolls back to the savepoint if this is a\n\t// pseudo nested transaction. Rollback will return an error where errors.Is(ErrTxClosed) is true if the Tx is already\n\t// closed, but is otherwise safe to call multiple times. Hence, a defer tx.Rollback() is safe even if tx.Commit() will\n\t// be called first in a non-error condition. Any other failure of a real transaction will result in the connection\n\t// being closed.\n\tRollback(ctx context.Context) error\n\n\tCopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error)\n\tSendBatch(ctx context.Context, b *Batch) BatchResults\n\tLargeObjects() LargeObjects\n\n\tPrepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error)\n\n\tExec(ctx context.Context, sql string, arguments ...any) (commandTag pgconn.CommandTag, err error)\n\tQuery(ctx context.Context, sql string, args ...any) (Rows, error)\n\tQueryRow(ctx context.Context, sql string, args ...any) Row\n\n\t// Conn returns the underlying *Conn that on which this transaction is executing.\n\tConn() *Conn\n}\n\n// dbTx represents a database transaction.\n//\n// All dbTx methods return ErrTxClosed if Commit or Rollback has already been\n// called on the dbTx.\ntype dbTx struct {\n\tconn         *Conn\n\tsavepointNum int64\n\tclosed       bool\n\tcommitQuery  string\n}\n\n// Begin starts a pseudo nested transaction implemented with a savepoint.\nfunc (tx *dbTx) Begin(ctx context.Context) (Tx, error) {\n\tif tx.closed {\n\t\treturn nil, ErrTxClosed\n\t}\n\n\ttx.savepointNum++\n\t_, err := tx.conn.Exec(ctx, \"savepoint sp_\"+strconv.FormatInt(tx.savepointNum, 10))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &dbSimulatedNestedTx{tx: tx, savepointNum: tx.savepointNum}, nil\n}\n\n// Commit commits the transaction.\nfunc (tx *dbTx) Commit(ctx context.Context) error {\n\tif tx.closed {\n\t\treturn ErrTxClosed\n\t}\n\n\tcommandSQL := \"commit\"\n\tif tx.commitQuery != \"\" {\n\t\tcommandSQL = tx.commitQuery\n\t}\n\n\tcommandTag, err := tx.conn.Exec(ctx, commandSQL)\n\ttx.closed = true\n\tif err != nil {\n\t\tif tx.conn.PgConn().TxStatus() != 'I' {\n\t\t\t_ = tx.conn.Close(ctx) // already have error to return\n\t\t}\n\t\treturn err\n\t}\n\tif commandTag.String() == \"ROLLBACK\" {\n\t\treturn ErrTxCommitRollback\n\t}\n\n\treturn nil\n}\n\n// Rollback rolls back the transaction. Rollback will return ErrTxClosed if the\n// Tx is already closed, but is otherwise safe to call multiple times. Hence, a\n// defer tx.Rollback() is safe even if tx.Commit() will be called first in a\n// non-error condition.\nfunc (tx *dbTx) Rollback(ctx context.Context) error {\n\tif tx.closed {\n\t\treturn ErrTxClosed\n\t}\n\n\t_, err := tx.conn.Exec(ctx, \"rollback\")\n\ttx.closed = true\n\tif err != nil {\n\t\t// A rollback failure leaves the connection in an undefined state\n\t\ttx.conn.die()\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Exec delegates to the underlying *Conn\nfunc (tx *dbTx) Exec(ctx context.Context, sql string, arguments ...any) (commandTag pgconn.CommandTag, err error) {\n\tif tx.closed {\n\t\treturn pgconn.CommandTag{}, ErrTxClosed\n\t}\n\n\treturn tx.conn.Exec(ctx, sql, arguments...)\n}\n\n// Prepare delegates to the underlying *Conn\nfunc (tx *dbTx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) {\n\tif tx.closed {\n\t\treturn nil, ErrTxClosed\n\t}\n\n\treturn tx.conn.Prepare(ctx, name, sql)\n}\n\n// Query delegates to the underlying *Conn\nfunc (tx *dbTx) Query(ctx context.Context, sql string, args ...any) (Rows, error) {\n\tif tx.closed {\n\t\t// Because checking for errors can be deferred to the *Rows, build one with the error\n\t\terr := ErrTxClosed\n\t\treturn &baseRows{closed: true, err: err}, err\n\t}\n\n\treturn tx.conn.Query(ctx, sql, args...)\n}\n\n// QueryRow delegates to the underlying *Conn\nfunc (tx *dbTx) QueryRow(ctx context.Context, sql string, args ...any) Row {\n\trows, _ := tx.Query(ctx, sql, args...)\n\treturn (*connRow)(rows.(*baseRows))\n}\n\n// CopyFrom delegates to the underlying *Conn\nfunc (tx *dbTx) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) {\n\tif tx.closed {\n\t\treturn 0, ErrTxClosed\n\t}\n\n\treturn tx.conn.CopyFrom(ctx, tableName, columnNames, rowSrc)\n}\n\n// SendBatch delegates to the underlying *Conn\nfunc (tx *dbTx) SendBatch(ctx context.Context, b *Batch) BatchResults {\n\tif tx.closed {\n\t\treturn &batchResults{err: ErrTxClosed}\n\t}\n\n\treturn tx.conn.SendBatch(ctx, b)\n}\n\n// LargeObjects returns a LargeObjects instance for the transaction.\nfunc (tx *dbTx) LargeObjects() LargeObjects {\n\treturn LargeObjects{tx: tx}\n}\n\nfunc (tx *dbTx) Conn() *Conn {\n\treturn tx.conn\n}\n\n// dbSimulatedNestedTx represents a simulated nested transaction implemented by a savepoint.\ntype dbSimulatedNestedTx struct {\n\ttx           Tx\n\tsavepointNum int64\n\tclosed       bool\n}\n\n// Begin starts a pseudo nested transaction implemented with a savepoint.\nfunc (sp *dbSimulatedNestedTx) Begin(ctx context.Context) (Tx, error) {\n\tif sp.closed {\n\t\treturn nil, ErrTxClosed\n\t}\n\n\treturn sp.tx.Begin(ctx)\n}\n\n// Commit releases the savepoint essentially committing the pseudo nested transaction.\nfunc (sp *dbSimulatedNestedTx) Commit(ctx context.Context) error {\n\tif sp.closed {\n\t\treturn ErrTxClosed\n\t}\n\n\t_, err := sp.Exec(ctx, \"release savepoint sp_\"+strconv.FormatInt(sp.savepointNum, 10))\n\tsp.closed = true\n\treturn err\n}\n\n// Rollback rolls back to the savepoint essentially rolling back the pseudo nested transaction. Rollback will return\n// ErrTxClosed if the dbSavepoint is already closed, but is otherwise safe to call multiple times. Hence, a defer sp.Rollback()\n// is safe even if sp.Commit() will be called first in a non-error condition.\nfunc (sp *dbSimulatedNestedTx) Rollback(ctx context.Context) error {\n\tif sp.closed {\n\t\treturn ErrTxClosed\n\t}\n\n\t_, err := sp.Exec(ctx, \"rollback to savepoint sp_\"+strconv.FormatInt(sp.savepointNum, 10))\n\tsp.closed = true\n\treturn err\n}\n\n// Exec delegates to the underlying Tx\nfunc (sp *dbSimulatedNestedTx) Exec(ctx context.Context, sql string, arguments ...any) (commandTag pgconn.CommandTag, err error) {\n\tif sp.closed {\n\t\treturn pgconn.CommandTag{}, ErrTxClosed\n\t}\n\n\treturn sp.tx.Exec(ctx, sql, arguments...)\n}\n\n// Prepare delegates to the underlying Tx\nfunc (sp *dbSimulatedNestedTx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) {\n\tif sp.closed {\n\t\treturn nil, ErrTxClosed\n\t}\n\n\treturn sp.tx.Prepare(ctx, name, sql)\n}\n\n// Query delegates to the underlying Tx\nfunc (sp *dbSimulatedNestedTx) Query(ctx context.Context, sql string, args ...any) (Rows, error) {\n\tif sp.closed {\n\t\t// Because checking for errors can be deferred to the *Rows, build one with the error\n\t\terr := ErrTxClosed\n\t\treturn &baseRows{closed: true, err: err}, err\n\t}\n\n\treturn sp.tx.Query(ctx, sql, args...)\n}\n\n// QueryRow delegates to the underlying Tx\nfunc (sp *dbSimulatedNestedTx) QueryRow(ctx context.Context, sql string, args ...any) Row {\n\trows, _ := sp.Query(ctx, sql, args...)\n\treturn (*connRow)(rows.(*baseRows))\n}\n\n// CopyFrom delegates to the underlying *Conn\nfunc (sp *dbSimulatedNestedTx) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) {\n\tif sp.closed {\n\t\treturn 0, ErrTxClosed\n\t}\n\n\treturn sp.tx.CopyFrom(ctx, tableName, columnNames, rowSrc)\n}\n\n// SendBatch delegates to the underlying *Conn\nfunc (sp *dbSimulatedNestedTx) SendBatch(ctx context.Context, b *Batch) BatchResults {\n\tif sp.closed {\n\t\treturn &batchResults{err: ErrTxClosed}\n\t}\n\n\treturn sp.tx.SendBatch(ctx, b)\n}\n\nfunc (sp *dbSimulatedNestedTx) LargeObjects() LargeObjects {\n\treturn LargeObjects{tx: sp}\n}\n\nfunc (sp *dbSimulatedNestedTx) Conn() *Conn {\n\treturn sp.tx.Conn()\n}\n\n// BeginFunc calls Begin on db and then calls fn. If fn does not return an error then it calls Commit on db. If fn\n// returns an error it calls Rollback on db. The context will be used when executing the transaction control statements\n// (BEGIN, ROLLBACK, and COMMIT) but does not otherwise affect the execution of fn.\nfunc BeginFunc(\n\tctx context.Context,\n\tdb interface {\n\t\tBegin(ctx context.Context) (Tx, error)\n\t},\n\tfn func(Tx) error,\n) (err error) {\n\tvar tx Tx\n\ttx, err = db.Begin(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn beginFuncExec(ctx, tx, fn)\n}\n\n// BeginTxFunc calls BeginTx on db and then calls fn. If fn does not return an error then it calls Commit on db. If fn\n// returns an error it calls Rollback on db. The context will be used when executing the transaction control statements\n// (BEGIN, ROLLBACK, and COMMIT) but does not otherwise affect the execution of fn.\nfunc BeginTxFunc(\n\tctx context.Context,\n\tdb interface {\n\t\tBeginTx(ctx context.Context, txOptions TxOptions) (Tx, error)\n\t},\n\ttxOptions TxOptions,\n\tfn func(Tx) error,\n) (err error) {\n\tvar tx Tx\n\ttx, err = db.BeginTx(ctx, txOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn beginFuncExec(ctx, tx, fn)\n}\n\nfunc beginFuncExec(ctx context.Context, tx Tx, fn func(Tx) error) (err error) {\n\tdefer func() {\n\t\trollbackErr := tx.Rollback(ctx)\n\t\tif rollbackErr != nil && !errors.Is(rollbackErr, ErrTxClosed) {\n\t\t\terr = rollbackErr\n\t\t}\n\t}()\n\n\tfErr := fn(tx)\n\tif fErr != nil {\n\t\t_ = tx.Rollback(ctx) // ignore rollback error as there is already an error to return\n\t\treturn fErr\n\t}\n\n\treturn tx.Commit(ctx)\n}\n"
  },
  {
    "path": "tx_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestTransactionSuccessfulCommit(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\tif _, err := conn.Exec(context.Background(), createSql); err != nil {\n\t\tt.Fatalf(\"Failed to create table: %v\", err)\n\t}\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Begin failed: %v\", err)\n\t}\n\n\t_, err = tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\terr = tx.Commit(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Commit failed: %v\", err)\n\t}\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t}\n\tif n != 1 {\n\t\tt.Fatalf(\"Did not receive correct number of rows: %v\", n)\n\t}\n}\n\nfunc TestTxCommitWhenTxBroken(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\tif _, err := conn.Exec(context.Background(), createSql); err != nil {\n\t\tt.Fatalf(\"Failed to create table: %v\", err)\n\t}\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Begin failed: %v\", err)\n\t}\n\n\tif _, err := tx.Exec(context.Background(), \"insert into foo(id) values (1)\"); err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\t// Purposely break transaction\n\tif _, err := tx.Exec(context.Background(), \"syntax error\"); err == nil {\n\t\tt.Fatal(\"Unexpected success\")\n\t}\n\n\terr = tx.Commit(context.Background())\n\tif err != pgx.ErrTxCommitRollback {\n\t\tt.Fatalf(\"Expected error %v, got %v\", pgx.ErrTxCommitRollback, err)\n\t}\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t}\n\tif n != 0 {\n\t\tt.Fatalf(\"Did not receive correct number of rows: %v\", n)\n\t}\n}\n\nfunc TestTxCommitWhenDeferredConstraintFailure(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server does not support deferred constraint (https://github.com/cockroachdb/cockroach/issues/31632)\")\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id) initially deferred\n    );\n  `\n\n\tif _, err := conn.Exec(context.Background(), createSql); err != nil {\n\t\tt.Fatalf(\"Failed to create table: %v\", err)\n\t}\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Begin failed: %v\", err)\n\t}\n\n\tif _, err := tx.Exec(context.Background(), \"insert into foo(id) values (1)\"); err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\tif _, err := tx.Exec(context.Background(), \"insert into foo(id) values (1)\"); err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\terr = tx.Commit(context.Background())\n\tif pgErr, ok := err.(*pgconn.PgError); !ok || pgErr.Code != \"23505\" {\n\t\tt.Fatalf(\"Expected unique constraint violation 23505, got %#v\", err)\n\t}\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t}\n\tif n != 0 {\n\t\tt.Fatalf(\"Did not receive correct number of rows: %v\", n)\n\t}\n}\n\nfunc TestTxCommitSerializationFailure(t *testing.T) {\n\tt.Parallel()\n\n\tc1 := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, c1)\n\n\tif c1.PgConn().ParameterStatus(\"crdb_version\") != \"\" {\n\t\tt.Skip(\"Skipping due to known server issue: (https://github.com/cockroachdb/cockroach/issues/60754)\")\n\t}\n\n\tc2 := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, c2)\n\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tc1.Exec(ctx, `drop table if exists tx_serializable_sums`)\n\t_, err := c1.Exec(ctx, `create table tx_serializable_sums(num integer);`)\n\tif err != nil {\n\t\tt.Fatalf(\"Unable to create temporary table: %v\", err)\n\t}\n\tdefer c1.Exec(ctx, `drop table tx_serializable_sums`)\n\n\ttx1, err := c1.BeginTx(ctx, pgx.TxOptions{IsoLevel: pgx.Serializable})\n\tif err != nil {\n\t\tt.Fatalf(\"Begin failed: %v\", err)\n\t}\n\tdefer tx1.Rollback(ctx)\n\n\ttx2, err := c2.BeginTx(ctx, pgx.TxOptions{IsoLevel: pgx.Serializable})\n\tif err != nil {\n\t\tt.Fatalf(\"Begin failed: %v\", err)\n\t}\n\tdefer tx2.Rollback(ctx)\n\n\t_, err = tx1.Exec(ctx, `insert into tx_serializable_sums(num) select sum(num)::int from tx_serializable_sums`)\n\tif err != nil {\n\t\tt.Fatalf(\"Exec failed: %v\", err)\n\t}\n\n\t_, err = tx2.Exec(ctx, `insert into tx_serializable_sums(num) select sum(num)::int from tx_serializable_sums`)\n\tif err != nil {\n\t\tt.Fatalf(\"Exec failed: %v\", err)\n\t}\n\n\terr = tx1.Commit(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"Commit failed: %v\", err)\n\t}\n\n\terr = tx2.Commit(ctx)\n\tif pgErr, ok := err.(*pgconn.PgError); !ok || pgErr.Code != \"40001\" {\n\t\tt.Fatalf(\"Expected serialization error 40001, got %#v\", err)\n\t}\n\n\tensureConnValid(t, c1)\n\tensureConnValid(t, c2)\n}\n\nfunc TestTransactionSuccessfulRollback(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\tif _, err := conn.Exec(context.Background(), createSql); err != nil {\n\t\tt.Fatalf(\"Failed to create table: %v\", err)\n\t}\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Begin failed: %v\", err)\n\t}\n\n\t_, err = tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\terr = tx.Rollback(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Rollback failed: %v\", err)\n\t}\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t}\n\tif n != 0 {\n\t\tt.Fatalf(\"Did not receive correct number of rows: %v\", n)\n\t}\n}\n\nfunc TestTransactionRollbackFailsClosesConnection(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\ttx, err := conn.Begin(ctx)\n\trequire.NoError(t, err)\n\n\tcancel()\n\n\terr = tx.Rollback(ctx)\n\trequire.Error(t, err)\n\n\trequire.True(t, conn.IsClosed())\n}\n\nfunc TestBeginIsoLevels(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tpgxtest.SkipCockroachDB(t, conn, \"Server always uses SERIALIZABLE isolation (https://www.cockroachlabs.com/docs/stable/demo-serializable.html)\")\n\n\tisoLevels := []pgx.TxIsoLevel{pgx.Serializable, pgx.RepeatableRead, pgx.ReadCommitted, pgx.ReadUncommitted}\n\tfor _, iso := range isoLevels {\n\t\ttx, err := conn.BeginTx(context.Background(), pgx.TxOptions{IsoLevel: iso})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"conn.Begin failed: %v\", err)\n\t\t}\n\n\t\tvar level pgx.TxIsoLevel\n\t\tconn.QueryRow(context.Background(), \"select current_setting('transaction_isolation')\").Scan(&level)\n\t\tif level != iso {\n\t\t\tt.Errorf(\"Expected to be in isolation level %v but was %v\", iso, level)\n\t\t}\n\n\t\terr = tx.Rollback(context.Background())\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"tx.Rollback failed: %v\", err)\n\t\t}\n\t}\n}\n\nfunc TestBeginFunc(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\t_, err := conn.Exec(context.Background(), createSql)\n\trequire.NoError(t, err)\n\n\terr = pgx.BeginFunc(context.Background(), conn, func(tx pgx.Tx) error {\n\t\t_, err := tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\t\trequire.NoError(t, err)\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 1, n)\n}\n\nfunc TestBeginFuncRollbackOnError(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\t_, err := conn.Exec(context.Background(), createSql)\n\trequire.NoError(t, err)\n\n\terr = pgx.BeginFunc(context.Background(), conn, func(tx pgx.Tx) error {\n\t\t_, err := tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\t\trequire.NoError(t, err)\n\t\treturn errors.New(\"some error\")\n\t})\n\trequire.EqualError(t, err, \"some error\")\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 0, n)\n}\n\nfunc TestBeginReadOnly(t *testing.T) {\n\tt.Parallel()\n\tskipCockroachDB(t, \"CockroachDB auto commits DDL by default\")\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\ttx, err := conn.BeginTx(context.Background(), pgx.TxOptions{AccessMode: pgx.ReadOnly})\n\tif err != nil {\n\t\tt.Fatalf(\"conn.Begin failed: %v\", err)\n\t}\n\tdefer tx.Rollback(context.Background())\n\n\t_, err = conn.Exec(context.Background(), \"create table foo(id serial primary key)\")\n\tif pgErr, ok := err.(*pgconn.PgError); !ok || pgErr.Code != \"25006\" {\n\t\tt.Errorf(\"Expected error SQLSTATE 25006, but got %#v\", err)\n\t}\n}\n\nfunc TestBeginTxBeginQuery(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttx, err := conn.BeginTx(ctx, pgx.TxOptions{BeginQuery: \"begin read only\"})\n\t\trequire.NoError(t, err)\n\t\tdefer tx.Rollback(ctx)\n\n\t\tvar readOnly bool\n\t\tconn.QueryRow(ctx, \"select current_setting('transaction_read_only')::bool\").Scan(&readOnly)\n\t\trequire.True(t, readOnly)\n\n\t\terr = tx.Rollback(ctx)\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestTxNestedTransactionCommit(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\tif _, err := conn.Exec(context.Background(), createSql); err != nil {\n\t\tt.Fatalf(\"Failed to create table: %v\", err)\n\t}\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\tnestedTx, err := tx.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = nestedTx.Exec(context.Background(), \"insert into foo(id) values (2)\")\n\tif err != nil {\n\t\tt.Fatalf(\"nestedTx.Exec failed: %v\", err)\n\t}\n\n\tdoubleNestedTx, err := nestedTx.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = doubleNestedTx.Exec(context.Background(), \"insert into foo(id) values (3)\")\n\tif err != nil {\n\t\tt.Fatalf(\"doubleNestedTx.Exec failed: %v\", err)\n\t}\n\n\terr = doubleNestedTx.Commit(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"doubleNestedTx.Commit failed: %v\", err)\n\t}\n\n\terr = nestedTx.Commit(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"nestedTx.Commit failed: %v\", err)\n\t}\n\n\terr = tx.Commit(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Commit failed: %v\", err)\n\t}\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t}\n\tif n != 3 {\n\t\tt.Fatalf(\"Did not receive correct number of rows: %v\", n)\n\t}\n}\n\nfunc TestTxNestedTransactionRollback(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\tif _, err := conn.Exec(context.Background(), createSql); err != nil {\n\t\tt.Fatalf(\"Failed to create table: %v\", err)\n\t}\n\n\ttx, err := conn.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = tx.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\tnestedTx, err := tx.Begin(context.Background())\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = nestedTx.Exec(context.Background(), \"insert into foo(id) values (2)\")\n\tif err != nil {\n\t\tt.Fatalf(\"nestedTx.Exec failed: %v\", err)\n\t}\n\n\terr = nestedTx.Rollback(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"nestedTx.Rollback failed: %v\", err)\n\t}\n\n\t_, err = tx.Exec(context.Background(), \"insert into foo(id) values (3)\")\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Exec failed: %v\", err)\n\t}\n\n\terr = tx.Commit(context.Background())\n\tif err != nil {\n\t\tt.Fatalf(\"tx.Commit failed: %v\", err)\n\t}\n\n\tvar n int64\n\terr = conn.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\tif err != nil {\n\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t}\n\tif n != 2 {\n\t\tt.Fatalf(\"Did not receive correct number of rows: %v\", n)\n\t}\n}\n\nfunc TestTxBeginFuncNestedTransactionCommit(t *testing.T) {\n\tt.Parallel()\n\n\tdb := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, db)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\t_, err := db.Exec(context.Background(), createSql)\n\trequire.NoError(t, err)\n\n\terr = pgx.BeginFunc(context.Background(), db, func(db pgx.Tx) error {\n\t\t_, err := db.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\t\trequire.NoError(t, err)\n\n\t\terr = pgx.BeginFunc(context.Background(), db, func(db pgx.Tx) error {\n\t\t\t_, err := db.Exec(context.Background(), \"insert into foo(id) values (2)\")\n\t\t\trequire.NoError(t, err)\n\n\t\t\terr = pgx.BeginFunc(context.Background(), db, func(db pgx.Tx) error {\n\t\t\t\t_, err := db.Exec(context.Background(), \"insert into foo(id) values (3)\")\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\trequire.NoError(t, err)\n\n\t\t\treturn nil\n\t\t})\n\t\trequire.NoError(t, err)\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\n\tvar n int64\n\terr = db.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 3, n)\n}\n\nfunc TestTxBeginFuncNestedTransactionRollback(t *testing.T) {\n\tt.Parallel()\n\n\tdb := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, db)\n\n\tcreateSql := `\n    create temporary table foo(\n      id integer,\n      unique (id)\n    );\n  `\n\n\t_, err := db.Exec(context.Background(), createSql)\n\trequire.NoError(t, err)\n\n\terr = pgx.BeginFunc(context.Background(), db, func(db pgx.Tx) error {\n\t\t_, err := db.Exec(context.Background(), \"insert into foo(id) values (1)\")\n\t\trequire.NoError(t, err)\n\n\t\terr = pgx.BeginFunc(context.Background(), db, func(db pgx.Tx) error {\n\t\t\t_, err := db.Exec(context.Background(), \"insert into foo(id) values (2)\")\n\t\t\trequire.NoError(t, err)\n\t\t\treturn errors.New(\"do a rollback\")\n\t\t})\n\t\trequire.EqualError(t, err, \"do a rollback\")\n\n\t\t_, err = db.Exec(context.Background(), \"insert into foo(id) values (3)\")\n\t\trequire.NoError(t, err)\n\n\t\treturn nil\n\t})\n\trequire.NoError(t, err)\n\n\tvar n int64\n\terr = db.QueryRow(context.Background(), \"select count(*) from foo\").Scan(&n)\n\trequire.NoError(t, err)\n\trequire.EqualValues(t, 2, n)\n}\n\nfunc TestTxSendBatchClosed(t *testing.T) {\n\tt.Parallel()\n\n\tdb := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, db)\n\n\ttx, err := db.Begin(context.Background())\n\trequire.NoError(t, err)\n\tdefer tx.Rollback(context.Background())\n\n\terr = tx.Commit(context.Background())\n\trequire.NoError(t, err)\n\n\tbatch := &pgx.Batch{}\n\tbatch.Queue(\"select 1\")\n\tbatch.Queue(\"select 2\")\n\tbatch.Queue(\"select 3\")\n\n\tbr := tx.SendBatch(context.Background(), batch)\n\tdefer br.Close()\n\n\tvar n int\n\n\t_, err = br.Exec()\n\trequire.Error(t, err)\n\n\terr = br.QueryRow().Scan(&n)\n\trequire.Error(t, err)\n\n\t_, err = br.Query()\n\trequire.Error(t, err)\n}\n"
  },
  {
    "path": "values.go",
    "content": "package pgx\n\nimport (\n\t\"errors\"\n\n\t\"github.com/jackc/pgx/v5/internal/pgio\"\n\t\"github.com/jackc/pgx/v5/pgtype\"\n)\n\n// PostgreSQL format codes\nconst (\n\tTextFormatCode   = 0\n\tBinaryFormatCode = 1\n)\n\nfunc convertSimpleArgument(m *pgtype.Map, arg any) (any, error) {\n\tbuf, err := m.Encode(0, TextFormatCode, arg, []byte{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif buf == nil {\n\t\treturn nil, nil\n\t}\n\treturn string(buf), nil\n}\n\nfunc encodeCopyValue(m *pgtype.Map, buf []byte, oid uint32, arg any) ([]byte, error) {\n\tsp := len(buf)\n\tbuf = pgio.AppendInt32(buf, -1)\n\targBuf, err := m.Encode(oid, BinaryFormatCode, arg, buf)\n\tif err != nil {\n\t\tif argBuf2, err2 := tryScanStringCopyValueThenEncode(m, buf, oid, arg); err2 == nil {\n\t\t\targBuf = argBuf2\n\t\t} else {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif argBuf != nil {\n\t\tbuf = argBuf\n\t\tpgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))\n\t}\n\treturn buf, nil\n}\n\nfunc tryScanStringCopyValueThenEncode(m *pgtype.Map, buf []byte, oid uint32, arg any) ([]byte, error) {\n\ts, ok := arg.(string)\n\tif !ok {\n\t\ttextBuf, err := m.Encode(oid, TextFormatCode, arg, nil)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"not a string and cannot be encoded as text\")\n\t\t}\n\t\ts = string(textBuf)\n\t}\n\n\tvar v any\n\terr := m.Scan(oid, TextFormatCode, []byte(s), &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn m.Encode(oid, BinaryFormatCode, v, buf)\n}\n"
  },
  {
    "path": "values_test.go",
    "content": "package pgx_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxtest\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDateTranscode(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tdates := []time.Time{\n\t\t\ttime.Date(1, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1000, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1600, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1700, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2001, 1, 2, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2004, 2, 29, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2013, 7, 4, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2013, 12, 25, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2029, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2081, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2096, 2, 29, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(2550, 1, 1, 0, 0, 0, 0, time.UTC),\n\t\t\ttime.Date(9999, 12, 31, 0, 0, 0, 0, time.UTC),\n\t\t}\n\n\t\tfor _, actualDate := range dates {\n\t\t\tvar d time.Time\n\n\t\t\terr := conn.QueryRow(context.Background(), \"select $1::date\", actualDate).Scan(&d)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Unexpected failure on QueryRow Scan: %v\", err)\n\t\t\t}\n\t\t\tif !actualDate.Equal(d) {\n\t\t\t\tt.Errorf(\"Did not transcode date successfully: %v is not %v\", d, actualDate)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestTimestampTzTranscode(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tinputTime := time.Date(2013, 1, 2, 3, 4, 5, 6000, time.Local)\n\n\t\tvar outputTime time.Time\n\n\t\terr := conn.QueryRow(context.Background(), \"select $1::timestamptz\", inputTime).Scan(&outputTime)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"QueryRow Scan failed: %v\", err)\n\t\t}\n\t\tif !inputTime.Equal(outputTime) {\n\t\t\tt.Errorf(\"Did not transcode time successfully: %v is not %v\", outputTime, inputTime)\n\t\t}\n\t})\n}\n\n// TODO - move these tests to pgtype\n\nfunc TestJSONAndJSONBTranscode(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tfor _, typename := range []string{\"json\", \"jsonb\"} {\n\t\t\tif _, ok := conn.TypeMap().TypeForName(typename); !ok {\n\t\t\t\tcontinue // No JSON/JSONB type -- must be running against old PostgreSQL\n\t\t\t}\n\n\t\t\ttestJSONString(t, conn, typename)\n\t\t\ttestJSONStringPointer(t, conn, typename)\n\t\t}\n\t})\n}\n\nfunc TestJSONAndJSONBTranscodeExtendedOnly(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\n\tfor _, typename := range []string{\"json\", \"jsonb\"} {\n\t\tif _, ok := conn.TypeMap().TypeForName(typename); !ok {\n\t\t\tcontinue // No JSON/JSONB type -- must be running against old PostgreSQL\n\t\t}\n\t\ttestJSONSingleLevelStringMap(t, conn, typename)\n\t\ttestJSONNestedMap(t, conn, typename)\n\t\ttestJSONStringArray(t, conn, typename)\n\t\ttestJSONInt64Array(t, conn, typename)\n\t\ttestJSONInt16ArrayFailureDueToOverflow(t, conn, typename)\n\t\ttestJSONStruct(t, conn, typename)\n\t}\n}\n\nfunc testJSONString(t testing.TB, conn *pgx.Conn, typename string) {\n\tinput := `{\"key\": \"value\"}`\n\texpectedOutput := map[string]string{\"key\": \"value\"}\n\tvar output map[string]string\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t\treturn\n\t}\n\n\tif !reflect.DeepEqual(expectedOutput, output) {\n\t\tt.Errorf(\"%s: Did not transcode map[string]string successfully: %v is not %v\", typename, expectedOutput, output)\n\t\treturn\n\t}\n}\n\nfunc testJSONStringPointer(t testing.TB, conn *pgx.Conn, typename string) {\n\tinput := `{\"key\": \"value\"}`\n\texpectedOutput := map[string]string{\"key\": \"value\"}\n\tvar output map[string]string\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, &input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t\treturn\n\t}\n\n\tif !reflect.DeepEqual(expectedOutput, output) {\n\t\tt.Errorf(\"%s: Did not transcode map[string]string successfully: %v is not %v\", typename, expectedOutput, output)\n\t\treturn\n\t}\n}\n\nfunc testJSONSingleLevelStringMap(t *testing.T, conn *pgx.Conn, typename string) {\n\tinput := map[string]string{\"key\": \"value\"}\n\tvar output map[string]string\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t\treturn\n\t}\n\n\tif !reflect.DeepEqual(input, output) {\n\t\tt.Errorf(\"%s: Did not transcode map[string]string successfully: %v is not %v\", typename, input, output)\n\t\treturn\n\t}\n}\n\nfunc testJSONNestedMap(t *testing.T, conn *pgx.Conn, typename string) {\n\tinput := map[string]any{\n\t\t\"name\":      \"Uncanny\",\n\t\t\"stats\":     map[string]any{\"hp\": float64(107), \"maxhp\": float64(150)},\n\t\t\"inventory\": []any{\"phone\", \"key\"},\n\t}\n\tvar output map[string]any\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t\treturn\n\t}\n\n\tif !reflect.DeepEqual(input, output) {\n\t\tt.Errorf(\"%s: Did not transcode map[string]any successfully: %v is not %v\", typename, input, output)\n\t\treturn\n\t}\n}\n\nfunc testJSONStringArray(t *testing.T, conn *pgx.Conn, typename string) {\n\tinput := []string{\"foo\", \"bar\", \"baz\"}\n\tvar output []string\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t}\n\n\tif !reflect.DeepEqual(input, output) {\n\t\tt.Errorf(\"%s: Did not transcode []string successfully: %v is not %v\", typename, input, output)\n\t}\n}\n\nfunc testJSONInt64Array(t *testing.T, conn *pgx.Conn, typename string) {\n\tinput := []int64{1, 2, 234432}\n\tvar output []int64\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t}\n\n\tif !reflect.DeepEqual(input, output) {\n\t\tt.Errorf(\"%s: Did not transcode []int64 successfully: %v is not %v\", typename, input, output)\n\t}\n}\n\nfunc testJSONInt16ArrayFailureDueToOverflow(t *testing.T, conn *pgx.Conn, typename string) {\n\tinput := []int{1, 2, 234432}\n\tvar output []int16\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tfieldName := typename\n\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" && typename == \"json\" {\n\t\tfieldName = \"jsonb\" // Seems like CockroachDB treats json as jsonb.\n\t}\n\texpectedMessage := fmt.Sprintf(\"can't scan into dest[0] (col: %s): json: cannot unmarshal number 234432 into Go value of type int16\", fieldName)\n\tif err == nil || err.Error() != expectedMessage {\n\t\tt.Errorf(\"%s: Expected *json.UnmarshalTypeError, but got %v\", typename, err)\n\t}\n}\n\nfunc testJSONStruct(t *testing.T, conn *pgx.Conn, typename string) {\n\ttype person struct {\n\t\tName string `json:\"name\"`\n\t\tAge  int    `json:\"age\"`\n\t}\n\n\tinput := person{\n\t\tName: \"John\",\n\t\tAge:  42,\n\t}\n\n\tvar output person\n\n\terr := conn.QueryRow(context.Background(), \"select $1::\"+typename, input).Scan(&output)\n\tif err != nil {\n\t\tt.Errorf(\"%s: QueryRow Scan failed: %v\", typename, err)\n\t}\n\n\tif !reflect.DeepEqual(input, output) {\n\t\tt.Errorf(\"%s: Did not transcode struct successfully: %v is not %v\", typename, input, output)\n\t}\n}\n\nfunc mustParseCIDR(t testing.TB, s string) *net.IPNet {\n\t_, ipnet, err := net.ParseCIDR(s)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn ipnet\n}\n\nfunc TestInetCIDRTranscodeIPNet(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttests := []struct {\n\t\t\tsql   string\n\t\t\tvalue *net.IPNet\n\t\t}{\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"0.0.0.0/32\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"127.0.0.1/32\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"12.34.56.0/32\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"192.168.1.0/24\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"255.0.0.0/8\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"255.255.255.255/32\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"::/128\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"::/0\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"::1/128\")},\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"2607:f8b0:4009:80b::200e/128\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"0.0.0.0/32\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"127.0.0.1/32\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"12.34.56.0/32\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"192.168.1.0/24\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"255.0.0.0/8\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"255.255.255.255/32\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"::/128\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"::/0\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"::1/128\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"2607:f8b0:4009:80b::200e/128\")},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" && strings.Contains(tt.sql, \"cidr\") {\n\t\t\t\tt.Log(\"Server does not support cidr type (https://github.com/cockroachdb/cockroach/issues/18846)\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar actual net.IPNet\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.value).Scan(&actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, value -> %v)\", i, err, tt.sql, tt.value)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif actual.String() != tt.value.String() {\n\t\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v)\", i, tt.value, actual, tt.sql)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestInetCIDRTranscodeIP(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttests := []struct {\n\t\t\tsql   string\n\t\t\tvalue net.IP\n\t\t}{\n\t\t\t{\"select $1::inet\", net.ParseIP(\"0.0.0.0\")},\n\t\t\t{\"select $1::inet\", net.ParseIP(\"127.0.0.1\")},\n\t\t\t{\"select $1::inet\", net.ParseIP(\"12.34.56.0\")},\n\t\t\t{\"select $1::inet\", net.ParseIP(\"255.255.255.255\")},\n\t\t\t{\"select $1::inet\", net.ParseIP(\"::1\")},\n\t\t\t{\"select $1::inet\", net.ParseIP(\"2607:f8b0:4009:80b::200e\")},\n\t\t\t{\"select $1::cidr\", net.ParseIP(\"0.0.0.0\")},\n\t\t\t{\"select $1::cidr\", net.ParseIP(\"127.0.0.1\")},\n\t\t\t{\"select $1::cidr\", net.ParseIP(\"12.34.56.0\")},\n\t\t\t{\"select $1::cidr\", net.ParseIP(\"255.255.255.255\")},\n\t\t\t{\"select $1::cidr\", net.ParseIP(\"::1\")},\n\t\t\t{\"select $1::cidr\", net.ParseIP(\"2607:f8b0:4009:80b::200e\")},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" && strings.Contains(tt.sql, \"cidr\") {\n\t\t\t\tt.Log(\"Server does not support cidr type (https://github.com/cockroachdb/cockroach/issues/18846)\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar actual net.IP\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.value).Scan(&actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, value -> %v)\", i, err, tt.sql, tt.value)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif !actual.Equal(tt.value) {\n\t\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v)\", i, tt.value, actual, tt.sql)\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\n\t\tfailTests := []struct {\n\t\t\tsql   string\n\t\t\tvalue *net.IPNet\n\t\t}{\n\t\t\t{\"select $1::inet\", mustParseCIDR(t, \"192.168.1.0/24\")},\n\t\t\t{\"select $1::cidr\", mustParseCIDR(t, \"192.168.1.0/24\")},\n\t\t}\n\t\tfor i, tt := range failTests {\n\t\t\tvar actual net.IP\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.value).Scan(&actual)\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"%d. Expected failure but got none\", i)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\t})\n}\n\nfunc TestInetCIDRArrayTranscodeIPNet(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttests := []struct {\n\t\t\tsql   string\n\t\t\tvalue []*net.IPNet\n\t\t}{\n\t\t\t{\n\t\t\t\t\"select $1::inet[]\",\n\t\t\t\t[]*net.IPNet{\n\t\t\t\t\tmustParseCIDR(t, \"0.0.0.0/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"127.0.0.1/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"12.34.56.0/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"192.168.1.0/24\"),\n\t\t\t\t\tmustParseCIDR(t, \"255.0.0.0/8\"),\n\t\t\t\t\tmustParseCIDR(t, \"255.255.255.255/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"::/128\"),\n\t\t\t\t\tmustParseCIDR(t, \"::/0\"),\n\t\t\t\t\tmustParseCIDR(t, \"::1/128\"),\n\t\t\t\t\tmustParseCIDR(t, \"2607:f8b0:4009:80b::200e/128\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::cidr[]\",\n\t\t\t\t[]*net.IPNet{\n\t\t\t\t\tmustParseCIDR(t, \"0.0.0.0/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"127.0.0.1/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"12.34.56.0/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"192.168.1.0/24\"),\n\t\t\t\t\tmustParseCIDR(t, \"255.0.0.0/8\"),\n\t\t\t\t\tmustParseCIDR(t, \"255.255.255.255/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"::/128\"),\n\t\t\t\t\tmustParseCIDR(t, \"::/0\"),\n\t\t\t\t\tmustParseCIDR(t, \"::1/128\"),\n\t\t\t\t\tmustParseCIDR(t, \"2607:f8b0:4009:80b::200e/128\"),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" && strings.Contains(tt.sql, \"cidr\") {\n\t\t\t\tt.Log(\"Server does not support cidr type (https://github.com/cockroachdb/cockroach/issues/18846)\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar actual []*net.IPNet\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.value).Scan(&actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, value -> %v)\", i, err, tt.sql, tt.value)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(actual, tt.value) {\n\t\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v)\", i, tt.value, actual, tt.sql)\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\t})\n}\n\nfunc TestInetCIDRArrayTranscodeIP(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttests := []struct {\n\t\t\tsql   string\n\t\t\tvalue []net.IP\n\t\t}{\n\t\t\t{\n\t\t\t\t\"select $1::inet[]\",\n\t\t\t\t[]net.IP{\n\t\t\t\t\tnet.ParseIP(\"0.0.0.0\"),\n\t\t\t\t\tnet.ParseIP(\"127.0.0.1\"),\n\t\t\t\t\tnet.ParseIP(\"12.34.56.0\"),\n\t\t\t\t\tnet.ParseIP(\"255.255.255.255\"),\n\t\t\t\t\tnet.ParseIP(\"2607:f8b0:4009:80b::200e\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::cidr[]\",\n\t\t\t\t[]net.IP{\n\t\t\t\t\tnet.ParseIP(\"0.0.0.0\"),\n\t\t\t\t\tnet.ParseIP(\"127.0.0.1\"),\n\t\t\t\t\tnet.ParseIP(\"12.34.56.0\"),\n\t\t\t\t\tnet.ParseIP(\"255.255.255.255\"),\n\t\t\t\t\tnet.ParseIP(\"2607:f8b0:4009:80b::200e\"),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" && strings.Contains(tt.sql, \"cidr\") {\n\t\t\t\tt.Log(\"Server does not support cidr type (https://github.com/cockroachdb/cockroach/issues/18846)\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar actual []net.IP\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.value).Scan(&actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, value -> %v)\", i, err, tt.sql, tt.value)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tassert.Equal(t, len(tt.value), len(actual), \"%d\", i)\n\t\t\tfor j := range actual {\n\t\t\t\tassert.True(t, actual[j].Equal(tt.value[j]), \"%d\", i)\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\n\t\tfailTests := []struct {\n\t\t\tsql   string\n\t\t\tvalue []*net.IPNet\n\t\t}{\n\t\t\t{\n\t\t\t\t\"select $1::inet[]\",\n\t\t\t\t[]*net.IPNet{\n\t\t\t\t\tmustParseCIDR(t, \"12.34.56.0/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"192.168.1.0/24\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::cidr[]\",\n\t\t\t\t[]*net.IPNet{\n\t\t\t\t\tmustParseCIDR(t, \"12.34.56.0/32\"),\n\t\t\t\t\tmustParseCIDR(t, \"192.168.1.0/24\"),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor i, tt := range failTests {\n\t\t\tvar actual []net.IP\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.value).Scan(&actual)\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"%d. Expected failure but got none\", i)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\t})\n}\n\nfunc TestInetCIDRTranscodeWithJustIP(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttests := []struct {\n\t\t\tsql   string\n\t\t\tvalue string\n\t\t}{\n\t\t\t{\"select $1::inet\", \"0.0.0.0/32\"},\n\t\t\t{\"select $1::inet\", \"127.0.0.1/32\"},\n\t\t\t{\"select $1::inet\", \"12.34.56.0/32\"},\n\t\t\t{\"select $1::inet\", \"255.255.255.255/32\"},\n\t\t\t{\"select $1::inet\", \"::/128\"},\n\t\t\t{\"select $1::inet\", \"2607:f8b0:4009:80b::200e/128\"},\n\t\t\t{\"select $1::cidr\", \"0.0.0.0/32\"},\n\t\t\t{\"select $1::cidr\", \"127.0.0.1/32\"},\n\t\t\t{\"select $1::cidr\", \"12.34.56.0/32\"},\n\t\t\t{\"select $1::cidr\", \"255.255.255.255/32\"},\n\t\t\t{\"select $1::cidr\", \"::/128\"},\n\t\t\t{\"select $1::cidr\", \"2607:f8b0:4009:80b::200e/128\"},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\tif conn.PgConn().ParameterStatus(\"crdb_version\") != \"\" && strings.Contains(tt.sql, \"cidr\") {\n\t\t\t\tt.Log(\"Server does not support cidr type (https://github.com/cockroachdb/cockroach/issues/18846)\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\texpected := mustParseCIDR(t, tt.value)\n\t\t\tvar actual net.IPNet\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, expected.IP).Scan(&actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, value -> %v)\", i, err, tt.sql, tt.value)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif actual.String() != expected.String() {\n\t\t\t\tt.Errorf(\"%d. Expected %v, got %v (sql -> %v)\", i, tt.value, actual, tt.sql)\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\t})\n}\n\nfunc TestArrayDecoding(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttests := []struct {\n\t\t\tsql    string\n\t\t\tquery  any\n\t\t\tscan   any\n\t\t\tassert func(testing.TB, any, any)\n\t\t}{\n\t\t\t{\n\t\t\t\t\"select $1::bool[]\",\n\t\t\t\t[]bool{true, false, true},\n\t\t\t\t&[]bool{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]bool))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode bool[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\t[]int16{2, 4, 484, 32767},\n\t\t\t\t&[]int16{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]int16))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode smallint[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::smallint[]\",\n\t\t\t\t[]uint16{2, 4, 484, 32767},\n\t\t\t\t&[]uint16{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]uint16))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode smallint[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::int[]\",\n\t\t\t\t[]int32{2, 4, 484},\n\t\t\t\t&[]int32{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]int32))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode int[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::int[]\",\n\t\t\t\t[]uint32{2, 4, 484, 2147483647},\n\t\t\t\t&[]uint32{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]uint32))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode int[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\t[]int64{2, 4, 484, 9223372036854775807},\n\t\t\t\t&[]int64{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]int64))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode bigint[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::bigint[]\",\n\t\t\t\t[]uint64{2, 4, 484, 9223372036854775807},\n\t\t\t\t&[]uint64{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]uint64))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode bigint[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::text[]\",\n\t\t\t\t[]string{\"it's\", \"over\", \"9000!\"},\n\t\t\t\t&[]string{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tif !reflect.DeepEqual(query, *(scan.(*[]string))) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode text[]\")\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::timestamptz[]\",\n\t\t\t\t[]time.Time{time.Unix(323232, 0), time.Unix(3239949334, 0o0)},\n\t\t\t\t&[]time.Time{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tqueryTimeSlice := query.([]time.Time)\n\t\t\t\t\tscanTimeSlice := *(scan.(*[]time.Time))\n\t\t\t\t\trequire.Equal(t, len(queryTimeSlice), len(scanTimeSlice))\n\t\t\t\t\tfor i := range queryTimeSlice {\n\t\t\t\t\t\tassert.Truef(t, queryTimeSlice[i].Equal(scanTimeSlice[i]), \"%d\", i)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"select $1::bytea[]\",\n\t\t\t\t[][]byte{{0, 1, 2, 3}, {4, 5, 6, 7}},\n\t\t\t\t&[][]byte{},\n\t\t\t\tfunc(t testing.TB, query, scan any) {\n\t\t\t\t\tqueryBytesSliceSlice := query.([][]byte)\n\t\t\t\t\tscanBytesSliceSlice := *(scan.(*[][]byte))\n\t\t\t\t\tif len(queryBytesSliceSlice) != len(scanBytesSliceSlice) {\n\t\t\t\t\t\tt.Errorf(\"failed to encode byte[][] to bytea[]: expected %d to equal %d\", len(queryBytesSliceSlice), len(scanBytesSliceSlice))\n\t\t\t\t\t}\n\t\t\t\t\tfor i := range queryBytesSliceSlice {\n\t\t\t\t\t\tqb := queryBytesSliceSlice[i]\n\t\t\t\t\t\tsb := scanBytesSliceSlice[i]\n\t\t\t\t\t\tif !bytes.Equal(qb, sb) {\n\t\t\t\t\t\t\tt.Errorf(\"failed to encode byte[][] to bytea[]: expected %v to equal %v\", qb, sb)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.query).Scan(tt.scan)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(`%d. error reading array: %v`, i, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttt.assert(t, tt.query, tt.scan)\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\t})\n}\n\nfunc TestEmptyArrayDecoding(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tvar val []string\n\n\t\terr := conn.QueryRow(context.Background(), \"select array[]::text[]\").Scan(&val)\n\t\tif err != nil {\n\t\t\tt.Errorf(`error reading array: %v`, err)\n\t\t}\n\t\tif len(val) != 0 {\n\t\t\tt.Errorf(\"Expected 0 values, got %d\", len(val))\n\t\t}\n\n\t\tvar n, m int32\n\n\t\terr = conn.QueryRow(context.Background(), \"select 1::integer, array[]::text[], 42::integer\").Scan(&n, &val, &m)\n\t\tif err != nil {\n\t\t\tt.Errorf(`error reading array: %v`, err)\n\t\t}\n\t\tif len(val) != 0 {\n\t\t\tt.Errorf(\"Expected 0 values, got %d\", len(val))\n\t\t}\n\t\tif n != 1 {\n\t\t\tt.Errorf(\"Expected n to be 1, but it was %d\", n)\n\t\t}\n\t\tif m != 42 {\n\t\t\tt.Errorf(\"Expected n to be 42, but it was %d\", n)\n\t\t}\n\n\t\trows, err := conn.Query(context.Background(), \"select 1::integer, array['test']::text[] union select 2::integer, array[]::text[] union select 3::integer, array['test']::text[]\")\n\t\tif err != nil {\n\t\t\tt.Errorf(`error retrieving rows with array: %v`, err)\n\t\t}\n\t\tdefer rows.Close()\n\n\t\tfor rows.Next() {\n\t\t\terr = rows.Scan(&n, &val)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(`error reading array: %v`, err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestPointerPointer(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tpgxtest.SkipCockroachDB(t, conn, \"Server auto converts ints to bigint and test relies on exact types\")\n\n\t\ttype allTypes struct {\n\t\t\ts   *string\n\t\t\ti16 *int16\n\t\t\ti32 *int32\n\t\t\ti64 *int64\n\t\t\tf32 *float32\n\t\t\tf64 *float64\n\t\t\tb   *bool\n\t\t\tt   *time.Time\n\t\t}\n\n\t\tvar actual, zero, expected allTypes\n\n\t\t{\n\t\t\ts := \"foo\"\n\t\t\texpected.s = &s\n\t\t\ti16 := int16(1)\n\t\t\texpected.i16 = &i16\n\t\t\ti32 := int32(1)\n\t\t\texpected.i32 = &i32\n\t\t\ti64 := int64(1)\n\t\t\texpected.i64 = &i64\n\t\t\tf32 := float32(1.23)\n\t\t\texpected.f32 = &f32\n\t\t\tf64 := float64(1.23)\n\t\t\texpected.f64 = &f64\n\t\t\tb := true\n\t\t\texpected.b = &b\n\t\t\tt := time.Unix(123, 5000)\n\t\t\texpected.t = &t\n\t\t}\n\n\t\ttests := []struct {\n\t\t\tsql       string\n\t\t\tqueryArgs []any\n\t\t\tscanArgs  []any\n\t\t\texpected  allTypes\n\t\t}{\n\t\t\t{\"select $1::text\", []any{expected.s}, []any{&actual.s}, allTypes{s: expected.s}},\n\t\t\t{\"select $1::text\", []any{zero.s}, []any{&actual.s}, allTypes{}},\n\t\t\t{\"select $1::int2\", []any{expected.i16}, []any{&actual.i16}, allTypes{i16: expected.i16}},\n\t\t\t{\"select $1::int2\", []any{zero.i16}, []any{&actual.i16}, allTypes{}},\n\t\t\t{\"select $1::int4\", []any{expected.i32}, []any{&actual.i32}, allTypes{i32: expected.i32}},\n\t\t\t{\"select $1::int4\", []any{zero.i32}, []any{&actual.i32}, allTypes{}},\n\t\t\t{\"select $1::int8\", []any{expected.i64}, []any{&actual.i64}, allTypes{i64: expected.i64}},\n\t\t\t{\"select $1::int8\", []any{zero.i64}, []any{&actual.i64}, allTypes{}},\n\t\t\t{\"select $1::float4\", []any{expected.f32}, []any{&actual.f32}, allTypes{f32: expected.f32}},\n\t\t\t{\"select $1::float4\", []any{zero.f32}, []any{&actual.f32}, allTypes{}},\n\t\t\t{\"select $1::float8\", []any{expected.f64}, []any{&actual.f64}, allTypes{f64: expected.f64}},\n\t\t\t{\"select $1::float8\", []any{zero.f64}, []any{&actual.f64}, allTypes{}},\n\t\t\t{\"select $1::bool\", []any{expected.b}, []any{&actual.b}, allTypes{b: expected.b}},\n\t\t\t{\"select $1::bool\", []any{zero.b}, []any{&actual.b}, allTypes{}},\n\t\t\t{\"select $1::timestamptz\", []any{expected.t}, []any{&actual.t}, allTypes{t: expected.t}},\n\t\t\t{\"select $1::timestamptz\", []any{zero.t}, []any{&actual.t}, allTypes{}},\n\t\t}\n\n\t\tfor i, tt := range tests {\n\t\t\tactual = zero\n\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, tt.queryArgs...).Scan(tt.scanArgs...)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v, queryArgs -> %v)\", i, err, tt.sql, tt.queryArgs)\n\t\t\t}\n\n\t\t\tassert.Equal(t, tt.expected.s, actual.s)\n\t\t\tassert.Equal(t, tt.expected.i16, actual.i16)\n\t\t\tassert.Equal(t, tt.expected.i32, actual.i32)\n\t\t\tassert.Equal(t, tt.expected.i64, actual.i64)\n\t\t\tassert.Equal(t, tt.expected.f32, actual.f32)\n\t\t\tassert.Equal(t, tt.expected.f64, actual.f64)\n\t\t\tassert.Equal(t, tt.expected.b, actual.b)\n\t\t\tif tt.expected.t != nil || actual.t != nil {\n\t\t\t\tassert.True(t, tt.expected.t.Equal(*actual.t))\n\t\t\t}\n\n\t\t\tensureConnValid(t, conn)\n\t\t}\n\t})\n}\n\nfunc TestPointerPointerNonZero(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tf := \"foo\"\n\t\tdest := &f\n\n\t\terr := conn.QueryRow(context.Background(), \"select $1::text\", nil).Scan(&dest)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unexpected failure scanning: %v\", err)\n\t\t}\n\t\tif dest != nil {\n\t\t\tt.Errorf(\"Expected dest to be nil, got %#v\", dest)\n\t\t}\n\t})\n}\n\nfunc TestEncodeTypeRename(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\ttype _int int\n\t\tinInt := _int(1)\n\t\tvar outInt _int\n\n\t\ttype _int8 int8\n\t\tinInt8 := _int8(2)\n\t\tvar outInt8 _int8\n\n\t\ttype _int16 int16\n\t\tinInt16 := _int16(3)\n\t\tvar outInt16 _int16\n\n\t\ttype _int32 int32\n\t\tinInt32 := _int32(4)\n\t\tvar outInt32 _int32\n\n\t\ttype _int64 int64\n\t\tinInt64 := _int64(5)\n\t\tvar outInt64 _int64\n\n\t\ttype _uint uint\n\t\tinUint := _uint(6)\n\t\tvar outUint _uint\n\n\t\ttype _uint8 uint8\n\t\tinUint8 := _uint8(7)\n\t\tvar outUint8 _uint8\n\n\t\ttype _uint16 uint16\n\t\tinUint16 := _uint16(8)\n\t\tvar outUint16 _uint16\n\n\t\ttype _uint32 uint32\n\t\tinUint32 := _uint32(9)\n\t\tvar outUint32 _uint32\n\n\t\ttype _uint64 uint64\n\t\tinUint64 := _uint64(10)\n\t\tvar outUint64 _uint64\n\n\t\ttype _string string\n\t\tinString := _string(\"foo\")\n\t\tvar outString _string\n\n\t\ttype _bool bool\n\t\tinBool := _bool(true)\n\t\tvar outBool _bool\n\n\t\t// pgx.QueryExecModeExec requires all types to be registered.\n\t\tconn.TypeMap().RegisterDefaultPgType(inInt, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inInt8, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inInt16, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inInt32, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inInt64, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inUint, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inUint8, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inUint16, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inUint32, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inUint64, \"int8\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inString, \"text\")\n\t\tconn.TypeMap().RegisterDefaultPgType(inBool, \"bool\")\n\n\t\terr := conn.QueryRow(context.Background(), \"select $1::int, $2::int, $3::int2, $4::int4, $5::int8, $6::int, $7::int, $8::int, $9::int, $10::int, $11::text, $12::bool\",\n\t\t\tinInt, inInt8, inInt16, inInt32, inInt64, inUint, inUint8, inUint16, inUint32, inUint64, inString, inBool,\n\t\t).Scan(&outInt, &outInt8, &outInt16, &outInt32, &outInt64, &outUint, &outUint8, &outUint16, &outUint32, &outUint64, &outString, &outBool)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Failed with type rename: %v\", err)\n\t\t}\n\n\t\tif inInt != outInt {\n\t\t\tt.Errorf(\"int rename: expected %v, got %v\", inInt, outInt)\n\t\t}\n\n\t\tif inInt8 != outInt8 {\n\t\t\tt.Errorf(\"int8 rename: expected %v, got %v\", inInt8, outInt8)\n\t\t}\n\n\t\tif inInt16 != outInt16 {\n\t\t\tt.Errorf(\"int16 rename: expected %v, got %v\", inInt16, outInt16)\n\t\t}\n\n\t\tif inInt32 != outInt32 {\n\t\t\tt.Errorf(\"int32 rename: expected %v, got %v\", inInt32, outInt32)\n\t\t}\n\n\t\tif inInt64 != outInt64 {\n\t\t\tt.Errorf(\"int64 rename: expected %v, got %v\", inInt64, outInt64)\n\t\t}\n\n\t\tif inUint != outUint {\n\t\t\tt.Errorf(\"uint rename: expected %v, got %v\", inUint, outUint)\n\t\t}\n\n\t\tif inUint8 != outUint8 {\n\t\t\tt.Errorf(\"uint8 rename: expected %v, got %v\", inUint8, outUint8)\n\t\t}\n\n\t\tif inUint16 != outUint16 {\n\t\t\tt.Errorf(\"uint16 rename: expected %v, got %v\", inUint16, outUint16)\n\t\t}\n\n\t\tif inUint32 != outUint32 {\n\t\t\tt.Errorf(\"uint32 rename: expected %v, got %v\", inUint32, outUint32)\n\t\t}\n\n\t\tif inUint64 != outUint64 {\n\t\t\tt.Errorf(\"uint64 rename: expected %v, got %v\", inUint64, outUint64)\n\t\t}\n\n\t\tif inString != outString {\n\t\t\tt.Errorf(\"string rename: expected %v, got %v\", inString, outString)\n\t\t}\n\n\t\tif inBool != outBool {\n\t\t\tt.Errorf(\"bool rename: expected %v, got %v\", inBool, outBool)\n\t\t}\n\t})\n}\n\n// func TestRowDecodeBinary(t *testing.T) {\n// \tt.Parallel()\n\n// \tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n// \tdefer closeConn(t, conn)\n\n// \ttests := []struct {\n// \t\tsql      string\n// \t\texpected []any\n// \t}{\n// \t\t{\n// \t\t\t\"select row(1, 'cat', '2015-01-01 08:12:42-00'::timestamptz)\",\n// \t\t\t[]any{\n// \t\t\t\tint32(1),\n// \t\t\t\t\"cat\",\n// \t\t\t\ttime.Date(2015, 1, 1, 8, 12, 42, 0, time.UTC).Local(),\n// \t\t\t},\n// \t\t},\n// \t\t{\n// \t\t\t\"select row(100.0::float, 1.09::float)\",\n// \t\t\t[]any{\n// \t\t\t\tfloat64(100),\n// \t\t\t\tfloat64(1.09),\n// \t\t\t},\n// \t\t},\n// \t}\n\n// \tfor i, tt := range tests {\n// \t\tvar actual []any\n\n// \t\terr := conn.QueryRow(context.Background(), tt.sql).Scan(&actual)\n// \t\tif err != nil {\n// \t\t\tt.Errorf(\"%d. Unexpected failure: %v (sql -> %v)\", i, err, tt.sql)\n// \t\t\tcontinue\n// \t\t}\n\n// \t\tfor j := range tt.expected {\n// \t\t\tassert.EqualValuesf(t, tt.expected[j], actual[j], \"%d. [%d]\", i, j)\n\n// \t\t}\n\n// \t\tensureConnValid(t, conn)\n// \t}\n// }\n\n// https://github.com/jackc/pgx/issues/810\nfunc TestRowsScanNilThenScanValue(t *testing.T) {\n\tt.Parallel()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\n\tdefer cancel()\n\n\tpgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {\n\t\tsql := `select null as a, null as b\nunion\nselect 1, 2\norder by a nulls first\n`\n\t\trows, err := conn.Query(context.Background(), sql)\n\t\trequire.NoError(t, err)\n\n\t\trequire.True(t, rows.Next())\n\n\t\terr = rows.Scan(nil, nil)\n\t\trequire.NoError(t, err)\n\n\t\trequire.True(t, rows.Next())\n\n\t\tvar a int\n\t\tvar b int\n\t\terr = rows.Scan(&a, &b)\n\t\trequire.NoError(t, err)\n\n\t\trequire.EqualValues(t, 1, a)\n\t\trequire.EqualValues(t, 2, b)\n\n\t\trows.Close()\n\t\trequire.NoError(t, rows.Err())\n\t})\n}\n\nfunc TestScanIntoByteSlice(t *testing.T) {\n\tt.Parallel()\n\n\tconn := mustConnectString(t, os.Getenv(\"PGX_TEST_DATABASE\"))\n\tdefer closeConn(t, conn)\n\t// Success cases\n\tfor _, tt := range []struct {\n\t\tname             string\n\t\tsql              string\n\t\tresultFormatCode int16\n\t\toutput           []byte\n\t}{\n\t\t{\"int - text\", \"select 42\", pgx.TextFormatCode, []byte(\"42\")},\n\t\t{\"int - binary\", \"select 42\", pgx.BinaryFormatCode, []byte(\"42\")},\n\t\t{\"text - text\", \"select 'hi'\", pgx.TextFormatCode, []byte(\"hi\")},\n\t\t{\"text - binary\", \"select 'hi'\", pgx.BinaryFormatCode, []byte(\"hi\")},\n\t\t{\"json - text\", \"select '{}'::json\", pgx.TextFormatCode, []byte(\"{}\")},\n\t\t{\"json - binary\", \"select '{}'::json\", pgx.BinaryFormatCode, []byte(\"{}\")},\n\t\t{\"jsonb - text\", \"select '{}'::jsonb\", pgx.TextFormatCode, []byte(\"{}\")},\n\t\t{\"jsonb - binary\", \"select '{}'::jsonb\", pgx.BinaryFormatCode, []byte(\"{}\")},\n\t} {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar buf []byte\n\t\t\terr := conn.QueryRow(context.Background(), tt.sql, pgx.QueryResultFormats{tt.resultFormatCode}).Scan(&buf)\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, tt.output, buf)\n\t\t})\n\t}\n}\n"
  }
]