[
  {
    "path": ".clang-format",
    "content": "# A clang-format style that approximates Python's PEP 7\nBasedOnStyle: Google\nAlwaysBreakAfterReturnType: All\nAllowShortIfStatementsOnASingleLine: false\nAlignAfterOpenBracket: Align\nBreakBeforeBraces: Stroustrup\nColumnLimit: 95\nDerivePointerAlignment: false\nIndentWidth: 4\nLanguage: Cpp\nPointerAlignment: Right\nReflowComments: true\nSpaceBeforeParens: ControlStatements\nSpacesInParentheses: false\nTabWidth: 4\nUseTab: Never\nSortIncludes: false\n"
  },
  {
    "path": ".clangd",
    "content": "Diagnostics:\n  Includes:\n    IgnoreHeader:\n      - \"pythoncapi_compat.*\\\\.h\"\n"
  },
  {
    "path": ".flake8",
    "content": "[flake8]\nselect = C90,E,F,W,Y0\nignore = E402,E731,W503,W504,E252\nexclude = .git,__pycache__,build,dist,.eggs,.github,.local,.venv*,.tox\nper-file-ignores = *.pyi: F401,F403,F405,F811,E127,E128,E203,E266,E301,E302,E305,E501,E701,E704,E741,B303,W503,W504\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\nThank you for reporting an issue/feature request.\n\nIf this is a feature request, please disregard this template.  If this is\na bug report, please answer to the questions below.\n\nIt will be much easier for us to fix the issue if a test case that reproduces\nthe problem is provided, with clear instructions on how to run it.\n\nThank you!\n-->\n\n* **asyncpg version**:\n* **PostgreSQL version**:\n* **Do you use a PostgreSQL SaaS?  If so, which?  Can you reproduce\n  the issue with a local PostgreSQL install?**:\n* **Python version**:\n* **Platform**:\n* **Do you use pgbouncer?**:\n* **Did you install asyncpg with pip?**:\n* **If you built asyncpg locally, which version of Cython did you use?**:\n* **Can the issue be reproduced under both asyncio and\n  [uvloop](https://github.com/magicstack/uvloop)?**:\n\n<!-- Enter your issue details below this comment. -->\n"
  },
  {
    "path": ".github/RELEASING.rst",
    "content": "Releasing asyncpg\n=================\n\nWhen making an asyncpg release follow the below checklist.\n\n1. Remove the ``.dev0`` suffix from ``__version__`` in ``asyncpg/__init__.py``.\n\n2. Make a release commit:\n\n   .. code-block:: shell\n\n      $ git commit -a -m \"asyncpg vX.Y.0\"\n\n   Here, X.Y.0 is the ``__version__`` in ``asyncpg/__init__.py``.\n\n3. Force push into the \"releases\" branch on Github:\n\n   .. code-block:: shell\n\n      $ git push --force origin master:releases\n\n4. Wait for CI to make the release build.  If there are errors,\n   investigate, fix and repeat steps 2 through 4.\n\n5. Prepare the release changelog by cleaning and categorizing the output of\n   ``.github/release_log.py``.  Look at previous releases for examples\n   of changelog formatting:\n\n   .. code-block:: shell\n\n      $ .github/release_log.py <previously-released-version-tag>\n\n6. Make an annotated, signed git tag and use the changelog as the tag\n   annotation:\n\n   .. code-block:: shell\n\n      $ git tag -s vX.Y.0\n      <paste changelog>\n\n7. Push the release commit and the new tag to master on Github:\n\n   .. code-block:: shell\n\n      $ git push --follow-tags\n\n8. Wait for CI to publish the build to PyPI.\n\n9. Edit the release on Github and paste the same content you used for\n   the tag annotation (Github treats tag annotations as plain text,\n   rather than Markdown.)\n\n10. Open master for development by bumping the minor component of\n    ``__version__`` in ``asyncpg/__init__.py`` and appending the ``.dev0``\n    suffix.\n"
  },
  {
    "path": ".github/release_log.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport json\nimport requests\nimport re\nimport sys\n\n\nBASE_URL = 'https://api.github.com/repos/magicstack/asyncpg/compare'\n\n\ndef main():\n    if len(sys.argv) < 2:\n        print('pass a sha1 hash as a first argument')\n        sys.exit(1)\n\n    from_hash = sys.argv[1]\n    if len(sys.argv) > 2:\n        to_hash = sys.argv[2]\n\n    r = requests.get(f'{BASE_URL}/{from_hash}...{to_hash}')\n    data = json.loads(r.text)\n\n    for commit in data['commits']:\n        message = commit['commit']['message']\n        first_line = message.partition('\\n\\n')[0]\n        if commit.get('author'):\n            username = '@{}'.format(commit['author']['login'])\n        else:\n            username = commit['commit']['author']['name']\n        sha = commit[\"sha\"][:8]\n\n        m = re.search(r'\\#(?P<num>\\d+)\\b', message)\n        if m:\n            issue_num = m.group('num')\n        else:\n            issue_num = None\n\n        print(f'* {first_line}')\n        print(f'  (by {username} in {sha}', end='')\n        print(')')\n        print()\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".github/workflows/install-krb5.sh",
    "content": "#!/bin/bash\n\nset -Eexuo pipefail\nshopt -s nullglob\n\nif [[ $OSTYPE == linux* ]]; then\n    if [ \"$(id -u)\" = \"0\" ]; then\n        SUDO=\n    else\n        SUDO=sudo\n    fi\n\n    if [ -e /etc/os-release ]; then\n        source /etc/os-release\n    elif [ -e /etc/centos-release ]; then\n        ID=\"centos\"\n        VERSION_ID=$(cat /etc/centos-release | cut -f3 -d' ' | cut -f1 -d.)\n    else\n        echo \"install-krb5.sh: cannot determine which Linux distro this is\" >&2\n        exit 1\n    fi\n\n    if [ \"${ID}\" = \"debian\" -o \"${ID}\" = \"ubuntu\" ]; then\n        export DEBIAN_FRONTEND=noninteractive\n\n        $SUDO apt-get update\n        $SUDO apt-get install -y --no-install-recommends \\\n            libkrb5-dev krb5-user krb5-kdc krb5-admin-server\n    elif [ \"${ID}\" = \"almalinux\" ]; then\n        $SUDO dnf install -y krb5-server krb5-workstation krb5-libs krb5-devel\n    elif [ \"${ID}\" = \"centos\" ]; then\n        $SUDO yum install -y krb5-server krb5-workstation krb5-libs krb5-devel\n    elif [ \"${ID}\" = \"alpine\" ]; then\n        $SUDO apk add krb5 krb5-server krb5-dev\n    else\n        echo \"install-krb5.sh: Unsupported linux distro: ${distro}\" >&2\n        exit 1\n    fi\nelse\n    echo \"install-krb5.sh: unsupported OS: ${OSTYPE}\" >&2\n    exit 1\nfi\n"
  },
  {
    "path": ".github/workflows/install-postgres.sh",
    "content": "#!/bin/bash\n\nset -Eexuo pipefail\nshopt -s nullglob\n\nif [[ $OSTYPE == linux* ]]; then\n    PGVERSION=${PGVERSION:-12}\n\n    if [ -e /etc/os-release ]; then\n        source /etc/os-release\n    elif [ -e /etc/centos-release ]; then\n        ID=\"centos\"\n        VERSION_ID=$(cat /etc/centos-release | cut -f3 -d' ' | cut -f1 -d.)\n    else\n        echo \"install-postgres.sh: cannot determine which Linux distro this is\" >&2\n        exit 1\n    fi\n\n    if [ \"${ID}\" = \"debian\" -o \"${ID}\" = \"ubuntu\" ]; then\n        export DEBIAN_FRONTEND=noninteractive\n\n        apt-get install -y --no-install-recommends curl gnupg ca-certificates\n        curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -\n        mkdir -p /etc/apt/sources.list.d/\n        echo \"deb https://apt.postgresql.org/pub/repos/apt/ ${VERSION_CODENAME}-pgdg main\" \\\n            >> /etc/apt/sources.list.d/pgdg.list\n        apt-get update\n        apt-get install -y --no-install-recommends \\\n            \"postgresql-${PGVERSION}\" \\\n            \"postgresql-contrib-${PGVERSION}\"\n    elif [ \"${ID}\" = \"almalinux\" ]; then\n        yum install -y \\\n            \"postgresql-server\" \\\n            \"postgresql-devel\" \\\n            \"postgresql-contrib\"\n    elif [ \"${ID}\" = \"centos\" ]; then\n        el=\"EL-${VERSION_ID%.*}-$(arch)\"\n        baseurl=\"https://download.postgresql.org/pub/repos/yum/reporpms\"\n        yum install -y \"${baseurl}/${el}/pgdg-redhat-repo-latest.noarch.rpm\"\n        if [ ${VERSION_ID%.*} -ge 8 ]; then\n            dnf -qy module disable postgresql\n        fi\n        yum install -y \\\n            \"postgresql${PGVERSION}-server\" \\\n            \"postgresql${PGVERSION}-contrib\"\n        ln -s \"/usr/pgsql-${PGVERSION}/bin/pg_config\" \"/usr/local/bin/pg_config\"\n    elif [ \"${ID}\" = \"alpine\" ]; then\n        apk add shadow postgresql postgresql-dev postgresql-contrib\n    else\n        echo \"install-postgres.sh: unsupported Linux distro: ${distro}\" >&2\n        exit 1\n    fi\n\n    useradd -m -s /bin/bash apgtest\n\nelif [[ $OSTYPE == darwin* ]]; then\n    brew install postgresql\n\nelse\n    echo \"install-postgres.sh: unsupported OS: ${OSTYPE}\" >&2\n    exit 1\nfi\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  pull_request:\n    branches:\n      - \"master\"\n      - \"ci\"\n      - \"[0-9]+.[0-9x]+*\"\n    paths:\n      - \"asyncpg/_version.py\"\n\njobs:\n  validate-release-request:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Validate release PR\n      uses: edgedb/action-release/validate-pr@master\n      id: checkver\n      with:\n        require_team: Release Managers\n        require_approval: no\n        github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}\n        version_file: asyncpg/_version.py\n        version_line_pattern: |\n          __version__(?:\\s*:\\s*typing\\.Final)?\\s*=\\s*(?:['\"])([[:PEP440:]])(?:['\"])\n\n    - name: Stop if not approved\n      if: steps.checkver.outputs.approved != 'true'\n      run: |\n        echo ::error::PR is not approved yet.\n        exit 1\n\n    - name: Store release version for later use\n      env:\n        VERSION: ${{ steps.checkver.outputs.version }}\n      run: |\n        mkdir -p dist/\n        echo \"${VERSION}\" > dist/VERSION\n\n    - uses: actions/upload-artifact@v4\n      with:\n        name: dist-version\n        path: dist/VERSION\n\n  build-sdist:\n    needs: validate-release-request\n    runs-on: ubuntu-latest\n\n    env:\n      PIP_DISABLE_PIP_VERSION_CHECK: 1\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 50\n        submodules: true\n        persist-credentials: false\n\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.x\"\n\n    - name: Build source distribution\n      run: |\n        pip install -U setuptools wheel pip\n        python setup.py sdist\n\n    - uses: actions/upload-artifact@v4\n      with:\n        name: dist-sdist\n        path: dist/*.tar.*\n\n  build-wheels-matrix:\n    needs: validate-release-request\n    runs-on: ubuntu-latest\n    outputs:\n      include: ${{ steps.set-matrix.outputs.include }}\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          persist-credentials: false\n      - uses: actions/setup-python@v6\n        with:\n          python-version: \"3.x\"\n      - run: pip install cibuildwheel==3.3.0\n      - id: set-matrix\n        run: |\n          MATRIX_INCLUDE=$(\n            {\n              cibuildwheel --print-build-identifiers --platform linux --archs x86_64,aarch64 | grep cp |  jq -nRc '{\"only\": inputs, \"os\": \"ubuntu-latest\"}' \\\n              && cibuildwheel --print-build-identifiers --platform macos --archs x86_64,arm64 | grep cp |  jq -nRc '{\"only\": inputs, \"os\": \"macos-latest\"}' \\\n              && cibuildwheel --print-build-identifiers --platform windows --archs x86,AMD64 | grep cp |  jq -nRc '{\"only\": inputs, \"os\": \"windows-latest\"}'\n            } | jq -sc\n          )\n          echo \"include=$MATRIX_INCLUDE\" >> $GITHUB_OUTPUT\n\n  build-wheels:\n    needs: build-wheels-matrix\n    runs-on: ${{ matrix.os }}\n    name: Build ${{ matrix.only }}\n\n    strategy:\n      fail-fast: false\n      matrix:\n        include: ${{ fromJson(needs.build-wheels-matrix.outputs.include) }}\n\n    defaults:\n      run:\n        shell: bash\n\n    env:\n      PIP_DISABLE_PIP_VERSION_CHECK: 1\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 50\n        submodules: true\n        persist-credentials: false\n\n    - name: Set up QEMU\n      if: runner.os == 'Linux'\n      uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392  # v3.6.0\n\n    - uses: pypa/cibuildwheel@63fd63b352a9a8bdcc24791c9dbee952ee9a8abc  # v3.3.0\n      with:\n        only: ${{ matrix.only }}\n      env:\n        CIBW_BUILD_VERBOSITY: 1\n\n    - uses: actions/upload-artifact@v4\n      with:\n        name: dist-wheels-${{ matrix.only }}\n        path: wheelhouse/*.whl\n\n  merge-artifacts:\n    runs-on: ubuntu-latest\n    needs: [build-sdist, build-wheels]\n    steps:\n      - name: Merge Artifacts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: dist\n          delete-merged: true\n\n  publish-docs:\n    needs: [build-sdist, build-wheels]\n    runs-on: ubuntu-latest\n\n    env:\n      PIP_DISABLE_PIP_VERSION_CHECK: 1\n\n    steps:\n    - name: Checkout source\n      uses: actions/checkout@v5\n      with:\n        fetch-depth: 5\n        submodules: true\n        persist-credentials: false\n\n    - name: Set up Python\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.x\"\n\n    - name: Build docs\n      run: |\n        pip install --group docs\n        pip install -e .\n        make htmldocs\n\n    - name: Checkout gh-pages\n      uses: actions/checkout@v5\n      with:\n        fetch-depth: 5\n        ref: gh-pages\n        path: docs/gh-pages\n        persist-credentials: false\n\n    - name: Sync docs\n      run: |\n        rsync -a docs/_build/html/ docs/gh-pages/current/\n\n    - name: Commit and push docs\n      uses: magicstack/gha-commit-and-push@master\n      with:\n        target_branch: gh-pages\n        workdir: docs/gh-pages\n        commit_message: Automatic documentation update\n        github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}\n        ssh_key: ${{ secrets.RELEASE_BOT_SSH_KEY }}\n        gpg_key: ${{ secrets.RELEASE_BOT_GPG_KEY }}\n        gpg_key_id: \"5C468778062D87BF!\"\n\n  publish:\n    needs: [build-sdist, build-wheels, publish-docs]\n    runs-on: ubuntu-latest\n\n    environment:\n      name: pypi\n      url: https://pypi.org/p/asyncpg\n    permissions:\n      id-token: write\n      attestations: write\n      contents: write\n      deployments: write\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 5\n        submodules: false\n        persist-credentials: false\n\n    - uses: actions/download-artifact@v4\n      with:\n        name: dist\n        path: dist/\n\n    - name: Extract Release Version\n      id: relver\n      run: |\n        set -e\n        echo \"version=$(cat dist/VERSION)\" >> $GITHUB_OUTPUT\n        rm dist/VERSION\n\n    - name: Merge and tag the PR\n      uses: edgedb/action-release/merge@master\n      with:\n        github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}\n        ssh_key: ${{ secrets.RELEASE_BOT_SSH_KEY }}\n        gpg_key: ${{ secrets.RELEASE_BOT_GPG_KEY }}\n        gpg_key_id: \"5C468778062D87BF!\"\n        tag_name: v${{ steps.relver.outputs.version }}\n\n    - name: Publish Github Release\n      uses: elprans/gh-action-create-release@master\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      with:\n        tag_name: v${{ steps.relver.outputs.version }}\n        release_name: v${{ steps.relver.outputs.version }}\n        target: ${{ github.event.pull_request.base.ref }}\n        body: ${{ github.event.pull_request.body }}\n\n    - run: |\n        ls -al dist/\n\n    - name: Upload to PyPI\n      uses: pypa/gh-action-pypi-publish@release/v1\n      with:\n        attestations: true\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\n\non:\n  push:\n    branches:\n      - master\n      - ci\n  pull_request:\n    branches:\n      - master\n\njobs:\n  test-platforms:\n    # NOTE: this matrix is for testing various combinations of Python and OS\n    # versions on the system-installed PostgreSQL version (which is usually\n    # fairly recent). For a PostgreSQL version matrix see the test-postgres\n    # job.\n    strategy:\n      matrix:\n        python-version: [\"3.9\", \"3.10\", \"3.11\", \"3.12\", \"3.13\", \"3.14\", \"3.14t\"]\n        os: [ubuntu-latest, macos-latest, windows-latest]\n        loop: [asyncio, uvloop]\n        exclude:\n          # uvloop does not support windows\n          - loop: uvloop\n            os: windows-latest\n\n    runs-on: ${{ matrix.os }}\n\n    permissions: {}\n\n    defaults:\n      run:\n        shell: bash\n\n    env:\n      PIP_DISABLE_PIP_VERSION_CHECK: 1\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 50\n        submodules: true\n        persist-credentials: false\n\n    - name: Check if release PR.\n      uses: edgedb/action-release/validate-pr@master\n      id: release\n      with:\n        github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}\n        missing_version_ok: yes\n        version_file: asyncpg/_version.py\n        version_line_pattern: |\n          __version__(?:\\s*:\\s*typing\\.Final)?\\s*=\\s*(?:['\"])([[:PEP440:]])(?:['\"])\n\n    - name: Setup PostgreSQL\n      if: \"!steps.release.outputs.is_release && matrix.os == 'macos-latest'\"\n      run: |\n        POSTGRES_FORMULA=\"postgresql@18\"\n        brew install \"$POSTGRES_FORMULA\"\n        echo \"$(brew --prefix \"$POSTGRES_FORMULA\")/bin\" >> $GITHUB_PATH\n\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v6\n      if: \"!steps.release.outputs.is_release\"\n      with:\n        python-version: ${{ matrix.python-version }}\n\n    - name: Install Python Deps\n      if: \"!steps.release.outputs.is_release\"\n      run: |\n        [ \"$RUNNER_OS\" = \"Linux\" ] && .github/workflows/install-krb5.sh\n        python -m pip install -U pip setuptools wheel\n        python -m pip install --group test\n        python -m pip install -e .\n\n    - name: Test\n      if: \"!steps.release.outputs.is_release\"\n      env:\n        LOOP_IMPL: ${{ matrix.loop }}\n      run: |\n        if [ \"${LOOP_IMPL}\" = \"uvloop\" ]; then\n          env USE_UVLOOP=1 python -m unittest -v tests.suite\n        else\n          python -m unittest -v tests.suite\n        fi\n\n  test-postgres:\n    strategy:\n      matrix:\n        postgres-version: [\"9.5\", \"9.6\", \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\"]\n\n    runs-on: ubuntu-latest\n\n    permissions: {}\n\n    env:\n      PIP_DISABLE_PIP_VERSION_CHECK: 1\n\n    steps:\n    - uses: actions/checkout@v5\n      with:\n        fetch-depth: 50\n        submodules: true\n        persist-credentials: false\n\n    - name: Check if release PR.\n      uses: edgedb/action-release/validate-pr@master\n      id: release\n      with:\n        github_token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }}\n        missing_version_ok: yes\n        version_file: asyncpg/_version.py\n        version_line_pattern: |\n          __version__(?:\\s*:\\s*typing\\.Final)?\\s*=\\s*(?:['\"])([[:PEP440:]])(?:['\"])\n\n    - name: Set up PostgreSQL\n      if: \"!steps.release.outputs.is_release\"\n      env:\n        PGVERSION: ${{ matrix.postgres-version }}\n        DISTRO_NAME: focal\n      run: |\n        sudo env DISTRO_NAME=\"${DISTRO_NAME}\" PGVERSION=\"${PGVERSION}\" \\\n          .github/workflows/install-postgres.sh\n        echo PGINSTALLATION=\"/usr/lib/postgresql/${PGVERSION}/bin\" \\\n          >> \"${GITHUB_ENV}\"\n\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v6\n      if: \"!steps.release.outputs.is_release\"\n      with:\n        python-version: \"3.x\"\n\n    - name: Install Python Deps\n      if: \"!steps.release.outputs.is_release\"\n      run: |\n        [ \"$RUNNER_OS\" = \"Linux\" ] && .github/workflows/install-krb5.sh\n        python -m pip install -U pip setuptools wheel\n        python -m pip install --group test\n        python -m pip install -e .\n\n    - name: Test\n      if: \"!steps.release.outputs.is_release\"\n      env:\n        PGVERSION: ${{ matrix.postgres-version }}\n      run: |\n        python -m unittest -v tests.suite\n\n  # This job exists solely to act as the test job aggregate to be\n  # targeted by branch policies.\n  regression-tests:\n    name: \"Regression Tests\"\n    needs: [test-platforms, test-postgres]\n    runs-on: ubuntu-latest\n    permissions: {}\n\n    steps:\n      - run: echo OK\n"
  },
  {
    "path": ".gitignore",
    "content": "*._*\n*.pyc\n*.pyo\n*.ymlc\n*.ymlc~\n*.scssc\n*.so\n*.pyd\n*~\n.#*\n.DS_Store\n.project\n.pydevproject\n.settings\n.idea\n/.ropeproject\n\\#*#\n/pub\n/test*.py\n/.local\n/perf.data*\n/config_local.yml\n/build\n__pycache__/\n.d8_history\n/*.egg\n/*.egg-info\n/dist\n/.cache\ndocs/_build\n*,cover\n.coverage\n/.pytest_cache/\n/.eggs\n/.vscode\n/.zed\n/.mypy_cache\n/.venv*\n/.tox\n/compile_commands.json\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"asyncpg/pgproto\"]\n\tpath = asyncpg/pgproto\n\turl = https://github.com/MagicStack/py-pgproto.git\n"
  },
  {
    "path": "AUTHORS",
    "content": "Main contributors\n=================\n\nMagicStack Inc.:\n    Elvis Pranskevichus <elvis@magic.io>\n    Yury Selivanov <yury@magic.io>\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (C) 2016-present the asyncpg authors and contributors.\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright (C) 2016-present the asyncpg authors and contributors\n   <see AUTHORS file>\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "recursive-include docs *.py *.rst Makefile *.css\nrecursive-include examples *.py\nrecursive-include tests *.py *.pem\nrecursive-include asyncpg *.pyx *.pxd *.pxi *.py *.pyi *.c *.h\ninclude LICENSE README.rst Makefile performance.png .flake8\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: compile debug test quicktest clean all\n\n\nPYTHON ?= python\nROOT = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))\n\n\nall: compile\n\n\nclean:\n\trm -fr dist/ doc/_build/\n\trm -fr asyncpg/pgproto/*.c asyncpg/pgproto/*.html\n\trm -fr asyncpg/pgproto/codecs/*.html\n\trm -fr asyncpg/pgproto/*.so\n\trm -fr asyncpg/protocol/*.c asyncpg/protocol/*.html\n\trm -fr asyncpg/protocol/*.so build *.egg-info\n\trm -fr asyncpg/protocol/codecs/*.html\n\tfind . -name '__pycache__' | xargs rm -rf\n\n\ncompile:\n\tenv ASYNCPG_BUILD_CYTHON_ALWAYS=1 $(PYTHON) -m pip install -e .\n\n\ndebug:\n\tenv ASYNCPG_DEBUG=1 $(PYTHON) -m pip install -e .\n\ntest:\n\tPYTHONASYNCIODEBUG=1 $(PYTHON) -m unittest -v tests.suite\n\t$(PYTHON) -m unittest -v tests.suite\n\tUSE_UVLOOP=1 $(PYTHON) -m unittest -v tests.suite\n\n\ntestinstalled:\n\tcd \"$${HOME}\" && $(PYTHON) $(ROOT)/tests/__init__.py\n\n\nquicktest:\n\t$(PYTHON) -m unittest -v tests.suite\n\n\nhtmldocs:\n\t$(PYTHON) -m pip install -e .[docs]\n\t$(MAKE) -C docs html\n"
  },
  {
    "path": "README.rst",
    "content": "asyncpg -- A fast PostgreSQL Database Client Library for Python/asyncio\n=======================================================================\n\n.. image:: https://github.com/MagicStack/asyncpg/workflows/Tests/badge.svg\n   :target: https://github.com/MagicStack/asyncpg/actions?query=workflow%3ATests+branch%3Amaster\n   :alt: GitHub Actions status\n.. image:: https://img.shields.io/pypi/v/asyncpg.svg\n   :target: https://pypi.python.org/pypi/asyncpg\n\n**asyncpg** is a database interface library designed specifically for\nPostgreSQL and Python/asyncio.  asyncpg is an efficient, clean implementation\nof PostgreSQL server binary protocol for use with Python's ``asyncio``\nframework.  You can read more about asyncpg in an introductory\n`blog post <http://magic.io/blog/asyncpg-1m-rows-from-postgres-to-python/>`_.\n\nasyncpg requires Python 3.9 or later and is supported for PostgreSQL\nversions 9.5 to 18.  Other PostgreSQL versions or other databases\nimplementing the PostgreSQL protocol *may* work, but are not being\nactively tested.\n\n\nDocumentation\n-------------\n\nThe project documentation can be found\n`here <https://magicstack.github.io/asyncpg/current/>`_.\n\n\nPerformance\n-----------\n\nIn our testing asyncpg is, on average, **5x** faster than psycopg3.\n\n.. image:: https://raw.githubusercontent.com/MagicStack/asyncpg/master/performance.png?fddca40ab0\n    :target: https://gistpreview.github.io/?0ed296e93523831ea0918d42dd1258c2\n\nThe above results are a geometric mean of benchmarks obtained with PostgreSQL\n`client driver benchmarking toolbench <https://github.com/MagicStack/pgbench>`_\nin June 2023 (click on the chart to see full details).\n\n\nFeatures\n--------\n\nasyncpg implements PostgreSQL server protocol natively and exposes its\nfeatures directly, as opposed to hiding them behind a generic facade\nlike DB-API.\n\nThis enables asyncpg to have easy-to-use support for:\n\n* **prepared statements**\n* **scrollable cursors**\n* **partial iteration** on query results\n* automatic encoding and decoding of composite types, arrays,\n  and any combination of those\n* straightforward support for custom data types\n\n\nInstallation\n------------\n\nasyncpg is available on PyPI.  When not using GSSAPI/SSPI authentication it\nhas no dependencies.  Use pip to install::\n\n    $ pip install asyncpg\n\nIf you need GSSAPI/SSPI authentication, use::\n\n    $ pip install 'asyncpg[gssauth]'\n\nFor more details, please `see the documentation\n<https://magicstack.github.io/asyncpg/current/installation.html>`_.\n\n\nBasic Usage\n-----------\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n\n    async def run():\n        conn = await asyncpg.connect(user='user', password='password',\n                                     database='database', host='127.0.0.1')\n        values = await conn.fetch(\n            'SELECT * FROM mytable WHERE id = $1',\n            10,\n        )\n        await conn.close()\n\n    asyncio.run(run())\n\n\nLicense\n-------\n\nasyncpg is developed and distributed under the Apache 2.0 license.\n"
  },
  {
    "path": "asyncpg/.gitignore",
    "content": "*.html\n"
  },
  {
    "path": "asyncpg/__init__.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nfrom .connection import connect, Connection  # NOQA\nfrom .exceptions import *  # NOQA\nfrom .pool import create_pool, Pool  # NOQA\nfrom .protocol import Record  # NOQA\nfrom .types import *  # NOQA\n\n\nfrom ._version import __version__  # NOQA\n\nfrom . import exceptions\n\n\n__all__: tuple[str, ...] = (\n    'connect', 'create_pool', 'Pool', 'Record', 'Connection'\n)\n__all__ += exceptions.__all__ # NOQA\n"
  },
  {
    "path": "asyncpg/_asyncio_compat.py",
    "content": "# Backports from Python/Lib/asyncio for older Pythons\n#\n# Copyright (c) 2001-2023 Python Software Foundation; All Rights Reserved\n#\n# SPDX-License-Identifier: PSF-2.0\n\nfrom __future__ import annotations\n\nimport asyncio\nimport functools\nimport sys\nimport typing\n\nif typing.TYPE_CHECKING:\n    from . import compat\n\nif sys.version_info < (3, 11):\n    from async_timeout import timeout as timeout_ctx\nelse:\n    from asyncio import timeout as timeout_ctx\n\n_T = typing.TypeVar('_T')\n\n\nasync def wait_for(fut: compat.Awaitable[_T], timeout: float | None) -> _T:\n    \"\"\"Wait for the single Future or coroutine to complete, with timeout.\n\n    Coroutine will be wrapped in Task.\n\n    Returns result of the Future or coroutine.  When a timeout occurs,\n    it cancels the task and raises TimeoutError.  To avoid the task\n    cancellation, wrap it in shield().\n\n    If the wait is cancelled, the task is also cancelled.\n\n    If the task supresses the cancellation and returns a value instead,\n    that value is returned.\n\n    This function is a coroutine.\n    \"\"\"\n    # The special case for timeout <= 0 is for the following case:\n    #\n    # async def test_waitfor():\n    #     func_started = False\n    #\n    #     async def func():\n    #         nonlocal func_started\n    #         func_started = True\n    #\n    #     try:\n    #         await asyncio.wait_for(func(), 0)\n    #     except asyncio.TimeoutError:\n    #         assert not func_started\n    #     else:\n    #         assert False\n    #\n    # asyncio.run(test_waitfor())\n\n    if timeout is not None and timeout <= 0:\n        fut = asyncio.ensure_future(fut)\n\n        if fut.done():\n            return fut.result()\n\n        await _cancel_and_wait(fut)\n        try:\n            return fut.result()\n        except asyncio.CancelledError as exc:\n            raise TimeoutError from exc\n\n    async with timeout_ctx(timeout):\n        return await fut\n\n\nasync def _cancel_and_wait(fut: asyncio.Future[_T]) -> None:\n    \"\"\"Cancel the *fut* future or task and wait until it completes.\"\"\"\n\n    loop = asyncio.get_running_loop()\n    waiter = loop.create_future()\n    cb = functools.partial(_release_waiter, waiter)\n    fut.add_done_callback(cb)\n\n    try:\n        fut.cancel()\n        # We cannot wait on *fut* directly to make\n        # sure _cancel_and_wait itself is reliably cancellable.\n        await waiter\n    finally:\n        fut.remove_done_callback(cb)\n\n\ndef _release_waiter(waiter: asyncio.Future[typing.Any], *args: object) -> None:\n    if not waiter.done():\n        waiter.set_result(None)\n"
  },
  {
    "path": "asyncpg/_testbase/__init__.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport atexit\nimport contextlib\nimport functools\nimport inspect\nimport logging\nimport os\nimport re\nimport textwrap\nimport time\nimport traceback\nimport unittest\n\n\nimport asyncpg\nfrom asyncpg import cluster as pg_cluster\nfrom asyncpg import connection as pg_connection\nfrom asyncpg import pool as pg_pool\n\nfrom . import fuzzer\n\n\n@contextlib.contextmanager\ndef silence_asyncio_long_exec_warning():\n    def flt(log_record):\n        msg = log_record.getMessage()\n        return not msg.startswith('Executing ')\n\n    logger = logging.getLogger('asyncio')\n    logger.addFilter(flt)\n    try:\n        yield\n    finally:\n        logger.removeFilter(flt)\n\n\ndef with_timeout(timeout):\n    def wrap(func):\n        func.__timeout__ = timeout\n        return func\n\n    return wrap\n\n\nclass TestCaseMeta(type(unittest.TestCase)):\n    TEST_TIMEOUT = None\n\n    @staticmethod\n    def _iter_methods(bases, ns):\n        for base in bases:\n            for methname in dir(base):\n                if not methname.startswith('test_'):\n                    continue\n\n                meth = getattr(base, methname)\n                if not inspect.iscoroutinefunction(meth):\n                    continue\n\n                yield methname, meth\n\n        for methname, meth in ns.items():\n            if not methname.startswith('test_'):\n                continue\n\n            if not inspect.iscoroutinefunction(meth):\n                continue\n\n            yield methname, meth\n\n    def __new__(mcls, name, bases, ns):\n        for methname, meth in mcls._iter_methods(bases, ns):\n            @functools.wraps(meth)\n            def wrapper(self, *args, __meth__=meth, **kwargs):\n                coro = __meth__(self, *args, **kwargs)\n                timeout = getattr(__meth__, '__timeout__', mcls.TEST_TIMEOUT)\n                if timeout:\n                    coro = asyncio.wait_for(coro, timeout)\n                    try:\n                        self.loop.run_until_complete(coro)\n                    except asyncio.TimeoutError:\n                        raise self.failureException(\n                            'test timed out after {} seconds'.format(\n                                timeout)) from None\n                else:\n                    self.loop.run_until_complete(coro)\n            ns[methname] = wrapper\n\n        return super().__new__(mcls, name, bases, ns)\n\n\nclass TestCase(unittest.TestCase, metaclass=TestCaseMeta):\n\n    @classmethod\n    def setUpClass(cls):\n        if os.environ.get('USE_UVLOOP'):\n            import uvloop\n            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())\n\n        loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(None)\n        cls.loop = loop\n\n    @classmethod\n    def tearDownClass(cls):\n        cls.loop.close()\n        asyncio.set_event_loop(None)\n\n    def setUp(self):\n        self.loop.set_exception_handler(self.loop_exception_handler)\n        self.__unhandled_exceptions = []\n\n    def tearDown(self):\n        excs = []\n        for exc in self.__unhandled_exceptions:\n            if isinstance(exc, ConnectionResetError):\n                texc = traceback.TracebackException.from_exception(\n                    exc, lookup_lines=False)\n                if texc.stack[-1].name == \"_call_connection_lost\":\n                    # On Windows calling socket.shutdown may raise\n                    # ConnectionResetError, which happens in the\n                    # finally block of _call_connection_lost.\n                    continue\n            excs.append(exc)\n\n        if excs:\n            formatted = []\n\n            for i, context in enumerate(excs):\n                formatted.append(self._format_loop_exception(context, i + 1))\n\n            self.fail(\n                'unexpected exceptions in asynchronous code:\\n' +\n                '\\n'.join(formatted))\n\n    @contextlib.contextmanager\n    def assertRunUnder(self, delta):\n        st = time.monotonic()\n        try:\n            yield\n        finally:\n            elapsed = time.monotonic() - st\n            if elapsed > delta:\n                raise AssertionError(\n                    'running block took {:0.3f}s which is longer '\n                    'than the expected maximum of {:0.3f}s'.format(\n                        elapsed, delta))\n\n    @contextlib.contextmanager\n    def assertLoopErrorHandlerCalled(self, msg_re: str):\n        contexts = []\n\n        def handler(loop, ctx):\n            contexts.append(ctx)\n\n        old_handler = self.loop.get_exception_handler()\n        self.loop.set_exception_handler(handler)\n        try:\n            yield\n\n            for ctx in contexts:\n                msg = ctx.get('message')\n                if msg and re.search(msg_re, msg):\n                    return\n\n            raise AssertionError(\n                'no message matching {!r} was logged with '\n                'loop.call_exception_handler()'.format(msg_re))\n\n        finally:\n            self.loop.set_exception_handler(old_handler)\n\n    def loop_exception_handler(self, loop, context):\n        self.__unhandled_exceptions.append(context)\n        loop.default_exception_handler(context)\n\n    def _format_loop_exception(self, context, n):\n        message = context.get('message', 'Unhandled exception in event loop')\n        exception = context.get('exception')\n        if exception is not None:\n            exc_info = (type(exception), exception, exception.__traceback__)\n        else:\n            exc_info = None\n\n        lines = []\n        for key in sorted(context):\n            if key in {'message', 'exception'}:\n                continue\n            value = context[key]\n            if key == 'source_traceback':\n                tb = ''.join(traceback.format_list(value))\n                value = 'Object created at (most recent call last):\\n'\n                value += tb.rstrip()\n            else:\n                try:\n                    value = repr(value)\n                except Exception as ex:\n                    value = ('Exception in __repr__ {!r}; '\n                             'value type: {!r}'.format(ex, type(value)))\n            lines.append('[{}]: {}\\n\\n'.format(key, value))\n\n        if exc_info is not None:\n            lines.append('[exception]:\\n')\n            formatted_exc = textwrap.indent(\n                ''.join(traceback.format_exception(*exc_info)), '  ')\n            lines.append(formatted_exc)\n\n        details = textwrap.indent(''.join(lines), '    ')\n        return '{:02d}. {}:\\n{}\\n'.format(n, message, details)\n\n\n_default_cluster = None\n\n\ndef _init_cluster(ClusterCls, cluster_kwargs, initdb_options=None):\n    cluster = ClusterCls(**cluster_kwargs)\n    cluster.init(**(initdb_options or {}))\n    cluster.trust_local_connections()\n    atexit.register(_shutdown_cluster, cluster)\n    return cluster\n\n\ndef _get_initdb_options(initdb_options=None):\n    if not initdb_options:\n        initdb_options = {}\n    else:\n        initdb_options = dict(initdb_options)\n\n    # Make the default superuser name stable.\n    if 'username' not in initdb_options:\n        initdb_options['username'] = 'postgres'\n\n    return initdb_options\n\n\ndef _init_default_cluster(initdb_options=None):\n    global _default_cluster\n\n    if _default_cluster is None:\n        pg_host = os.environ.get('PGHOST')\n        if pg_host:\n            # Using existing cluster, assuming it is initialized and running\n            _default_cluster = pg_cluster.RunningCluster()\n        else:\n            _default_cluster = _init_cluster(\n                pg_cluster.TempCluster,\n                cluster_kwargs={\n                    \"data_dir_suffix\": \".apgtest\",\n                },\n                initdb_options=_get_initdb_options(initdb_options),\n            )\n\n    return _default_cluster\n\n\ndef _shutdown_cluster(cluster):\n    if cluster.get_status() == 'running':\n        cluster.stop()\n    if cluster.get_status() != 'not-initialized':\n        cluster.destroy()\n\n\ndef create_pool(dsn=None, *,\n                min_size=10,\n                max_size=10,\n                max_queries=50000,\n                max_inactive_connection_lifetime=60.0,\n                connect=None,\n                setup=None,\n                init=None,\n                loop=None,\n                pool_class=pg_pool.Pool,\n                connection_class=pg_connection.Connection,\n                record_class=asyncpg.Record,\n                **connect_kwargs):\n    return pool_class(\n        dsn,\n        min_size=min_size,\n        max_size=max_size,\n        max_queries=max_queries,\n        loop=loop,\n        connect=connect,\n        setup=setup,\n        init=init,\n        max_inactive_connection_lifetime=max_inactive_connection_lifetime,\n        connection_class=connection_class,\n        record_class=record_class,\n        **connect_kwargs,\n    )\n\n\nclass ClusterTestCase(TestCase):\n    @classmethod\n    def get_server_settings(cls):\n        settings = {\n            'log_connections': 'on'\n        }\n\n        if cls.cluster.get_pg_version() >= (11, 0):\n            # JITting messes up timing tests, and\n            # is not essential for testing.\n            settings['jit'] = 'off'\n\n        return settings\n\n    @classmethod\n    def new_cluster(cls, ClusterCls, *, cluster_kwargs={}, initdb_options={}):\n        cluster = _init_cluster(ClusterCls, cluster_kwargs,\n                                _get_initdb_options(initdb_options))\n        cls._clusters.append(cluster)\n        return cluster\n\n    @classmethod\n    def start_cluster(cls, cluster, *, server_settings={}):\n        cluster.start(port='dynamic', server_settings=server_settings)\n\n    @classmethod\n    def setup_cluster(cls):\n        cls.cluster = _init_default_cluster()\n\n        if cls.cluster.get_status() != 'running':\n            cls.cluster.start(\n                port='dynamic', server_settings=cls.get_server_settings())\n\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        cls._clusters = []\n        cls.setup_cluster()\n\n    @classmethod\n    def tearDownClass(cls):\n        super().tearDownClass()\n        for cluster in cls._clusters:\n            if cluster is not _default_cluster:\n                cluster.stop()\n                cluster.destroy()\n        cls._clusters = []\n\n    @classmethod\n    def get_connection_spec(cls, kwargs={}):\n        conn_spec = cls.cluster.get_connection_spec()\n        if kwargs.get('dsn'):\n            conn_spec.pop('host')\n        conn_spec.update(kwargs)\n        if not os.environ.get('PGHOST') and not kwargs.get('dsn'):\n            if 'database' not in conn_spec:\n                conn_spec['database'] = 'postgres'\n            if 'user' not in conn_spec:\n                conn_spec['user'] = 'postgres'\n        return conn_spec\n\n    @classmethod\n    def connect(cls, **kwargs):\n        conn_spec = cls.get_connection_spec(kwargs)\n        return pg_connection.connect(**conn_spec, loop=cls.loop)\n\n    def setUp(self):\n        super().setUp()\n        self._pools = []\n\n    def tearDown(self):\n        super().tearDown()\n        for pool in self._pools:\n            pool.terminate()\n        self._pools = []\n\n    def create_pool(self, pool_class=pg_pool.Pool,\n                    connection_class=pg_connection.Connection, **kwargs):\n        conn_spec = self.get_connection_spec(kwargs)\n        pool = create_pool(loop=self.loop, pool_class=pool_class,\n                           connection_class=connection_class, **conn_spec)\n        self._pools.append(pool)\n        return pool\n\n\nclass ProxiedClusterTestCase(ClusterTestCase):\n    @classmethod\n    def get_server_settings(cls):\n        settings = dict(super().get_server_settings())\n        settings['listen_addresses'] = '127.0.0.1'\n        return settings\n\n    @classmethod\n    def get_proxy_settings(cls):\n        return {'fuzzing-mode': None}\n\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        conn_spec = cls.cluster.get_connection_spec()\n        host = conn_spec.get('host')\n        if not host:\n            host = '127.0.0.1'\n        elif host.startswith('/'):\n            host = '127.0.0.1'\n        cls.proxy = fuzzer.TCPFuzzingProxy(\n            backend_host=host,\n            backend_port=conn_spec['port'],\n        )\n        cls.proxy.start()\n\n    @classmethod\n    def tearDownClass(cls):\n        cls.proxy.stop()\n        super().tearDownClass()\n\n    @classmethod\n    def get_connection_spec(cls, kwargs):\n        conn_spec = super().get_connection_spec(kwargs)\n        conn_spec['host'] = cls.proxy.listening_addr\n        conn_spec['port'] = cls.proxy.listening_port\n        return conn_spec\n\n    def tearDown(self):\n        self.proxy.reset()\n        super().tearDown()\n\n\ndef with_connection_options(**options):\n    if not options:\n        raise ValueError('no connection options were specified')\n\n    def wrap(func):\n        func.__connect_options__ = options\n        return func\n\n    return wrap\n\n\nclass ConnectedTestCase(ClusterTestCase):\n\n    def setUp(self):\n        super().setUp()\n\n        # Extract options set up with `with_connection_options`.\n        test_func = getattr(self, self._testMethodName).__func__\n        opts = getattr(test_func, '__connect_options__', {})\n        self.con = self.loop.run_until_complete(self.connect(**opts))\n        self.server_version = self.con.get_server_version()\n\n    def tearDown(self):\n        try:\n            self.loop.run_until_complete(self.con.close())\n            self.con = None\n        finally:\n            super().tearDown()\n\n\nclass HotStandbyTestCase(ClusterTestCase):\n\n    @classmethod\n    def setup_cluster(cls):\n        cls.master_cluster = cls.new_cluster(pg_cluster.TempCluster)\n        cls.start_cluster(\n            cls.master_cluster,\n            server_settings={\n                'max_wal_senders': 10,\n                'wal_level': 'hot_standby'\n            }\n        )\n\n        con = None\n\n        try:\n            con = cls.loop.run_until_complete(\n                cls.master_cluster.connect(\n                    database='postgres', user='postgres', loop=cls.loop))\n\n            cls.loop.run_until_complete(\n                con.execute('''\n                    CREATE ROLE replication WITH LOGIN REPLICATION\n                '''))\n\n            cls.master_cluster.trust_local_replication_by('replication')\n\n            conn_spec = cls.master_cluster.get_connection_spec()\n\n            cls.standby_cluster = cls.new_cluster(\n                pg_cluster.HotStandbyCluster,\n                cluster_kwargs={\n                    'master': conn_spec,\n                    'replication_user': 'replication'\n                }\n            )\n            cls.start_cluster(\n                cls.standby_cluster,\n                server_settings={\n                    'hot_standby': True\n                }\n            )\n\n        finally:\n            if con is not None:\n                cls.loop.run_until_complete(con.close())\n\n    @classmethod\n    def get_cluster_connection_spec(cls, cluster, kwargs={}):\n        conn_spec = cluster.get_connection_spec()\n        if kwargs.get('dsn'):\n            conn_spec.pop('host')\n        conn_spec.update(kwargs)\n        if not os.environ.get('PGHOST') and not kwargs.get('dsn'):\n            if 'database' not in conn_spec:\n                conn_spec['database'] = 'postgres'\n            if 'user' not in conn_spec:\n                conn_spec['user'] = 'postgres'\n        return conn_spec\n\n    @classmethod\n    def get_connection_spec(cls, kwargs={}):\n        primary_spec = cls.get_cluster_connection_spec(\n            cls.master_cluster, kwargs\n        )\n        standby_spec = cls.get_cluster_connection_spec(\n            cls.standby_cluster, kwargs\n        )\n        return {\n            'host': [primary_spec['host'], standby_spec['host']],\n            'port': [primary_spec['port'], standby_spec['port']],\n            'database': primary_spec['database'],\n            'user': primary_spec['user'],\n            **kwargs\n        }\n\n    @classmethod\n    def connect_primary(cls, **kwargs):\n        conn_spec = cls.get_cluster_connection_spec(cls.master_cluster, kwargs)\n        return pg_connection.connect(**conn_spec, loop=cls.loop)\n\n    @classmethod\n    def connect_standby(cls, **kwargs):\n        conn_spec = cls.get_cluster_connection_spec(\n            cls.standby_cluster,\n            kwargs\n        )\n        return pg_connection.connect(**conn_spec, loop=cls.loop)\n"
  },
  {
    "path": "asyncpg/_testbase/fuzzer.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport socket\nimport threading\nimport typing\n\nfrom asyncpg import cluster\n\n\nclass StopServer(Exception):\n    pass\n\n\nclass TCPFuzzingProxy:\n    def __init__(self, *, listening_addr: str='127.0.0.1',\n                 listening_port: typing.Optional[int]=None,\n                 backend_host: str, backend_port: int,\n                 settings: typing.Optional[dict]=None) -> None:\n        self.listening_addr = listening_addr\n        self.listening_port = listening_port\n        self.backend_host = backend_host\n        self.backend_port = backend_port\n        self.settings = settings or {}\n        self.loop = None\n        self.connectivity = None\n        self.connectivity_loss = None\n        self.stop_event = None\n        self.connections = {}\n        self.sock = None\n        self.listen_task = None\n\n    async def _wait(self, work):\n        work_task = asyncio.ensure_future(work)\n        stop_event_task = asyncio.ensure_future(self.stop_event.wait())\n\n        try:\n            await asyncio.wait(\n                [work_task, stop_event_task],\n                return_when=asyncio.FIRST_COMPLETED)\n\n            if self.stop_event.is_set():\n                raise StopServer()\n            else:\n                return work_task.result()\n        finally:\n            if not work_task.done():\n                work_task.cancel()\n            if not stop_event_task.done():\n                stop_event_task.cancel()\n\n    def start(self):\n        started = threading.Event()\n        self.thread = threading.Thread(\n            target=self._start_thread, args=(started,))\n        self.thread.start()\n        if not started.wait(timeout=2):\n            raise RuntimeError('fuzzer proxy failed to start')\n\n    def stop(self):\n        self.loop.call_soon_threadsafe(self._stop)\n        self.thread.join()\n\n    def _stop(self):\n        self.stop_event.set()\n\n    def _start_thread(self, started_event):\n        self.loop = asyncio.new_event_loop()\n        asyncio.set_event_loop(self.loop)\n\n        self.connectivity = asyncio.Event()\n        self.connectivity.set()\n        self.connectivity_loss = asyncio.Event()\n        self.stop_event = asyncio.Event()\n\n        if self.listening_port is None:\n            self.listening_port = cluster.find_available_port()\n\n        self.sock = socket.socket()\n        self.sock.bind((self.listening_addr, self.listening_port))\n        self.sock.listen(50)\n        self.sock.setblocking(False)\n\n        try:\n            self.loop.run_until_complete(self._main(started_event))\n        finally:\n            self.loop.close()\n\n    async def _main(self, started_event):\n        self.listen_task = asyncio.ensure_future(self.listen())\n        # Notify the main thread that we are ready to go.\n        started_event.set()\n        try:\n            await self.listen_task\n        finally:\n            for c in list(self.connections):\n                c.close()\n            await asyncio.sleep(0.01)\n            if hasattr(self.loop, 'remove_reader'):\n                self.loop.remove_reader(self.sock.fileno())\n            self.sock.close()\n\n    async def listen(self):\n        while True:\n            try:\n                client_sock, _ = await self._wait(\n                    self.loop.sock_accept(self.sock))\n\n                backend_sock = socket.socket()\n                backend_sock.setblocking(False)\n\n                await self._wait(self.loop.sock_connect(\n                    backend_sock, (self.backend_host, self.backend_port)))\n            except StopServer:\n                break\n\n            conn = Connection(client_sock, backend_sock, self)\n            conn_task = self.loop.create_task(conn.handle())\n            self.connections[conn] = conn_task\n\n    def trigger_connectivity_loss(self):\n        self.loop.call_soon_threadsafe(self._trigger_connectivity_loss)\n\n    def _trigger_connectivity_loss(self):\n        self.connectivity.clear()\n        self.connectivity_loss.set()\n\n    def restore_connectivity(self):\n        self.loop.call_soon_threadsafe(self._restore_connectivity)\n\n    def _restore_connectivity(self):\n        self.connectivity.set()\n        self.connectivity_loss.clear()\n\n    def reset(self):\n        self.restore_connectivity()\n\n    def _close_connection(self, connection):\n        conn_task = self.connections.pop(connection, None)\n        if conn_task is not None:\n            conn_task.cancel()\n\n    def close_all_connections(self):\n        for conn in list(self.connections):\n            self.loop.call_soon_threadsafe(self._close_connection, conn)\n\n\nclass Connection:\n    def __init__(self, client_sock, backend_sock, proxy):\n        self.client_sock = client_sock\n        self.backend_sock = backend_sock\n        self.proxy = proxy\n        self.loop = proxy.loop\n        self.connectivity = proxy.connectivity\n        self.connectivity_loss = proxy.connectivity_loss\n        self.proxy_to_backend_task = None\n        self.proxy_from_backend_task = None\n        self.is_closed = False\n\n    def close(self):\n        if self.is_closed:\n            return\n\n        self.is_closed = True\n\n        if self.proxy_to_backend_task is not None:\n            self.proxy_to_backend_task.cancel()\n            self.proxy_to_backend_task = None\n\n        if self.proxy_from_backend_task is not None:\n            self.proxy_from_backend_task.cancel()\n            self.proxy_from_backend_task = None\n\n        self.proxy._close_connection(self)\n\n    async def handle(self):\n        self.proxy_to_backend_task = asyncio.ensure_future(\n            self.proxy_to_backend())\n\n        self.proxy_from_backend_task = asyncio.ensure_future(\n            self.proxy_from_backend())\n\n        try:\n            await asyncio.wait(\n                [self.proxy_to_backend_task, self.proxy_from_backend_task],\n                return_when=asyncio.FIRST_COMPLETED)\n\n        finally:\n            if self.proxy_to_backend_task is not None:\n                self.proxy_to_backend_task.cancel()\n\n            if self.proxy_from_backend_task is not None:\n                self.proxy_from_backend_task.cancel()\n\n            # Asyncio fails to properly remove the readers and writers\n            # when the task doing recv() or send() is cancelled, so\n            # we must remove the readers and writers manually before\n            # closing the sockets.\n            self.loop.remove_reader(self.client_sock.fileno())\n            self.loop.remove_writer(self.client_sock.fileno())\n            self.loop.remove_reader(self.backend_sock.fileno())\n            self.loop.remove_writer(self.backend_sock.fileno())\n\n            self.client_sock.close()\n            self.backend_sock.close()\n\n    async def _read(self, sock, n):\n        read_task = asyncio.ensure_future(\n            self.loop.sock_recv(sock, n))\n        conn_event_task = asyncio.ensure_future(\n            self.connectivity_loss.wait())\n\n        try:\n            await asyncio.wait(\n                [read_task, conn_event_task],\n                return_when=asyncio.FIRST_COMPLETED)\n\n            if self.connectivity_loss.is_set():\n                return None\n            else:\n                return read_task.result()\n        finally:\n            if not self.loop.is_closed():\n                if not read_task.done():\n                    read_task.cancel()\n                if not conn_event_task.done():\n                    conn_event_task.cancel()\n\n    async def _write(self, sock, data):\n        write_task = asyncio.ensure_future(\n            self.loop.sock_sendall(sock, data))\n        conn_event_task = asyncio.ensure_future(\n            self.connectivity_loss.wait())\n\n        try:\n            await asyncio.wait(\n                [write_task, conn_event_task],\n                return_when=asyncio.FIRST_COMPLETED)\n\n            if self.connectivity_loss.is_set():\n                return None\n            else:\n                return write_task.result()\n        finally:\n            if not self.loop.is_closed():\n                if not write_task.done():\n                    write_task.cancel()\n                if not conn_event_task.done():\n                    conn_event_task.cancel()\n\n    async def proxy_to_backend(self):\n        buf = None\n\n        try:\n            while True:\n                await self.connectivity.wait()\n                if buf is not None:\n                    data = buf\n                    buf = None\n                else:\n                    data = await self._read(self.client_sock, 4096)\n                if data == b'':\n                    break\n                if self.connectivity_loss.is_set():\n                    if data:\n                        buf = data\n                    continue\n                await self._write(self.backend_sock, data)\n\n        except ConnectionError:\n            pass\n\n        finally:\n            if not self.loop.is_closed():\n                self.loop.call_soon(self.close)\n\n    async def proxy_from_backend(self):\n        buf = None\n\n        try:\n            while True:\n                await self.connectivity.wait()\n                if buf is not None:\n                    data = buf\n                    buf = None\n                else:\n                    data = await self._read(self.backend_sock, 4096)\n                if data == b'':\n                    break\n                if self.connectivity_loss.is_set():\n                    if data:\n                        buf = data\n                    continue\n                await self._write(self.client_sock, data)\n\n        except ConnectionError:\n            pass\n\n        finally:\n            if not self.loop.is_closed():\n                self.loop.call_soon(self.close)\n"
  },
  {
    "path": "asyncpg/_version.py",
    "content": "# This file MUST NOT contain anything but the __version__ assignment.\n#\n# When making a release, change the value of __version__\n# to an appropriate value, and open a pull request against\n# the correct branch (master if making a new feature release).\n# The commit message MUST contain a properly formatted release\n# log, and the commit must be signed.\n#\n# The release automation will: build and test the packages for the\n# supported platforms, publish the packages on PyPI, merge the PR\n# to the target branch, create a Git tag pointing to the commit.\n\nfrom __future__ import annotations\n\nimport typing\n\n__version__: typing.Final = '0.32.0.dev0'\n"
  },
  {
    "path": "asyncpg/cluster.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport os\nimport os.path\nimport platform\nimport random\nimport re\nimport shutil\nimport socket\nimport string\nimport subprocess\nimport sys\nimport tempfile\nimport textwrap\nimport time\n\nimport asyncpg\nfrom asyncpg import serverversion\n\n\n_system = platform.uname().system\n\nif _system == 'Windows':\n    def platform_exe(name):\n        if name.endswith('.exe'):\n            return name\n        return name + '.exe'\nelse:\n    def platform_exe(name):\n        return name\n\n\ndef find_available_port():\n    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    try:\n        sock.bind(('127.0.0.1', 0))\n        return sock.getsockname()[1]\n    except Exception:\n        return None\n    finally:\n        sock.close()\n\n\ndef _world_readable_mkdtemp(suffix=None, prefix=None, dir=None):\n    name = \"\".join(random.choices(string.ascii_lowercase, k=8))\n    if dir is None:\n        dir = tempfile.gettempdir()\n    if prefix is None:\n        prefix = tempfile.gettempprefix()\n    if suffix is None:\n        suffix = \"\"\n    fn = os.path.join(dir, prefix + name + suffix)\n    os.mkdir(fn, 0o755)\n    return fn\n\n\ndef _mkdtemp(suffix=None, prefix=None, dir=None):\n    if _system == 'Windows' and os.environ.get(\"GITHUB_ACTIONS\"):\n        # Due to mitigations introduced in python/cpython#118486\n        # when Python runs in a session created via an SSH connection\n        # tempfile.mkdtemp creates directories that are not accessible.\n        return _world_readable_mkdtemp(suffix, prefix, dir)\n    else:\n        return tempfile.mkdtemp(suffix, prefix, dir)\n\n\nclass ClusterError(Exception):\n    pass\n\n\nclass Cluster:\n    def __init__(self, data_dir, *, pg_config_path=None):\n        self._data_dir = data_dir\n        self._pg_config_path = pg_config_path\n        self._pg_bin_dir = (\n            os.environ.get('PGINSTALLATION')\n            or os.environ.get('PGBIN')\n        )\n        self._pg_ctl = None\n        self._daemon_pid = None\n        self._daemon_process = None\n        self._connection_addr = None\n        self._connection_spec_override = None\n\n    def get_pg_version(self):\n        return self._pg_version\n\n    def is_managed(self):\n        return True\n\n    def get_data_dir(self):\n        return self._data_dir\n\n    def get_status(self):\n        if self._pg_ctl is None:\n            self._init_env()\n\n        process = subprocess.run(\n            [self._pg_ctl, 'status', '-D', self._data_dir],\n            stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = process.stdout, process.stderr\n\n        if (process.returncode == 4 or not os.path.exists(self._data_dir) or\n                not os.listdir(self._data_dir)):\n            return 'not-initialized'\n        elif process.returncode == 3:\n            return 'stopped'\n        elif process.returncode == 0:\n            r = re.match(r'.*PID\\s?:\\s+(\\d+).*', stdout.decode())\n            if not r:\n                raise ClusterError(\n                    'could not parse pg_ctl status output: {}'.format(\n                        stdout.decode()))\n            self._daemon_pid = int(r.group(1))\n            return self._test_connection(timeout=0)\n        else:\n            raise ClusterError(\n                'pg_ctl status exited with status {:d}: {}'.format(\n                    process.returncode, stderr))\n\n    async def connect(self, loop=None, **kwargs):\n        conn_info = self.get_connection_spec()\n        conn_info.update(kwargs)\n        return await asyncpg.connect(loop=loop, **conn_info)\n\n    def init(self, **settings):\n        \"\"\"Initialize cluster.\"\"\"\n        if self.get_status() != 'not-initialized':\n            raise ClusterError(\n                'cluster in {!r} has already been initialized'.format(\n                    self._data_dir))\n\n        settings = dict(settings)\n        if 'encoding' not in settings:\n            settings['encoding'] = 'UTF-8'\n\n        if settings:\n            settings_args = ['--{}={}'.format(k, v)\n                             for k, v in settings.items()]\n            extra_args = ['-o'] + [' '.join(settings_args)]\n        else:\n            extra_args = []\n\n        os.makedirs(self._data_dir, exist_ok=True)\n        process = subprocess.run(\n            [self._pg_ctl, 'init', '-D', self._data_dir] + extra_args,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.STDOUT,\n            cwd=self._data_dir,\n        )\n\n        output = process.stdout\n\n        if process.returncode != 0:\n            raise ClusterError(\n                'pg_ctl init exited with status {:d}:\\n{}'.format(\n                    process.returncode, output.decode()))\n\n        return output.decode()\n\n    def start(self, wait=60, *, server_settings={}, **opts):\n        \"\"\"Start the cluster.\"\"\"\n        status = self.get_status()\n        if status == 'running':\n            return\n        elif status == 'not-initialized':\n            raise ClusterError(\n                'cluster in {!r} has not been initialized'.format(\n                    self._data_dir))\n\n        port = opts.pop('port', None)\n        if port == 'dynamic':\n            port = find_available_port()\n\n        extra_args = ['--{}={}'.format(k, v) for k, v in opts.items()]\n        extra_args.append('--port={}'.format(port))\n\n        sockdir = server_settings.get('unix_socket_directories')\n        if sockdir is None:\n            sockdir = server_settings.get('unix_socket_directory')\n        if sockdir is None and _system != 'Windows':\n            sockdir = tempfile.gettempdir()\n\n        ssl_key = server_settings.get('ssl_key_file')\n        if ssl_key:\n            # Make sure server certificate key file has correct permissions.\n            keyfile = os.path.join(self._data_dir, 'srvkey.pem')\n            shutil.copy(ssl_key, keyfile)\n            os.chmod(keyfile, 0o600)\n            server_settings = server_settings.copy()\n            server_settings['ssl_key_file'] = keyfile\n\n        if sockdir is not None:\n            if self._pg_version < (9, 3):\n                sockdir_opt = 'unix_socket_directory'\n            else:\n                sockdir_opt = 'unix_socket_directories'\n\n            server_settings[sockdir_opt] = sockdir\n\n        for k, v in server_settings.items():\n            extra_args.extend(['-c', '{}={}'.format(k, v)])\n\n        if _system == 'Windows':\n            # On Windows we have to use pg_ctl as direct execution\n            # of postgres daemon under an Administrative account\n            # is not permitted and there is no easy way to drop\n            # privileges.\n            if os.getenv('ASYNCPG_DEBUG_SERVER'):\n                stdout = sys.stdout\n                print(\n                    'asyncpg.cluster: Running',\n                    ' '.join([\n                        self._pg_ctl, 'start', '-D', self._data_dir,\n                        '-o', ' '.join(extra_args)\n                    ]),\n                    file=sys.stderr,\n                )\n            else:\n                stdout = subprocess.DEVNULL\n\n            process = subprocess.run(\n                [self._pg_ctl, 'start', '-D', self._data_dir,\n                 '-o', ' '.join(extra_args)],\n                stdout=stdout,\n                stderr=subprocess.STDOUT,\n                cwd=self._data_dir,\n            )\n\n            if process.returncode != 0:\n                if process.stderr:\n                    stderr = ':\\n{}'.format(process.stderr.decode())\n                else:\n                    stderr = ''\n                raise ClusterError(\n                    'pg_ctl start exited with status {:d}{}'.format(\n                        process.returncode, stderr))\n        else:\n            if os.getenv('ASYNCPG_DEBUG_SERVER'):\n                stdout = sys.stdout\n            else:\n                stdout = subprocess.DEVNULL\n\n            self._daemon_process = \\\n                subprocess.Popen(\n                    [self._postgres, '-D', self._data_dir, *extra_args],\n                    stdout=stdout,\n                    stderr=subprocess.STDOUT,\n                    cwd=self._data_dir,\n                )\n\n            self._daemon_pid = self._daemon_process.pid\n\n        self._test_connection(timeout=wait)\n\n    def reload(self):\n        \"\"\"Reload server configuration.\"\"\"\n        status = self.get_status()\n        if status != 'running':\n            raise ClusterError('cannot reload: cluster is not running')\n\n        process = subprocess.run(\n            [self._pg_ctl, 'reload', '-D', self._data_dir],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            cwd=self._data_dir,\n        )\n\n        stderr = process.stderr\n\n        if process.returncode != 0:\n            raise ClusterError(\n                'pg_ctl stop exited with status {:d}: {}'.format(\n                    process.returncode, stderr.decode()))\n\n    def stop(self, wait=60):\n        process = subprocess.run(\n            [self._pg_ctl, 'stop', '-D', self._data_dir, '-t', str(wait),\n             '-m', 'fast'],\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            cwd=self._data_dir,\n        )\n\n        stderr = process.stderr\n\n        if process.returncode != 0:\n            raise ClusterError(\n                'pg_ctl stop exited with status {:d}: {}'.format(\n                    process.returncode, stderr.decode()))\n\n        if (self._daemon_process is not None and\n                self._daemon_process.returncode is None):\n            self._daemon_process.kill()\n\n    def destroy(self):\n        status = self.get_status()\n        if status == 'stopped' or status == 'not-initialized':\n            shutil.rmtree(self._data_dir)\n        else:\n            raise ClusterError('cannot destroy {} cluster'.format(status))\n\n    def _get_connection_spec(self):\n        if self._connection_addr is None:\n            self._connection_addr = self._connection_addr_from_pidfile()\n\n        if self._connection_addr is not None:\n            if self._connection_spec_override:\n                args = self._connection_addr.copy()\n                args.update(self._connection_spec_override)\n                return args\n            else:\n                return self._connection_addr\n\n    def get_connection_spec(self):\n        status = self.get_status()\n        if status != 'running':\n            raise ClusterError('cluster is not running')\n\n        return self._get_connection_spec()\n\n    def override_connection_spec(self, **kwargs):\n        self._connection_spec_override = kwargs\n\n    def reset_wal(self, *, oid=None, xid=None):\n        status = self.get_status()\n        if status == 'not-initialized':\n            raise ClusterError(\n                'cannot modify WAL status: cluster is not initialized')\n\n        if status == 'running':\n            raise ClusterError(\n                'cannot modify WAL status: cluster is running')\n\n        opts = []\n        if oid is not None:\n            opts.extend(['-o', str(oid)])\n        if xid is not None:\n            opts.extend(['-x', str(xid)])\n        if not opts:\n            return\n\n        opts.append(self._data_dir)\n\n        try:\n            reset_wal = self._find_pg_binary('pg_resetwal')\n        except ClusterError:\n            reset_wal = self._find_pg_binary('pg_resetxlog')\n\n        process = subprocess.run(\n            [reset_wal] + opts,\n            stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n\n        stderr = process.stderr\n\n        if process.returncode != 0:\n            raise ClusterError(\n                'pg_resetwal exited with status {:d}: {}'.format(\n                    process.returncode, stderr.decode()))\n\n    def reset_hba(self):\n        \"\"\"Remove all records from pg_hba.conf.\"\"\"\n        status = self.get_status()\n        if status == 'not-initialized':\n            raise ClusterError(\n                'cannot modify HBA records: cluster is not initialized')\n\n        pg_hba = os.path.join(self._data_dir, 'pg_hba.conf')\n\n        try:\n            with open(pg_hba, 'w'):\n                pass\n        except IOError as e:\n            raise ClusterError(\n                'cannot modify HBA records: {}'.format(e)) from e\n\n    def add_hba_entry(self, *, type='host', database, user, address=None,\n                      auth_method, auth_options=None):\n        \"\"\"Add a record to pg_hba.conf.\"\"\"\n        status = self.get_status()\n        if status == 'not-initialized':\n            raise ClusterError(\n                'cannot modify HBA records: cluster is not initialized')\n\n        if type not in {'local', 'host', 'hostssl', 'hostnossl'}:\n            raise ValueError('invalid HBA record type: {!r}'.format(type))\n\n        pg_hba = os.path.join(self._data_dir, 'pg_hba.conf')\n\n        record = '{} {} {}'.format(type, database, user)\n\n        if type != 'local':\n            if address is None:\n                raise ValueError(\n                    '{!r} entry requires a valid address'.format(type))\n            else:\n                record += ' {}'.format(address)\n\n        record += ' {}'.format(auth_method)\n\n        if auth_options is not None:\n            record += ' ' + ' '.join(\n                '{}={}'.format(k, v) for k, v in auth_options)\n\n        try:\n            with open(pg_hba, 'a') as f:\n                print(record, file=f)\n        except IOError as e:\n            raise ClusterError(\n                'cannot modify HBA records: {}'.format(e)) from e\n\n    def trust_local_connections(self):\n        self.reset_hba()\n\n        if _system != 'Windows':\n            self.add_hba_entry(type='local', database='all',\n                               user='all', auth_method='trust')\n        self.add_hba_entry(type='host', address='127.0.0.1/32',\n                           database='all', user='all',\n                           auth_method='trust')\n        self.add_hba_entry(type='host', address='::1/128',\n                           database='all', user='all',\n                           auth_method='trust')\n        status = self.get_status()\n        if status == 'running':\n            self.reload()\n\n    def trust_local_replication_by(self, user):\n        if _system != 'Windows':\n            self.add_hba_entry(type='local', database='replication',\n                               user=user, auth_method='trust')\n        self.add_hba_entry(type='host', address='127.0.0.1/32',\n                           database='replication', user=user,\n                           auth_method='trust')\n        self.add_hba_entry(type='host', address='::1/128',\n                           database='replication', user=user,\n                           auth_method='trust')\n        status = self.get_status()\n        if status == 'running':\n            self.reload()\n\n    def _init_env(self):\n        if not self._pg_bin_dir:\n            pg_config = self._find_pg_config(self._pg_config_path)\n            pg_config_data = self._run_pg_config(pg_config)\n\n            self._pg_bin_dir = pg_config_data.get('bindir')\n            if not self._pg_bin_dir:\n                raise ClusterError(\n                    'pg_config output did not provide the BINDIR value')\n\n        self._pg_ctl = self._find_pg_binary('pg_ctl')\n        self._postgres = self._find_pg_binary('postgres')\n        self._pg_version = self._get_pg_version()\n\n    def _connection_addr_from_pidfile(self):\n        pidfile = os.path.join(self._data_dir, 'postmaster.pid')\n\n        try:\n            with open(pidfile, 'rt') as f:\n                piddata = f.read()\n        except FileNotFoundError:\n            return None\n\n        lines = piddata.splitlines()\n\n        if len(lines) < 6:\n            # A complete postgres pidfile is at least 6 lines\n            return None\n\n        pmpid = int(lines[0])\n        if self._daemon_pid and pmpid != self._daemon_pid:\n            # This might be an old pidfile left from previous postgres\n            # daemon run.\n            return None\n\n        portnum = lines[3]\n        sockdir = lines[4]\n        hostaddr = lines[5]\n\n        if sockdir:\n            if sockdir[0] != '/':\n                # Relative sockdir\n                sockdir = os.path.normpath(\n                    os.path.join(self._data_dir, sockdir))\n            host_str = sockdir\n        else:\n            host_str = hostaddr\n\n        if host_str == '*':\n            host_str = 'localhost'\n        elif host_str == '0.0.0.0':\n            host_str = '127.0.0.1'\n        elif host_str == '::':\n            host_str = '::1'\n\n        return {\n            'host': host_str,\n            'port': portnum\n        }\n\n    def _test_connection(self, timeout=60):\n        self._connection_addr = None\n\n        loop = asyncio.new_event_loop()\n\n        try:\n            for i in range(timeout):\n                if self._connection_addr is None:\n                    conn_spec = self._get_connection_spec()\n                    if conn_spec is None:\n                        time.sleep(1)\n                        continue\n\n                try:\n                    con = loop.run_until_complete(\n                        asyncpg.connect(database='postgres',\n                                        user='postgres',\n                                        timeout=5, loop=loop,\n                                        **self._connection_addr))\n                except (OSError, asyncio.TimeoutError,\n                        asyncpg.CannotConnectNowError,\n                        asyncpg.PostgresConnectionError):\n                    time.sleep(1)\n                    continue\n                except asyncpg.PostgresError:\n                    # Any other error other than ServerNotReadyError or\n                    # ConnectionError is interpreted to indicate the server is\n                    # up.\n                    break\n                else:\n                    loop.run_until_complete(con.close())\n                    break\n        finally:\n            loop.close()\n\n        return 'running'\n\n    def _run_pg_config(self, pg_config_path):\n        process = subprocess.run(\n            pg_config_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = process.stdout, process.stderr\n\n        if process.returncode != 0:\n            raise ClusterError('pg_config exited with status {:d}: {}'.format(\n                process.returncode, stderr))\n        else:\n            config = {}\n\n            for line in stdout.splitlines():\n                k, eq, v = line.decode('utf-8').partition('=')\n                if eq:\n                    config[k.strip().lower()] = v.strip()\n\n            return config\n\n    def _find_pg_config(self, pg_config_path):\n        if pg_config_path is None:\n            pg_install = (\n                os.environ.get('PGINSTALLATION')\n                or os.environ.get('PGBIN')\n            )\n            if pg_install:\n                pg_config_path = platform_exe(\n                    os.path.join(pg_install, 'pg_config'))\n            else:\n                pathenv = os.environ.get('PATH').split(os.pathsep)\n                for path in pathenv:\n                    pg_config_path = platform_exe(\n                        os.path.join(path, 'pg_config'))\n                    if os.path.exists(pg_config_path):\n                        break\n                else:\n                    pg_config_path = None\n\n        if not pg_config_path:\n            raise ClusterError('could not find pg_config executable')\n\n        if not os.path.isfile(pg_config_path):\n            raise ClusterError('{!r} is not an executable'.format(\n                pg_config_path))\n\n        return pg_config_path\n\n    def _find_pg_binary(self, binary):\n        bpath = platform_exe(os.path.join(self._pg_bin_dir, binary))\n\n        if not os.path.isfile(bpath):\n            raise ClusterError(\n                'could not find {} executable: '.format(binary) +\n                '{!r} does not exist or is not a file'.format(bpath))\n\n        return bpath\n\n    def _get_pg_version(self):\n        process = subprocess.run(\n            [self._postgres, '--version'],\n            stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        stdout, stderr = process.stdout, process.stderr\n\n        if process.returncode != 0:\n            raise ClusterError(\n                'postgres --version exited with status {:d}: {}'.format(\n                    process.returncode, stderr))\n\n        version_string = stdout.decode('utf-8').strip(' \\n')\n        prefix = 'postgres (PostgreSQL) '\n        if not version_string.startswith(prefix):\n            raise ClusterError(\n                'could not determine server version from {!r}'.format(\n                    version_string))\n        version_string = version_string[len(prefix):]\n\n        return serverversion.split_server_version_string(version_string)\n\n\nclass TempCluster(Cluster):\n    def __init__(self, *,\n                 data_dir_suffix=None, data_dir_prefix=None,\n                 data_dir_parent=None, pg_config_path=None):\n        self._data_dir = _mkdtemp(suffix=data_dir_suffix,\n                                  prefix=data_dir_prefix,\n                                  dir=data_dir_parent)\n        super().__init__(self._data_dir, pg_config_path=pg_config_path)\n\n\nclass HotStandbyCluster(TempCluster):\n    def __init__(self, *,\n                 master, replication_user,\n                 data_dir_suffix=None, data_dir_prefix=None,\n                 data_dir_parent=None, pg_config_path=None):\n        self._master = master\n        self._repl_user = replication_user\n        super().__init__(\n            data_dir_suffix=data_dir_suffix,\n            data_dir_prefix=data_dir_prefix,\n            data_dir_parent=data_dir_parent,\n            pg_config_path=pg_config_path)\n\n    def _init_env(self):\n        super()._init_env()\n        self._pg_basebackup = self._find_pg_binary('pg_basebackup')\n\n    def init(self, **settings):\n        \"\"\"Initialize cluster.\"\"\"\n        if self.get_status() != 'not-initialized':\n            raise ClusterError(\n                'cluster in {!r} has already been initialized'.format(\n                    self._data_dir))\n\n        process = subprocess.run(\n            [self._pg_basebackup, '-h', self._master['host'],\n             '-p', self._master['port'], '-D', self._data_dir,\n             '-U', self._repl_user],\n            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n\n        output = process.stdout\n\n        if process.returncode != 0:\n            raise ClusterError(\n                'pg_basebackup init exited with status {:d}:\\n{}'.format(\n                    process.returncode, output.decode()))\n\n        if self._pg_version < (12, 0):\n            with open(os.path.join(self._data_dir, 'recovery.conf'), 'w') as f:\n                f.write(textwrap.dedent(\"\"\"\\\n                    standby_mode = 'on'\n                    primary_conninfo = 'host={host} port={port} user={user}'\n                \"\"\".format(\n                    host=self._master['host'],\n                    port=self._master['port'],\n                    user=self._repl_user)))\n        else:\n            f = open(os.path.join(self._data_dir, 'standby.signal'), 'w')\n            f.close()\n\n        return output.decode()\n\n    def start(self, wait=60, *, server_settings={}, **opts):\n        if self._pg_version >= (12, 0):\n            server_settings = server_settings.copy()\n            server_settings['primary_conninfo'] = (\n                '\"host={host} port={port} user={user}\"'.format(\n                    host=self._master['host'],\n                    port=self._master['port'],\n                    user=self._repl_user,\n                )\n            )\n\n        super().start(wait=wait, server_settings=server_settings, **opts)\n\n\nclass RunningCluster(Cluster):\n    def __init__(self, **kwargs):\n        self.conn_spec = kwargs\n\n    def is_managed(self):\n        return False\n\n    def get_connection_spec(self):\n        return dict(self.conn_spec)\n\n    def get_status(self):\n        return 'running'\n\n    def init(self, **settings):\n        pass\n\n    def start(self, wait=60, **settings):\n        pass\n\n    def stop(self, wait=60):\n        pass\n\n    def destroy(self):\n        pass\n\n    def reset_hba(self):\n        raise ClusterError('cannot modify HBA records of unmanaged cluster')\n\n    def add_hba_entry(self, *, type='host', database, user, address=None,\n                      auth_method, auth_options=None):\n        raise ClusterError('cannot modify HBA records of unmanaged cluster')\n"
  },
  {
    "path": "asyncpg/compat.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nimport enum\nimport pathlib\nimport platform\nimport typing\nimport sys\n\nif typing.TYPE_CHECKING:\n    import asyncio\n\nSYSTEM: typing.Final = platform.uname().system\n\n\nif sys.platform == 'win32':\n    import ctypes.wintypes\n\n    CSIDL_APPDATA: typing.Final = 0x001a\n\n    def get_pg_home_directory() -> pathlib.Path | None:\n        # We cannot simply use expanduser() as that returns the user's\n        # home directory, whereas Postgres stores its config in\n        # %AppData% on Windows.\n        buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)\n        r = ctypes.windll.shell32.SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, buf)\n        if r:\n            return None\n        else:\n            return pathlib.Path(buf.value) / 'postgresql'\n\nelse:\n    def get_pg_home_directory() -> pathlib.Path | None:\n        try:\n            return pathlib.Path.home()\n        except (RuntimeError, KeyError):\n            return None\n\n\nasync def wait_closed(stream: asyncio.StreamWriter) -> None:\n    # Not all asyncio versions have StreamWriter.wait_closed().\n    if hasattr(stream, 'wait_closed'):\n        try:\n            await stream.wait_closed()\n        except ConnectionResetError:\n            # On Windows wait_closed() sometimes propagates\n            # ConnectionResetError which is totally unnecessary.\n            pass\n\n\nif sys.version_info < (3, 12):\n    def markcoroutinefunction(c):  # type: ignore\n        pass\nelse:\n    from inspect import markcoroutinefunction  # noqa: F401\n\n\nif sys.version_info < (3, 12):\n    from ._asyncio_compat import wait_for as wait_for  # noqa: F401\nelse:\n    from asyncio import wait_for as wait_for  # noqa: F401\n\n\nif sys.version_info < (3, 11):\n    from ._asyncio_compat import timeout_ctx as timeout  # noqa: F401\nelse:\n    from asyncio import timeout as timeout  # noqa: F401\n\nif sys.version_info < (3, 9):\n    from typing import (  # noqa: F401\n        Awaitable as Awaitable,\n    )\nelse:\n    from collections.abc import (  # noqa: F401\n        Awaitable as Awaitable,\n    )\n\nif sys.version_info < (3, 11):\n    class StrEnum(str, enum.Enum):\n        __str__ = str.__str__\n        __repr__ = enum.Enum.__repr__\nelse:\n    from enum import StrEnum as StrEnum  # noqa: F401\n"
  },
  {
    "path": "asyncpg/connect_utils.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nimport asyncio\nimport configparser\nimport collections\nfrom collections.abc import Callable\nimport enum\nimport functools\nimport getpass\nimport os\nimport pathlib\nimport platform\nimport random\nimport re\nimport socket\nimport ssl as ssl_module\nimport stat\nimport struct\nimport sys\nimport typing\nimport urllib.parse\nimport warnings\nimport inspect\n\nfrom . import compat\nfrom . import exceptions\nfrom . import protocol\n\n\nclass SSLMode(enum.IntEnum):\n    disable = 0\n    allow = 1\n    prefer = 2\n    require = 3\n    verify_ca = 4\n    verify_full = 5\n\n    @classmethod\n    def parse(cls, sslmode):\n        if isinstance(sslmode, cls):\n            return sslmode\n        return getattr(cls, sslmode.replace('-', '_'))\n\n\nclass SSLNegotiation(compat.StrEnum):\n    postgres = \"postgres\"\n    direct = \"direct\"\n\n\n_ConnectionParameters = collections.namedtuple(\n    'ConnectionParameters',\n    [\n        'user',\n        'password',\n        'database',\n        'ssl',\n        'sslmode',\n        'ssl_negotiation',\n        'server_settings',\n        'target_session_attrs',\n        'krbsrvname',\n        'gsslib',\n    ])\n\n\n_ClientConfiguration = collections.namedtuple(\n    'ConnectionConfiguration',\n    [\n        'command_timeout',\n        'statement_cache_size',\n        'max_cached_statement_lifetime',\n        'max_cacheable_statement_size',\n    ])\n\n\n_system = platform.uname().system\n\n\nif _system == 'Windows':\n    PGPASSFILE = 'pgpass.conf'\nelse:\n    PGPASSFILE = '.pgpass'\n\n\nPG_SERVICEFILE = '.pg_service.conf'\n\n\ndef _read_password_file(passfile: pathlib.Path) \\\n        -> typing.List[typing.Tuple[str, ...]]:\n\n    passtab = []\n\n    try:\n        if not passfile.exists():\n            return []\n\n        if not passfile.is_file():\n            warnings.warn(\n                'password file {!r} is not a plain file'.format(passfile))\n\n            return []\n\n        if _system != 'Windows':\n            if passfile.stat().st_mode & (stat.S_IRWXG | stat.S_IRWXO):\n                warnings.warn(\n                    'password file {!r} has group or world access; '\n                    'permissions should be u=rw (0600) or less'.format(\n                        passfile))\n\n                return []\n\n        with passfile.open('rt') as f:\n            for line in f:\n                line = line.strip()\n                if not line or line.startswith('#'):\n                    # Skip empty lines and comments.\n                    continue\n                # Backslash escapes both itself and the colon,\n                # which is a record separator.\n                line = line.replace(R'\\\\', '\\n')\n                passtab.append(tuple(\n                    p.replace('\\n', R'\\\\')\n                    for p in re.split(r'(?<!\\\\):', line, maxsplit=4)\n                ))\n    except IOError:\n        pass\n\n    return passtab\n\n\ndef _read_password_from_pgpass(\n        *, passfile: typing.Optional[pathlib.Path],\n        hosts: typing.List[str],\n        ports: typing.List[int],\n        database: str,\n        user: str):\n    \"\"\"Parse the pgpass file and return the matching password.\n\n    :return:\n        Password string, if found, ``None`` otherwise.\n    \"\"\"\n\n    passtab = _read_password_file(passfile)\n    if not passtab:\n        return None\n\n    for host, port in zip(hosts, ports):\n        if host.startswith('/'):\n            # Unix sockets get normalized into 'localhost'\n            host = 'localhost'\n\n        for phost, pport, pdatabase, puser, ppassword in passtab:\n            if phost != '*' and phost != host:\n                continue\n            if pport != '*' and pport != str(port):\n                continue\n            if pdatabase != '*' and pdatabase != database:\n                continue\n            if puser != '*' and puser != user:\n                continue\n\n            # Found a match.\n            return ppassword\n\n    return None\n\n\ndef _validate_port_spec(hosts, port):\n    if isinstance(port, list) and len(port) > 1:\n        # If there is a list of ports, its length must\n        # match that of the host list.\n        if len(port) != len(hosts):\n            raise exceptions.ClientConfigurationError(\n                'could not match {} port numbers to {} hosts'.format(\n                    len(port), len(hosts)))\n    elif isinstance(port, list) and len(port) == 1:\n        port = [port[0] for _ in range(len(hosts))]\n    else:\n        port = [port for _ in range(len(hosts))]\n\n    return port\n\n\ndef _parse_hostlist(hostlist, port, *, unquote=False):\n    if ',' in hostlist:\n        # A comma-separated list of host addresses.\n        hostspecs = hostlist.split(',')\n    else:\n        hostspecs = [hostlist]\n\n    hosts = []\n    hostlist_ports = []\n\n    if not port:\n        portspec = os.environ.get('PGPORT')\n        if portspec:\n            if ',' in portspec:\n                default_port = [int(p) for p in portspec.split(',')]\n            else:\n                default_port = int(portspec)\n        else:\n            default_port = 5432\n\n        default_port = _validate_port_spec(hostspecs, default_port)\n\n    else:\n        port = _validate_port_spec(hostspecs, port)\n\n    for i, hostspec in enumerate(hostspecs):\n        if hostspec[0] == '/':\n            # Unix socket\n            addr = hostspec\n            hostspec_port = ''\n        elif hostspec[0] == '[':\n            # IPv6 address\n            m = re.match(r'(?:\\[([^\\]]+)\\])(?::([0-9]+))?', hostspec)\n            if m:\n                addr = m.group(1)\n                hostspec_port = m.group(2)\n            else:\n                raise exceptions.ClientConfigurationError(\n                    'invalid IPv6 address in the connection URI: {!r}'.format(\n                        hostspec\n                    )\n                )\n        else:\n            # IPv4 address\n            addr, _, hostspec_port = hostspec.partition(':')\n\n        if unquote:\n            addr = urllib.parse.unquote(addr)\n\n        hosts.append(addr)\n        if not port:\n            if hostspec_port:\n                if unquote:\n                    hostspec_port = urllib.parse.unquote(hostspec_port)\n                hostlist_ports.append(int(hostspec_port))\n            else:\n                hostlist_ports.append(default_port[i])\n\n    if not port:\n        port = hostlist_ports\n\n    return hosts, port\n\n\ndef _parse_tls_version(tls_version):\n    if tls_version.startswith('SSL'):\n        raise exceptions.ClientConfigurationError(\n            f\"Unsupported TLS version: {tls_version}\"\n        )\n    try:\n        return ssl_module.TLSVersion[tls_version.replace('.', '_')]\n    except KeyError:\n        raise exceptions.ClientConfigurationError(\n            f\"No such TLS version: {tls_version}\"\n        )\n\n\ndef _dot_postgresql_path(filename) -> typing.Optional[pathlib.Path]:\n    try:\n        homedir = pathlib.Path.home()\n    except (RuntimeError, KeyError):\n        return None\n\n    return (homedir / '.postgresql' / filename).resolve()\n\n\ndef _parse_connect_dsn_and_args(*, dsn, host, port, user,\n                                password, passfile, database, ssl,\n                                service, servicefile,\n                                direct_tls, server_settings,\n                                target_session_attrs, krbsrvname, gsslib):\n    # `auth_hosts` is the version of host information for the purposes\n    # of reading the pgpass file.\n    auth_hosts = None\n    sslcert = sslkey = sslrootcert = sslcrl = sslpassword = None\n    ssl_min_protocol_version = ssl_max_protocol_version = None\n    sslnegotiation = None\n\n    if dsn:\n        parsed = urllib.parse.urlparse(dsn)\n\n        query = None\n        if parsed.query:\n            query = urllib.parse.parse_qs(parsed.query, strict_parsing=True)\n            for key, val in query.items():\n                if isinstance(val, list):\n                    query[key] = val[-1]\n\n            if 'service' in query:\n                val = query.pop('service')\n                if not service and val:\n                    service = val\n\n        connection_service_file = servicefile\n\n        if connection_service_file is None:\n            connection_service_file = os.getenv('PGSERVICEFILE')\n\n        if connection_service_file is None:\n            homedir = compat.get_pg_home_directory()\n            if homedir:\n                connection_service_file = homedir / PG_SERVICEFILE\n            else:\n                connection_service_file = None\n        else:\n            connection_service_file = pathlib.Path(connection_service_file)\n\n        if parsed.scheme not in {'postgresql', 'postgres'}:\n            raise exceptions.ClientConfigurationError(\n                'invalid DSN: scheme is expected to be either '\n                '\"postgresql\" or \"postgres\", got {!r}'.format(parsed.scheme))\n\n        if parsed.netloc:\n            if '@' in parsed.netloc:\n                dsn_auth, _, dsn_hostspec = parsed.netloc.partition('@')\n            else:\n                dsn_hostspec = parsed.netloc\n                dsn_auth = ''\n        else:\n            dsn_auth = dsn_hostspec = ''\n\n        if dsn_auth:\n            dsn_user, _, dsn_password = dsn_auth.partition(':')\n        else:\n            dsn_user = dsn_password = ''\n\n        if not host and dsn_hostspec:\n            host, port = _parse_hostlist(dsn_hostspec, port, unquote=True)\n\n        if parsed.path and database is None:\n            dsn_database = parsed.path\n            if dsn_database.startswith('/'):\n                dsn_database = dsn_database[1:]\n            database = urllib.parse.unquote(dsn_database)\n\n        if user is None and dsn_user:\n            user = urllib.parse.unquote(dsn_user)\n\n        if password is None and dsn_password:\n            password = urllib.parse.unquote(dsn_password)\n\n        if query:\n\n            if 'port' in query:\n                val = query.pop('port')\n                if not port and val:\n                    port = [int(p) for p in val.split(',')]\n\n            if 'host' in query:\n                val = query.pop('host')\n                if not host and val:\n                    host, port = _parse_hostlist(val, port)\n\n            if 'dbname' in query:\n                val = query.pop('dbname')\n                if database is None:\n                    database = val\n\n            if 'database' in query:\n                val = query.pop('database')\n                if database is None:\n                    database = val\n\n            if 'user' in query:\n                val = query.pop('user')\n                if user is None:\n                    user = val\n\n            if 'password' in query:\n                val = query.pop('password')\n                if password is None:\n                    password = val\n\n            if 'passfile' in query:\n                val = query.pop('passfile')\n                if passfile is None:\n                    passfile = val\n\n            if 'sslmode' in query:\n                val = query.pop('sslmode')\n                if ssl is None:\n                    ssl = val\n\n            if 'sslcert' in query:\n                sslcert = query.pop('sslcert')\n\n            if 'sslkey' in query:\n                sslkey = query.pop('sslkey')\n\n            if 'sslrootcert' in query:\n                sslrootcert = query.pop('sslrootcert')\n\n            if 'sslnegotiation' in query:\n                sslnegotiation = query.pop('sslnegotiation')\n\n            if 'sslcrl' in query:\n                sslcrl = query.pop('sslcrl')\n\n            if 'sslpassword' in query:\n                sslpassword = query.pop('sslpassword')\n\n            if 'ssl_min_protocol_version' in query:\n                ssl_min_protocol_version = query.pop(\n                    'ssl_min_protocol_version'\n                )\n\n            if 'ssl_max_protocol_version' in query:\n                ssl_max_protocol_version = query.pop(\n                    'ssl_max_protocol_version'\n                )\n\n            if 'target_session_attrs' in query:\n                dsn_target_session_attrs = query.pop(\n                    'target_session_attrs'\n                )\n                if target_session_attrs is None:\n                    target_session_attrs = dsn_target_session_attrs\n\n            if 'krbsrvname' in query:\n                val = query.pop('krbsrvname')\n                if krbsrvname is None:\n                    krbsrvname = val\n\n            if 'gsslib' in query:\n                val = query.pop('gsslib')\n                if gsslib is None:\n                    gsslib = val\n\n            if 'service' in query:\n                val = query.pop('service')\n                if service is None:\n                    service = val\n\n            if query:\n                if server_settings is None:\n                    server_settings = query\n                else:\n                    server_settings = {**query, **server_settings}\n\n        if connection_service_file is not None and service is not None:\n            pg_service = configparser.ConfigParser()\n            pg_service.read(connection_service_file)\n            if service in pg_service.sections():\n                service_params = pg_service[service]\n                if 'port' in service_params:\n                    val = service_params.pop('port')\n                    if not port and val:\n                        port = [int(p) for p in val.split(',')]\n\n                if 'host' in service_params:\n                    val = service_params.pop('host')\n                    if not host and val:\n                        host, port = _parse_hostlist(val, port)\n\n                if 'dbname' in service_params:\n                    val = service_params.pop('dbname')\n                    if database is None:\n                        database = val\n\n                if 'database' in service_params:\n                    val = service_params.pop('database')\n                    if database is None:\n                        database = val\n\n                if 'user' in service_params:\n                    val = service_params.pop('user')\n                    if user is None:\n                        user = val\n\n                if 'password' in service_params:\n                    val = service_params.pop('password')\n                    if password is None:\n                        password = val\n\n                if 'passfile' in service_params:\n                    val = service_params.pop('passfile')\n                    if passfile is None:\n                        passfile = val\n\n                if 'sslmode' in service_params:\n                    val = service_params.pop('sslmode')\n                    if ssl is None:\n                        ssl = val\n\n                if 'sslcert' in service_params:\n                    val = service_params.pop('sslcert')\n                    if sslcert is None:\n                        sslcert = val\n\n                if 'sslkey' in service_params:\n                    val = service_params.pop('sslkey')\n                    if sslkey is None:\n                        sslkey = val\n\n                if 'sslrootcert' in service_params:\n                    val = service_params.pop('sslrootcert')\n                    if sslrootcert is None:\n                        sslrootcert = val\n\n                if 'sslnegotiation' in service_params:\n                    val = service_params.pop('sslnegotiation')\n                    if sslnegotiation is None:\n                        sslnegotiation = val\n\n                if 'sslcrl' in service_params:\n                    val = service_params.pop('sslcrl')\n                    if sslcrl is None:\n                        sslcrl = val\n\n                if 'sslpassword' in service_params:\n                    val = service_params.pop('sslpassword')\n                    if sslpassword is None:\n                        sslpassword = val\n\n                if 'ssl_min_protocol_version' in service_params:\n                    val = service_params.pop(\n                        'ssl_min_protocol_version'\n                    )\n                    if ssl_min_protocol_version is None:\n                        ssl_min_protocol_version = val\n\n                if 'ssl_max_protocol_version' in service_params:\n                    val = service_params.pop(\n                        'ssl_max_protocol_version'\n                    )\n                    if ssl_max_protocol_version is None:\n                        ssl_max_protocol_version = val\n\n                if 'target_session_attrs' in service_params:\n                    dsn_target_session_attrs = service_params.pop(\n                        'target_session_attrs'\n                    )\n                    if target_session_attrs is None:\n                        target_session_attrs = dsn_target_session_attrs\n\n                if 'krbsrvname' in service_params:\n                    val = service_params.pop('krbsrvname')\n                    if krbsrvname is None:\n                        krbsrvname = val\n\n                if 'gsslib' in service_params:\n                    val = service_params.pop('gsslib')\n                    if gsslib is None:\n                        gsslib = val\n    if not service:\n        service = os.environ.get('PGSERVICE')\n    if not host:\n        hostspec = os.environ.get('PGHOST')\n        if hostspec:\n            host, port = _parse_hostlist(hostspec, port)\n\n    if not host:\n        auth_hosts = ['localhost']\n\n        if _system == 'Windows':\n            host = ['localhost']\n        else:\n            host = ['/run/postgresql', '/var/run/postgresql',\n                    '/tmp', '/private/tmp', 'localhost']\n\n    if not isinstance(host, (list, tuple)):\n        host = [host]\n\n    if auth_hosts is None:\n        auth_hosts = host\n\n    if not port:\n        portspec = os.environ.get('PGPORT')\n        if portspec:\n            if ',' in portspec:\n                port = [int(p) for p in portspec.split(',')]\n            else:\n                port = int(portspec)\n        else:\n            port = 5432\n\n    elif isinstance(port, (list, tuple)):\n        port = [int(p) for p in port]\n\n    else:\n        port = int(port)\n\n    port = _validate_port_spec(host, port)\n\n    if user is None:\n        user = os.getenv('PGUSER')\n        if not user:\n            user = getpass.getuser()\n\n    if password is None:\n        password = os.getenv('PGPASSWORD')\n\n    if database is None:\n        database = os.getenv('PGDATABASE')\n\n    if database is None:\n        database = user\n\n    if user is None:\n        raise exceptions.ClientConfigurationError(\n            'could not determine user name to connect with')\n\n    if database is None:\n        raise exceptions.ClientConfigurationError(\n            'could not determine database name to connect to')\n\n    if password is None:\n        if passfile is None:\n            passfile = os.getenv('PGPASSFILE')\n\n        if passfile is None:\n            homedir = compat.get_pg_home_directory()\n            if homedir:\n                passfile = homedir / PGPASSFILE\n            else:\n                passfile = None\n        else:\n            passfile = pathlib.Path(passfile)\n\n        if passfile is not None:\n            password = _read_password_from_pgpass(\n                hosts=auth_hosts, ports=port,\n                database=database, user=user,\n                passfile=passfile)\n\n    addrs = []\n    have_tcp_addrs = False\n    for h, p in zip(host, port):\n        if h.startswith('/'):\n            # UNIX socket name\n            if '.s.PGSQL.' not in h:\n                h = os.path.join(h, '.s.PGSQL.{}'.format(p))\n            addrs.append(h)\n        else:\n            # TCP host/port\n            addrs.append((h, p))\n            have_tcp_addrs = True\n\n    if not addrs:\n        raise exceptions.InternalClientError(\n            'could not determine the database address to connect to')\n\n    if ssl is None:\n        ssl = os.getenv('PGSSLMODE')\n\n    if ssl is None and have_tcp_addrs:\n        ssl = 'prefer'\n\n    if direct_tls is not None:\n        sslneg = (\n            SSLNegotiation.direct if direct_tls else SSLNegotiation.postgres\n        )\n    else:\n        if sslnegotiation is None:\n            sslnegotiation = os.environ.get(\"PGSSLNEGOTIATION\")\n\n        if sslnegotiation is not None:\n            try:\n                sslneg = SSLNegotiation(sslnegotiation)\n            except ValueError:\n                modes = ', '.join(\n                    m.name.replace('_', '-')\n                    for m in SSLNegotiation\n                )\n                raise exceptions.ClientConfigurationError(\n                    f'`sslnegotiation` parameter must be one of: {modes}'\n                ) from None\n        else:\n            sslneg = SSLNegotiation.postgres\n\n    if isinstance(ssl, (str, SSLMode)):\n        try:\n            sslmode = SSLMode.parse(ssl)\n        except AttributeError:\n            modes = ', '.join(m.name.replace('_', '-') for m in SSLMode)\n            raise exceptions.ClientConfigurationError(\n                '`sslmode` parameter must be one of: {}'.format(modes)\n            ) from None\n\n        # docs at https://www.postgresql.org/docs/10/static/libpq-connect.html\n        if sslmode < SSLMode.allow:\n            ssl = False\n        else:\n            ssl = ssl_module.SSLContext(ssl_module.PROTOCOL_TLS_CLIENT)\n            ssl.check_hostname = sslmode >= SSLMode.verify_full\n            if sslmode < SSLMode.require:\n                ssl.verify_mode = ssl_module.CERT_NONE\n            else:\n                if sslrootcert is None:\n                    sslrootcert = os.getenv('PGSSLROOTCERT')\n                if sslrootcert:\n                    ssl.load_verify_locations(cafile=sslrootcert)\n                    ssl.verify_mode = ssl_module.CERT_REQUIRED\n                else:\n                    try:\n                        sslrootcert = _dot_postgresql_path('root.crt')\n                        if sslrootcert is not None:\n                            ssl.load_verify_locations(cafile=sslrootcert)\n                        else:\n                            raise exceptions.ClientConfigurationError(\n                                'cannot determine location of user '\n                                'PostgreSQL configuration directory'\n                            )\n                    except (\n                        exceptions.ClientConfigurationError,\n                        FileNotFoundError,\n                        NotADirectoryError,\n                    ):\n                        if sslmode > SSLMode.require:\n                            if sslrootcert is None:\n                                sslrootcert = '~/.postgresql/root.crt'\n                                detail = (\n                                    'Could not determine location of user '\n                                    'home directory (HOME is either unset, '\n                                    'inaccessible, or does not point to a '\n                                    'valid directory)'\n                                )\n                            else:\n                                detail = None\n                            raise exceptions.ClientConfigurationError(\n                                f'root certificate file \"{sslrootcert}\" does '\n                                f'not exist or cannot be accessed',\n                                hint='Provide the certificate file directly '\n                                     f'or make sure \"{sslrootcert}\" '\n                                     'exists and is readable.',\n                                detail=detail,\n                            )\n                        elif sslmode == SSLMode.require:\n                            ssl.verify_mode = ssl_module.CERT_NONE\n                        else:\n                            assert False, 'unreachable'\n                    else:\n                        ssl.verify_mode = ssl_module.CERT_REQUIRED\n\n                if sslcrl is None:\n                    sslcrl = os.getenv('PGSSLCRL')\n                if sslcrl:\n                    ssl.load_verify_locations(cafile=sslcrl)\n                    ssl.verify_flags |= ssl_module.VERIFY_CRL_CHECK_CHAIN\n                else:\n                    sslcrl = _dot_postgresql_path('root.crl')\n                    if sslcrl is not None:\n                        try:\n                            ssl.load_verify_locations(cafile=sslcrl)\n                        except (\n                            FileNotFoundError,\n                            NotADirectoryError,\n                        ):\n                            pass\n                        else:\n                            ssl.verify_flags |= \\\n                                ssl_module.VERIFY_CRL_CHECK_CHAIN\n\n            if sslkey is None:\n                sslkey = os.getenv('PGSSLKEY')\n            if not sslkey:\n                sslkey = _dot_postgresql_path('postgresql.key')\n                if sslkey is not None and not sslkey.exists():\n                    sslkey = None\n            if not sslpassword:\n                sslpassword = ''\n            if sslcert is None:\n                sslcert = os.getenv('PGSSLCERT')\n            if sslcert:\n                ssl.load_cert_chain(\n                    sslcert, keyfile=sslkey, password=lambda: sslpassword\n                )\n            else:\n                sslcert = _dot_postgresql_path('postgresql.crt')\n                if sslcert is not None:\n                    try:\n                        ssl.load_cert_chain(\n                            sslcert,\n                            keyfile=sslkey,\n                            password=lambda: sslpassword\n                        )\n                    except (FileNotFoundError, NotADirectoryError):\n                        pass\n\n            # OpenSSL 1.1.1 keylog file, copied from create_default_context()\n            if hasattr(ssl, 'keylog_filename'):\n                keylogfile = os.environ.get('SSLKEYLOGFILE')\n                if keylogfile and not sys.flags.ignore_environment:\n                    ssl.keylog_filename = keylogfile\n\n            if ssl_min_protocol_version is None:\n                ssl_min_protocol_version = os.getenv('PGSSLMINPROTOCOLVERSION')\n            if ssl_min_protocol_version:\n                ssl.minimum_version = _parse_tls_version(\n                    ssl_min_protocol_version\n                )\n            else:\n                ssl.minimum_version = _parse_tls_version('TLSv1.2')\n\n            if ssl_max_protocol_version is None:\n                ssl_max_protocol_version = os.getenv('PGSSLMAXPROTOCOLVERSION')\n            if ssl_max_protocol_version:\n                ssl.maximum_version = _parse_tls_version(\n                    ssl_max_protocol_version\n                )\n\n    elif ssl is True:\n        ssl = ssl_module.create_default_context()\n        sslmode = SSLMode.verify_full\n    else:\n        sslmode = SSLMode.disable\n\n    if server_settings is not None and (\n            not isinstance(server_settings, dict) or\n            not all(isinstance(k, str) for k in server_settings) or\n            not all(isinstance(v, str) for v in server_settings.values())):\n        raise exceptions.ClientConfigurationError(\n            'server_settings is expected to be None or '\n            'a Dict[str, str]')\n\n    if target_session_attrs is None:\n        target_session_attrs = os.getenv(\n            \"PGTARGETSESSIONATTRS\", SessionAttribute.any\n        )\n    try:\n        target_session_attrs = SessionAttribute(target_session_attrs)\n    except ValueError:\n        raise exceptions.ClientConfigurationError(\n            \"target_session_attrs is expected to be one of \"\n            \"{!r}\"\n            \", got {!r}\".format(\n                SessionAttribute.__members__.values, target_session_attrs\n            )\n        ) from None\n\n    if krbsrvname is None:\n        krbsrvname = os.getenv('PGKRBSRVNAME')\n\n    if gsslib is None:\n        gsslib = os.getenv('PGGSSLIB')\n        if gsslib is None:\n            gsslib = 'sspi' if _system == 'Windows' else 'gssapi'\n    if gsslib not in {'gssapi', 'sspi'}:\n        raise exceptions.ClientConfigurationError(\n            \"gsslib parameter must be either 'gssapi' or 'sspi'\"\n            \", got {!r}\".format(gsslib))\n\n    params = _ConnectionParameters(\n        user=user, password=password, database=database, ssl=ssl,\n        sslmode=sslmode, ssl_negotiation=sslneg,\n        server_settings=server_settings,\n        target_session_attrs=target_session_attrs,\n        krbsrvname=krbsrvname, gsslib=gsslib)\n\n    return addrs, params\n\n\ndef _parse_connect_arguments(*, dsn, host, port, user, password, passfile,\n                             database, command_timeout,\n                             statement_cache_size,\n                             max_cached_statement_lifetime,\n                             max_cacheable_statement_size,\n                             ssl, direct_tls, server_settings,\n                             target_session_attrs, krbsrvname, gsslib,\n                             service, servicefile):\n    local_vars = locals()\n    for var_name in {'max_cacheable_statement_size',\n                     'max_cached_statement_lifetime',\n                     'statement_cache_size'}:\n        var_val = local_vars[var_name]\n        if var_val is None or isinstance(var_val, bool) or var_val < 0:\n            raise ValueError(\n                '{} is expected to be greater '\n                'or equal to 0, got {!r}'.format(var_name, var_val))\n\n    if command_timeout is not None:\n        try:\n            if isinstance(command_timeout, bool):\n                raise ValueError\n            command_timeout = float(command_timeout)\n            if command_timeout <= 0:\n                raise ValueError\n        except ValueError:\n            raise ValueError(\n                'invalid command_timeout value: '\n                'expected greater than 0 float (got {!r})'.format(\n                    command_timeout)) from None\n\n    addrs, params = _parse_connect_dsn_and_args(\n        dsn=dsn, host=host, port=port, user=user,\n        password=password, passfile=passfile, ssl=ssl,\n        direct_tls=direct_tls, database=database,\n        server_settings=server_settings,\n        target_session_attrs=target_session_attrs,\n        krbsrvname=krbsrvname, gsslib=gsslib,\n        service=service, servicefile=servicefile)\n\n    config = _ClientConfiguration(\n        command_timeout=command_timeout,\n        statement_cache_size=statement_cache_size,\n        max_cached_statement_lifetime=max_cached_statement_lifetime,\n        max_cacheable_statement_size=max_cacheable_statement_size,)\n\n    return addrs, params, config\n\n\nclass TLSUpgradeProto(asyncio.Protocol):\n    def __init__(\n        self,\n        loop: asyncio.AbstractEventLoop,\n        host: str,\n        port: int,\n        ssl_context: ssl_module.SSLContext,\n        ssl_is_advisory: bool,\n    ) -> None:\n        self.on_data = _create_future(loop)\n        self.host = host\n        self.port = port\n        self.ssl_context = ssl_context\n        self.ssl_is_advisory = ssl_is_advisory\n\n    def data_received(self, data: bytes) -> None:\n        if data == b'S':\n            self.on_data.set_result(True)\n        elif (self.ssl_is_advisory and\n                self.ssl_context.verify_mode == ssl_module.CERT_NONE and\n                data == b'N'):\n            # ssl_is_advisory will imply that ssl.verify_mode == CERT_NONE,\n            # since the only way to get ssl_is_advisory is from\n            # sslmode=prefer. But be extra sure to disallow insecure\n            # connections when the ssl context asks for real security.\n            self.on_data.set_result(False)\n        else:\n            self.on_data.set_exception(\n                ConnectionError(\n                    'PostgreSQL server at \"{host}:{port}\" '\n                    'rejected SSL upgrade'.format(\n                        host=self.host, port=self.port)))\n\n    def connection_lost(self, exc: typing.Optional[Exception]) -> None:\n        if not self.on_data.done():\n            if exc is None:\n                exc = ConnectionError('unexpected connection_lost() call')\n            self.on_data.set_exception(exc)\n\n\n_ProctolFactoryR = typing.TypeVar(\n    \"_ProctolFactoryR\", bound=asyncio.protocols.Protocol\n)\n\n\nasync def _create_ssl_connection(\n    # TODO: The return type is a specific combination of subclasses of\n    # asyncio.protocols.Protocol that we can't express. For now, having the\n    # return type be dependent on signature of the factory is an improvement\n    protocol_factory: Callable[[], _ProctolFactoryR],\n    host: str,\n    port: int,\n    *,\n    loop: asyncio.AbstractEventLoop,\n    ssl_context: ssl_module.SSLContext,\n    ssl_is_advisory: bool = False,\n) -> typing.Tuple[asyncio.Transport, _ProctolFactoryR]:\n\n    tr, pr = await loop.create_connection(\n        lambda: TLSUpgradeProto(loop, host, port,\n                                ssl_context, ssl_is_advisory),\n        host, port)\n\n    tr.write(struct.pack('!ll', 8, 80877103))  # SSLRequest message.\n\n    try:\n        do_ssl_upgrade = await pr.on_data\n    except (Exception, asyncio.CancelledError):\n        tr.close()\n        raise\n\n    if hasattr(loop, 'start_tls'):\n        if do_ssl_upgrade:\n            try:\n                new_tr = await loop.start_tls(\n                    tr, pr, ssl_context, server_hostname=host)\n                assert new_tr is not None\n            except (Exception, asyncio.CancelledError):\n                tr.close()\n                raise\n        else:\n            new_tr = tr\n\n        pg_proto = protocol_factory()\n        pg_proto.is_ssl = do_ssl_upgrade\n        pg_proto.connection_made(new_tr)\n        new_tr.set_protocol(pg_proto)\n\n        return new_tr, pg_proto\n    else:\n        conn_factory = functools.partial(\n            loop.create_connection, protocol_factory)\n\n        if do_ssl_upgrade:\n            conn_factory = functools.partial(\n                conn_factory, ssl=ssl_context, server_hostname=host)\n\n        sock = _get_socket(tr)\n        sock = sock.dup()\n        _set_nodelay(sock)\n        tr.close()\n\n        try:\n            new_tr, pg_proto = await conn_factory(sock=sock)\n            pg_proto.is_ssl = do_ssl_upgrade\n            return new_tr, pg_proto\n        except (Exception, asyncio.CancelledError):\n            sock.close()\n            raise\n\n\nasync def _connect_addr(\n    *,\n    addr,\n    loop,\n    params,\n    config,\n    connection_class,\n    record_class\n):\n    assert loop is not None\n\n    params_input = params\n    if callable(params.password):\n        password = params.password()\n        if inspect.isawaitable(password):\n            password = await password\n\n        params = params._replace(password=password)\n    args = (addr, loop, config, connection_class, record_class, params_input)\n\n    # prepare the params (which attempt has ssl) for the 2 attempts\n    if params.sslmode == SSLMode.allow:\n        params_retry = params\n        params = params._replace(ssl=None)\n    elif params.sslmode == SSLMode.prefer:\n        params_retry = params._replace(ssl=None)\n    else:\n        # skip retry if we don't have to\n        return await __connect_addr(params, False, *args)\n\n    # first attempt\n    try:\n        return await __connect_addr(params, True, *args)\n    except _RetryConnectSignal:\n        pass\n\n    # second attempt\n    return await __connect_addr(params_retry, False, *args)\n\n\nclass _RetryConnectSignal(Exception):\n    pass\n\n\nasync def __connect_addr(\n    params,\n    retry,\n    addr,\n    loop,\n    config,\n    connection_class,\n    record_class,\n    params_input,\n):\n    connected = _create_future(loop)\n\n    proto_factory = lambda: protocol.Protocol(\n        addr, connected, params, record_class, loop)\n\n    if isinstance(addr, str):\n        # UNIX socket\n        connector = loop.create_unix_connection(proto_factory, addr)\n\n    elif params.ssl and params.ssl_negotiation is SSLNegotiation.direct:\n        # if ssl and ssl_negotiation is `direct`, skip STARTTLS and perform\n        # direct SSL connection\n        connector = loop.create_connection(\n            proto_factory, *addr, ssl=params.ssl\n        )\n\n    elif params.ssl:\n        connector = _create_ssl_connection(\n            proto_factory, *addr, loop=loop, ssl_context=params.ssl,\n            ssl_is_advisory=params.sslmode == SSLMode.prefer)\n    else:\n        connector = loop.create_connection(proto_factory, *addr)\n\n    tr, pr = await connector\n\n    try:\n        await connected\n    except (\n        exceptions.InvalidAuthorizationSpecificationError,\n        exceptions.ConnectionDoesNotExistError,  # seen on Windows\n    ):\n        tr.close()\n\n        # retry=True here is a redundant check because we don't want to\n        # accidentally raise the internal _RetryConnectSignal to the user\n        if retry and (\n            params.sslmode == SSLMode.allow and not pr.is_ssl or\n            params.sslmode == SSLMode.prefer and pr.is_ssl\n        ):\n            # Trigger retry when:\n            #   1. First attempt with sslmode=allow, ssl=None failed\n            #   2. First attempt with sslmode=prefer, ssl=ctx failed while the\n            #      server claimed to support SSL (returning \"S\" for SSLRequest)\n            #      (likely because pg_hba.conf rejected the connection)\n            raise _RetryConnectSignal()\n\n        else:\n            # but will NOT retry if:\n            #   1. First attempt with sslmode=prefer failed but the server\n            #      doesn't support SSL (returning 'N' for SSLRequest), because\n            #      we already tried to connect without SSL thru ssl_is_advisory\n            #   2. Second attempt with sslmode=prefer, ssl=None failed\n            #   3. Second attempt with sslmode=allow, ssl=ctx failed\n            #   4. Any other sslmode\n            raise\n\n    except (Exception, asyncio.CancelledError):\n        tr.close()\n        raise\n\n    con = connection_class(pr, tr, loop, addr, config, params_input)\n    pr.set_connection(con)\n    return con\n\n\nclass SessionAttribute(str, enum.Enum):\n    any = 'any'\n    primary = 'primary'\n    standby = 'standby'\n    prefer_standby = 'prefer-standby'\n    read_write = \"read-write\"\n    read_only = \"read-only\"\n\n\ndef _accept_in_hot_standby(should_be_in_hot_standby: bool):\n    \"\"\"\n    If the server didn't report \"in_hot_standby\" at startup, we must determine\n    the state by checking \"SELECT pg_catalog.pg_is_in_recovery()\".\n    If the server allows a connection and states it is in recovery it must\n    be a replica/standby server.\n    \"\"\"\n    async def can_be_used(connection):\n        settings = connection.get_settings()\n        hot_standby_status = getattr(settings, 'in_hot_standby', None)\n        if hot_standby_status is not None:\n            is_in_hot_standby = hot_standby_status == 'on'\n        else:\n            is_in_hot_standby = await connection.fetchval(\n                \"SELECT pg_catalog.pg_is_in_recovery()\"\n            )\n        return is_in_hot_standby == should_be_in_hot_standby\n\n    return can_be_used\n\n\ndef _accept_read_only(should_be_read_only: bool):\n    \"\"\"\n    Verify the server has not set default_transaction_read_only=True\n    \"\"\"\n    async def can_be_used(connection):\n        settings = connection.get_settings()\n        is_readonly = getattr(settings, 'default_transaction_read_only', 'off')\n\n        if is_readonly == \"on\":\n            return should_be_read_only\n\n        return await _accept_in_hot_standby(should_be_read_only)(connection)\n    return can_be_used\n\n\nasync def _accept_any(_):\n    return True\n\n\ntarget_attrs_check = {\n    SessionAttribute.any: _accept_any,\n    SessionAttribute.primary: _accept_in_hot_standby(False),\n    SessionAttribute.standby: _accept_in_hot_standby(True),\n    SessionAttribute.prefer_standby: _accept_in_hot_standby(True),\n    SessionAttribute.read_write: _accept_read_only(False),\n    SessionAttribute.read_only: _accept_read_only(True),\n}\n\n\nasync def _can_use_connection(connection, attr: SessionAttribute):\n    can_use = target_attrs_check[attr]\n    return await can_use(connection)\n\n\nasync def _connect(*, loop, connection_class, record_class, **kwargs):\n    if loop is None:\n        loop = asyncio.get_event_loop()\n\n    addrs, params, config = _parse_connect_arguments(**kwargs)\n    target_attr = params.target_session_attrs\n\n    candidates = []\n    chosen_connection = None\n    last_error = None\n    try:\n        for addr in addrs:\n            try:\n                conn = await _connect_addr(\n                    addr=addr,\n                    loop=loop,\n                    params=params,\n                    config=config,\n                    connection_class=connection_class,\n                    record_class=record_class,\n                )\n                candidates.append(conn)\n                if await _can_use_connection(conn, target_attr):\n                    chosen_connection = conn\n                    break\n            except OSError as ex:\n                last_error = ex\n        else:\n            if target_attr == SessionAttribute.prefer_standby and candidates:\n                chosen_connection = random.choice(candidates)\n    finally:\n\n        async def _close_candidates(conns, chosen):\n            await asyncio.gather(\n                *(c.close() for c in conns if c is not chosen),\n                return_exceptions=True\n            )\n        if candidates:\n            asyncio.create_task(\n                _close_candidates(candidates, chosen_connection))\n\n    if chosen_connection:\n        return chosen_connection\n\n    raise last_error or exceptions.TargetServerAttributeNotMatched(\n        'None of the hosts match the target attribute requirement '\n        '{!r}'.format(target_attr)\n    )\n\n\nasync def _cancel(*, loop, addr, params: _ConnectionParameters,\n                  backend_pid, backend_secret):\n\n    class CancelProto(asyncio.Protocol):\n\n        def __init__(self):\n            self.on_disconnect = _create_future(loop)\n            self.is_ssl = False\n\n        def connection_lost(self, exc):\n            if not self.on_disconnect.done():\n                self.on_disconnect.set_result(True)\n\n    if isinstance(addr, str):\n        tr, pr = await loop.create_unix_connection(CancelProto, addr)\n    else:\n        if params.ssl and params.sslmode != SSLMode.allow:\n            tr, pr = await _create_ssl_connection(\n                CancelProto,\n                *addr,\n                loop=loop,\n                ssl_context=params.ssl,\n                ssl_is_advisory=params.sslmode == SSLMode.prefer)\n        else:\n            tr, pr = await loop.create_connection(\n                CancelProto, *addr)\n            _set_nodelay(_get_socket(tr))\n\n    # Pack a CancelRequest message\n    msg = struct.pack('!llll', 16, 80877102, backend_pid, backend_secret)\n\n    try:\n        tr.write(msg)\n        await pr.on_disconnect\n    finally:\n        tr.close()\n\n\ndef _get_socket(transport):\n    sock = transport.get_extra_info('socket')\n    if sock is None:\n        # Shouldn't happen with any asyncio-complaint event loop.\n        raise ConnectionError(\n            'could not get the socket for transport {!r}'.format(transport))\n    return sock\n\n\ndef _set_nodelay(sock):\n    if not hasattr(socket, 'AF_UNIX') or sock.family != socket.AF_UNIX:\n        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)\n\n\ndef _create_future(loop):\n    try:\n        create_future = loop.create_future\n    except AttributeError:\n        return asyncio.Future(loop=loop)\n    else:\n        return create_future()\n"
  },
  {
    "path": "asyncpg/connection.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport asyncpg\nimport collections\nimport collections.abc\nimport contextlib\nimport functools\nimport itertools\nimport inspect\nimport os\nimport sys\nimport time\nimport traceback\nimport typing\nimport warnings\nimport weakref\n\nfrom . import compat\nfrom . import connect_utils\nfrom . import cursor\nfrom . import exceptions\nfrom . import introspection\nfrom . import prepared_stmt\nfrom . import protocol\nfrom . import serverversion\nfrom . import transaction\nfrom . import utils\n\n\nclass ConnectionMeta(type):\n\n    def __instancecheck__(cls, instance):\n        mro = type(instance).__mro__\n        return Connection in mro or _ConnectionProxy in mro\n\n\nclass Connection(metaclass=ConnectionMeta):\n    \"\"\"A representation of a database session.\n\n    Connections are created by calling :func:`~asyncpg.connection.connect`.\n    \"\"\"\n\n    __slots__ = ('_protocol', '_transport', '_loop',\n                 '_top_xact', '_aborted',\n                 '_pool_release_ctr', '_stmt_cache', '_stmts_to_close',\n                 '_stmt_cache_enabled',\n                 '_listeners', '_server_version', '_server_caps',\n                 '_intro_query', '_reset_query', '_proxy',\n                 '_stmt_exclusive_section', '_config', '_params', '_addr',\n                 '_log_listeners', '_termination_listeners', '_cancellations',\n                 '_source_traceback', '_query_loggers', '__weakref__')\n\n    def __init__(self, protocol, transport, loop,\n                 addr,\n                 config: connect_utils._ClientConfiguration,\n                 params: connect_utils._ConnectionParameters):\n        self._protocol = protocol\n        self._transport = transport\n        self._loop = loop\n        self._top_xact = None\n        self._aborted = False\n        # Incremented every time the connection is released back to a pool.\n        # Used to catch invalid references to connection-related resources\n        # post-release (e.g. explicit prepared statements).\n        self._pool_release_ctr = 0\n\n        self._addr = addr\n        self._config = config\n        self._params = params\n\n        self._stmt_cache = _StatementCache(\n            loop=loop,\n            max_size=config.statement_cache_size,\n            on_remove=functools.partial(\n                _weak_maybe_gc_stmt, weakref.ref(self)),\n            max_lifetime=config.max_cached_statement_lifetime)\n\n        self._stmts_to_close = set()\n        self._stmt_cache_enabled = config.statement_cache_size > 0\n\n        self._listeners = {}\n        self._log_listeners = set()\n        self._cancellations = set()\n        self._termination_listeners = set()\n        self._query_loggers = set()\n\n        settings = self._protocol.get_settings()\n        ver_string = settings.server_version\n        self._server_version = \\\n            serverversion.split_server_version_string(ver_string)\n\n        self._server_caps = _detect_server_capabilities(\n            self._server_version, settings)\n\n        if self._server_version < (14, 0):\n            self._intro_query = introspection.INTRO_LOOKUP_TYPES_13\n        else:\n            self._intro_query = introspection.INTRO_LOOKUP_TYPES\n\n        self._reset_query = None\n        self._proxy = None\n\n        # Used to serialize operations that might involve anonymous\n        # statements.  Specifically, we want to make the following\n        # operation atomic:\n        #    (\"prepare an anonymous statement\", \"use the statement\")\n        #\n        # Used for `con.fetchval()`, `con.fetch()`, `con.fetchrow()`,\n        # `con.execute()`, and `con.executemany()`.\n        self._stmt_exclusive_section = _Atomic()\n\n        if loop.get_debug():\n            self._source_traceback = _extract_stack()\n        else:\n            self._source_traceback = None\n\n    def __del__(self):\n        if not self.is_closed() and self._protocol is not None:\n            if self._source_traceback:\n                msg = \"unclosed connection {!r}; created at:\\n {}\".format(\n                    self, self._source_traceback)\n            else:\n                msg = (\n                    \"unclosed connection {!r}; run in asyncio debug \"\n                    \"mode to show the traceback of connection \"\n                    \"origin\".format(self)\n                )\n\n            warnings.warn(msg, ResourceWarning)\n            if not self._loop.is_closed():\n                self.terminate()\n\n    async def add_listener(self, channel, callback):\n        \"\"\"Add a listener for Postgres notifications.\n\n        :param str channel: Channel to listen on.\n\n        :param callable callback:\n            A callable or a coroutine function receiving the following\n            arguments:\n            **connection**: a Connection the callback is registered with;\n            **pid**: PID of the Postgres server that sent the notification;\n            **channel**: name of the channel the notification was sent to;\n            **payload**: the payload.\n\n        .. versionchanged:: 0.24.0\n            The ``callback`` argument may be a coroutine function.\n        \"\"\"\n        self._check_open()\n        if channel not in self._listeners:\n            await self.fetch('LISTEN {}'.format(utils._quote_ident(channel)))\n            self._listeners[channel] = set()\n        self._listeners[channel].add(_Callback.from_callable(callback))\n\n    async def remove_listener(self, channel, callback):\n        \"\"\"Remove a listening callback on the specified channel.\"\"\"\n        if self.is_closed():\n            return\n        if channel not in self._listeners:\n            return\n        cb = _Callback.from_callable(callback)\n        if cb not in self._listeners[channel]:\n            return\n        self._listeners[channel].remove(cb)\n        if not self._listeners[channel]:\n            del self._listeners[channel]\n            await self.fetch('UNLISTEN {}'.format(utils._quote_ident(channel)))\n\n    def add_log_listener(self, callback):\n        \"\"\"Add a listener for Postgres log messages.\n\n        It will be called when asyncronous NoticeResponse is received\n        from the connection.  Possible message types are: WARNING, NOTICE,\n        DEBUG, INFO, or LOG.\n\n        :param callable callback:\n            A callable or a coroutine function receiving the following\n            arguments:\n            **connection**: a Connection the callback is registered with;\n            **message**: the `exceptions.PostgresLogMessage` message.\n\n        .. versionadded:: 0.12.0\n\n        .. versionchanged:: 0.24.0\n            The ``callback`` argument may be a coroutine function.\n        \"\"\"\n        if self.is_closed():\n            raise exceptions.InterfaceError('connection is closed')\n        self._log_listeners.add(_Callback.from_callable(callback))\n\n    def remove_log_listener(self, callback):\n        \"\"\"Remove a listening callback for log messages.\n\n        .. versionadded:: 0.12.0\n        \"\"\"\n        self._log_listeners.discard(_Callback.from_callable(callback))\n\n    def add_termination_listener(self, callback):\n        \"\"\"Add a listener that will be called when the connection is closed.\n\n        :param callable callback:\n            A callable or a coroutine function receiving one argument:\n            **connection**: a Connection the callback is registered with.\n\n        .. versionadded:: 0.21.0\n\n        .. versionchanged:: 0.24.0\n            The ``callback`` argument may be a coroutine function.\n        \"\"\"\n        self._termination_listeners.add(_Callback.from_callable(callback))\n\n    def remove_termination_listener(self, callback):\n        \"\"\"Remove a listening callback for connection termination.\n\n        :param callable callback:\n            The callable or coroutine function that was passed to\n            :meth:`Connection.add_termination_listener`.\n\n        .. versionadded:: 0.21.0\n        \"\"\"\n        self._termination_listeners.discard(_Callback.from_callable(callback))\n\n    def add_query_logger(self, callback):\n        \"\"\"Add a logger that will be called when queries are executed.\n\n        :param callable callback:\n            A callable or a coroutine function receiving one argument:\n            **record**, a LoggedQuery containing `query`, `args`, `timeout`,\n            `elapsed`, `exception`, `conn_addr`, and `conn_params`.\n\n        .. versionadded:: 0.29.0\n        \"\"\"\n        self._query_loggers.add(_Callback.from_callable(callback))\n\n    def remove_query_logger(self, callback):\n        \"\"\"Remove a query logger callback.\n\n        :param callable callback:\n            The callable or coroutine function that was passed to\n            :meth:`Connection.add_query_logger`.\n\n        .. versionadded:: 0.29.0\n        \"\"\"\n        self._query_loggers.discard(_Callback.from_callable(callback))\n\n    def get_server_pid(self):\n        \"\"\"Return the PID of the Postgres server the connection is bound to.\"\"\"\n        return self._protocol.get_server_pid()\n\n    def get_server_version(self):\n        \"\"\"Return the version of the connected PostgreSQL server.\n\n        The returned value is a named tuple similar to that in\n        ``sys.version_info``:\n\n        .. code-block:: pycon\n\n            >>> con.get_server_version()\n            ServerVersion(major=9, minor=6, micro=1,\n                          releaselevel='final', serial=0)\n\n        .. versionadded:: 0.8.0\n        \"\"\"\n        return self._server_version\n\n    def get_settings(self):\n        \"\"\"Return connection settings.\n\n        :return: :class:`~asyncpg.ConnectionSettings`.\n        \"\"\"\n        return self._protocol.get_settings()\n\n    def transaction(self, *, isolation=None, readonly=False,\n                    deferrable=False):\n        \"\"\"Create a :class:`~transaction.Transaction` object.\n\n        Refer to `PostgreSQL documentation`_ on the meaning of transaction\n        parameters.\n\n        :param isolation: Transaction isolation mode, can be one of:\n                          `'serializable'`, `'repeatable_read'`,\n                          `'read_uncommitted'`, `'read_committed'`. If not\n                          specified, the behavior is up to the server and\n                          session, which is usually ``read_committed``.\n\n        :param readonly: Specifies whether or not this transaction is\n                         read-only.\n\n        :param deferrable: Specifies whether or not this transaction is\n                           deferrable.\n\n        .. _`PostgreSQL documentation`:\n                https://www.postgresql.org/docs/\n                current/static/sql-set-transaction.html\n        \"\"\"\n        self._check_open()\n        return transaction.Transaction(self, isolation, readonly, deferrable)\n\n    def is_in_transaction(self):\n        \"\"\"Return True if Connection is currently inside a transaction.\n\n        :return bool: True if inside transaction, False otherwise.\n\n        .. versionadded:: 0.16.0\n        \"\"\"\n        return self._protocol.is_in_transaction()\n\n    async def execute(\n        self,\n        query: str,\n        *args,\n        timeout: typing.Optional[float]=None,\n    ) -> str:\n        \"\"\"Execute an SQL command (or commands).\n\n        This method can execute many SQL commands at once, when no arguments\n        are provided.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> await con.execute('''\n            ...     CREATE TABLE mytab (a int);\n            ...     INSERT INTO mytab (a) VALUES (100), (200), (300);\n            ... ''')\n            INSERT 0 3\n\n            >>> await con.execute('''\n            ...     INSERT INTO mytab (a) VALUES ($1), ($2)\n            ... ''', 10, 20)\n            INSERT 0 2\n\n        :param args: Query arguments.\n        :param float timeout: Optional timeout value in seconds.\n        :return str: Status of the last SQL command.\n\n        .. versionchanged:: 0.5.4\n           Made it possible to pass query arguments.\n        \"\"\"\n        self._check_open()\n\n        if not args:\n            if self._query_loggers:\n                with self._time_and_log(query, args, timeout):\n                    result = await self._protocol.query(query, timeout)\n            else:\n                result = await self._protocol.query(query, timeout)\n            return result\n\n        _, status, _ = await self._execute(\n            query,\n            args,\n            0,\n            timeout,\n            return_status=True,\n        )\n        return status.decode()\n\n    async def executemany(\n        self,\n        command: str,\n        args,\n        *,\n        timeout: typing.Optional[float]=None,\n    ):\n        \"\"\"Execute an SQL *command* for each sequence of arguments in *args*.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> await con.executemany('''\n            ...     INSERT INTO mytab (a) VALUES ($1, $2, $3);\n            ... ''', [(1, 2, 3), (4, 5, 6)])\n\n        :param command: Command to execute.\n        :param args: An iterable containing sequences of arguments.\n        :param float timeout: Optional timeout value in seconds.\n        :return None: This method discards the results of the operations.\n\n        .. versionadded:: 0.7.0\n\n        .. versionchanged:: 0.11.0\n           `timeout` became a keyword-only parameter.\n\n        .. versionchanged:: 0.22.0\n           ``executemany()`` is now an atomic operation, which means that\n           either all executions succeed, or none at all.  This is in contrast\n           to prior versions, where the effect of already-processed iterations\n           would remain in place when an error has occurred, unless\n           ``executemany()`` was called in a transaction.\n        \"\"\"\n        self._check_open()\n        return await self._executemany(command, args, timeout)\n\n    async def _get_statement(\n        self,\n        query,\n        timeout,\n        *,\n        named: typing.Union[str, bool, None] = False,\n        use_cache=True,\n        ignore_custom_codec=False,\n        record_class=None\n    ):\n        if record_class is None:\n            record_class = self._protocol.get_record_class()\n        else:\n            _check_record_class(record_class)\n\n        if use_cache:\n            statement = self._stmt_cache.get(\n                (query, record_class, ignore_custom_codec)\n            )\n            if statement is not None:\n                return statement\n\n            # Only use the cache when:\n            #  * `statement_cache_size` is greater than 0;\n            #  * query size is less than `max_cacheable_statement_size`.\n            use_cache = (\n                self._stmt_cache_enabled\n                and (\n                    not self._config.max_cacheable_statement_size\n                    or len(query) <= self._config.max_cacheable_statement_size\n                )\n            )\n\n        if isinstance(named, str):\n            stmt_name = named\n        elif use_cache or named:\n            stmt_name = self._get_unique_id('stmt')\n        else:\n            stmt_name = ''\n\n        statement = await self._protocol.prepare(\n            stmt_name,\n            query,\n            timeout,\n            record_class=record_class,\n            ignore_custom_codec=ignore_custom_codec,\n        )\n        need_reprepare = False\n        types_with_missing_codecs = statement._init_types()\n        tries = 0\n        while types_with_missing_codecs:\n            settings = self._protocol.get_settings()\n\n            # Introspect newly seen types and populate the\n            # codec cache.\n            types, intro_stmt = await self._introspect_types(\n                types_with_missing_codecs, timeout)\n\n            settings.register_data_types(types)\n\n            # The introspection query has used an anonymous statement,\n            # which has blown away the anonymous statement we've prepared\n            # for the query, so we need to re-prepare it.\n            need_reprepare = not intro_stmt.name and not statement.name\n            types_with_missing_codecs = statement._init_types()\n            tries += 1\n            if tries > 5:\n                # In the vast majority of cases there will be only\n                # one iteration.  In rare cases, there might be a race\n                # with reload_schema_state(), which would cause a\n                # second try.  More than five is clearly a bug.\n                raise exceptions.InternalClientError(\n                    'could not resolve query result and/or argument types '\n                    'in {} attempts'.format(tries)\n                )\n\n        # Now that types have been resolved, populate the codec pipeline\n        # for the statement.\n        statement._init_codecs()\n\n        if (\n            need_reprepare\n            or (not statement.name and not self._stmt_cache_enabled)\n        ):\n            # Mark this anonymous prepared statement as \"unprepared\",\n            # causing it to get re-Parsed in next bind_execute.\n            # We always do this when stmt_cache_size is set to 0 assuming\n            # people are running PgBouncer which is mishandling implicit\n            # transactions.\n            statement.mark_unprepared()\n\n        if use_cache:\n            self._stmt_cache.put(\n                (query, record_class, ignore_custom_codec), statement)\n\n        # If we've just created a new statement object, check if there\n        # are any statements for GC.\n        if self._stmts_to_close:\n            await self._cleanup_stmts()\n\n        return statement\n\n    async def _introspect_types(self, typeoids, timeout):\n        if self._server_caps.jit:\n            try:\n                cfgrow, _ = await self.__execute(\n                    \"\"\"\n                    SELECT\n                        current_setting('jit') AS cur,\n                        set_config('jit', 'off', false) AS new\n                    \"\"\",\n                    (),\n                    0,\n                    timeout,\n                    ignore_custom_codec=True,\n                )\n                jit_state = cfgrow[0]['cur']\n            except exceptions.UndefinedObjectError:\n                jit_state = 'off'\n        else:\n            jit_state = 'off'\n\n        result = await self.__execute(\n            self._intro_query,\n            (list(typeoids),),\n            0,\n            timeout,\n            ignore_custom_codec=True,\n        )\n\n        if jit_state != 'off':\n            await self.__execute(\n                \"\"\"\n                SELECT\n                    set_config('jit', $1, false)\n                \"\"\",\n                (jit_state,),\n                0,\n                timeout,\n                ignore_custom_codec=True,\n            )\n\n        return result\n\n    async def _introspect_type(self, typename, schema):\n        if schema == 'pg_catalog' and not typename.endswith(\"[]\"):\n            typeoid = protocol.BUILTIN_TYPE_NAME_MAP.get(typename.lower())\n            if typeoid is not None:\n                return introspection.TypeRecord((typeoid, None, b\"b\"))\n\n        rows = await self._execute(\n            introspection.TYPE_BY_NAME,\n            [typename, schema],\n            limit=1,\n            timeout=None,\n            ignore_custom_codec=True,\n        )\n\n        if not rows:\n            raise ValueError(\n                'unknown type: {}.{}'.format(schema, typename))\n\n        return rows[0]\n\n    def cursor(\n        self,\n        query,\n        *args,\n        prefetch=None,\n        timeout=None,\n        record_class=None\n    ):\n        \"\"\"Return a *cursor factory* for the specified query.\n\n        :param args:\n            Query arguments.\n        :param int prefetch:\n            The number of rows the *cursor iterator*\n            will prefetch (defaults to ``50``.)\n        :param float timeout:\n            Optional timeout in seconds.\n        :param type record_class:\n            If specified, the class to use for records returned by this cursor.\n            Must be a subclass of :class:`~asyncpg.Record`.  If not specified,\n            a per-connection *record_class* is used.\n\n        :return:\n            A :class:`~cursor.CursorFactory` object.\n\n        .. versionchanged:: 0.22.0\n            Added the *record_class* parameter.\n        \"\"\"\n        self._check_open()\n        return cursor.CursorFactory(\n            self,\n            query,\n            None,\n            args,\n            prefetch,\n            timeout,\n            record_class,\n        )\n\n    async def prepare(\n        self,\n        query,\n        *,\n        name=None,\n        timeout=None,\n        record_class=None,\n    ):\n        \"\"\"Create a *prepared statement* for the specified query.\n\n        :param str query:\n            Text of the query to create a prepared statement for.\n        :param str name:\n            Optional name of the returned prepared statement.  If not\n            specified, the name is auto-generated.\n        :param float timeout:\n            Optional timeout value in seconds.\n        :param type record_class:\n            If specified, the class to use for records returned by the\n            prepared statement.  Must be a subclass of\n            :class:`~asyncpg.Record`.  If not specified, a per-connection\n            *record_class* is used.\n\n        :return:\n            A :class:`~prepared_stmt.PreparedStatement` instance.\n\n        .. versionchanged:: 0.22.0\n            Added the *record_class* parameter.\n\n        .. versionchanged:: 0.25.0\n            Added the *name* parameter.\n        \"\"\"\n        return await self._prepare(\n            query,\n            name=name,\n            timeout=timeout,\n            record_class=record_class,\n        )\n\n    async def _prepare(\n        self,\n        query,\n        *,\n        name: typing.Union[str, bool, None] = None,\n        timeout=None,\n        use_cache: bool=False,\n        record_class=None\n    ):\n        self._check_open()\n        if name is None:\n            name = self._stmt_cache_enabled\n        stmt = await self._get_statement(\n            query,\n            timeout,\n            named=name,\n            use_cache=use_cache,\n            record_class=record_class,\n        )\n        return prepared_stmt.PreparedStatement(self, query, stmt)\n\n    async def fetch(\n        self,\n        query,\n        *args,\n        timeout=None,\n        record_class=None\n    ) -> list:\n        \"\"\"Run a query and return the results as a list of :class:`Record`.\n\n        :param str query:\n            Query text.\n        :param args:\n            Query arguments.\n        :param float timeout:\n            Optional timeout value in seconds.\n        :param type record_class:\n            If specified, the class to use for records returned by this method.\n            Must be a subclass of :class:`~asyncpg.Record`.  If not specified,\n            a per-connection *record_class* is used.\n\n        :return list:\n            A list of :class:`~asyncpg.Record` instances.  If specified, the\n            actual type of list elements would be *record_class*.\n\n        .. versionchanged:: 0.22.0\n            Added the *record_class* parameter.\n        \"\"\"\n        self._check_open()\n        return await self._execute(\n            query,\n            args,\n            0,\n            timeout,\n            record_class=record_class,\n        )\n\n    async def fetchval(self, query, *args, column=0, timeout=None):\n        \"\"\"Run a query and return a value in the first row.\n\n        :param str query: Query text.\n        :param args: Query arguments.\n        :param int column: Numeric index within the record of the value to\n                           return (defaults to 0).\n        :param float timeout: Optional timeout value in seconds.\n                            If not specified, defaults to the value of\n                            ``command_timeout`` argument to the ``Connection``\n                            instance constructor.\n\n        :return: The value of the specified column of the first record, or\n                 None if no records were returned by the query.\n        \"\"\"\n        self._check_open()\n        data = await self._execute(query, args, 1, timeout)\n        if not data:\n            return None\n        return data[0][column]\n\n    async def fetchrow(\n        self,\n        query,\n        *args,\n        timeout=None,\n        record_class=None\n    ):\n        \"\"\"Run a query and return the first row.\n\n        :param str query:\n            Query text\n        :param args:\n            Query arguments\n        :param float timeout:\n            Optional timeout value in seconds.\n        :param type record_class:\n            If specified, the class to use for the value returned by this\n            method.  Must be a subclass of :class:`~asyncpg.Record`.\n            If not specified, a per-connection *record_class* is used.\n\n        :return:\n            The first row as a :class:`~asyncpg.Record` instance, or None if\n            no records were returned by the query.  If specified,\n            *record_class* is used as the type for the result value.\n\n        .. versionchanged:: 0.22.0\n            Added the *record_class* parameter.\n        \"\"\"\n        self._check_open()\n        data = await self._execute(\n            query,\n            args,\n            1,\n            timeout,\n            record_class=record_class,\n        )\n        if not data:\n            return None\n        return data[0]\n\n    async def fetchmany(\n        self,\n        query,\n        args,\n        *,\n        timeout: typing.Optional[float]=None,\n        record_class=None,\n    ):\n        \"\"\"Run a query for each sequence of arguments in *args*\n        and return the results as a list of :class:`Record`.\n\n        :param query:\n            Query to execute.\n        :param args:\n            An iterable containing sequences of arguments for the query.\n        :param float timeout:\n            Optional timeout value in seconds.\n        :param type record_class:\n            If specified, the class to use for records returned by this method.\n            Must be a subclass of :class:`~asyncpg.Record`.  If not specified,\n            a per-connection *record_class* is used.\n\n        :return list:\n            A list of :class:`~asyncpg.Record` instances.  If specified, the\n            actual type of list elements would be *record_class*.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> rows = await con.fetchmany('''\n            ...         INSERT INTO mytab (a, b) VALUES ($1, $2) RETURNING a;\n            ...     ''', [('x', 1), ('y', 2), ('z', 3)])\n            >>> rows\n            [<Record row=('x',)>, <Record row=('y',)>, <Record row=('z',)>]\n\n        .. versionadded:: 0.30.0\n        \"\"\"\n        self._check_open()\n        return await self._executemany(\n            query, args, timeout, return_rows=True, record_class=record_class\n        )\n\n    async def copy_from_table(self, table_name, *, output,\n                              columns=None, schema_name=None, timeout=None,\n                              format=None, oids=None, delimiter=None,\n                              null=None, header=None, quote=None,\n                              escape=None, force_quote=None, encoding=None):\n        \"\"\"Copy table contents to a file or file-like object.\n\n        :param str table_name:\n            The name of the table to copy data from.\n\n        :param output:\n            A :term:`path-like object <python:path-like object>`,\n            or a :term:`file-like object <python:file-like object>`, or\n            a :term:`coroutine function <python:coroutine function>`\n            that takes a ``bytes`` instance as a sole argument.\n\n        :param list columns:\n            An optional list of column names to copy.\n\n        :param str schema_name:\n            An optional schema name to qualify the table.\n\n        :param float timeout:\n            Optional timeout value in seconds.\n\n        The remaining keyword arguments are ``COPY`` statement options,\n        see `COPY statement documentation`_ for details.\n\n        :return: The status string of the COPY command.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> async def run():\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     result = await con.copy_from_table(\n            ...         'mytable', columns=('foo', 'bar'),\n            ...         output='file.csv', format='csv')\n            ...     print(result)\n            ...\n            >>> asyncio.run(run())\n            'COPY 100'\n\n        .. _`COPY statement documentation`:\n            https://www.postgresql.org/docs/current/static/sql-copy.html\n\n        .. versionadded:: 0.11.0\n        \"\"\"\n        tabname = utils._quote_ident(table_name)\n        if schema_name:\n            tabname = utils._quote_ident(schema_name) + '.' + tabname\n\n        if columns:\n            cols = '({})'.format(\n                ', '.join(utils._quote_ident(c) for c in columns))\n        else:\n            cols = ''\n\n        opts = self._format_copy_opts(\n            format=format, oids=oids, delimiter=delimiter,\n            null=null, header=header, quote=quote, escape=escape,\n            force_quote=force_quote, encoding=encoding\n        )\n\n        copy_stmt = 'COPY {tab}{cols} TO STDOUT {opts}'.format(\n            tab=tabname, cols=cols, opts=opts)\n\n        return await self._copy_out(copy_stmt, output, timeout)\n\n    async def copy_from_query(self, query, *args, output,\n                              timeout=None, format=None, oids=None,\n                              delimiter=None, null=None, header=None,\n                              quote=None, escape=None, force_quote=None,\n                              encoding=None):\n        \"\"\"Copy the results of a query to a file or file-like object.\n\n        :param str query:\n            The query to copy the results of.\n\n        :param args:\n            Query arguments.\n\n        :param output:\n            A :term:`path-like object <python:path-like object>`,\n            or a :term:`file-like object <python:file-like object>`, or\n            a :term:`coroutine function <python:coroutine function>`\n            that takes a ``bytes`` instance as a sole argument.\n\n        :param float timeout:\n            Optional timeout value in seconds.\n\n        The remaining keyword arguments are ``COPY`` statement options,\n        see `COPY statement documentation`_ for details.\n\n        :return: The status string of the COPY command.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> async def run():\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     result = await con.copy_from_query(\n            ...         'SELECT foo, bar FROM mytable WHERE foo > $1', 10,\n            ...         output='file.csv', format='csv')\n            ...     print(result)\n            ...\n            >>> asyncio.run(run())\n            'COPY 10'\n\n        .. _`COPY statement documentation`:\n            https://www.postgresql.org/docs/current/static/sql-copy.html\n\n        .. versionadded:: 0.11.0\n        \"\"\"\n        opts = self._format_copy_opts(\n            format=format, oids=oids, delimiter=delimiter,\n            null=null, header=header, quote=quote, escape=escape,\n            force_quote=force_quote, encoding=encoding\n        )\n\n        if args:\n            query = await utils._mogrify(self, query, args)\n\n        copy_stmt = 'COPY ({query}) TO STDOUT {opts}'.format(\n            query=query, opts=opts)\n\n        return await self._copy_out(copy_stmt, output, timeout)\n\n    async def copy_to_table(self, table_name, *, source,\n                            columns=None, schema_name=None, timeout=None,\n                            format=None, oids=None, freeze=None,\n                            delimiter=None, null=None, header=None,\n                            quote=None, escape=None, force_quote=None,\n                            force_not_null=None, force_null=None,\n                            encoding=None, where=None):\n        \"\"\"Copy data to the specified table.\n\n        :param str table_name:\n            The name of the table to copy data to.\n\n        :param source:\n            A :term:`path-like object <python:path-like object>`,\n            or a :term:`file-like object <python:file-like object>`, or\n            an :term:`asynchronous iterable <python:asynchronous iterable>`\n            that returns ``bytes``, or an object supporting the\n            :ref:`buffer protocol <python:bufferobjects>`.\n\n        :param list columns:\n            An optional list of column names to copy.\n\n        :param str schema_name:\n            An optional schema name to qualify the table.\n\n        :param str where:\n            An optional SQL expression used to filter rows when copying.\n\n            .. note::\n\n                Usage of this parameter requires support for the\n                ``COPY FROM ... WHERE`` syntax, introduced in\n                PostgreSQL version 12.\n\n        :param float timeout:\n            Optional timeout value in seconds.\n\n        The remaining keyword arguments are ``COPY`` statement options,\n        see `COPY statement documentation`_ for details.\n\n        :return: The status string of the COPY command.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> async def run():\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     result = await con.copy_to_table(\n            ...         'mytable', source='datafile.tbl')\n            ...     print(result)\n            ...\n            >>> asyncio.run(run())\n            'COPY 140000'\n\n        .. _`COPY statement documentation`:\n            https://www.postgresql.org/docs/current/static/sql-copy.html\n\n        .. versionadded:: 0.11.0\n\n        .. versionadded:: 0.29.0\n            Added the *where* parameter.\n        \"\"\"\n        tabname = utils._quote_ident(table_name)\n        if schema_name:\n            tabname = utils._quote_ident(schema_name) + '.' + tabname\n\n        if columns:\n            cols = '({})'.format(\n                ', '.join(utils._quote_ident(c) for c in columns))\n        else:\n            cols = ''\n\n        cond = self._format_copy_where(where)\n        opts = self._format_copy_opts(\n            format=format, oids=oids, freeze=freeze, delimiter=delimiter,\n            null=null, header=header, quote=quote, escape=escape,\n            force_not_null=force_not_null, force_null=force_null,\n            encoding=encoding\n        )\n\n        copy_stmt = 'COPY {tab}{cols} FROM STDIN {opts} {cond}'.format(\n            tab=tabname, cols=cols, opts=opts, cond=cond)\n\n        return await self._copy_in(copy_stmt, source, timeout)\n\n    async def copy_records_to_table(self, table_name, *, records,\n                                    columns=None, schema_name=None,\n                                    timeout=None, where=None):\n        \"\"\"Copy a list of records to the specified table using binary COPY.\n\n        :param str table_name:\n            The name of the table to copy data to.\n\n        :param records:\n            An iterable returning row tuples to copy into the table.\n            :term:`Asynchronous iterables <python:asynchronous iterable>`\n            are also supported.\n\n        :param list columns:\n            An optional list of column names to copy.\n\n        :param str schema_name:\n            An optional schema name to qualify the table.\n\n        :param str where:\n            An optional SQL expression used to filter rows when copying.\n\n            .. note::\n\n                Usage of this parameter requires support for the\n                ``COPY FROM ... WHERE`` syntax, introduced in\n                PostgreSQL version 12.\n\n\n        :param float timeout:\n            Optional timeout value in seconds.\n\n        :return: The status string of the COPY command.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> async def run():\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     result = await con.copy_records_to_table(\n            ...         'mytable', records=[\n            ...             (1, 'foo', 'bar'),\n            ...             (2, 'ham', 'spam')])\n            ...     print(result)\n            ...\n            >>> asyncio.run(run())\n            'COPY 2'\n\n        Asynchronous record iterables are also supported:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> async def run():\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     async def record_gen(size):\n            ...         for i in range(size):\n            ...             yield (i,)\n            ...     result = await con.copy_records_to_table(\n            ...         'mytable', records=record_gen(100))\n            ...     print(result)\n            ...\n            >>> asyncio.run(run())\n            'COPY 100'\n\n        .. versionadded:: 0.11.0\n\n        .. versionchanged:: 0.24.0\n            The ``records`` argument may be an asynchronous iterable.\n\n        .. versionadded:: 0.29.0\n            Added the *where* parameter.\n        \"\"\"\n        tabname = utils._quote_ident(table_name)\n        if schema_name:\n            tabname = utils._quote_ident(schema_name) + '.' + tabname\n\n        if columns:\n            col_list = ', '.join(utils._quote_ident(c) for c in columns)\n            cols = '({})'.format(col_list)\n        else:\n            col_list = '*'\n            cols = ''\n\n        intro_query = 'SELECT {cols} FROM {tab} LIMIT 1'.format(\n            tab=tabname, cols=col_list)\n\n        intro_ps = await self.prepare(intro_query)\n\n        cond = self._format_copy_where(where)\n        opts = '(FORMAT binary)'\n\n        copy_stmt = 'COPY {tab}{cols} FROM STDIN {opts} {cond}'.format(\n            tab=tabname, cols=cols, opts=opts, cond=cond)\n\n        return await self._protocol.copy_in(\n            copy_stmt, None, None, records, intro_ps._state, timeout)\n\n    def _format_copy_where(self, where):\n        if where and not self._server_caps.sql_copy_from_where:\n            raise exceptions.UnsupportedServerFeatureError(\n                'the `where` parameter requires PostgreSQL 12 or later')\n\n        if where:\n            where_clause = 'WHERE ' + where\n        else:\n            where_clause = ''\n\n        return where_clause\n\n    def _format_copy_opts(self, *, format=None, oids=None, freeze=None,\n                          delimiter=None, null=None, header=None, quote=None,\n                          escape=None, force_quote=None, force_not_null=None,\n                          force_null=None, encoding=None):\n        kwargs = dict(locals())\n        kwargs.pop('self')\n        opts = []\n\n        if force_quote is not None and isinstance(force_quote, bool):\n            kwargs.pop('force_quote')\n            if force_quote:\n                opts.append('FORCE_QUOTE *')\n\n        for k, v in kwargs.items():\n            if v is not None:\n                if k in ('force_not_null', 'force_null', 'force_quote'):\n                    v = '(' + ', '.join(utils._quote_ident(c) for c in v) + ')'\n                elif k in ('oids', 'freeze', 'header'):\n                    v = str(v)\n                else:\n                    v = utils._quote_literal(v)\n\n                opts.append('{} {}'.format(k.upper(), v))\n\n        if opts:\n            return '(' + ', '.join(opts) + ')'\n        else:\n            return ''\n\n    async def _copy_out(self, copy_stmt, output, timeout):\n        try:\n            path = os.fspath(output)\n        except TypeError:\n            # output is not a path-like object\n            path = None\n\n        writer = None\n        opened_by_us = False\n        run_in_executor = self._loop.run_in_executor\n\n        if path is not None:\n            # a path\n            f = await run_in_executor(None, open, path, 'wb')\n            opened_by_us = True\n        elif hasattr(output, 'write'):\n            # file-like\n            f = output\n        elif callable(output):\n            # assuming calling output returns an awaitable.\n            writer = output\n        else:\n            raise TypeError(\n                'output is expected to be a file-like object, '\n                'a path-like object or a coroutine function, '\n                'not {}'.format(type(output).__name__)\n            )\n\n        if writer is None:\n            async def _writer(data):\n                await run_in_executor(None, f.write, data)\n            writer = _writer\n\n        try:\n            return await self._protocol.copy_out(copy_stmt, writer, timeout)\n        finally:\n            if opened_by_us:\n                f.close()\n\n    async def _copy_in(self, copy_stmt, source, timeout):\n        try:\n            path = os.fspath(source)\n        except TypeError:\n            # source is not a path-like object\n            path = None\n\n        f = None\n        reader = None\n        data = None\n        opened_by_us = False\n        run_in_executor = self._loop.run_in_executor\n\n        if path is not None:\n            # a path\n            f = await run_in_executor(None, open, path, 'rb')\n            opened_by_us = True\n        elif hasattr(source, 'read'):\n            # file-like\n            f = source\n        elif isinstance(source, collections.abc.AsyncIterable):\n            # assuming calling output returns an awaitable.\n            # copy_in() is designed to handle very large amounts of data, and\n            # the source async iterable is allowed to return an arbitrary\n            # amount of data on every iteration.\n            reader = source\n        else:\n            # assuming source is an instance supporting the buffer protocol.\n            data = source\n\n        if f is not None:\n            # Copying from a file-like object.\n            class _Reader:\n                def __aiter__(self):\n                    return self\n\n                async def __anext__(self):\n                    data = await run_in_executor(None, f.read, 524288)\n                    if len(data) == 0:\n                        raise StopAsyncIteration\n                    else:\n                        return data\n\n            reader = _Reader()\n\n        try:\n            return await self._protocol.copy_in(\n                copy_stmt, reader, data, None, None, timeout)\n        finally:\n            if opened_by_us:\n                await run_in_executor(None, f.close)\n\n    async def set_type_codec(self, typename, *,\n                             schema='public', encoder, decoder,\n                             format='text'):\n        \"\"\"Set an encoder/decoder pair for the specified data type.\n\n        :param typename:\n            Name of the data type the codec is for.\n\n        :param schema:\n            Schema name of the data type the codec is for\n            (defaults to ``'public'``)\n\n        :param format:\n            The type of the argument received by the *decoder* callback,\n            and the type of the *encoder* callback return value.\n\n            If *format* is ``'text'`` (the default), the exchange datum is a\n            ``str`` instance containing valid text representation of the\n            data type.\n\n            If *format* is ``'binary'``, the exchange datum is a ``bytes``\n            instance containing valid _binary_ representation of the\n            data type.\n\n            If *format* is ``'tuple'``, the exchange datum is a type-specific\n            ``tuple`` of values.  The table below lists supported data\n            types and their format for this mode.\n\n            +-----------------+---------------------------------------------+\n            |  Type           |                Tuple layout                 |\n            +=================+=============================================+\n            | ``interval``    | (``months``, ``days``, ``microseconds``)    |\n            +-----------------+---------------------------------------------+\n            | ``date``        | (``date ordinal relative to Jan 1 2000``,)  |\n            |                 | ``-2^31`` for negative infinity timestamp   |\n            |                 | ``2^31-1`` for positive infinity timestamp. |\n            +-----------------+---------------------------------------------+\n            | ``timestamp``   | (``microseconds relative to Jan 1 2000``,)  |\n            |                 | ``-2^63`` for negative infinity timestamp   |\n            |                 | ``2^63-1`` for positive infinity timestamp. |\n            +-----------------+---------------------------------------------+\n            | ``timestamp     | (``microseconds relative to Jan 1 2000      |\n            | with time zone``| UTC``,)                                     |\n            |                 | ``-2^63`` for negative infinity timestamp   |\n            |                 | ``2^63-1`` for positive infinity timestamp. |\n            +-----------------+---------------------------------------------+\n            | ``time``        | (``microseconds``,)                         |\n            +-----------------+---------------------------------------------+\n            | ``time with     | (``microseconds``,                          |\n            | time zone``     | ``time zone offset in seconds``)            |\n            +-----------------+---------------------------------------------+\n            | any composite   | Composite value elements                    |\n            | type            |                                             |\n            +-----------------+---------------------------------------------+\n\n        :param encoder:\n            Callable accepting a Python object as a single argument and\n            returning a value encoded according to *format*.\n\n        :param decoder:\n            Callable accepting a single argument encoded according to *format*\n            and returning a decoded Python object.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> import datetime\n            >>> from dateutil.relativedelta import relativedelta\n            >>> async def run():\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     def encoder(delta):\n            ...         ndelta = delta.normalized()\n            ...         return (ndelta.years * 12 + ndelta.months,\n            ...                 ndelta.days,\n            ...                 ((ndelta.hours * 3600 +\n            ...                    ndelta.minutes * 60 +\n            ...                    ndelta.seconds) * 1000000 +\n            ...                  ndelta.microseconds))\n            ...     def decoder(tup):\n            ...         return relativedelta(months=tup[0], days=tup[1],\n            ...                              microseconds=tup[2])\n            ...     await con.set_type_codec(\n            ...         'interval', schema='pg_catalog', encoder=encoder,\n            ...         decoder=decoder, format='tuple')\n            ...     result = await con.fetchval(\n            ...         \"SELECT '2 years 3 mons 1 day'::interval\")\n            ...     print(result)\n            ...     print(datetime.datetime(2002, 1, 1) + result)\n            ...\n            >>> asyncio.run(run())\n            relativedelta(years=+2, months=+3, days=+1)\n            2004-04-02 00:00:00\n\n        .. versionadded:: 0.12.0\n            Added the ``format`` keyword argument and support for 'tuple'\n            format.\n\n        .. versionchanged:: 0.12.0\n            The ``binary`` keyword argument is deprecated in favor of\n            ``format``.\n\n        .. versionchanged:: 0.13.0\n            The ``binary`` keyword argument was removed in favor of\n            ``format``.\n\n        .. versionchanged:: 0.29.0\n            Custom codecs for composite types are now supported with\n            ``format='tuple'``.\n\n        .. note::\n\n           It is recommended to use the ``'binary'`` or ``'tuple'`` *format*\n           whenever possible and if the underlying type supports it. Asyncpg\n           currently does not support text I/O for composite and range types,\n           and some other functionality, such as\n           :meth:`Connection.copy_to_table`, does not support types with text\n           codecs.\n        \"\"\"\n        self._check_open()\n        settings = self._protocol.get_settings()\n        typeinfo = await self._introspect_type(typename, schema)\n        full_typeinfos = []\n        if introspection.is_scalar_type(typeinfo):\n            kind = 'scalar'\n        elif introspection.is_composite_type(typeinfo):\n            if format != 'tuple':\n                raise exceptions.UnsupportedClientFeatureError(\n                    'only tuple-format codecs can be used on composite types',\n                    hint=\"Use `set_type_codec(..., format='tuple')` and \"\n                         \"pass/interpret data as a Python tuple.  See an \"\n                         \"example at https://magicstack.github.io/asyncpg/\"\n                         \"current/usage.html#example-decoding-complex-types\",\n                )\n            kind = 'composite'\n            full_typeinfos, _ = await self._introspect_types(\n                (typeinfo['oid'],), 10)\n        else:\n            raise exceptions.InterfaceError(\n                f'cannot use custom codec on type {schema}.{typename}: '\n                f'it is neither a scalar type nor a composite type'\n            )\n        if introspection.is_domain_type(typeinfo):\n            raise exceptions.UnsupportedClientFeatureError(\n                'custom codecs on domain types are not supported',\n                hint='Set the codec on the base type.',\n                detail=(\n                    'PostgreSQL does not distinguish domains from '\n                    'their base types in query results at the protocol level.'\n                )\n            )\n\n        oid = typeinfo['oid']\n        settings.add_python_codec(\n            oid, typename, schema, full_typeinfos, kind,\n            encoder, decoder, format)\n\n        # Statement cache is no longer valid due to codec changes.\n        self._drop_local_statement_cache()\n\n    async def reset_type_codec(self, typename, *, schema='public'):\n        \"\"\"Reset *typename* codec to the default implementation.\n\n        :param typename:\n            Name of the data type the codec is for.\n\n        :param schema:\n            Schema name of the data type the codec is for\n            (defaults to ``'public'``)\n\n        .. versionadded:: 0.12.0\n        \"\"\"\n\n        typeinfo = await self._introspect_type(typename, schema)\n        self._protocol.get_settings().remove_python_codec(\n            typeinfo['oid'], typename, schema)\n\n        # Statement cache is no longer valid due to codec changes.\n        self._drop_local_statement_cache()\n\n    async def set_builtin_type_codec(self, typename, *,\n                                     schema='public', codec_name,\n                                     format=None):\n        \"\"\"Set a builtin codec for the specified scalar data type.\n\n        This method has two uses.  The first is to register a builtin\n        codec for an extension type without a stable OID, such as 'hstore'.\n        The second use is to declare that an extension type or a\n        user-defined type is wire-compatible with a certain builtin\n        data type and should be exchanged as such.\n\n        :param typename:\n            Name of the data type the codec is for.\n\n        :param schema:\n            Schema name of the data type the codec is for\n            (defaults to ``'public'``).\n\n        :param codec_name:\n            The name of the builtin codec to use for the type.\n            This should be either the name of a known core type\n            (such as ``\"int\"``), or the name of a supported extension\n            type.  Currently, the only supported extension type is\n            ``\"pg_contrib.hstore\"``.\n\n        :param format:\n            If *format* is ``None`` (the default), all formats supported\n            by the target codec are declared to be supported for *typename*.\n            If *format* is ``'text'`` or ``'binary'``, then only the\n            specified format is declared to be supported for *typename*.\n\n        .. versionchanged:: 0.18.0\n            The *codec_name* argument can be the name of any known\n            core data type.  Added the *format* keyword argument.\n        \"\"\"\n        self._check_open()\n        typeinfo = await self._introspect_type(typename, schema)\n        if not introspection.is_scalar_type(typeinfo):\n            raise exceptions.InterfaceError(\n                'cannot alias non-scalar type {}.{}'.format(\n                    schema, typename))\n\n        oid = typeinfo['oid']\n\n        self._protocol.get_settings().set_builtin_type_codec(\n            oid, typename, schema, 'scalar', codec_name, format)\n\n        # Statement cache is no longer valid due to codec changes.\n        self._drop_local_statement_cache()\n\n    def is_closed(self):\n        \"\"\"Return ``True`` if the connection is closed, ``False`` otherwise.\n\n        :return bool: ``True`` if the connection is closed, ``False``\n                      otherwise.\n        \"\"\"\n        return self._aborted or not self._protocol.is_connected()\n\n    async def close(self, *, timeout=None):\n        \"\"\"Close the connection gracefully.\n\n        :param float timeout:\n            Optional timeout value in seconds.\n\n        .. versionchanged:: 0.14.0\n           Added the *timeout* parameter.\n        \"\"\"\n        try:\n            if not self.is_closed():\n                await self._protocol.close(timeout)\n        except (Exception, asyncio.CancelledError):\n            # If we fail to close gracefully, abort the connection.\n            self._abort()\n            raise\n        finally:\n            self._cleanup()\n\n    def terminate(self):\n        \"\"\"Terminate the connection without waiting for pending data.\"\"\"\n        if not self.is_closed():\n            self._abort()\n        self._cleanup()\n\n    async def _reset(self):\n        self._check_open()\n        self._listeners.clear()\n        self._log_listeners.clear()\n\n        if self._protocol.is_in_transaction() or self._top_xact is not None:\n            if self._top_xact is None or not self._top_xact._managed:\n                # Managed transactions are guaranteed to __aexit__\n                # correctly.\n                self._loop.call_exception_handler({\n                    'message': 'Resetting connection with an '\n                               'active transaction {!r}'.format(self)\n                })\n\n            self._top_xact = None\n            await self.execute(\"ROLLBACK\")\n\n    async def reset(self, *, timeout=None):\n        \"\"\"Reset the connection state.\n\n        Calling this will reset the connection session state to a state\n        resembling that of a newly obtained connection.  Namely, an open\n        transaction (if any) is rolled back, open cursors are closed,\n        all `LISTEN <https://www.postgresql.org/docs/current/sql-listen.html>`_\n        registrations are removed, all session configuration\n        variables are reset to their default values, and all advisory locks\n        are released.\n\n        Note that the above describes the default query returned by\n        :meth:`Connection.get_reset_query`.  If one overloads the method\n        by subclassing ``Connection``, then this method will do whatever\n        the overloaded method returns, except open transactions are always\n        terminated and any callbacks registered by\n        :meth:`Connection.add_listener` or :meth:`Connection.add_log_listener`\n        are removed.\n\n        :param float timeout:\n            A timeout for resetting the connection.  If not specified, defaults\n            to no timeout.\n        \"\"\"\n        async with compat.timeout(timeout):\n            await self._reset()\n            reset_query = self.get_reset_query()\n            if reset_query:\n                await self.execute(reset_query)\n\n    def _abort(self):\n        # Put the connection into the aborted state.\n        self._aborted = True\n        self._protocol.abort()\n        self._protocol = None\n\n    def _cleanup(self):\n        self._call_termination_listeners()\n        # Free the resources associated with this connection.\n        # This must be called when a connection is terminated.\n\n        if self._proxy is not None:\n            # Connection is a member of a pool, so let the pool\n            # know that this connection is dead.\n            self._proxy._holder._release_on_close()\n\n        self._mark_stmts_as_closed()\n        self._listeners.clear()\n        self._log_listeners.clear()\n        self._query_loggers.clear()\n        self._clean_tasks()\n\n    def _clean_tasks(self):\n        # Wrap-up any remaining tasks associated with this connection.\n        if self._cancellations:\n            for fut in self._cancellations:\n                if not fut.done():\n                    fut.cancel()\n            self._cancellations.clear()\n\n    def _check_open(self):\n        if self.is_closed():\n            raise exceptions.InterfaceError('connection is closed')\n\n    def _get_unique_id(self, prefix):\n        global _uid\n        _uid += 1\n        return '__asyncpg_{}_{:x}__'.format(prefix, _uid)\n\n    def _mark_stmts_as_closed(self):\n        for stmt in self._stmt_cache.iter_statements():\n            stmt.mark_closed()\n\n        for stmt in self._stmts_to_close:\n            stmt.mark_closed()\n\n        self._stmt_cache.clear()\n        self._stmts_to_close.clear()\n\n    def _maybe_gc_stmt(self, stmt):\n        if (\n            stmt.refs == 0\n            and stmt.name\n            and not self._stmt_cache.has(\n                (stmt.query, stmt.record_class, stmt.ignore_custom_codec)\n            )\n        ):\n            # If low-level `stmt` isn't referenced from any high-level\n            # `PreparedStatement` object and is not in the `_stmt_cache`:\n            #\n            #  * mark it as closed, which will make it non-usable\n            #    for any `PreparedStatement` or for methods like\n            #    `Connection.fetch()`.\n            #\n            # * schedule it to be formally closed on the server.\n            stmt.mark_closed()\n            self._stmts_to_close.add(stmt)\n\n    async def _cleanup_stmts(self):\n        # Called whenever we create a new prepared statement in\n        # `Connection._get_statement()` and `_stmts_to_close` is\n        # not empty.\n        to_close = self._stmts_to_close\n        self._stmts_to_close = set()\n        for stmt in to_close:\n            # It is imperative that statements are cleaned properly,\n            # so we ignore the timeout.\n            await self._protocol.close_statement(stmt, protocol.NO_TIMEOUT)\n\n    async def _cancel(self, waiter):\n        try:\n            # Open new connection to the server\n            await connect_utils._cancel(\n                loop=self._loop, addr=self._addr, params=self._params,\n                backend_pid=self._protocol.backend_pid,\n                backend_secret=self._protocol.backend_secret)\n        except ConnectionResetError as ex:\n            # On some systems Postgres will reset the connection\n            # after processing the cancellation command.\n            if not waiter.done():\n                waiter.set_exception(ex)\n        except asyncio.CancelledError:\n            # There are two scenarios in which the cancellation\n            # itself will be cancelled: 1) the connection is being closed,\n            # 2) the event loop is being shut down.\n            # In either case we do not care about the propagation of\n            # the CancelledError, and don't want the loop to warn about\n            # an unretrieved exception.\n            pass\n        except (Exception, asyncio.CancelledError) as ex:\n            if not waiter.done():\n                waiter.set_exception(ex)\n        finally:\n            self._cancellations.discard(\n                asyncio.current_task(self._loop))\n            if not waiter.done():\n                waiter.set_result(None)\n\n    def _cancel_current_command(self, waiter):\n        self._cancellations.add(self._loop.create_task(self._cancel(waiter)))\n\n    def _process_log_message(self, fields, last_query):\n        if not self._log_listeners:\n            return\n\n        message = exceptions.PostgresLogMessage.new(fields, query=last_query)\n\n        con_ref = self._unwrap()\n        for cb in self._log_listeners:\n            if cb.is_async:\n                self._loop.create_task(cb.cb(con_ref, message))\n            else:\n                self._loop.call_soon(cb.cb, con_ref, message)\n\n    def _call_termination_listeners(self):\n        if not self._termination_listeners:\n            return\n\n        con_ref = self._unwrap()\n        for cb in self._termination_listeners:\n            if cb.is_async:\n                self._loop.create_task(cb.cb(con_ref))\n            else:\n                self._loop.call_soon(cb.cb, con_ref)\n\n        self._termination_listeners.clear()\n\n    def _process_notification(self, pid, channel, payload):\n        if channel not in self._listeners:\n            return\n\n        con_ref = self._unwrap()\n        for cb in self._listeners[channel]:\n            if cb.is_async:\n                self._loop.create_task(cb.cb(con_ref, pid, channel, payload))\n            else:\n                self._loop.call_soon(cb.cb, con_ref, pid, channel, payload)\n\n    def _unwrap(self):\n        if self._proxy is None:\n            con_ref = self\n        else:\n            # `_proxy` is not None when the connection is a member\n            # of a connection pool.  Which means that the user is working\n            # with a `PoolConnectionProxy` instance, and expects to see it\n            # (and not the actual Connection) in their event callbacks.\n            con_ref = self._proxy\n        return con_ref\n\n    def get_reset_query(self):\n        \"\"\"Return the query sent to server on connection release.\n\n        The query returned by this method is used by :meth:`Connection.reset`,\n        which is, in turn, used by :class:`~asyncpg.pool.Pool` before making\n        the connection available to another acquirer.\n\n        .. versionadded:: 0.30.0\n        \"\"\"\n        if self._reset_query is not None:\n            return self._reset_query\n\n        caps = self._server_caps\n\n        _reset_query = []\n        if caps.advisory_locks:\n            _reset_query.append('SELECT pg_advisory_unlock_all();')\n        if caps.sql_close_all:\n            _reset_query.append('CLOSE ALL;')\n        if caps.notifications and caps.plpgsql:\n            _reset_query.append('UNLISTEN *;')\n        if caps.sql_reset:\n            _reset_query.append('RESET ALL;')\n\n        _reset_query = '\\n'.join(_reset_query)\n        self._reset_query = _reset_query\n\n        return _reset_query\n\n    def _set_proxy(self, proxy):\n        if self._proxy is not None and proxy is not None:\n            # Should not happen unless there is a bug in `Pool`.\n            raise exceptions.InterfaceError(\n                'internal asyncpg error: connection is already proxied')\n\n        self._proxy = proxy\n\n    def _check_listeners(self, listeners, listener_type):\n        if listeners:\n            count = len(listeners)\n\n            w = exceptions.InterfaceWarning(\n                '{conn!r} is being released to the pool but has {c} active '\n                '{type} listener{s}'.format(\n                    conn=self, c=count, type=listener_type,\n                    s='s' if count > 1 else ''))\n\n            warnings.warn(w)\n\n    def _on_release(self, stacklevel=1):\n        # Invalidate external references to the connection.\n        self._pool_release_ctr += 1\n        # Called when the connection is about to be released to the pool.\n        # Let's check that the user has not left any listeners on it.\n        self._check_listeners(\n            list(itertools.chain.from_iterable(self._listeners.values())),\n            'notification')\n        self._check_listeners(\n            self._log_listeners, 'log')\n\n    def _drop_local_statement_cache(self):\n        self._stmt_cache.clear()\n\n    def _drop_global_statement_cache(self):\n        if self._proxy is not None:\n            # This connection is a member of a pool, so we delegate\n            # the cache drop to the pool.\n            pool = self._proxy._holder._pool\n            pool._drop_statement_cache()\n        else:\n            self._drop_local_statement_cache()\n\n    def _drop_local_type_cache(self):\n        self._protocol.get_settings().clear_type_cache()\n\n    def _drop_global_type_cache(self):\n        if self._proxy is not None:\n            # This connection is a member of a pool, so we delegate\n            # the cache drop to the pool.\n            pool = self._proxy._holder._pool\n            pool._drop_type_cache()\n        else:\n            self._drop_local_type_cache()\n\n    async def reload_schema_state(self):\n        \"\"\"Indicate that the database schema information must be reloaded.\n\n        For performance reasons, asyncpg caches certain aspects of the\n        database schema, such as the layout of composite types.  Consequently,\n        when the database schema changes, and asyncpg is not able to\n        gracefully recover from an error caused by outdated schema\n        assumptions, an :exc:`~asyncpg.exceptions.OutdatedSchemaCacheError`\n        is raised.  To prevent the exception, this method may be used to inform\n        asyncpg that the database schema has changed.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> async def change_type(con):\n            ...     result = await con.fetch('SELECT id, info FROM tbl')\n            ...     # Change composite's attribute type \"int\"=>\"text\"\n            ...     await con.execute('ALTER TYPE custom DROP ATTRIBUTE y')\n            ...     await con.execute('ALTER TYPE custom ADD ATTRIBUTE y text')\n            ...     await con.reload_schema_state()\n            ...     for id_, info in result:\n            ...         new = (info['x'], str(info['y']))\n            ...         await con.execute(\n            ...             'UPDATE tbl SET info=$2 WHERE id=$1', id_, new)\n            ...\n            >>> async def run():\n            ...     # Initial schema:\n            ...     # CREATE TYPE custom AS (x int, y int);\n            ...     # CREATE TABLE tbl(id int, info custom);\n            ...     con = await asyncpg.connect(user='postgres')\n            ...     async with con.transaction():\n            ...         # Prevent concurrent changes in the table\n            ...         await con.execute('LOCK TABLE tbl')\n            ...         await change_type(con)\n            ...\n            >>> asyncio.run(run())\n\n        .. versionadded:: 0.14.0\n        \"\"\"\n        self._drop_global_type_cache()\n        self._drop_global_statement_cache()\n\n    async def _execute(\n        self,\n        query,\n        args,\n        limit,\n        timeout,\n        *,\n        return_status=False,\n        ignore_custom_codec=False,\n        record_class=None\n    ):\n        with self._stmt_exclusive_section:\n            result, _ = await self.__execute(\n                query,\n                args,\n                limit,\n                timeout,\n                return_status=return_status,\n                record_class=record_class,\n                ignore_custom_codec=ignore_custom_codec,\n            )\n        return result\n\n    @contextlib.contextmanager\n    def query_logger(self, callback):\n        \"\"\"Context manager that adds `callback` to the list of query loggers,\n        and removes it upon exit.\n\n        :param callable callback:\n            A callable or a coroutine function receiving one argument:\n            **record**, a LoggedQuery containing `query`, `args`, `timeout`,\n            `elapsed`, `exception`, `conn_addr`, and `conn_params`.\n\n        Example:\n\n        .. code-block:: pycon\n\n            >>> class QuerySaver:\n                    def __init__(self):\n                        self.queries = []\n                    def __call__(self, record):\n                        self.queries.append(record.query)\n            >>> with con.query_logger(QuerySaver()):\n            >>>     await con.execute(\"SELECT 1\")\n            >>> print(log.queries)\n            ['SELECT 1']\n\n        .. versionadded:: 0.29.0\n        \"\"\"\n        self.add_query_logger(callback)\n        yield\n        self.remove_query_logger(callback)\n\n    @contextlib.contextmanager\n    def _time_and_log(self, query, args, timeout):\n        start = time.monotonic()\n        exception = None\n        try:\n            yield\n        except BaseException as ex:\n            exception = ex\n            raise\n        finally:\n            elapsed = time.monotonic() - start\n            record = LoggedQuery(\n                query=query,\n                args=args,\n                timeout=timeout,\n                elapsed=elapsed,\n                exception=exception,\n                conn_addr=self._addr,\n                conn_params=self._params,\n            )\n            for cb in self._query_loggers:\n                if cb.is_async:\n                    self._loop.create_task(cb.cb(record))\n                else:\n                    self._loop.call_soon(cb.cb, record)\n\n    async def __execute(\n        self,\n        query,\n        args,\n        limit,\n        timeout,\n        *,\n        return_status=False,\n        ignore_custom_codec=False,\n        record_class=None\n    ):\n        executor = lambda stmt, timeout: self._protocol.bind_execute(\n            state=stmt,\n            args=args,\n            portal_name='',\n            limit=limit,\n            return_extra=return_status,\n            timeout=timeout,\n        )\n        timeout = self._protocol._get_timeout(timeout)\n        if self._query_loggers:\n            with self._time_and_log(query, args, timeout):\n                result, stmt = await self._do_execute(\n                    query,\n                    executor,\n                    timeout,\n                    record_class=record_class,\n                    ignore_custom_codec=ignore_custom_codec,\n                )\n        else:\n            result, stmt = await self._do_execute(\n                query,\n                executor,\n                timeout,\n                record_class=record_class,\n                ignore_custom_codec=ignore_custom_codec,\n            )\n        return result, stmt\n\n    async def _executemany(\n        self,\n        query,\n        args,\n        timeout,\n        return_rows=False,\n        record_class=None,\n    ):\n        executor = lambda stmt, timeout: self._protocol.bind_execute_many(\n            state=stmt,\n            args=args,\n            portal_name='',\n            timeout=timeout,\n            return_rows=return_rows,\n        )\n        timeout = self._protocol._get_timeout(timeout)\n        with self._stmt_exclusive_section:\n            with self._time_and_log(query, args, timeout):\n                result, _ = await self._do_execute(\n                    query, executor, timeout, record_class=record_class\n                )\n        return result\n\n    async def _do_execute(\n        self,\n        query,\n        executor,\n        timeout,\n        retry=True,\n        *,\n        ignore_custom_codec=False,\n        record_class=None\n    ):\n        if timeout is None:\n            stmt = await self._get_statement(\n                query,\n                None,\n                record_class=record_class,\n                ignore_custom_codec=ignore_custom_codec,\n            )\n        else:\n            before = time.monotonic()\n            stmt = await self._get_statement(\n                query,\n                timeout,\n                record_class=record_class,\n                ignore_custom_codec=ignore_custom_codec,\n            )\n            after = time.monotonic()\n            timeout -= after - before\n            before = after\n\n        try:\n            if timeout is None:\n                result = await executor(stmt, None)\n            else:\n                try:\n                    result = await executor(stmt, timeout)\n                finally:\n                    after = time.monotonic()\n                    timeout -= after - before\n\n        except exceptions.OutdatedSchemaCacheError:\n            # This exception is raised when we detect a difference between\n            # cached type's info and incoming tuple from the DB (when a type is\n            # changed by the ALTER TYPE).\n            # It is not possible to recover (the statement is already done at\n            # the server's side), the only way is to drop our caches and\n            # reraise the exception to the caller.\n            await self.reload_schema_state()\n            raise\n        except exceptions.InvalidCachedStatementError:\n            # PostgreSQL will raise an exception when it detects\n            # that the result type of the query has changed from\n            # when the statement was prepared.  This may happen,\n            # for example, after an ALTER TABLE or SET search_path.\n            #\n            # When this happens, and there is no transaction running,\n            # we can simply re-prepare the statement and try once\n            # again.  We deliberately retry only once as this is\n            # supposed to be a rare occurrence.\n            #\n            # If the transaction _is_ running, this error will put it\n            # into an error state, and we have no choice but to\n            # re-raise the exception.\n            #\n            # In either case we clear the statement cache for this\n            # connection and all other connections of the pool this\n            # connection belongs to (if any).\n            #\n            # See https://github.com/MagicStack/asyncpg/issues/72\n            # and https://github.com/MagicStack/asyncpg/issues/76\n            # for discussion.\n            #\n            self._drop_global_statement_cache()\n            if self._protocol.is_in_transaction() or not retry:\n                raise\n            else:\n                return await self._do_execute(\n                    query, executor, timeout, retry=False)\n\n        return result, stmt\n\n\nasync def connect(dsn=None, *,\n                  host=None, port=None,\n                  user=None, password=None, passfile=None,\n                  service=None,\n                  servicefile=None,\n                  database=None,\n                  loop=None,\n                  timeout=60,\n                  statement_cache_size=100,\n                  max_cached_statement_lifetime=300,\n                  max_cacheable_statement_size=1024 * 15,\n                  command_timeout=None,\n                  ssl=None,\n                  direct_tls=None,\n                  connection_class=Connection,\n                  record_class=protocol.Record,\n                  server_settings=None,\n                  target_session_attrs=None,\n                  krbsrvname=None,\n                  gsslib=None):\n    r\"\"\"A coroutine to establish a connection to a PostgreSQL server.\n\n    The connection parameters may be specified either as a connection\n    URI in *dsn*, or as specific keyword arguments, or both.\n    If both *dsn* and keyword arguments are specified, the latter\n    override the corresponding values parsed from the connection URI.\n    The default values for the majority of arguments can be specified\n    using `environment variables <postgres envvars_>`_.\n\n    Returns a new :class:`~asyncpg.connection.Connection` object.\n\n    :param dsn:\n        Connection arguments specified using as a single string in the\n        `libpq connection URI format`_:\n        ``postgres://user:password@host:port/database?option=value``.\n        The following options are recognized by asyncpg: ``host``,\n        ``port``, ``user``, ``database`` (or ``dbname``), ``password``,\n        ``passfile``, ``sslmode``, ``sslcert``, ``sslkey``, ``sslrootcert``,\n        and ``sslcrl``.  Unlike libpq, asyncpg will treat unrecognized\n        options as `server settings`_ to be used for the connection.\n\n        .. note::\n\n           The URI must be *valid*, which means that all components must\n           be properly quoted with :py:func:`urllib.parse.quote_plus`, and\n           any literal IPv6 addresses must be enclosed in square brackets.\n           For example:\n\n           .. code-block:: text\n\n              postgres://dbuser@[fe80::1ff:fe23:4567:890a%25eth0]/dbname\n\n    :param host:\n        Database host address as one of the following:\n\n        - an IP address or a domain name;\n        - an absolute path to the directory containing the database\n          server Unix-domain socket (not supported on Windows);\n        - a sequence of any of the above, in which case the addresses\n          will be tried in order, and the first successful connection\n          will be returned.\n\n        If not specified, asyncpg will try the following, in order:\n\n        - host address(es) parsed from the *dsn* argument,\n        - the value of the ``PGHOST`` environment variable,\n        - on Unix, common directories used for PostgreSQL Unix-domain\n          sockets: ``\"/run/postgresql\"``, ``\"/var/run/postgresl\"``,\n          ``\"/var/pgsql_socket\"``, ``\"/private/tmp\"``, and ``\"/tmp\"``,\n        - ``\"localhost\"``.\n\n    :param port:\n        Port number to connect to at the server host\n        (or Unix-domain socket file extension).  If multiple host\n        addresses were specified, this parameter may specify a\n        sequence of port numbers of the same length as the host sequence,\n        or it may specify a single port number to be used for all host\n        addresses.\n\n        If not specified, the value parsed from the *dsn* argument is used,\n        or the value of the ``PGPORT`` environment variable, or ``5432`` if\n        neither is specified.\n\n    :param user:\n        The name of the database role used for authentication.\n\n        If not specified, the value parsed from the *dsn* argument is used,\n        or the value of the ``PGUSER`` environment variable, or the\n        operating system name of the user running the application.\n\n    :param database:\n        The name of the database to connect to.\n\n        If not specified, the value parsed from the *dsn* argument is used,\n        or the value of the ``PGDATABASE`` environment variable, or the\n        computed value of the *user* argument.\n\n    :param password:\n        Password to be used for authentication, if the server requires\n        one.  If not specified, the value parsed from the *dsn* argument\n        is used, or the value of the ``PGPASSWORD`` environment variable.\n        Note that the use of the environment variable is discouraged as\n        other users and applications may be able to read it without needing\n        specific privileges.  It is recommended to use *passfile* instead.\n\n        Password may be either a string, or a callable that returns a string.\n        If a callable is provided, it will be called each time a new connection\n        is established.\n\n    :param passfile:\n        The name of the file used to store passwords\n        (defaults to ``~/.pgpass``, or ``%APPDATA%\\postgresql\\pgpass.conf``\n        on Windows).\n\n    :param service:\n        The name of the postgres connection service stored in the postgres\n        connection service file.\n\n    :param servicefile:\n        The location of the connnection service file used to store\n        connection parameters.\n\n    :param loop:\n        An asyncio event loop instance.  If ``None``, the default\n        event loop will be used.\n\n    :param float timeout:\n        Connection timeout in seconds.\n\n    :param int statement_cache_size:\n        The size of prepared statement LRU cache.  Pass ``0`` to\n        disable the cache.\n\n    :param int max_cached_statement_lifetime:\n        The maximum time in seconds a prepared statement will stay\n        in the cache.  Pass ``0`` to allow statements be cached\n        indefinitely.\n\n    :param int max_cacheable_statement_size:\n        The maximum size of a statement that can be cached (15KiB by\n        default).  Pass ``0`` to allow all statements to be cached\n        regardless of their size.\n\n    :param float command_timeout:\n        The default timeout for operations on this connection\n        (the default is ``None``: no timeout).\n\n    :param ssl:\n        Pass ``True`` or an `ssl.SSLContext <SSLContext_>`_ instance to\n        require an SSL connection.  If ``True``, a default SSL context\n        returned by `ssl.create_default_context() <create_default_context_>`_\n        will be used.  The value can also be one of the following strings:\n\n        - ``'disable'`` - SSL is disabled (equivalent to ``False``)\n        - ``'prefer'`` - try SSL first, fallback to non-SSL connection\n          if SSL connection fails\n        - ``'allow'`` - try without SSL first, then retry with SSL if the first\n          attempt fails.\n        - ``'require'`` - only try an SSL connection.  Certificate\n          verification errors are ignored\n        - ``'verify-ca'`` - only try an SSL connection, and verify\n          that the server certificate is issued by a trusted certificate\n          authority (CA)\n        - ``'verify-full'`` - only try an SSL connection, verify\n          that the server certificate is issued by a trusted CA and\n          that the requested server host name matches that in the\n          certificate.\n\n        The default is ``'prefer'``: try an SSL connection and fallback to\n        non-SSL connection if that fails.\n\n        .. note::\n\n           *ssl* is ignored for Unix domain socket communication.\n\n        Example of programmatic SSL context configuration that is equivalent\n        to ``sslmode=verify-full&sslcert=..&sslkey=..&sslrootcert=..``:\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> import ssl\n            >>> async def main():\n            ...     # Load CA bundle for server certificate verification,\n            ...     # equivalent to sslrootcert= in DSN.\n            ...     sslctx = ssl.create_default_context(\n            ...         ssl.Purpose.SERVER_AUTH,\n            ...         cafile=\"path/to/ca_bundle.pem\")\n            ...     # If True, equivalent to sslmode=verify-full, if False:\n            ...     # sslmode=verify-ca.\n            ...     sslctx.check_hostname = True\n            ...     # Load client certificate and private key for client\n            ...     # authentication, equivalent to sslcert= and sslkey= in\n            ...     # DSN.\n            ...     sslctx.load_cert_chain(\n            ...         \"path/to/client.cert\",\n            ...         keyfile=\"path/to/client.key\",\n            ...     )\n            ...     con = await asyncpg.connect(user='postgres', ssl=sslctx)\n            ...     await con.close()\n            >>> asyncio.run(main())\n\n        Example of programmatic SSL context configuration that is equivalent\n        to ``sslmode=require`` (no server certificate or host verification):\n\n        .. code-block:: pycon\n\n            >>> import asyncpg\n            >>> import asyncio\n            >>> import ssl\n            >>> async def main():\n            ...     sslctx = ssl.create_default_context(\n            ...         ssl.Purpose.SERVER_AUTH)\n            ...     sslctx.check_hostname = False\n            ...     sslctx.verify_mode = ssl.CERT_NONE\n            ...     con = await asyncpg.connect(user='postgres', ssl=sslctx)\n            ...     await con.close()\n            >>> asyncio.run(main())\n\n    :param bool direct_tls:\n        Pass ``True`` to skip PostgreSQL STARTTLS mode and perform a direct\n        SSL connection. Must be used alongside ``ssl`` param.\n\n    :param dict server_settings:\n        An optional dict of server runtime parameters.  Refer to\n        PostgreSQL documentation for\n        a `list of supported options <server settings_>`_.\n\n    :param type connection_class:\n        Class of the returned connection object.  Must be a subclass of\n        :class:`~asyncpg.connection.Connection`.\n\n    :param type record_class:\n        If specified, the class to use for records returned by queries on\n        this connection object.  Must be a subclass of\n        :class:`~asyncpg.Record`.\n\n    :param SessionAttribute target_session_attrs:\n        If specified, check that the host has the correct attribute.\n        Can be one of:\n\n        - ``\"any\"`` - the first successfully connected host\n        - ``\"primary\"`` - the host must NOT be in hot standby mode\n        - ``\"standby\"`` - the host must be in hot standby mode\n        - ``\"read-write\"`` - the host must allow writes\n        - ``\"read-only\"`` - the host most NOT allow writes\n        - ``\"prefer-standby\"`` - first try to find a standby host, but if\n          none of the listed hosts is a standby server,\n          return any of them.\n\n        If not specified, the value parsed from the *dsn* argument is used,\n        or the value of the ``PGTARGETSESSIONATTRS`` environment variable,\n        or ``\"any\"`` if neither is specified.\n\n    :param str krbsrvname:\n        Kerberos service name to use when authenticating with GSSAPI. This\n        must match the server configuration. Defaults to 'postgres'.\n\n    :param str gsslib:\n        GSS library to use for GSSAPI/SSPI authentication. Can be 'gssapi'\n        or 'sspi'. Defaults to 'sspi' on Windows and 'gssapi' otherwise.\n\n    :return: A :class:`~asyncpg.connection.Connection` instance.\n\n    Example:\n\n    .. code-block:: pycon\n\n        >>> import asyncpg\n        >>> import asyncio\n        >>> async def run():\n        ...     con = await asyncpg.connect(user='postgres')\n        ...     types = await con.fetch('SELECT * FROM pg_type')\n        ...     print(types)\n        ...\n        >>> asyncio.run(run())\n        [<Record typname='bool' typnamespace=11 ...\n\n    .. versionadded:: 0.10.0\n       Added ``max_cached_statement_use_count`` parameter.\n\n    .. versionchanged:: 0.11.0\n       Removed ability to pass arbitrary keyword arguments to set\n       server settings.  Added a dedicated parameter ``server_settings``\n       for that.\n\n    .. versionadded:: 0.11.0\n       Added ``connection_class`` parameter.\n\n    .. versionadded:: 0.16.0\n       Added ``passfile`` parameter\n       (and support for password files in general).\n\n    .. versionadded:: 0.18.0\n       Added ability to specify multiple hosts in the *dsn*\n       and *host* arguments.\n\n    .. versionchanged:: 0.21.0\n       The *password* argument now accepts a callable or an async function.\n\n    .. versionchanged:: 0.22.0\n       Added the *record_class* parameter.\n\n    .. versionchanged:: 0.22.0\n       The *ssl* argument now defaults to ``'prefer'``.\n\n    .. versionchanged:: 0.24.0\n       The ``sslcert``, ``sslkey``, ``sslrootcert``, and ``sslcrl`` options\n       are supported in the *dsn* argument.\n\n    .. versionchanged:: 0.25.0\n       The ``sslpassword``, ``ssl_min_protocol_version``,\n       and ``ssl_max_protocol_version`` options are supported in the *dsn*\n       argument.\n\n    .. versionchanged:: 0.25.0\n       Default system root CA certificates won't be loaded when specifying a\n       particular sslmode, following the same behavior in libpq.\n\n    .. versionchanged:: 0.25.0\n       The ``sslcert``, ``sslkey``, ``sslrootcert``, and ``sslcrl`` options\n       in the *dsn* argument now have consistent default values of files under\n       ``~/.postgresql/`` as libpq.\n\n    .. versionchanged:: 0.26.0\n       Added the *direct_tls* parameter.\n\n    .. versionchanged:: 0.28.0\n       Added the *target_session_attrs* parameter.\n\n    .. versionchanged:: 0.30.0\n       Added the *krbsrvname* and *gsslib* parameters.\n\n    .. versionchanged:: 0.31.0\n       Added the *servicefile* and *service* parameters.\n\n    .. _SSLContext: https://docs.python.org/3/library/ssl.html#ssl.SSLContext\n    .. _create_default_context:\n        https://docs.python.org/3/library/ssl.html#ssl.create_default_context\n    .. _server settings:\n        https://www.postgresql.org/docs/current/static/runtime-config.html\n    .. _postgres envvars:\n        https://www.postgresql.org/docs/current/static/libpq-envars.html\n    .. _libpq connection URI format:\n        https://www.postgresql.org/docs/current/static/\n        libpq-connect.html#LIBPQ-CONNSTRING\n    \"\"\"\n    if not issubclass(connection_class, Connection):\n        raise exceptions.InterfaceError(\n            'connection_class is expected to be a subclass of '\n            'asyncpg.Connection, got {!r}'.format(connection_class))\n\n    if record_class is not protocol.Record:\n        _check_record_class(record_class)\n\n    if loop is None:\n        loop = asyncio.get_event_loop()\n\n    async with compat.timeout(timeout):\n        return await connect_utils._connect(\n            loop=loop,\n            connection_class=connection_class,\n            record_class=record_class,\n            dsn=dsn,\n            host=host,\n            port=port,\n            user=user,\n            password=password,\n            passfile=passfile,\n            service=service,\n            servicefile=servicefile,\n            ssl=ssl,\n            direct_tls=direct_tls,\n            database=database,\n            server_settings=server_settings,\n            command_timeout=command_timeout,\n            statement_cache_size=statement_cache_size,\n            max_cached_statement_lifetime=max_cached_statement_lifetime,\n            max_cacheable_statement_size=max_cacheable_statement_size,\n            target_session_attrs=target_session_attrs,\n            krbsrvname=krbsrvname,\n            gsslib=gsslib,\n        )\n\n\nclass _StatementCacheEntry:\n\n    __slots__ = ('_query', '_statement', '_cache', '_cleanup_cb')\n\n    def __init__(self, cache, query, statement):\n        self._cache = cache\n        self._query = query\n        self._statement = statement\n        self._cleanup_cb = None\n\n\nclass _StatementCache:\n\n    __slots__ = ('_loop', '_entries', '_max_size', '_on_remove',\n                 '_max_lifetime')\n\n    def __init__(self, *, loop, max_size, on_remove, max_lifetime):\n        self._loop = loop\n        self._max_size = max_size\n        self._on_remove = on_remove\n        self._max_lifetime = max_lifetime\n\n        # We use an OrderedDict for LRU implementation.  Operations:\n        #\n        # * We use a simple `__setitem__` to push a new entry:\n        #       `entries[key] = new_entry`\n        #   That will push `new_entry` to the *end* of the entries dict.\n        #\n        # * When we have a cache hit, we call\n        #       `entries.move_to_end(key, last=True)`\n        #   to move the entry to the *end* of the entries dict.\n        #\n        # * When we need to remove entries to maintain `max_size`, we call\n        #       `entries.popitem(last=False)`\n        #   to remove an entry from the *beginning* of the entries dict.\n        #\n        # So new entries and hits are always promoted to the end of the\n        # entries dict, whereas the unused one will group in the\n        # beginning of it.\n        self._entries = collections.OrderedDict()\n\n    def __len__(self):\n        return len(self._entries)\n\n    def get_max_size(self):\n        return self._max_size\n\n    def set_max_size(self, new_size):\n        assert new_size >= 0\n        self._max_size = new_size\n        self._maybe_cleanup()\n\n    def get_max_lifetime(self):\n        return self._max_lifetime\n\n    def set_max_lifetime(self, new_lifetime):\n        assert new_lifetime >= 0\n        self._max_lifetime = new_lifetime\n        for entry in self._entries.values():\n            # For every entry cancel the existing callback\n            # and setup a new one if necessary.\n            self._set_entry_timeout(entry)\n\n    def get(self, query, *, promote=True):\n        if not self._max_size:\n            # The cache is disabled.\n            return\n\n        entry = self._entries.get(query)  # type: _StatementCacheEntry\n        if entry is None:\n            return\n\n        if entry._statement.closed:\n            # Happens in unittests when we call `stmt._state.mark_closed()`\n            # manually or when a prepared statement closes itself on type\n            # cache error.\n            self._entries.pop(query)\n            self._clear_entry_callback(entry)\n            return\n\n        if promote:\n            # `promote` is `False` when `get()` is called by `has()`.\n            self._entries.move_to_end(query, last=True)\n\n        return entry._statement\n\n    def has(self, query):\n        return self.get(query, promote=False) is not None\n\n    def put(self, query, statement):\n        if not self._max_size:\n            # The cache is disabled.\n            return\n\n        self._entries[query] = self._new_entry(query, statement)\n\n        # Check if the cache is bigger than max_size and trim it\n        # if necessary.\n        self._maybe_cleanup()\n\n    def iter_statements(self):\n        return (e._statement for e in self._entries.values())\n\n    def clear(self):\n        # Store entries for later.\n        entries = tuple(self._entries.values())\n\n        # Clear the entries dict.\n        self._entries.clear()\n\n        # Make sure that we cancel all scheduled callbacks\n        # and call on_remove callback for each entry.\n        for entry in entries:\n            self._clear_entry_callback(entry)\n            self._on_remove(entry._statement)\n\n    def _set_entry_timeout(self, entry):\n        # Clear the existing timeout.\n        self._clear_entry_callback(entry)\n\n        # Set the new timeout if it's not 0.\n        if self._max_lifetime:\n            entry._cleanup_cb = self._loop.call_later(\n                self._max_lifetime, self._on_entry_expired, entry)\n\n    def _new_entry(self, query, statement):\n        entry = _StatementCacheEntry(self, query, statement)\n        self._set_entry_timeout(entry)\n        return entry\n\n    def _on_entry_expired(self, entry):\n        # `call_later` callback, called when an entry stayed longer\n        # than `self._max_lifetime`.\n        if self._entries.get(entry._query) is entry:\n            self._entries.pop(entry._query)\n            self._on_remove(entry._statement)\n\n    def _clear_entry_callback(self, entry):\n        if entry._cleanup_cb is not None:\n            entry._cleanup_cb.cancel()\n\n    def _maybe_cleanup(self):\n        # Delete cache entries until the size of the cache is `max_size`.\n        while len(self._entries) > self._max_size:\n            old_query, old_entry = self._entries.popitem(last=False)\n            self._clear_entry_callback(old_entry)\n\n            # Let the connection know that the statement was removed\n            # from the cache.\n            self._on_remove(old_entry._statement)\n\n\nclass _Callback(typing.NamedTuple):\n\n    cb: typing.Callable[..., None]\n    is_async: bool\n\n    @classmethod\n    def from_callable(cls, cb: typing.Callable[..., None]) -> '_Callback':\n        if inspect.iscoroutinefunction(cb):\n            is_async = True\n        elif callable(cb):\n            is_async = False\n        else:\n            raise exceptions.InterfaceError(\n                'expected a callable or an `async def` function,'\n                'got {!r}'.format(cb)\n            )\n\n        return cls(cb, is_async)\n\n\nclass _Atomic:\n    __slots__ = ('_acquired',)\n\n    def __init__(self):\n        self._acquired = 0\n\n    def __enter__(self):\n        if self._acquired:\n            raise exceptions.InterfaceError(\n                'cannot perform operation: another operation is in progress')\n        self._acquired = 1\n\n    def __exit__(self, t, e, tb):\n        self._acquired = 0\n\n\nclass _ConnectionProxy:\n    # Base class to enable `isinstance(Connection)` check.\n    __slots__ = ()\n\n\nLoggedQuery = collections.namedtuple(\n    'LoggedQuery',\n    ['query', 'args', 'timeout', 'elapsed', 'exception', 'conn_addr',\n     'conn_params'])\nLoggedQuery.__doc__ = 'Log record of an executed query.'\n\n\nServerCapabilities = collections.namedtuple(\n    'ServerCapabilities',\n    ['advisory_locks', 'notifications', 'plpgsql', 'sql_reset',\n     'sql_close_all', 'sql_copy_from_where', 'jit'])\nServerCapabilities.__doc__ = 'PostgreSQL server capabilities.'\n\n\ndef _detect_server_capabilities(server_version, connection_settings):\n    if hasattr(connection_settings, 'padb_revision'):\n        # Amazon Redshift detected.\n        advisory_locks = False\n        notifications = False\n        plpgsql = False\n        sql_reset = True\n        sql_close_all = False\n        jit = False\n        sql_copy_from_where = False\n    elif hasattr(connection_settings, 'crdb_version'):\n        # CockroachDB detected.\n        advisory_locks = False\n        notifications = False\n        plpgsql = False\n        sql_reset = False\n        sql_close_all = False\n        jit = False\n        sql_copy_from_where = False\n    elif hasattr(connection_settings, 'crate_version'):\n        # CrateDB detected.\n        advisory_locks = False\n        notifications = False\n        plpgsql = False\n        sql_reset = False\n        sql_close_all = False\n        jit = False\n        sql_copy_from_where = False\n    else:\n        # Standard PostgreSQL server assumed.\n        advisory_locks = True\n        notifications = True\n        plpgsql = True\n        sql_reset = True\n        sql_close_all = True\n        jit = server_version >= (11, 0)\n        sql_copy_from_where = server_version.major >= 12\n\n    return ServerCapabilities(\n        advisory_locks=advisory_locks,\n        notifications=notifications,\n        plpgsql=plpgsql,\n        sql_reset=sql_reset,\n        sql_close_all=sql_close_all,\n        sql_copy_from_where=sql_copy_from_where,\n        jit=jit,\n    )\n\n\ndef _extract_stack(limit=10):\n    \"\"\"Replacement for traceback.extract_stack() that only does the\n    necessary work for asyncio debug mode.\n    \"\"\"\n    frame = sys._getframe().f_back\n    try:\n        stack = traceback.StackSummary.extract(\n            traceback.walk_stack(frame), lookup_lines=False)\n    finally:\n        del frame\n\n    apg_path = asyncpg.__path__[0]\n    i = 0\n    while i < len(stack) and stack[i][0].startswith(apg_path):\n        i += 1\n    stack = stack[i:i + limit]\n\n    stack.reverse()\n    return ''.join(traceback.format_list(stack))\n\n\ndef _check_record_class(record_class):\n    if record_class is protocol.Record:\n        pass\n    elif (\n        isinstance(record_class, type)\n        and issubclass(record_class, protocol.Record)\n    ):\n        if (\n            record_class.__new__ is not protocol.Record.__new__\n            or record_class.__init__ is not protocol.Record.__init__\n        ):\n            raise exceptions.InterfaceError(\n                'record_class must not redefine __new__ or __init__'\n            )\n    else:\n        raise exceptions.InterfaceError(\n            'record_class is expected to be a subclass of '\n            'asyncpg.Record, got {!r}'.format(record_class)\n        )\n\n\ndef _weak_maybe_gc_stmt(weak_ref, stmt):\n    self = weak_ref()\n    if self is not None:\n        self._maybe_gc_stmt(stmt)\n\n\n_uid = 0\n"
  },
  {
    "path": "asyncpg/connresource.py",
    "content": "\n# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport functools\n\nfrom . import exceptions\n\n\ndef guarded(meth):\n    \"\"\"A decorator to add a sanity check to ConnectionResource methods.\"\"\"\n\n    @functools.wraps(meth)\n    def _check(self, *args, **kwargs):\n        self._check_conn_validity(meth.__name__)\n        return meth(self, *args, **kwargs)\n\n    return _check\n\n\nclass ConnectionResource:\n    __slots__ = ('_connection', '_con_release_ctr')\n\n    def __init__(self, connection):\n        self._connection = connection\n        self._con_release_ctr = connection._pool_release_ctr\n\n    def _check_conn_validity(self, meth_name):\n        con_release_ctr = self._connection._pool_release_ctr\n        if con_release_ctr != self._con_release_ctr:\n            raise exceptions.InterfaceError(\n                'cannot call {}.{}(): '\n                'the underlying connection has been released back '\n                'to the pool'.format(self.__class__.__name__, meth_name))\n\n        if self._connection.is_closed():\n            raise exceptions.InterfaceError(\n                'cannot call {}.{}(): '\n                'the underlying connection is closed'.format(\n                    self.__class__.__name__, meth_name))\n"
  },
  {
    "path": "asyncpg/cursor.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport collections\n\nfrom . import connresource\nfrom . import exceptions\n\n\nclass CursorFactory(connresource.ConnectionResource):\n    \"\"\"A cursor interface for the results of a query.\n\n    A cursor interface can be used to initiate efficient traversal of the\n    results of a large query.\n    \"\"\"\n\n    __slots__ = (\n        '_state',\n        '_args',\n        '_prefetch',\n        '_query',\n        '_timeout',\n        '_record_class',\n    )\n\n    def __init__(\n        self,\n        connection,\n        query,\n        state,\n        args,\n        prefetch,\n        timeout,\n        record_class\n    ):\n        super().__init__(connection)\n        self._args = args\n        self._prefetch = prefetch\n        self._query = query\n        self._timeout = timeout\n        self._state = state\n        self._record_class = record_class\n        if state is not None:\n            state.attach()\n\n    @connresource.guarded\n    def __aiter__(self):\n        prefetch = 50 if self._prefetch is None else self._prefetch\n        return CursorIterator(\n            self._connection,\n            self._query,\n            self._state,\n            self._args,\n            self._record_class,\n            prefetch,\n            self._timeout,\n        )\n\n    @connresource.guarded\n    def __await__(self):\n        if self._prefetch is not None:\n            raise exceptions.InterfaceError(\n                'prefetch argument can only be specified for iterable cursor')\n        cursor = Cursor(\n            self._connection,\n            self._query,\n            self._state,\n            self._args,\n            self._record_class,\n        )\n        return cursor._init(self._timeout).__await__()\n\n    def __del__(self):\n        if self._state is not None:\n            self._state.detach()\n            self._connection._maybe_gc_stmt(self._state)\n\n\nclass BaseCursor(connresource.ConnectionResource):\n\n    __slots__ = (\n        '_state',\n        '_args',\n        '_portal_name',\n        '_exhausted',\n        '_query',\n        '_record_class',\n    )\n\n    def __init__(self, connection, query, state, args, record_class):\n        super().__init__(connection)\n        self._args = args\n        self._state = state\n        if state is not None:\n            state.attach()\n        self._portal_name = None\n        self._exhausted = False\n        self._query = query\n        self._record_class = record_class\n\n    def _check_ready(self):\n        if self._state is None:\n            raise exceptions.InterfaceError(\n                'cursor: no associated prepared statement')\n\n        if self._state.closed:\n            raise exceptions.InterfaceError(\n                'cursor: the prepared statement is closed')\n\n        if not self._connection._top_xact:\n            raise exceptions.NoActiveSQLTransactionError(\n                'cursor cannot be created outside of a transaction')\n\n    async def _bind_exec(self, n, timeout):\n        self._check_ready()\n\n        if self._portal_name:\n            raise exceptions.InterfaceError(\n                'cursor already has an open portal')\n\n        con = self._connection\n        protocol = con._protocol\n\n        self._portal_name = con._get_unique_id('portal')\n        buffer, _, self._exhausted = await protocol.bind_execute(\n            self._state, self._args, self._portal_name, n, True, timeout)\n        return buffer\n\n    async def _bind(self, timeout):\n        self._check_ready()\n\n        if self._portal_name:\n            raise exceptions.InterfaceError(\n                'cursor already has an open portal')\n\n        con = self._connection\n        protocol = con._protocol\n\n        self._portal_name = con._get_unique_id('portal')\n        buffer = await protocol.bind(self._state, self._args,\n                                     self._portal_name,\n                                     timeout)\n        return buffer\n\n    async def _exec(self, n, timeout):\n        self._check_ready()\n\n        if not self._portal_name:\n            raise exceptions.InterfaceError(\n                'cursor does not have an open portal')\n\n        protocol = self._connection._protocol\n        buffer, _, self._exhausted = await protocol.execute(\n            self._state, self._portal_name, n, True, timeout)\n        return buffer\n\n    async def _close_portal(self, timeout):\n        self._check_ready()\n\n        if not self._portal_name:\n            raise exceptions.InterfaceError(\n                'cursor does not have an open portal')\n\n        protocol = self._connection._protocol\n        await protocol.close_portal(self._portal_name, timeout)\n        self._portal_name = None\n\n    def __repr__(self):\n        attrs = []\n        if self._exhausted:\n            attrs.append('exhausted')\n        attrs.append('')  # to separate from id\n\n        if self.__class__.__module__.startswith('asyncpg.'):\n            mod = 'asyncpg'\n        else:\n            mod = self.__class__.__module__\n\n        return '<{}.{} \"{!s:.30}\" {}{:#x}>'.format(\n            mod, self.__class__.__name__,\n            self._state.query,\n            ' '.join(attrs), id(self))\n\n    def __del__(self):\n        if self._state is not None:\n            self._state.detach()\n            self._connection._maybe_gc_stmt(self._state)\n\n\nclass CursorIterator(BaseCursor):\n\n    __slots__ = ('_buffer', '_prefetch', '_timeout')\n\n    def __init__(\n        self,\n        connection,\n        query,\n        state,\n        args,\n        record_class,\n        prefetch,\n        timeout\n    ):\n        super().__init__(connection, query, state, args, record_class)\n\n        if prefetch <= 0:\n            raise exceptions.InterfaceError(\n                'prefetch argument must be greater than zero')\n\n        self._buffer = collections.deque()\n        self._prefetch = prefetch\n        self._timeout = timeout\n\n    @connresource.guarded\n    def __aiter__(self):\n        return self\n\n    @connresource.guarded\n    async def __anext__(self):\n        if self._state is None:\n            self._state = await self._connection._get_statement(\n                self._query,\n                self._timeout,\n                named=True,\n                record_class=self._record_class,\n            )\n            self._state.attach()\n\n        if not self._portal_name and not self._exhausted:\n            buffer = await self._bind_exec(self._prefetch, self._timeout)\n            self._buffer.extend(buffer)\n\n        if not self._buffer and not self._exhausted:\n            buffer = await self._exec(self._prefetch, self._timeout)\n            self._buffer.extend(buffer)\n\n        if self._portal_name and self._exhausted:\n            await self._close_portal(self._timeout)\n\n        if self._buffer:\n            return self._buffer.popleft()\n\n        raise StopAsyncIteration\n\n\nclass Cursor(BaseCursor):\n    \"\"\"An open *portal* into the results of a query.\"\"\"\n\n    __slots__ = ()\n\n    async def _init(self, timeout):\n        if self._state is None:\n            self._state = await self._connection._get_statement(\n                self._query,\n                timeout,\n                named=True,\n                record_class=self._record_class,\n            )\n            self._state.attach()\n        self._check_ready()\n        await self._bind(timeout)\n        return self\n\n    @connresource.guarded\n    async def fetch(self, n, *, timeout=None):\n        r\"\"\"Return the next *n* rows as a list of :class:`Record` objects.\n\n        :param float timeout: Optional timeout value in seconds.\n\n        :return: A list of :class:`Record` instances.\n        \"\"\"\n        self._check_ready()\n        if n <= 0:\n            raise exceptions.InterfaceError('n must be greater than zero')\n        if self._exhausted:\n            return []\n        recs = await self._exec(n, timeout)\n        if len(recs) < n:\n            self._exhausted = True\n        return recs\n\n    @connresource.guarded\n    async def fetchrow(self, *, timeout=None):\n        r\"\"\"Return the next row.\n\n        :param float timeout: Optional timeout value in seconds.\n\n        :return: A :class:`Record` instance.\n        \"\"\"\n        self._check_ready()\n        if self._exhausted:\n            return None\n        recs = await self._exec(1, timeout)\n        if len(recs) < 1:\n            self._exhausted = True\n            return None\n        return recs[0]\n\n    @connresource.guarded\n    async def forward(self, n, *, timeout=None) -> int:\n        r\"\"\"Skip over the next *n* rows.\n\n        :param float timeout: Optional timeout value in seconds.\n\n        :return: A number of rows actually skipped over (<= *n*).\n        \"\"\"\n        self._check_ready()\n        if n <= 0:\n            raise exceptions.InterfaceError('n must be greater than zero')\n\n        protocol = self._connection._protocol\n        status = await protocol.query('MOVE FORWARD {:d} {}'.format(\n            n, self._portal_name), timeout)\n\n        advanced = int(status.split()[1])\n        if advanced < n:\n            self._exhausted = True\n\n        return advanced\n"
  },
  {
    "path": "asyncpg/exceptions/__init__.py",
    "content": "# GENERATED FROM postgresql/src/backend/utils/errcodes.txt\n# DO NOT MODIFY, use tools/generate_exceptions.py to update\n\nfrom ._base import *  # NOQA\nfrom . import _base\n\n\nclass PostgresWarning(_base.PostgresLogMessage, Warning):\n    sqlstate = '01000'\n\n\nclass DynamicResultSetsReturned(PostgresWarning):\n    sqlstate = '0100C'\n\n\nclass ImplicitZeroBitPadding(PostgresWarning):\n    sqlstate = '01008'\n\n\nclass NullValueEliminatedInSetFunction(PostgresWarning):\n    sqlstate = '01003'\n\n\nclass PrivilegeNotGranted(PostgresWarning):\n    sqlstate = '01007'\n\n\nclass PrivilegeNotRevoked(PostgresWarning):\n    sqlstate = '01006'\n\n\nclass StringDataRightTruncation(PostgresWarning):\n    sqlstate = '01004'\n\n\nclass DeprecatedFeature(PostgresWarning):\n    sqlstate = '01P01'\n\n\nclass NoData(PostgresWarning):\n    sqlstate = '02000'\n\n\nclass NoAdditionalDynamicResultSetsReturned(NoData):\n    sqlstate = '02001'\n\n\nclass SQLStatementNotYetCompleteError(_base.PostgresError):\n    sqlstate = '03000'\n\n\nclass PostgresConnectionError(_base.PostgresError):\n    sqlstate = '08000'\n\n\nclass ConnectionDoesNotExistError(PostgresConnectionError):\n    sqlstate = '08003'\n\n\nclass ConnectionFailureError(PostgresConnectionError):\n    sqlstate = '08006'\n\n\nclass ClientCannotConnectError(PostgresConnectionError):\n    sqlstate = '08001'\n\n\nclass ConnectionRejectionError(PostgresConnectionError):\n    sqlstate = '08004'\n\n\nclass TransactionResolutionUnknownError(PostgresConnectionError):\n    sqlstate = '08007'\n\n\nclass ProtocolViolationError(PostgresConnectionError):\n    sqlstate = '08P01'\n\n\nclass TriggeredActionError(_base.PostgresError):\n    sqlstate = '09000'\n\n\nclass FeatureNotSupportedError(_base.PostgresError):\n    sqlstate = '0A000'\n\n\nclass InvalidCachedStatementError(FeatureNotSupportedError):\n    pass\n\n\nclass InvalidTransactionInitiationError(_base.PostgresError):\n    sqlstate = '0B000'\n\n\nclass LocatorError(_base.PostgresError):\n    sqlstate = '0F000'\n\n\nclass InvalidLocatorSpecificationError(LocatorError):\n    sqlstate = '0F001'\n\n\nclass InvalidGrantorError(_base.PostgresError):\n    sqlstate = '0L000'\n\n\nclass InvalidGrantOperationError(InvalidGrantorError):\n    sqlstate = '0LP01'\n\n\nclass InvalidRoleSpecificationError(_base.PostgresError):\n    sqlstate = '0P000'\n\n\nclass DiagnosticsError(_base.PostgresError):\n    sqlstate = '0Z000'\n\n\nclass StackedDiagnosticsAccessedWithoutActiveHandlerError(DiagnosticsError):\n    sqlstate = '0Z002'\n\n\nclass InvalidArgumentForXqueryError(_base.PostgresError):\n    sqlstate = '10608'\n\n\nclass CaseNotFoundError(_base.PostgresError):\n    sqlstate = '20000'\n\n\nclass CardinalityViolationError(_base.PostgresError):\n    sqlstate = '21000'\n\n\nclass DataError(_base.PostgresError):\n    sqlstate = '22000'\n\n\nclass ArraySubscriptError(DataError):\n    sqlstate = '2202E'\n\n\nclass CharacterNotInRepertoireError(DataError):\n    sqlstate = '22021'\n\n\nclass DatetimeFieldOverflowError(DataError):\n    sqlstate = '22008'\n\n\nclass DivisionByZeroError(DataError):\n    sqlstate = '22012'\n\n\nclass ErrorInAssignmentError(DataError):\n    sqlstate = '22005'\n\n\nclass EscapeCharacterConflictError(DataError):\n    sqlstate = '2200B'\n\n\nclass IndicatorOverflowError(DataError):\n    sqlstate = '22022'\n\n\nclass IntervalFieldOverflowError(DataError):\n    sqlstate = '22015'\n\n\nclass InvalidArgumentForLogarithmError(DataError):\n    sqlstate = '2201E'\n\n\nclass InvalidArgumentForNtileFunctionError(DataError):\n    sqlstate = '22014'\n\n\nclass InvalidArgumentForNthValueFunctionError(DataError):\n    sqlstate = '22016'\n\n\nclass InvalidArgumentForPowerFunctionError(DataError):\n    sqlstate = '2201F'\n\n\nclass InvalidArgumentForWidthBucketFunctionError(DataError):\n    sqlstate = '2201G'\n\n\nclass InvalidCharacterValueForCastError(DataError):\n    sqlstate = '22018'\n\n\nclass InvalidDatetimeFormatError(DataError):\n    sqlstate = '22007'\n\n\nclass InvalidEscapeCharacterError(DataError):\n    sqlstate = '22019'\n\n\nclass InvalidEscapeOctetError(DataError):\n    sqlstate = '2200D'\n\n\nclass InvalidEscapeSequenceError(DataError):\n    sqlstate = '22025'\n\n\nclass NonstandardUseOfEscapeCharacterError(DataError):\n    sqlstate = '22P06'\n\n\nclass InvalidIndicatorParameterValueError(DataError):\n    sqlstate = '22010'\n\n\nclass InvalidParameterValueError(DataError):\n    sqlstate = '22023'\n\n\nclass InvalidPrecedingOrFollowingSizeError(DataError):\n    sqlstate = '22013'\n\n\nclass InvalidRegularExpressionError(DataError):\n    sqlstate = '2201B'\n\n\nclass InvalidRowCountInLimitClauseError(DataError):\n    sqlstate = '2201W'\n\n\nclass InvalidRowCountInResultOffsetClauseError(DataError):\n    sqlstate = '2201X'\n\n\nclass InvalidTablesampleArgumentError(DataError):\n    sqlstate = '2202H'\n\n\nclass InvalidTablesampleRepeatError(DataError):\n    sqlstate = '2202G'\n\n\nclass InvalidTimeZoneDisplacementValueError(DataError):\n    sqlstate = '22009'\n\n\nclass InvalidUseOfEscapeCharacterError(DataError):\n    sqlstate = '2200C'\n\n\nclass MostSpecificTypeMismatchError(DataError):\n    sqlstate = '2200G'\n\n\nclass NullValueNotAllowedError(DataError):\n    sqlstate = '22004'\n\n\nclass NullValueNoIndicatorParameterError(DataError):\n    sqlstate = '22002'\n\n\nclass NumericValueOutOfRangeError(DataError):\n    sqlstate = '22003'\n\n\nclass SequenceGeneratorLimitExceededError(DataError):\n    sqlstate = '2200H'\n\n\nclass StringDataLengthMismatchError(DataError):\n    sqlstate = '22026'\n\n\nclass StringDataRightTruncationError(DataError):\n    sqlstate = '22001'\n\n\nclass SubstringError(DataError):\n    sqlstate = '22011'\n\n\nclass TrimError(DataError):\n    sqlstate = '22027'\n\n\nclass UnterminatedCStringError(DataError):\n    sqlstate = '22024'\n\n\nclass ZeroLengthCharacterStringError(DataError):\n    sqlstate = '2200F'\n\n\nclass PostgresFloatingPointError(DataError):\n    sqlstate = '22P01'\n\n\nclass InvalidTextRepresentationError(DataError):\n    sqlstate = '22P02'\n\n\nclass InvalidBinaryRepresentationError(DataError):\n    sqlstate = '22P03'\n\n\nclass BadCopyFileFormatError(DataError):\n    sqlstate = '22P04'\n\n\nclass UntranslatableCharacterError(DataError):\n    sqlstate = '22P05'\n\n\nclass NotAnXmlDocumentError(DataError):\n    sqlstate = '2200L'\n\n\nclass InvalidXmlDocumentError(DataError):\n    sqlstate = '2200M'\n\n\nclass InvalidXmlContentError(DataError):\n    sqlstate = '2200N'\n\n\nclass InvalidXmlCommentError(DataError):\n    sqlstate = '2200S'\n\n\nclass InvalidXmlProcessingInstructionError(DataError):\n    sqlstate = '2200T'\n\n\nclass DuplicateJsonObjectKeyValueError(DataError):\n    sqlstate = '22030'\n\n\nclass InvalidArgumentForSQLJsonDatetimeFunctionError(DataError):\n    sqlstate = '22031'\n\n\nclass InvalidJsonTextError(DataError):\n    sqlstate = '22032'\n\n\nclass InvalidSQLJsonSubscriptError(DataError):\n    sqlstate = '22033'\n\n\nclass MoreThanOneSQLJsonItemError(DataError):\n    sqlstate = '22034'\n\n\nclass NoSQLJsonItemError(DataError):\n    sqlstate = '22035'\n\n\nclass NonNumericSQLJsonItemError(DataError):\n    sqlstate = '22036'\n\n\nclass NonUniqueKeysInAJsonObjectError(DataError):\n    sqlstate = '22037'\n\n\nclass SingletonSQLJsonItemRequiredError(DataError):\n    sqlstate = '22038'\n\n\nclass SQLJsonArrayNotFoundError(DataError):\n    sqlstate = '22039'\n\n\nclass SQLJsonMemberNotFoundError(DataError):\n    sqlstate = '2203A'\n\n\nclass SQLJsonNumberNotFoundError(DataError):\n    sqlstate = '2203B'\n\n\nclass SQLJsonObjectNotFoundError(DataError):\n    sqlstate = '2203C'\n\n\nclass TooManyJsonArrayElementsError(DataError):\n    sqlstate = '2203D'\n\n\nclass TooManyJsonObjectMembersError(DataError):\n    sqlstate = '2203E'\n\n\nclass SQLJsonScalarRequiredError(DataError):\n    sqlstate = '2203F'\n\n\nclass SQLJsonItemCannotBeCastToTargetTypeError(DataError):\n    sqlstate = '2203G'\n\n\nclass IntegrityConstraintViolationError(_base.PostgresError):\n    sqlstate = '23000'\n\n\nclass RestrictViolationError(IntegrityConstraintViolationError):\n    sqlstate = '23001'\n\n\nclass NotNullViolationError(IntegrityConstraintViolationError):\n    sqlstate = '23502'\n\n\nclass ForeignKeyViolationError(IntegrityConstraintViolationError):\n    sqlstate = '23503'\n\n\nclass UniqueViolationError(IntegrityConstraintViolationError):\n    sqlstate = '23505'\n\n\nclass CheckViolationError(IntegrityConstraintViolationError):\n    sqlstate = '23514'\n\n\nclass ExclusionViolationError(IntegrityConstraintViolationError):\n    sqlstate = '23P01'\n\n\nclass InvalidCursorStateError(_base.PostgresError):\n    sqlstate = '24000'\n\n\nclass InvalidTransactionStateError(_base.PostgresError):\n    sqlstate = '25000'\n\n\nclass ActiveSQLTransactionError(InvalidTransactionStateError):\n    sqlstate = '25001'\n\n\nclass BranchTransactionAlreadyActiveError(InvalidTransactionStateError):\n    sqlstate = '25002'\n\n\nclass HeldCursorRequiresSameIsolationLevelError(InvalidTransactionStateError):\n    sqlstate = '25008'\n\n\nclass InappropriateAccessModeForBranchTransactionError(\n        InvalidTransactionStateError):\n    sqlstate = '25003'\n\n\nclass InappropriateIsolationLevelForBranchTransactionError(\n        InvalidTransactionStateError):\n    sqlstate = '25004'\n\n\nclass NoActiveSQLTransactionForBranchTransactionError(\n        InvalidTransactionStateError):\n    sqlstate = '25005'\n\n\nclass ReadOnlySQLTransactionError(InvalidTransactionStateError):\n    sqlstate = '25006'\n\n\nclass SchemaAndDataStatementMixingNotSupportedError(\n        InvalidTransactionStateError):\n    sqlstate = '25007'\n\n\nclass NoActiveSQLTransactionError(InvalidTransactionStateError):\n    sqlstate = '25P01'\n\n\nclass InFailedSQLTransactionError(InvalidTransactionStateError):\n    sqlstate = '25P02'\n\n\nclass IdleInTransactionSessionTimeoutError(InvalidTransactionStateError):\n    sqlstate = '25P03'\n\n\nclass TransactionTimeoutError(InvalidTransactionStateError):\n    sqlstate = '25P04'\n\n\nclass InvalidSQLStatementNameError(_base.PostgresError):\n    sqlstate = '26000'\n\n\nclass TriggeredDataChangeViolationError(_base.PostgresError):\n    sqlstate = '27000'\n\n\nclass InvalidAuthorizationSpecificationError(_base.PostgresError):\n    sqlstate = '28000'\n\n\nclass InvalidPasswordError(InvalidAuthorizationSpecificationError):\n    sqlstate = '28P01'\n\n\nclass DependentPrivilegeDescriptorsStillExistError(_base.PostgresError):\n    sqlstate = '2B000'\n\n\nclass DependentObjectsStillExistError(\n        DependentPrivilegeDescriptorsStillExistError):\n    sqlstate = '2BP01'\n\n\nclass InvalidTransactionTerminationError(_base.PostgresError):\n    sqlstate = '2D000'\n\n\nclass SQLRoutineError(_base.PostgresError):\n    sqlstate = '2F000'\n\n\nclass FunctionExecutedNoReturnStatementError(SQLRoutineError):\n    sqlstate = '2F005'\n\n\nclass ModifyingSQLDataNotPermittedError(SQLRoutineError):\n    sqlstate = '2F002'\n\n\nclass ProhibitedSQLStatementAttemptedError(SQLRoutineError):\n    sqlstate = '2F003'\n\n\nclass ReadingSQLDataNotPermittedError(SQLRoutineError):\n    sqlstate = '2F004'\n\n\nclass InvalidCursorNameError(_base.PostgresError):\n    sqlstate = '34000'\n\n\nclass ExternalRoutineError(_base.PostgresError):\n    sqlstate = '38000'\n\n\nclass ContainingSQLNotPermittedError(ExternalRoutineError):\n    sqlstate = '38001'\n\n\nclass ModifyingExternalRoutineSQLDataNotPermittedError(ExternalRoutineError):\n    sqlstate = '38002'\n\n\nclass ProhibitedExternalRoutineSQLStatementAttemptedError(\n        ExternalRoutineError):\n    sqlstate = '38003'\n\n\nclass ReadingExternalRoutineSQLDataNotPermittedError(ExternalRoutineError):\n    sqlstate = '38004'\n\n\nclass ExternalRoutineInvocationError(_base.PostgresError):\n    sqlstate = '39000'\n\n\nclass InvalidSqlstateReturnedError(ExternalRoutineInvocationError):\n    sqlstate = '39001'\n\n\nclass NullValueInExternalRoutineNotAllowedError(\n        ExternalRoutineInvocationError):\n    sqlstate = '39004'\n\n\nclass TriggerProtocolViolatedError(ExternalRoutineInvocationError):\n    sqlstate = '39P01'\n\n\nclass SrfProtocolViolatedError(ExternalRoutineInvocationError):\n    sqlstate = '39P02'\n\n\nclass EventTriggerProtocolViolatedError(ExternalRoutineInvocationError):\n    sqlstate = '39P03'\n\n\nclass SavepointError(_base.PostgresError):\n    sqlstate = '3B000'\n\n\nclass InvalidSavepointSpecificationError(SavepointError):\n    sqlstate = '3B001'\n\n\nclass InvalidCatalogNameError(_base.PostgresError):\n    sqlstate = '3D000'\n\n\nclass InvalidSchemaNameError(_base.PostgresError):\n    sqlstate = '3F000'\n\n\nclass TransactionRollbackError(_base.PostgresError):\n    sqlstate = '40000'\n\n\nclass TransactionIntegrityConstraintViolationError(TransactionRollbackError):\n    sqlstate = '40002'\n\n\nclass SerializationError(TransactionRollbackError):\n    sqlstate = '40001'\n\n\nclass StatementCompletionUnknownError(TransactionRollbackError):\n    sqlstate = '40003'\n\n\nclass DeadlockDetectedError(TransactionRollbackError):\n    sqlstate = '40P01'\n\n\nclass SyntaxOrAccessError(_base.PostgresError):\n    sqlstate = '42000'\n\n\nclass PostgresSyntaxError(SyntaxOrAccessError):\n    sqlstate = '42601'\n\n\nclass InsufficientPrivilegeError(SyntaxOrAccessError):\n    sqlstate = '42501'\n\n\nclass CannotCoerceError(SyntaxOrAccessError):\n    sqlstate = '42846'\n\n\nclass GroupingError(SyntaxOrAccessError):\n    sqlstate = '42803'\n\n\nclass WindowingError(SyntaxOrAccessError):\n    sqlstate = '42P20'\n\n\nclass InvalidRecursionError(SyntaxOrAccessError):\n    sqlstate = '42P19'\n\n\nclass InvalidForeignKeyError(SyntaxOrAccessError):\n    sqlstate = '42830'\n\n\nclass InvalidNameError(SyntaxOrAccessError):\n    sqlstate = '42602'\n\n\nclass NameTooLongError(SyntaxOrAccessError):\n    sqlstate = '42622'\n\n\nclass ReservedNameError(SyntaxOrAccessError):\n    sqlstate = '42939'\n\n\nclass DatatypeMismatchError(SyntaxOrAccessError):\n    sqlstate = '42804'\n\n\nclass IndeterminateDatatypeError(SyntaxOrAccessError):\n    sqlstate = '42P18'\n\n\nclass CollationMismatchError(SyntaxOrAccessError):\n    sqlstate = '42P21'\n\n\nclass IndeterminateCollationError(SyntaxOrAccessError):\n    sqlstate = '42P22'\n\n\nclass WrongObjectTypeError(SyntaxOrAccessError):\n    sqlstate = '42809'\n\n\nclass GeneratedAlwaysError(SyntaxOrAccessError):\n    sqlstate = '428C9'\n\n\nclass UndefinedColumnError(SyntaxOrAccessError):\n    sqlstate = '42703'\n\n\nclass UndefinedFunctionError(SyntaxOrAccessError):\n    sqlstate = '42883'\n\n\nclass UndefinedTableError(SyntaxOrAccessError):\n    sqlstate = '42P01'\n\n\nclass UndefinedParameterError(SyntaxOrAccessError):\n    sqlstate = '42P02'\n\n\nclass UndefinedObjectError(SyntaxOrAccessError):\n    sqlstate = '42704'\n\n\nclass DuplicateColumnError(SyntaxOrAccessError):\n    sqlstate = '42701'\n\n\nclass DuplicateCursorError(SyntaxOrAccessError):\n    sqlstate = '42P03'\n\n\nclass DuplicateDatabaseError(SyntaxOrAccessError):\n    sqlstate = '42P04'\n\n\nclass DuplicateFunctionError(SyntaxOrAccessError):\n    sqlstate = '42723'\n\n\nclass DuplicatePreparedStatementError(SyntaxOrAccessError):\n    sqlstate = '42P05'\n\n\nclass DuplicateSchemaError(SyntaxOrAccessError):\n    sqlstate = '42P06'\n\n\nclass DuplicateTableError(SyntaxOrAccessError):\n    sqlstate = '42P07'\n\n\nclass DuplicateAliasError(SyntaxOrAccessError):\n    sqlstate = '42712'\n\n\nclass DuplicateObjectError(SyntaxOrAccessError):\n    sqlstate = '42710'\n\n\nclass AmbiguousColumnError(SyntaxOrAccessError):\n    sqlstate = '42702'\n\n\nclass AmbiguousFunctionError(SyntaxOrAccessError):\n    sqlstate = '42725'\n\n\nclass AmbiguousParameterError(SyntaxOrAccessError):\n    sqlstate = '42P08'\n\n\nclass AmbiguousAliasError(SyntaxOrAccessError):\n    sqlstate = '42P09'\n\n\nclass InvalidColumnReferenceError(SyntaxOrAccessError):\n    sqlstate = '42P10'\n\n\nclass InvalidColumnDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42611'\n\n\nclass InvalidCursorDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P11'\n\n\nclass InvalidDatabaseDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P12'\n\n\nclass InvalidFunctionDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P13'\n\n\nclass InvalidPreparedStatementDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P14'\n\n\nclass InvalidSchemaDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P15'\n\n\nclass InvalidTableDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P16'\n\n\nclass InvalidObjectDefinitionError(SyntaxOrAccessError):\n    sqlstate = '42P17'\n\n\nclass WithCheckOptionViolationError(_base.PostgresError):\n    sqlstate = '44000'\n\n\nclass InsufficientResourcesError(_base.PostgresError):\n    sqlstate = '53000'\n\n\nclass DiskFullError(InsufficientResourcesError):\n    sqlstate = '53100'\n\n\nclass OutOfMemoryError(InsufficientResourcesError):\n    sqlstate = '53200'\n\n\nclass TooManyConnectionsError(InsufficientResourcesError):\n    sqlstate = '53300'\n\n\nclass ConfigurationLimitExceededError(InsufficientResourcesError):\n    sqlstate = '53400'\n\n\nclass ProgramLimitExceededError(_base.PostgresError):\n    sqlstate = '54000'\n\n\nclass StatementTooComplexError(ProgramLimitExceededError):\n    sqlstate = '54001'\n\n\nclass TooManyColumnsError(ProgramLimitExceededError):\n    sqlstate = '54011'\n\n\nclass TooManyArgumentsError(ProgramLimitExceededError):\n    sqlstate = '54023'\n\n\nclass ObjectNotInPrerequisiteStateError(_base.PostgresError):\n    sqlstate = '55000'\n\n\nclass ObjectInUseError(ObjectNotInPrerequisiteStateError):\n    sqlstate = '55006'\n\n\nclass CantChangeRuntimeParamError(ObjectNotInPrerequisiteStateError):\n    sqlstate = '55P02'\n\n\nclass LockNotAvailableError(ObjectNotInPrerequisiteStateError):\n    sqlstate = '55P03'\n\n\nclass UnsafeNewEnumValueUsageError(ObjectNotInPrerequisiteStateError):\n    sqlstate = '55P04'\n\n\nclass OperatorInterventionError(_base.PostgresError):\n    sqlstate = '57000'\n\n\nclass QueryCanceledError(OperatorInterventionError):\n    sqlstate = '57014'\n\n\nclass AdminShutdownError(OperatorInterventionError):\n    sqlstate = '57P01'\n\n\nclass CrashShutdownError(OperatorInterventionError):\n    sqlstate = '57P02'\n\n\nclass CannotConnectNowError(OperatorInterventionError):\n    sqlstate = '57P03'\n\n\nclass DatabaseDroppedError(OperatorInterventionError):\n    sqlstate = '57P04'\n\n\nclass IdleSessionTimeoutError(OperatorInterventionError):\n    sqlstate = '57P05'\n\n\nclass PostgresSystemError(_base.PostgresError):\n    sqlstate = '58000'\n\n\nclass PostgresIOError(PostgresSystemError):\n    sqlstate = '58030'\n\n\nclass UndefinedFileError(PostgresSystemError):\n    sqlstate = '58P01'\n\n\nclass DuplicateFileError(PostgresSystemError):\n    sqlstate = '58P02'\n\n\nclass FileNameTooLongError(PostgresSystemError):\n    sqlstate = '58P03'\n\n\nclass SnapshotTooOldError(_base.PostgresError):\n    sqlstate = '72000'\n\n\nclass ConfigFileError(_base.PostgresError):\n    sqlstate = 'F0000'\n\n\nclass LockFileExistsError(ConfigFileError):\n    sqlstate = 'F0001'\n\n\nclass FDWError(_base.PostgresError):\n    sqlstate = 'HV000'\n\n\nclass FDWColumnNameNotFoundError(FDWError):\n    sqlstate = 'HV005'\n\n\nclass FDWDynamicParameterValueNeededError(FDWError):\n    sqlstate = 'HV002'\n\n\nclass FDWFunctionSequenceError(FDWError):\n    sqlstate = 'HV010'\n\n\nclass FDWInconsistentDescriptorInformationError(FDWError):\n    sqlstate = 'HV021'\n\n\nclass FDWInvalidAttributeValueError(FDWError):\n    sqlstate = 'HV024'\n\n\nclass FDWInvalidColumnNameError(FDWError):\n    sqlstate = 'HV007'\n\n\nclass FDWInvalidColumnNumberError(FDWError):\n    sqlstate = 'HV008'\n\n\nclass FDWInvalidDataTypeError(FDWError):\n    sqlstate = 'HV004'\n\n\nclass FDWInvalidDataTypeDescriptorsError(FDWError):\n    sqlstate = 'HV006'\n\n\nclass FDWInvalidDescriptorFieldIdentifierError(FDWError):\n    sqlstate = 'HV091'\n\n\nclass FDWInvalidHandleError(FDWError):\n    sqlstate = 'HV00B'\n\n\nclass FDWInvalidOptionIndexError(FDWError):\n    sqlstate = 'HV00C'\n\n\nclass FDWInvalidOptionNameError(FDWError):\n    sqlstate = 'HV00D'\n\n\nclass FDWInvalidStringLengthOrBufferLengthError(FDWError):\n    sqlstate = 'HV090'\n\n\nclass FDWInvalidStringFormatError(FDWError):\n    sqlstate = 'HV00A'\n\n\nclass FDWInvalidUseOfNullPointerError(FDWError):\n    sqlstate = 'HV009'\n\n\nclass FDWTooManyHandlesError(FDWError):\n    sqlstate = 'HV014'\n\n\nclass FDWOutOfMemoryError(FDWError):\n    sqlstate = 'HV001'\n\n\nclass FDWNoSchemasError(FDWError):\n    sqlstate = 'HV00P'\n\n\nclass FDWOptionNameNotFoundError(FDWError):\n    sqlstate = 'HV00J'\n\n\nclass FDWReplyHandleError(FDWError):\n    sqlstate = 'HV00K'\n\n\nclass FDWSchemaNotFoundError(FDWError):\n    sqlstate = 'HV00Q'\n\n\nclass FDWTableNotFoundError(FDWError):\n    sqlstate = 'HV00R'\n\n\nclass FDWUnableToCreateExecutionError(FDWError):\n    sqlstate = 'HV00L'\n\n\nclass FDWUnableToCreateReplyError(FDWError):\n    sqlstate = 'HV00M'\n\n\nclass FDWUnableToEstablishConnectionError(FDWError):\n    sqlstate = 'HV00N'\n\n\nclass PLPGSQLError(_base.PostgresError):\n    sqlstate = 'P0000'\n\n\nclass RaiseError(PLPGSQLError):\n    sqlstate = 'P0001'\n\n\nclass NoDataFoundError(PLPGSQLError):\n    sqlstate = 'P0002'\n\n\nclass TooManyRowsError(PLPGSQLError):\n    sqlstate = 'P0003'\n\n\nclass AssertError(PLPGSQLError):\n    sqlstate = 'P0004'\n\n\nclass InternalServerError(_base.PostgresError):\n    sqlstate = 'XX000'\n\n\nclass DataCorruptedError(InternalServerError):\n    sqlstate = 'XX001'\n\n\nclass IndexCorruptedError(InternalServerError):\n    sqlstate = 'XX002'\n\n\n__all__ = (\n    'ActiveSQLTransactionError', 'AdminShutdownError',\n    'AmbiguousAliasError', 'AmbiguousColumnError',\n    'AmbiguousFunctionError', 'AmbiguousParameterError',\n    'ArraySubscriptError', 'AssertError', 'BadCopyFileFormatError',\n    'BranchTransactionAlreadyActiveError', 'CannotCoerceError',\n    'CannotConnectNowError', 'CantChangeRuntimeParamError',\n    'CardinalityViolationError', 'CaseNotFoundError',\n    'CharacterNotInRepertoireError', 'CheckViolationError',\n    'ClientCannotConnectError', 'CollationMismatchError',\n    'ConfigFileError', 'ConfigurationLimitExceededError',\n    'ConnectionDoesNotExistError', 'ConnectionFailureError',\n    'ConnectionRejectionError', 'ContainingSQLNotPermittedError',\n    'CrashShutdownError', 'DataCorruptedError', 'DataError',\n    'DatabaseDroppedError', 'DatatypeMismatchError',\n    'DatetimeFieldOverflowError', 'DeadlockDetectedError',\n    'DependentObjectsStillExistError',\n    'DependentPrivilegeDescriptorsStillExistError', 'DeprecatedFeature',\n    'DiagnosticsError', 'DiskFullError', 'DivisionByZeroError',\n    'DuplicateAliasError', 'DuplicateColumnError', 'DuplicateCursorError',\n    'DuplicateDatabaseError', 'DuplicateFileError',\n    'DuplicateFunctionError', 'DuplicateJsonObjectKeyValueError',\n    'DuplicateObjectError', 'DuplicatePreparedStatementError',\n    'DuplicateSchemaError', 'DuplicateTableError',\n    'DynamicResultSetsReturned', 'ErrorInAssignmentError',\n    'EscapeCharacterConflictError', 'EventTriggerProtocolViolatedError',\n    'ExclusionViolationError', 'ExternalRoutineError',\n    'ExternalRoutineInvocationError', 'FDWColumnNameNotFoundError',\n    'FDWDynamicParameterValueNeededError', 'FDWError',\n    'FDWFunctionSequenceError',\n    'FDWInconsistentDescriptorInformationError',\n    'FDWInvalidAttributeValueError', 'FDWInvalidColumnNameError',\n    'FDWInvalidColumnNumberError', 'FDWInvalidDataTypeDescriptorsError',\n    'FDWInvalidDataTypeError', 'FDWInvalidDescriptorFieldIdentifierError',\n    'FDWInvalidHandleError', 'FDWInvalidOptionIndexError',\n    'FDWInvalidOptionNameError', 'FDWInvalidStringFormatError',\n    'FDWInvalidStringLengthOrBufferLengthError',\n    'FDWInvalidUseOfNullPointerError', 'FDWNoSchemasError',\n    'FDWOptionNameNotFoundError', 'FDWOutOfMemoryError',\n    'FDWReplyHandleError', 'FDWSchemaNotFoundError',\n    'FDWTableNotFoundError', 'FDWTooManyHandlesError',\n    'FDWUnableToCreateExecutionError', 'FDWUnableToCreateReplyError',\n    'FDWUnableToEstablishConnectionError', 'FeatureNotSupportedError',\n    'FileNameTooLongError', 'ForeignKeyViolationError',\n    'FunctionExecutedNoReturnStatementError', 'GeneratedAlwaysError',\n    'GroupingError', 'HeldCursorRequiresSameIsolationLevelError',\n    'IdleInTransactionSessionTimeoutError', 'IdleSessionTimeoutError',\n    'ImplicitZeroBitPadding', 'InFailedSQLTransactionError',\n    'InappropriateAccessModeForBranchTransactionError',\n    'InappropriateIsolationLevelForBranchTransactionError',\n    'IndeterminateCollationError', 'IndeterminateDatatypeError',\n    'IndexCorruptedError', 'IndicatorOverflowError',\n    'InsufficientPrivilegeError', 'InsufficientResourcesError',\n    'IntegrityConstraintViolationError', 'InternalServerError',\n    'IntervalFieldOverflowError', 'InvalidArgumentForLogarithmError',\n    'InvalidArgumentForNthValueFunctionError',\n    'InvalidArgumentForNtileFunctionError',\n    'InvalidArgumentForPowerFunctionError',\n    'InvalidArgumentForSQLJsonDatetimeFunctionError',\n    'InvalidArgumentForWidthBucketFunctionError',\n    'InvalidArgumentForXqueryError',\n    'InvalidAuthorizationSpecificationError',\n    'InvalidBinaryRepresentationError', 'InvalidCachedStatementError',\n    'InvalidCatalogNameError', 'InvalidCharacterValueForCastError',\n    'InvalidColumnDefinitionError', 'InvalidColumnReferenceError',\n    'InvalidCursorDefinitionError', 'InvalidCursorNameError',\n    'InvalidCursorStateError', 'InvalidDatabaseDefinitionError',\n    'InvalidDatetimeFormatError', 'InvalidEscapeCharacterError',\n    'InvalidEscapeOctetError', 'InvalidEscapeSequenceError',\n    'InvalidForeignKeyError', 'InvalidFunctionDefinitionError',\n    'InvalidGrantOperationError', 'InvalidGrantorError',\n    'InvalidIndicatorParameterValueError', 'InvalidJsonTextError',\n    'InvalidLocatorSpecificationError', 'InvalidNameError',\n    'InvalidObjectDefinitionError', 'InvalidParameterValueError',\n    'InvalidPasswordError', 'InvalidPrecedingOrFollowingSizeError',\n    'InvalidPreparedStatementDefinitionError', 'InvalidRecursionError',\n    'InvalidRegularExpressionError', 'InvalidRoleSpecificationError',\n    'InvalidRowCountInLimitClauseError',\n    'InvalidRowCountInResultOffsetClauseError',\n    'InvalidSQLJsonSubscriptError', 'InvalidSQLStatementNameError',\n    'InvalidSavepointSpecificationError', 'InvalidSchemaDefinitionError',\n    'InvalidSchemaNameError', 'InvalidSqlstateReturnedError',\n    'InvalidTableDefinitionError', 'InvalidTablesampleArgumentError',\n    'InvalidTablesampleRepeatError', 'InvalidTextRepresentationError',\n    'InvalidTimeZoneDisplacementValueError',\n    'InvalidTransactionInitiationError', 'InvalidTransactionStateError',\n    'InvalidTransactionTerminationError',\n    'InvalidUseOfEscapeCharacterError', 'InvalidXmlCommentError',\n    'InvalidXmlContentError', 'InvalidXmlDocumentError',\n    'InvalidXmlProcessingInstructionError', 'LocatorError',\n    'LockFileExistsError', 'LockNotAvailableError',\n    'ModifyingExternalRoutineSQLDataNotPermittedError',\n    'ModifyingSQLDataNotPermittedError', 'MoreThanOneSQLJsonItemError',\n    'MostSpecificTypeMismatchError', 'NameTooLongError',\n    'NoActiveSQLTransactionError',\n    'NoActiveSQLTransactionForBranchTransactionError',\n    'NoAdditionalDynamicResultSetsReturned', 'NoData', 'NoDataFoundError',\n    'NoSQLJsonItemError', 'NonNumericSQLJsonItemError',\n    'NonUniqueKeysInAJsonObjectError',\n    'NonstandardUseOfEscapeCharacterError', 'NotAnXmlDocumentError',\n    'NotNullViolationError', 'NullValueEliminatedInSetFunction',\n    'NullValueInExternalRoutineNotAllowedError',\n    'NullValueNoIndicatorParameterError', 'NullValueNotAllowedError',\n    'NumericValueOutOfRangeError', 'ObjectInUseError',\n    'ObjectNotInPrerequisiteStateError', 'OperatorInterventionError',\n    'OutOfMemoryError', 'PLPGSQLError', 'PostgresConnectionError',\n    'PostgresFloatingPointError', 'PostgresIOError',\n    'PostgresSyntaxError', 'PostgresSystemError', 'PostgresWarning',\n    'PrivilegeNotGranted', 'PrivilegeNotRevoked',\n    'ProgramLimitExceededError',\n    'ProhibitedExternalRoutineSQLStatementAttemptedError',\n    'ProhibitedSQLStatementAttemptedError', 'ProtocolViolationError',\n    'QueryCanceledError', 'RaiseError', 'ReadOnlySQLTransactionError',\n    'ReadingExternalRoutineSQLDataNotPermittedError',\n    'ReadingSQLDataNotPermittedError', 'ReservedNameError',\n    'RestrictViolationError', 'SQLJsonArrayNotFoundError',\n    'SQLJsonItemCannotBeCastToTargetTypeError',\n    'SQLJsonMemberNotFoundError', 'SQLJsonNumberNotFoundError',\n    'SQLJsonObjectNotFoundError', 'SQLJsonScalarRequiredError',\n    'SQLRoutineError', 'SQLStatementNotYetCompleteError',\n    'SavepointError', 'SchemaAndDataStatementMixingNotSupportedError',\n    'SequenceGeneratorLimitExceededError', 'SerializationError',\n    'SingletonSQLJsonItemRequiredError', 'SnapshotTooOldError',\n    'SrfProtocolViolatedError',\n    'StackedDiagnosticsAccessedWithoutActiveHandlerError',\n    'StatementCompletionUnknownError', 'StatementTooComplexError',\n    'StringDataLengthMismatchError', 'StringDataRightTruncation',\n    'StringDataRightTruncationError', 'SubstringError',\n    'SyntaxOrAccessError', 'TooManyArgumentsError', 'TooManyColumnsError',\n    'TooManyConnectionsError', 'TooManyJsonArrayElementsError',\n    'TooManyJsonObjectMembersError', 'TooManyRowsError',\n    'TransactionIntegrityConstraintViolationError',\n    'TransactionResolutionUnknownError', 'TransactionRollbackError',\n    'TransactionTimeoutError', 'TriggerProtocolViolatedError',\n    'TriggeredActionError', 'TriggeredDataChangeViolationError',\n    'TrimError', 'UndefinedColumnError', 'UndefinedFileError',\n    'UndefinedFunctionError', 'UndefinedObjectError',\n    'UndefinedParameterError', 'UndefinedTableError',\n    'UniqueViolationError', 'UnsafeNewEnumValueUsageError',\n    'UnterminatedCStringError', 'UntranslatableCharacterError',\n    'WindowingError', 'WithCheckOptionViolationError',\n    'WrongObjectTypeError', 'ZeroLengthCharacterStringError'\n)\n\n__all__ += _base.__all__\n"
  },
  {
    "path": "asyncpg/exceptions/_base.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncpg\nimport sys\nimport textwrap\n\n\n__all__ = ('PostgresError', 'FatalPostgresError', 'UnknownPostgresError',\n           'InterfaceError', 'InterfaceWarning', 'PostgresLogMessage',\n           'ClientConfigurationError',\n           'InternalClientError', 'OutdatedSchemaCacheError', 'ProtocolError',\n           'UnsupportedClientFeatureError', 'TargetServerAttributeNotMatched',\n           'UnsupportedServerFeatureError')\n\n\ndef _is_asyncpg_class(cls):\n    modname = cls.__module__\n    return modname == 'asyncpg' or modname.startswith('asyncpg.')\n\n\nclass PostgresMessageMeta(type):\n\n    _message_map = {}\n    _field_map = {\n        'S': 'severity',\n        'V': 'severity_en',\n        'C': 'sqlstate',\n        'M': 'message',\n        'D': 'detail',\n        'H': 'hint',\n        'P': 'position',\n        'p': 'internal_position',\n        'q': 'internal_query',\n        'W': 'context',\n        's': 'schema_name',\n        't': 'table_name',\n        'c': 'column_name',\n        'd': 'data_type_name',\n        'n': 'constraint_name',\n        'F': 'server_source_filename',\n        'L': 'server_source_line',\n        'R': 'server_source_function'\n    }\n\n    def __new__(mcls, name, bases, dct):\n        cls = super().__new__(mcls, name, bases, dct)\n        if cls.__module__ == mcls.__module__ and name == 'PostgresMessage':\n            for f in mcls._field_map.values():\n                setattr(cls, f, None)\n\n        if _is_asyncpg_class(cls):\n            mod = sys.modules[cls.__module__]\n            if hasattr(mod, name):\n                raise RuntimeError('exception class redefinition: {}'.format(\n                    name))\n\n        code = dct.get('sqlstate')\n        if code is not None:\n            existing = mcls._message_map.get(code)\n            if existing is not None:\n                raise TypeError('{} has duplicate SQLSTATE code, which is'\n                                'already defined by {}'.format(\n                                    name, existing.__name__))\n            mcls._message_map[code] = cls\n\n        return cls\n\n    @classmethod\n    def get_message_class_for_sqlstate(mcls, code):\n        return mcls._message_map.get(code, UnknownPostgresError)\n\n\nclass PostgresMessage(metaclass=PostgresMessageMeta):\n\n    @classmethod\n    def _get_error_class(cls, fields):\n        sqlstate = fields.get('C')\n        return type(cls).get_message_class_for_sqlstate(sqlstate)\n\n    @classmethod\n    def _get_error_dict(cls, fields, query):\n        dct = {\n            'query': query\n        }\n\n        field_map = type(cls)._field_map\n        for k, v in fields.items():\n            field = field_map.get(k)\n            if field:\n                dct[field] = v\n\n        return dct\n\n    @classmethod\n    def _make_constructor(cls, fields, query=None):\n        dct = cls._get_error_dict(fields, query)\n\n        exccls = cls._get_error_class(fields)\n        message = dct.get('message', '')\n\n        # PostgreSQL will raise an exception when it detects\n        # that the result type of the query has changed from\n        # when the statement was prepared.\n        #\n        # The original error is somewhat cryptic and unspecific,\n        # so we raise a custom subclass that is easier to handle\n        # and identify.\n        #\n        # Note that we specifically do not rely on the error\n        # message, as it is localizable.\n        is_icse = (\n            exccls.__name__ == 'FeatureNotSupportedError' and\n            _is_asyncpg_class(exccls) and\n            dct.get('server_source_function') == 'RevalidateCachedQuery'\n        )\n\n        if is_icse:\n            exceptions = sys.modules[exccls.__module__]\n            exccls = exceptions.InvalidCachedStatementError\n            message = ('cached statement plan is invalid due to a database '\n                       'schema or configuration change')\n\n        is_prepared_stmt_error = (\n            exccls.__name__ in ('DuplicatePreparedStatementError',\n                                'InvalidSQLStatementNameError') and\n            _is_asyncpg_class(exccls)\n        )\n\n        if is_prepared_stmt_error:\n            hint = dct.get('hint', '')\n            hint += textwrap.dedent(\"\"\"\\\n\n                NOTE: pgbouncer with pool_mode set to \"transaction\" or\n                \"statement\" does not support prepared statements properly.\n                You have two options:\n\n                * if you are using pgbouncer for connection pooling to a\n                  single server, switch to the connection pool functionality\n                  provided by asyncpg, it is a much better option for this\n                  purpose;\n\n                * if you have no option of avoiding the use of pgbouncer,\n                  then you can set statement_cache_size to 0 when creating\n                  the asyncpg connection object.\n            \"\"\")\n\n            dct['hint'] = hint\n\n        return exccls, message, dct\n\n    def as_dict(self):\n        dct = {}\n        for f in type(self)._field_map.values():\n            val = getattr(self, f)\n            if val is not None:\n                dct[f] = val\n        return dct\n\n\nclass PostgresError(PostgresMessage, Exception):\n    \"\"\"Base class for all Postgres errors.\"\"\"\n\n    def __str__(self):\n        msg = self.args[0]\n        if self.detail:\n            msg += '\\nDETAIL:  {}'.format(self.detail)\n        if self.hint:\n            msg += '\\nHINT:  {}'.format(self.hint)\n\n        return msg\n\n    @classmethod\n    def new(cls, fields, query=None):\n        exccls, message, dct = cls._make_constructor(fields, query)\n        ex = exccls(message)\n        ex.__dict__.update(dct)\n        return ex\n\n\nclass FatalPostgresError(PostgresError):\n    \"\"\"A fatal error that should result in server disconnection.\"\"\"\n\n\nclass UnknownPostgresError(FatalPostgresError):\n    \"\"\"An error with an unknown SQLSTATE code.\"\"\"\n\n\nclass InterfaceMessage:\n    def __init__(self, *, detail=None, hint=None):\n        self.detail = detail\n        self.hint = hint\n\n    def __str__(self):\n        msg = self.args[0]\n        if self.detail:\n            msg += '\\nDETAIL:  {}'.format(self.detail)\n        if self.hint:\n            msg += '\\nHINT:  {}'.format(self.hint)\n\n        return msg\n\n\nclass InterfaceError(InterfaceMessage, Exception):\n    \"\"\"An error caused by improper use of asyncpg API.\"\"\"\n\n    def __init__(self, msg, *, detail=None, hint=None):\n        InterfaceMessage.__init__(self, detail=detail, hint=hint)\n        Exception.__init__(self, msg)\n\n    def with_msg(self, msg):\n        return type(self)(\n            msg,\n            detail=self.detail,\n            hint=self.hint,\n        ).with_traceback(\n            self.__traceback__\n        )\n\n\nclass ClientConfigurationError(InterfaceError, ValueError):\n    \"\"\"An error caused by improper client configuration.\"\"\"\n\n\nclass DataError(InterfaceError, ValueError):\n    \"\"\"An error caused by invalid query input.\"\"\"\n\n\nclass UnsupportedClientFeatureError(InterfaceError):\n    \"\"\"Requested feature is unsupported by asyncpg.\"\"\"\n\n\nclass UnsupportedServerFeatureError(InterfaceError):\n    \"\"\"Requested feature is unsupported by PostgreSQL server.\"\"\"\n\n\nclass InterfaceWarning(InterfaceMessage, UserWarning):\n    \"\"\"A warning caused by an improper use of asyncpg API.\"\"\"\n\n    def __init__(self, msg, *, detail=None, hint=None):\n        InterfaceMessage.__init__(self, detail=detail, hint=hint)\n        UserWarning.__init__(self, msg)\n\n\nclass InternalClientError(Exception):\n    \"\"\"All unexpected errors not classified otherwise.\"\"\"\n\n\nclass ProtocolError(InternalClientError):\n    \"\"\"Unexpected condition in the handling of PostgreSQL protocol input.\"\"\"\n\n\nclass TargetServerAttributeNotMatched(InternalClientError):\n    \"\"\"Could not find a host that satisfies the target attribute requirement\"\"\"\n\n\nclass OutdatedSchemaCacheError(InternalClientError):\n    \"\"\"A value decoding error caused by a schema change before row fetching.\"\"\"\n\n    def __init__(self, msg, *, schema=None, data_type=None, position=None):\n        super().__init__(msg)\n        self.schema_name = schema\n        self.data_type_name = data_type\n        self.position = position\n\n\nclass PostgresLogMessage(PostgresMessage):\n    \"\"\"A base class for non-error server messages.\"\"\"\n\n    def __str__(self):\n        return '{}: {}'.format(type(self).__name__, self.message)\n\n    def __setattr__(self, name, val):\n        raise TypeError('instances of {} are immutable'.format(\n            type(self).__name__))\n\n    @classmethod\n    def new(cls, fields, query=None):\n        exccls, message_text, dct = cls._make_constructor(fields, query)\n\n        if exccls is UnknownPostgresError:\n            exccls = PostgresLogMessage\n\n        if exccls is PostgresLogMessage:\n            severity = dct.get('severity_en') or dct.get('severity')\n            if severity and severity.upper() == 'WARNING':\n                exccls = asyncpg.PostgresWarning\n\n        if issubclass(exccls, (BaseException, Warning)):\n            msg = exccls(message_text)\n        else:\n            msg = exccls()\n\n        msg.__dict__.update(dct)\n        return msg\n"
  },
  {
    "path": "asyncpg/introspection.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nimport typing\nfrom .protocol.protocol import _create_record  # type: ignore\n\nif typing.TYPE_CHECKING:\n    from . import protocol\n\n\n_TYPEINFO_13: typing.Final = '''\\\n    (\n        SELECT\n            t.oid                           AS oid,\n            ns.nspname                      AS ns,\n            t.typname                       AS name,\n            t.typtype                       AS kind,\n            (CASE WHEN t.typtype = 'd' THEN\n                (WITH RECURSIVE typebases(oid, depth) AS (\n                    SELECT\n                        t2.typbasetype      AS oid,\n                        0                   AS depth\n                    FROM\n                        pg_type t2\n                    WHERE\n                        t2.oid = t.oid\n\n                    UNION ALL\n\n                    SELECT\n                        t2.typbasetype      AS oid,\n                        tb.depth + 1        AS depth\n                    FROM\n                        pg_type t2,\n                        typebases tb\n                    WHERE\n                       tb.oid = t2.oid\n                       AND t2.typbasetype != 0\n               ) SELECT oid FROM typebases ORDER BY depth DESC LIMIT 1)\n\n               ELSE NULL\n            END)                            AS basetype,\n            t.typelem                       AS elemtype,\n            elem_t.typdelim                 AS elemdelim,\n            range_t.rngsubtype              AS range_subtype,\n            (CASE WHEN t.typtype = 'c' THEN\n                (SELECT\n                    array_agg(ia.atttypid ORDER BY ia.attnum)\n                FROM\n                    pg_attribute ia\n                    INNER JOIN pg_class c\n                        ON (ia.attrelid = c.oid)\n                WHERE\n                    ia.attnum > 0 AND NOT ia.attisdropped\n                    AND c.reltype = t.oid)\n\n                ELSE NULL\n            END)                            AS attrtypoids,\n            (CASE WHEN t.typtype = 'c' THEN\n                (SELECT\n                    array_agg(ia.attname::text ORDER BY ia.attnum)\n                FROM\n                    pg_attribute ia\n                    INNER JOIN pg_class c\n                        ON (ia.attrelid = c.oid)\n                WHERE\n                    ia.attnum > 0 AND NOT ia.attisdropped\n                    AND c.reltype = t.oid)\n\n                ELSE NULL\n            END)                            AS attrnames\n        FROM\n            pg_catalog.pg_type AS t\n            INNER JOIN pg_catalog.pg_namespace ns ON (\n                ns.oid = t.typnamespace)\n            LEFT JOIN pg_type elem_t ON (\n                t.typlen = -1 AND\n                t.typelem != 0 AND\n                t.typelem = elem_t.oid\n            )\n            LEFT JOIN pg_range range_t ON (\n                t.oid = range_t.rngtypid\n            )\n    )\n'''\n\n\nINTRO_LOOKUP_TYPES_13 = '''\\\nWITH RECURSIVE typeinfo_tree(\n    oid, ns, name, kind, basetype, elemtype, elemdelim,\n    range_subtype, attrtypoids, attrnames, depth)\nAS (\n    SELECT\n        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,\n        ti.elemtype, ti.elemdelim, ti.range_subtype,\n        ti.attrtypoids, ti.attrnames, 0\n    FROM\n        {typeinfo} AS ti\n    WHERE\n        ti.oid = any($1::oid[])\n\n    UNION ALL\n\n    SELECT\n        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,\n        ti.elemtype, ti.elemdelim, ti.range_subtype,\n        ti.attrtypoids, ti.attrnames, tt.depth + 1\n    FROM\n        {typeinfo} ti,\n        typeinfo_tree tt\n    WHERE\n        (tt.elemtype IS NOT NULL AND ti.oid = tt.elemtype)\n        OR (tt.attrtypoids IS NOT NULL AND ti.oid = any(tt.attrtypoids))\n        OR (tt.range_subtype IS NOT NULL AND ti.oid = tt.range_subtype)\n        OR (tt.basetype IS NOT NULL AND ti.oid = tt.basetype)\n)\n\nSELECT DISTINCT\n    *,\n    basetype::regtype::text AS basetype_name,\n    elemtype::regtype::text AS elemtype_name,\n    range_subtype::regtype::text AS range_subtype_name\nFROM\n    typeinfo_tree\nORDER BY\n    depth DESC\n'''.format(typeinfo=_TYPEINFO_13)\n\n\n_TYPEINFO: typing.Final = '''\\\n    (\n        SELECT\n            t.oid                           AS oid,\n            ns.nspname                      AS ns,\n            t.typname                       AS name,\n            t.typtype                       AS kind,\n            (CASE WHEN t.typtype = 'd' THEN\n                (WITH RECURSIVE typebases(oid, depth) AS (\n                    SELECT\n                        t2.typbasetype      AS oid,\n                        0                   AS depth\n                    FROM\n                        pg_type t2\n                    WHERE\n                        t2.oid = t.oid\n\n                    UNION ALL\n\n                    SELECT\n                        t2.typbasetype      AS oid,\n                        tb.depth + 1        AS depth\n                    FROM\n                        pg_type t2,\n                        typebases tb\n                    WHERE\n                       tb.oid = t2.oid\n                       AND t2.typbasetype != 0\n               ) SELECT oid FROM typebases ORDER BY depth DESC LIMIT 1)\n\n               ELSE NULL\n            END)                            AS basetype,\n            t.typelem                       AS elemtype,\n            elem_t.typdelim                 AS elemdelim,\n            COALESCE(\n                range_t.rngsubtype,\n                multirange_t.rngsubtype)    AS range_subtype,\n            (CASE WHEN t.typtype = 'c' THEN\n                (SELECT\n                    array_agg(ia.atttypid ORDER BY ia.attnum)\n                FROM\n                    pg_attribute ia\n                    INNER JOIN pg_class c\n                        ON (ia.attrelid = c.oid)\n                WHERE\n                    ia.attnum > 0 AND NOT ia.attisdropped\n                    AND c.reltype = t.oid)\n\n                ELSE NULL\n            END)                            AS attrtypoids,\n            (CASE WHEN t.typtype = 'c' THEN\n                (SELECT\n                    array_agg(ia.attname::text ORDER BY ia.attnum)\n                FROM\n                    pg_attribute ia\n                    INNER JOIN pg_class c\n                        ON (ia.attrelid = c.oid)\n                WHERE\n                    ia.attnum > 0 AND NOT ia.attisdropped\n                    AND c.reltype = t.oid)\n\n                ELSE NULL\n            END)                            AS attrnames\n        FROM\n            pg_catalog.pg_type AS t\n            INNER JOIN pg_catalog.pg_namespace ns ON (\n                ns.oid = t.typnamespace)\n            LEFT JOIN pg_type elem_t ON (\n                t.typlen = -1 AND\n                t.typelem != 0 AND\n                t.typelem = elem_t.oid\n            )\n            LEFT JOIN pg_range range_t ON (\n                t.oid = range_t.rngtypid\n            )\n            LEFT JOIN pg_range multirange_t ON (\n                t.oid = multirange_t.rngmultitypid\n            )\n    )\n'''\n\n\nINTRO_LOOKUP_TYPES = '''\\\nWITH RECURSIVE typeinfo_tree(\n    oid, ns, name, kind, basetype, elemtype, elemdelim,\n    range_subtype, attrtypoids, attrnames, depth)\nAS (\n    SELECT\n        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,\n        ti.elemtype, ti.elemdelim, ti.range_subtype,\n        ti.attrtypoids, ti.attrnames, 0\n    FROM\n        {typeinfo} AS ti\n    WHERE\n        ti.oid = any($1::oid[])\n\n    UNION ALL\n\n    SELECT\n        ti.oid, ti.ns, ti.name, ti.kind, ti.basetype,\n        ti.elemtype, ti.elemdelim, ti.range_subtype,\n        ti.attrtypoids, ti.attrnames, tt.depth + 1\n    FROM\n        {typeinfo} ti,\n        typeinfo_tree tt\n    WHERE\n        (tt.elemtype IS NOT NULL AND ti.oid = tt.elemtype)\n        OR (tt.attrtypoids IS NOT NULL AND ti.oid = any(tt.attrtypoids))\n        OR (tt.range_subtype IS NOT NULL AND ti.oid = tt.range_subtype)\n        OR (tt.basetype IS NOT NULL AND ti.oid = tt.basetype)\n)\n\nSELECT DISTINCT\n    *,\n    basetype::regtype::text AS basetype_name,\n    elemtype::regtype::text AS elemtype_name,\n    range_subtype::regtype::text AS range_subtype_name\nFROM\n    typeinfo_tree\nORDER BY\n    depth DESC\n'''.format(typeinfo=_TYPEINFO)\n\n\nTYPE_BY_NAME: typing.Final = '''\\\nSELECT\n    t.oid,\n    t.typelem     AS elemtype,\n    t.typtype     AS kind\nFROM\n    pg_catalog.pg_type AS t\n    INNER JOIN pg_catalog.pg_namespace ns ON (ns.oid = t.typnamespace)\nWHERE\n    t.typname = $1 AND ns.nspname = $2\n'''\n\n\ndef TypeRecord(\n    rec: typing.Tuple[int, typing.Optional[int], bytes],\n) -> protocol.Record:\n    assert len(rec) == 3\n    return _create_record(  # type: ignore\n        {\"oid\": 0, \"elemtype\": 1, \"kind\": 2}, rec)\n\n\n# 'b' for a base type, 'd' for a domain, 'e' for enum.\nSCALAR_TYPE_KINDS = (b'b', b'd', b'e')\n\n\ndef is_scalar_type(typeinfo: protocol.Record) -> bool:\n    return (\n        typeinfo['kind'] in SCALAR_TYPE_KINDS and\n        not typeinfo['elemtype']\n    )\n\n\ndef is_domain_type(typeinfo: protocol.Record) -> bool:\n    return typeinfo['kind'] == b'd'  # type: ignore[no-any-return]\n\n\ndef is_composite_type(typeinfo: protocol.Record) -> bool:\n    return typeinfo['kind'] == b'c'  # type: ignore[no-any-return]\n"
  },
  {
    "path": "asyncpg/pool.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom collections.abc import Awaitable, Callable\nimport functools\nimport inspect\nimport logging\nimport time\nfrom types import TracebackType\nfrom typing import Any, Optional, Type\nimport warnings\n\nfrom . import compat\nfrom . import connection\nfrom . import exceptions\nfrom . import protocol\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass PoolConnectionProxyMeta(type):\n\n    def __new__(\n        mcls,\n        name: str,\n        bases: tuple[Type[Any], ...],\n        dct: dict[str, Any],\n        *,\n        wrap: bool = False,\n    ) -> PoolConnectionProxyMeta:\n        if wrap:\n            for attrname in dir(connection.Connection):\n                if attrname.startswith('_') or attrname in dct:\n                    continue\n\n                meth = getattr(connection.Connection, attrname)\n                if not inspect.isfunction(meth):\n                    continue\n\n                iscoroutine = inspect.iscoroutinefunction(meth)\n                wrapper = mcls._wrap_connection_method(attrname, iscoroutine)\n                wrapper = functools.update_wrapper(wrapper, meth)\n                dct[attrname] = wrapper\n\n            if '__doc__' not in dct:\n                dct['__doc__'] = connection.Connection.__doc__\n\n        return super().__new__(mcls, name, bases, dct)\n\n    @staticmethod\n    def _wrap_connection_method(\n        meth_name: str, iscoroutine: bool\n    ) -> Callable[..., Any]:\n        def call_con_method(self: Any, *args: Any, **kwargs: Any) -> Any:\n            # This method will be owned by PoolConnectionProxy class.\n            if self._con is None:\n                raise exceptions.InterfaceError(\n                    'cannot call Connection.{}(): '\n                    'connection has been released back to the pool'.format(\n                        meth_name))\n\n            meth = getattr(self._con.__class__, meth_name)\n            return meth(self._con, *args, **kwargs)\n\n        if iscoroutine:\n            compat.markcoroutinefunction(call_con_method)\n\n        return call_con_method\n\n\nclass PoolConnectionProxy(connection._ConnectionProxy,\n                          metaclass=PoolConnectionProxyMeta,\n                          wrap=True):\n\n    __slots__ = ('_con', '_holder')\n\n    def __init__(\n        self, holder: PoolConnectionHolder, con: connection.Connection\n    ) -> None:\n        self._con = con\n        self._holder = holder\n        con._set_proxy(self)\n\n    def __getattr__(self, attr: str) -> Any:\n        # Proxy all unresolved attributes to the wrapped Connection object.\n        return getattr(self._con, attr)\n\n    def _detach(self) -> Optional[connection.Connection]:\n        if self._con is None:\n            return\n\n        con, self._con = self._con, None\n        con._set_proxy(None)\n        return con\n\n    def __repr__(self) -> str:\n        if self._con is None:\n            return '<{classname} [released] {id:#x}>'.format(\n                classname=self.__class__.__name__, id=id(self))\n        else:\n            return '<{classname} {con!r} {id:#x}>'.format(\n                classname=self.__class__.__name__, con=self._con, id=id(self))\n\n\nclass PoolConnectionHolder:\n\n    __slots__ = ('_con', '_pool', '_loop', '_proxy',\n                 '_max_queries', '_setup',\n                 '_max_inactive_time', '_in_use',\n                 '_inactive_callback', '_timeout',\n                 '_generation')\n\n    def __init__(\n        self,\n        pool: \"Pool\",\n        *,\n        max_queries: float,\n        setup: Optional[Callable[[PoolConnectionProxy], Awaitable[None]]],\n        max_inactive_time: float,\n    ) -> None:\n\n        self._pool = pool\n        self._con: Optional[connection.Connection] = None\n        self._proxy: Optional[PoolConnectionProxy] = None\n\n        self._max_queries = max_queries\n        self._max_inactive_time = max_inactive_time\n        self._setup = setup\n        self._inactive_callback: Optional[Callable] = None\n        self._in_use: Optional[asyncio.Future] = None\n        self._timeout: Optional[float] = None\n        self._generation: Optional[int] = None\n\n    def is_connected(self) -> bool:\n        return self._con is not None and not self._con.is_closed()\n\n    def is_idle(self) -> bool:\n        return not self._in_use\n\n    async def connect(self) -> None:\n        if self._con is not None:\n            raise exceptions.InternalClientError(\n                'PoolConnectionHolder.connect() called while another '\n                'connection already exists')\n\n        self._con = await self._pool._get_new_connection()\n        self._generation = self._pool._generation\n        self._maybe_cancel_inactive_callback()\n        self._setup_inactive_callback()\n\n    async def acquire(self) -> PoolConnectionProxy:\n        if self._con is None or self._con.is_closed():\n            self._con = None\n            await self.connect()\n\n        elif self._generation != self._pool._generation:\n            # Connections have been expired, re-connect the holder.\n            self._pool._loop.create_task(\n                self._con.close(timeout=self._timeout))\n            self._con = None\n            await self.connect()\n\n        self._maybe_cancel_inactive_callback()\n\n        self._proxy = proxy = PoolConnectionProxy(self, self._con)\n\n        if self._setup is not None:\n            try:\n                await self._setup(proxy)\n            except (Exception, asyncio.CancelledError) as ex:\n                # If a user-defined `setup` function fails, we don't\n                # know if the connection is safe for re-use, hence\n                # we close it.  A new connection will be created\n                # when `acquire` is called again.\n                try:\n                    # Use `close()` to close the connection gracefully.\n                    # An exception in `setup` isn't necessarily caused\n                    # by an IO or a protocol error.  close() will\n                    # do the necessary cleanup via _release_on_close().\n                    await self._con.close()\n                finally:\n                    raise ex\n\n        self._in_use = self._pool._loop.create_future()\n\n        return proxy\n\n    async def release(self, timeout: Optional[float]) -> None:\n        if self._in_use is None:\n            raise exceptions.InternalClientError(\n                'PoolConnectionHolder.release() called on '\n                'a free connection holder')\n\n        if self._con.is_closed():\n            # When closing, pool connections perform the necessary\n            # cleanup, so we don't have to do anything else here.\n            return\n\n        self._timeout = None\n\n        if self._con._protocol.queries_count >= self._max_queries:\n            # The connection has reached its maximum utilization limit,\n            # so close it.  Connection.close() will call _release().\n            await self._con.close(timeout=timeout)\n            return\n\n        if self._generation != self._pool._generation:\n            # The connection has expired because it belongs to\n            # an older generation (Pool.expire_connections() has\n            # been called.)\n            await self._con.close(timeout=timeout)\n            return\n\n        try:\n            budget = timeout\n\n            if self._con._protocol._is_cancelling():\n                # If the connection is in cancellation state,\n                # wait for the cancellation\n                started = time.monotonic()\n                await compat.wait_for(\n                    self._con._protocol._wait_for_cancellation(),\n                    budget)\n                if budget is not None:\n                    budget -= time.monotonic() - started\n\n            if self._pool._reset is not None:\n                async with compat.timeout(budget):\n                    await self._con._reset()\n                    await self._pool._reset(self._con)\n            else:\n                await self._con.reset(timeout=budget)\n        except (Exception, asyncio.CancelledError) as ex:\n            # If the `reset` call failed, terminate the connection.\n            # A new one will be created when `acquire` is called\n            # again.\n            try:\n                # An exception in `reset` is most likely caused by\n                # an IO error, so terminate the connection.\n                self._con.terminate()\n            finally:\n                raise ex\n\n        # Free this connection holder and invalidate the\n        # connection proxy.\n        self._release()\n\n        # Rearm the connection inactivity timer.\n        self._setup_inactive_callback()\n\n    async def wait_until_released(self) -> None:\n        if self._in_use is None:\n            return\n        else:\n            await self._in_use\n\n    async def close(self) -> None:\n        if self._con is not None:\n            # Connection.close() will call _release_on_close() to\n            # finish holder cleanup.\n            await self._con.close()\n\n    def terminate(self) -> None:\n        if self._con is not None:\n            # Connection.terminate() will call _release_on_close() to\n            # finish holder cleanup.\n            self._con.terminate()\n\n    def _setup_inactive_callback(self) -> None:\n        if self._inactive_callback is not None:\n            raise exceptions.InternalClientError(\n                'pool connection inactivity timer already exists')\n\n        if self._max_inactive_time:\n            self._inactive_callback = self._pool._loop.call_later(\n                self._max_inactive_time, self._deactivate_inactive_connection)\n\n    def _maybe_cancel_inactive_callback(self) -> None:\n        if self._inactive_callback is not None:\n            self._inactive_callback.cancel()\n            self._inactive_callback = None\n\n    def _deactivate_inactive_connection(self) -> None:\n        if self._in_use is not None:\n            raise exceptions.InternalClientError(\n                'attempting to deactivate an acquired connection')\n\n        if self._con is not None:\n            # The connection is idle and not in use, so it's fine to\n            # use terminate() instead of close().\n            self._con.terminate()\n            # Must call clear_connection, because _deactivate_connection\n            # is called when the connection is *not* checked out, and\n            # so terminate() above will not call the below.\n            self._release_on_close()\n\n    def _release_on_close(self) -> None:\n        self._maybe_cancel_inactive_callback()\n        self._release()\n        self._con = None\n\n    def _release(self) -> None:\n        \"\"\"Release this connection holder.\"\"\"\n        if self._in_use is None:\n            # The holder is not checked out.\n            return\n\n        if not self._in_use.done():\n            self._in_use.set_result(None)\n        self._in_use = None\n\n        # Deinitialize the connection proxy.  All subsequent\n        # operations on it will fail.\n        if self._proxy is not None:\n            self._proxy._detach()\n            self._proxy = None\n\n        # Put ourselves back to the pool queue.\n        self._pool._queue.put_nowait(self)\n\n\nclass Pool:\n    \"\"\"A connection pool.\n\n    Connection pool can be used to manage a set of connections to the database.\n    Connections are first acquired from the pool, then used, and then released\n    back to the pool.  Once a connection is released, it's reset to close all\n    open cursors and other resources *except* prepared statements.\n\n    Pools are created by calling :func:`~asyncpg.pool.create_pool`.\n    \"\"\"\n\n    __slots__ = (\n        '_queue', '_loop', '_minsize', '_maxsize',\n        '_init', '_connect', '_reset', '_connect_args', '_connect_kwargs',\n        '_holders', '_initialized', '_initializing', '_closing',\n        '_closed', '_connection_class', '_record_class', '_generation',\n        '_setup', '_max_queries', '_max_inactive_connection_lifetime'\n    )\n\n    def __init__(self, *connect_args,\n                 min_size,\n                 max_size,\n                 max_queries,\n                 max_inactive_connection_lifetime,\n                 connect=None,\n                 setup=None,\n                 init=None,\n                 reset=None,\n                 loop,\n                 connection_class,\n                 record_class,\n                 **connect_kwargs):\n\n        if len(connect_args) > 1:\n            warnings.warn(\n                \"Passing multiple positional arguments to asyncpg.Pool \"\n                \"constructor is deprecated and will be removed in \"\n                \"asyncpg 0.17.0.  The non-deprecated form is \"\n                \"asyncpg.Pool(<dsn>, **kwargs)\",\n                DeprecationWarning, stacklevel=2)\n\n        if loop is None:\n            loop = asyncio.get_event_loop()\n        self._loop = loop\n\n        if max_size <= 0:\n            raise ValueError('max_size is expected to be greater than zero')\n\n        if min_size < 0:\n            raise ValueError(\n                'min_size is expected to be greater or equal to zero')\n\n        if min_size > max_size:\n            raise ValueError('min_size is greater than max_size')\n\n        if max_queries <= 0:\n            raise ValueError('max_queries is expected to be greater than zero')\n\n        if max_inactive_connection_lifetime < 0:\n            raise ValueError(\n                'max_inactive_connection_lifetime is expected to be greater '\n                'or equal to zero')\n\n        if not issubclass(connection_class, connection.Connection):\n            raise TypeError(\n                'connection_class is expected to be a subclass of '\n                'asyncpg.Connection, got {!r}'.format(connection_class))\n\n        if not issubclass(record_class, protocol.Record):\n            raise TypeError(\n                'record_class is expected to be a subclass of '\n                'asyncpg.Record, got {!r}'.format(record_class))\n\n        self._minsize = min_size\n        self._maxsize = max_size\n\n        self._holders = []\n        self._initialized = False\n        self._initializing = False\n        self._queue = None\n\n        self._connection_class = connection_class\n        self._record_class = record_class\n\n        self._closing = False\n        self._closed = False\n        self._generation = 0\n\n        self._connect = connect if connect is not None else connection.connect\n        self._connect_args = connect_args\n        self._connect_kwargs = connect_kwargs\n\n        self._setup = setup\n        self._init = init\n        self._reset = reset\n\n        self._max_queries = max_queries\n        self._max_inactive_connection_lifetime = \\\n            max_inactive_connection_lifetime\n\n    async def _async__init__(self):\n        if self._initialized:\n            return self\n        if self._initializing:\n            raise exceptions.InterfaceError(\n                'pool is being initialized in another task')\n        if self._closed:\n            raise exceptions.InterfaceError('pool is closed')\n        self._initializing = True\n        try:\n            await self._initialize()\n            return self\n        finally:\n            self._initializing = False\n            self._initialized = True\n\n    async def _initialize(self):\n        self._queue = asyncio.LifoQueue(maxsize=self._maxsize)\n        for _ in range(self._maxsize):\n            ch = PoolConnectionHolder(\n                self,\n                max_queries=self._max_queries,\n                max_inactive_time=self._max_inactive_connection_lifetime,\n                setup=self._setup)\n\n            self._holders.append(ch)\n            self._queue.put_nowait(ch)\n\n        if self._minsize:\n            # Since we use a LIFO queue, the first items in the queue will be\n            # the last ones in `self._holders`.  We want to pre-connect the\n            # first few connections in the queue, therefore we want to walk\n            # `self._holders` in reverse.\n\n            # Connect the first connection holder in the queue so that\n            # any connection issues are visible early.\n            first_ch = self._holders[-1]  # type: PoolConnectionHolder\n            await first_ch.connect()\n\n            if self._minsize > 1:\n                connect_tasks = []\n                for i, ch in enumerate(reversed(self._holders[:-1])):\n                    # `minsize - 1` because we already have first_ch\n                    if i >= self._minsize - 1:\n                        break\n                    connect_tasks.append(ch.connect())\n\n                await asyncio.gather(*connect_tasks)\n\n    def is_closing(self):\n        \"\"\"Return ``True`` if the pool is closing or is closed.\n\n        .. versionadded:: 0.28.0\n        \"\"\"\n        return self._closed or self._closing\n\n    def get_size(self):\n        \"\"\"Return the current number of connections in this pool.\n\n        .. versionadded:: 0.25.0\n        \"\"\"\n        return sum(h.is_connected() for h in self._holders)\n\n    def get_min_size(self):\n        \"\"\"Return the minimum number of connections in this pool.\n\n        .. versionadded:: 0.25.0\n        \"\"\"\n        return self._minsize\n\n    def get_max_size(self):\n        \"\"\"Return the maximum allowed number of connections in this pool.\n\n        .. versionadded:: 0.25.0\n        \"\"\"\n        return self._maxsize\n\n    def get_idle_size(self):\n        \"\"\"Return the current number of idle connections in this pool.\n\n        .. versionadded:: 0.25.0\n        \"\"\"\n        return sum(h.is_connected() and h.is_idle() for h in self._holders)\n\n    def set_connect_args(self, dsn=None, **connect_kwargs):\n        r\"\"\"Set the new connection arguments for this pool.\n\n        The new connection arguments will be used for all subsequent\n        new connection attempts.  Existing connections will remain until\n        they expire. Use :meth:`Pool.expire_connections()\n        <asyncpg.pool.Pool.expire_connections>` to expedite the connection\n        expiry.\n\n        :param str dsn:\n            Connection arguments specified using as a single string in\n            the following format:\n            ``postgres://user:pass@host:port/database?option=value``.\n\n        :param \\*\\*connect_kwargs:\n            Keyword arguments for the :func:`~asyncpg.connection.connect`\n            function.\n\n        .. versionadded:: 0.16.0\n        \"\"\"\n\n        self._connect_args = [dsn]\n        self._connect_kwargs = connect_kwargs\n\n    async def _get_new_connection(self):\n        con = await self._connect(\n            *self._connect_args,\n            loop=self._loop,\n            connection_class=self._connection_class,\n            record_class=self._record_class,\n            **self._connect_kwargs,\n        )\n        if not isinstance(con, self._connection_class):\n            good = self._connection_class\n            good_n = f'{good.__module__}.{good.__name__}'\n            bad = type(con)\n            if bad.__module__ == \"builtins\":\n                bad_n = bad.__name__\n            else:\n                bad_n = f'{bad.__module__}.{bad.__name__}'\n            raise exceptions.InterfaceError(\n                \"expected pool connect callback to return an instance of \"\n                f\"'{good_n}', got \" f\"'{bad_n}'\"\n            )\n\n        if self._init is not None:\n            try:\n                await self._init(con)\n            except (Exception, asyncio.CancelledError) as ex:\n                # If a user-defined `init` function fails, we don't\n                # know if the connection is safe for re-use, hence\n                # we close it.  A new connection will be created\n                # when `acquire` is called again.\n                try:\n                    # Use `close()` to close the connection gracefully.\n                    # An exception in `init` isn't necessarily caused\n                    # by an IO or a protocol error.  close() will\n                    # do the necessary cleanup via _release_on_close().\n                    await con.close()\n                finally:\n                    raise ex\n\n        return con\n\n    async def execute(\n        self,\n        query: str,\n        *args,\n        timeout: Optional[float]=None,\n    ) -> str:\n        \"\"\"Execute an SQL command (or commands).\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.execute() <asyncpg.connection.Connection.execute>`.\n\n        .. versionadded:: 0.10.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.execute(query, *args, timeout=timeout)\n\n    async def executemany(\n        self,\n        command: str,\n        args,\n        *,\n        timeout: Optional[float]=None,\n    ):\n        \"\"\"Execute an SQL *command* for each sequence of arguments in *args*.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.executemany()\n        <asyncpg.connection.Connection.executemany>`.\n\n        .. versionadded:: 0.10.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.executemany(command, args, timeout=timeout)\n\n    async def fetch(\n        self,\n        query,\n        *args,\n        timeout=None,\n        record_class=None\n    ) -> list:\n        \"\"\"Run a query and return the results as a list of :class:`Record`.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.fetch() <asyncpg.connection.Connection.fetch>`.\n\n        .. versionadded:: 0.10.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.fetch(\n                query,\n                *args,\n                timeout=timeout,\n                record_class=record_class\n            )\n\n    async def fetchval(self, query, *args, column=0, timeout=None):\n        \"\"\"Run a query and return a value in the first row.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.fetchval()\n        <asyncpg.connection.Connection.fetchval>`.\n\n        .. versionadded:: 0.10.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.fetchval(\n                query, *args, column=column, timeout=timeout)\n\n    async def fetchrow(self, query, *args, timeout=None, record_class=None):\n        \"\"\"Run a query and return the first row.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.fetchrow() <asyncpg.connection.Connection.fetchrow>`.\n\n        .. versionadded:: 0.10.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.fetchrow(\n                query,\n                *args,\n                timeout=timeout,\n                record_class=record_class\n            )\n\n    async def fetchmany(self, query, args, *, timeout=None, record_class=None):\n        \"\"\"Run a query for each sequence of arguments in *args*\n        and return the results as a list of :class:`Record`.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.fetchmany()\n        <asyncpg.connection.Connection.fetchmany>`.\n\n        .. versionadded:: 0.30.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.fetchmany(\n                query, args, timeout=timeout, record_class=record_class\n            )\n\n    async def copy_from_table(\n        self,\n        table_name,\n        *,\n        output,\n        columns=None,\n        schema_name=None,\n        timeout=None,\n        format=None,\n        oids=None,\n        delimiter=None,\n        null=None,\n        header=None,\n        quote=None,\n        escape=None,\n        force_quote=None,\n        encoding=None\n    ):\n        \"\"\"Copy table contents to a file or file-like object.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.copy_from_table()\n        <asyncpg.connection.Connection.copy_from_table>`.\n\n        .. versionadded:: 0.24.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.copy_from_table(\n                table_name,\n                output=output,\n                columns=columns,\n                schema_name=schema_name,\n                timeout=timeout,\n                format=format,\n                oids=oids,\n                delimiter=delimiter,\n                null=null,\n                header=header,\n                quote=quote,\n                escape=escape,\n                force_quote=force_quote,\n                encoding=encoding\n            )\n\n    async def copy_from_query(\n        self,\n        query,\n        *args,\n        output,\n        timeout=None,\n        format=None,\n        oids=None,\n        delimiter=None,\n        null=None,\n        header=None,\n        quote=None,\n        escape=None,\n        force_quote=None,\n        encoding=None\n    ):\n        \"\"\"Copy the results of a query to a file or file-like object.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.copy_from_query()\n        <asyncpg.connection.Connection.copy_from_query>`.\n\n        .. versionadded:: 0.24.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.copy_from_query(\n                query,\n                *args,\n                output=output,\n                timeout=timeout,\n                format=format,\n                oids=oids,\n                delimiter=delimiter,\n                null=null,\n                header=header,\n                quote=quote,\n                escape=escape,\n                force_quote=force_quote,\n                encoding=encoding\n            )\n\n    async def copy_to_table(\n        self,\n        table_name,\n        *,\n        source,\n        columns=None,\n        schema_name=None,\n        timeout=None,\n        format=None,\n        oids=None,\n        freeze=None,\n        delimiter=None,\n        null=None,\n        header=None,\n        quote=None,\n        escape=None,\n        force_quote=None,\n        force_not_null=None,\n        force_null=None,\n        encoding=None,\n        where=None\n    ):\n        \"\"\"Copy data to the specified table.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.copy_to_table()\n        <asyncpg.connection.Connection.copy_to_table>`.\n\n        .. versionadded:: 0.24.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.copy_to_table(\n                table_name,\n                source=source,\n                columns=columns,\n                schema_name=schema_name,\n                timeout=timeout,\n                format=format,\n                oids=oids,\n                freeze=freeze,\n                delimiter=delimiter,\n                null=null,\n                header=header,\n                quote=quote,\n                escape=escape,\n                force_quote=force_quote,\n                force_not_null=force_not_null,\n                force_null=force_null,\n                encoding=encoding,\n                where=where\n            )\n\n    async def copy_records_to_table(\n        self,\n        table_name,\n        *,\n        records,\n        columns=None,\n        schema_name=None,\n        timeout=None,\n        where=None\n    ):\n        \"\"\"Copy a list of records to the specified table using binary COPY.\n\n        Pool performs this operation using one of its connections.  Other than\n        that, it behaves identically to\n        :meth:`Connection.copy_records_to_table()\n        <asyncpg.connection.Connection.copy_records_to_table>`.\n\n        .. versionadded:: 0.24.0\n        \"\"\"\n        async with self.acquire() as con:\n            return await con.copy_records_to_table(\n                table_name,\n                records=records,\n                columns=columns,\n                schema_name=schema_name,\n                timeout=timeout,\n                where=where\n            )\n\n    def acquire(self, *, timeout=None):\n        \"\"\"Acquire a database connection from the pool.\n\n        :param float timeout: A timeout for acquiring a Connection.\n        :return: An instance of :class:`~asyncpg.connection.Connection`.\n\n        Can be used in an ``await`` expression or with an ``async with`` block.\n\n        .. code-block:: python\n\n            async with pool.acquire() as con:\n                await con.execute(...)\n\n        Or:\n\n        .. code-block:: python\n\n            con = await pool.acquire()\n            try:\n                await con.execute(...)\n            finally:\n                await pool.release(con)\n        \"\"\"\n        return PoolAcquireContext(self, timeout)\n\n    async def _acquire(self, timeout):\n        async def _acquire_impl():\n            ch = await self._queue.get()  # type: PoolConnectionHolder\n            try:\n                proxy = await ch.acquire()  # type: PoolConnectionProxy\n            except (Exception, asyncio.CancelledError):\n                self._queue.put_nowait(ch)\n                raise\n            else:\n                # Record the timeout, as we will apply it by default\n                # in release().\n                ch._timeout = timeout\n                return proxy\n\n        if self._closing:\n            raise exceptions.InterfaceError('pool is closing')\n        self._check_init()\n\n        if timeout is None:\n            return await _acquire_impl()\n        else:\n            return await compat.wait_for(\n                _acquire_impl(), timeout=timeout)\n\n    async def release(self, connection, *, timeout=None):\n        \"\"\"Release a database connection back to the pool.\n\n        :param Connection connection:\n            A :class:`~asyncpg.connection.Connection` object to release.\n        :param float timeout:\n            A timeout for releasing the connection.  If not specified, defaults\n            to the timeout provided in the corresponding call to the\n            :meth:`Pool.acquire() <asyncpg.pool.Pool.acquire>` method.\n\n        .. versionchanged:: 0.14.0\n            Added the *timeout* parameter.\n        \"\"\"\n        if (type(connection) is not PoolConnectionProxy or\n                connection._holder._pool is not self):\n            raise exceptions.InterfaceError(\n                'Pool.release() received invalid connection: '\n                '{connection!r} is not a member of this pool'.format(\n                    connection=connection))\n\n        if connection._con is None:\n            # Already released, do nothing.\n            return\n\n        self._check_init()\n\n        # Let the connection do its internal housekeeping when its released.\n        connection._con._on_release()\n\n        ch = connection._holder\n        if timeout is None:\n            timeout = ch._timeout\n\n        # Use asyncio.shield() to guarantee that task cancellation\n        # does not prevent the connection from being returned to the\n        # pool properly.\n        return await asyncio.shield(ch.release(timeout))\n\n    async def close(self):\n        \"\"\"Attempt to gracefully close all connections in the pool.\n\n        Wait until all pool connections are released, close them and\n        shut down the pool.  If any error (including cancellation) occurs\n        in ``close()`` the pool will terminate by calling\n        :meth:`Pool.terminate() <pool.Pool.terminate>`.\n\n        It is advisable to use :func:`python:asyncio.wait_for` to set\n        a timeout.\n\n        .. versionchanged:: 0.16.0\n            ``close()`` now waits until all pool connections are released\n            before closing them and the pool.  Errors raised in ``close()``\n            will cause immediate pool termination.\n        \"\"\"\n        if self._closed:\n            return\n        self._check_init()\n\n        self._closing = True\n\n        warning_callback = None\n        try:\n            warning_callback = self._loop.call_later(\n                60, self._warn_on_long_close)\n\n            release_coros = [\n                ch.wait_until_released() for ch in self._holders]\n            await asyncio.gather(*release_coros)\n\n            close_coros = [\n                ch.close() for ch in self._holders]\n            await asyncio.gather(*close_coros)\n\n        except (Exception, asyncio.CancelledError):\n            self.terminate()\n            raise\n\n        finally:\n            if warning_callback is not None:\n                warning_callback.cancel()\n            self._closed = True\n            self._closing = False\n\n    def _warn_on_long_close(self):\n        logger.warning('Pool.close() is taking over 60 seconds to complete. '\n                       'Check if you have any unreleased connections left. '\n                       'Use asyncio.wait_for() to set a timeout for '\n                       'Pool.close().')\n\n    def terminate(self):\n        \"\"\"Terminate all connections in the pool.\"\"\"\n        if self._closed:\n            return\n        self._check_init()\n        for ch in self._holders:\n            ch.terminate()\n        self._closed = True\n\n    async def expire_connections(self):\n        \"\"\"Expire all currently open connections.\n\n        Cause all currently open connections to get replaced on the\n        next :meth:`~asyncpg.pool.Pool.acquire()` call.\n\n        .. versionadded:: 0.16.0\n        \"\"\"\n        self._generation += 1\n\n    def _check_init(self):\n        if not self._initialized:\n            if self._initializing:\n                raise exceptions.InterfaceError(\n                    'pool is being initialized, but not yet ready: '\n                    'likely there is a race between creating a pool and '\n                    'using it')\n            raise exceptions.InterfaceError('pool is not initialized')\n        if self._closed:\n            raise exceptions.InterfaceError('pool is closed')\n\n    def _drop_statement_cache(self):\n        # Drop statement cache for all connections in the pool.\n        for ch in self._holders:\n            if ch._con is not None:\n                ch._con._drop_local_statement_cache()\n\n    def _drop_type_cache(self):\n        # Drop type codec cache for all connections in the pool.\n        for ch in self._holders:\n            if ch._con is not None:\n                ch._con._drop_local_type_cache()\n\n    def __await__(self):\n        return self._async__init__().__await__()\n\n    async def __aenter__(self):\n        await self._async__init__()\n        return self\n\n    async def __aexit__(self, *exc):\n        await self.close()\n\n\nclass PoolAcquireContext:\n\n    __slots__ = ('timeout', 'connection', 'done', 'pool')\n\n    def __init__(self, pool: Pool, timeout: Optional[float]) -> None:\n        self.pool = pool\n        self.timeout = timeout\n        self.connection = None\n        self.done = False\n\n    async def __aenter__(self):\n        if self.connection is not None or self.done:\n            raise exceptions.InterfaceError('a connection is already acquired')\n        self.connection = await self.pool._acquire(self.timeout)\n        return self.connection\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]] = None,\n        exc_val: Optional[BaseException] = None,\n        exc_tb: Optional[TracebackType] = None,\n    ) -> None:\n        self.done = True\n        con = self.connection\n        self.connection = None\n        await self.pool.release(con)\n\n    def __await__(self):\n        self.done = True\n        return self.pool._acquire(self.timeout).__await__()\n\n\ndef create_pool(dsn=None, *,\n                min_size=10,\n                max_size=10,\n                max_queries=50000,\n                max_inactive_connection_lifetime=300.0,\n                connect=None,\n                setup=None,\n                init=None,\n                reset=None,\n                loop=None,\n                connection_class=connection.Connection,\n                record_class=protocol.Record,\n                **connect_kwargs):\n    r\"\"\"Create a connection pool.\n\n    Can be used either with an ``async with`` block:\n\n    .. code-block:: python\n\n        async with asyncpg.create_pool(user='postgres',\n                                       command_timeout=60) as pool:\n            await pool.fetch('SELECT 1')\n\n    Or to perform multiple operations on a single connection:\n\n    .. code-block:: python\n\n        async with asyncpg.create_pool(user='postgres',\n                                       command_timeout=60) as pool:\n            async with pool.acquire() as con:\n                await con.execute('''\n                   CREATE TABLE names (\n                      id serial PRIMARY KEY,\n                      name VARCHAR (255) NOT NULL)\n                ''')\n                await con.fetch('SELECT 1')\n\n    Or directly with ``await`` (not recommended):\n\n    .. code-block:: python\n\n        pool = await asyncpg.create_pool(user='postgres', command_timeout=60)\n        con = await pool.acquire()\n        try:\n            await con.fetch('SELECT 1')\n        finally:\n            await pool.release(con)\n\n    .. warning::\n        Prepared statements and cursors returned by\n        :meth:`Connection.prepare() <asyncpg.connection.Connection.prepare>`\n        and :meth:`Connection.cursor() <asyncpg.connection.Connection.cursor>`\n        become invalid once the connection is released.  Likewise, all\n        notification and log listeners are removed, and ``asyncpg`` will\n        issue a warning if there are any listener callbacks registered on a\n        connection that is being released to the pool.\n\n    :param str dsn:\n        Connection arguments specified using as a single string in\n        the following format:\n        ``postgres://user:pass@host:port/database?option=value``.\n\n    :param \\*\\*connect_kwargs:\n        Keyword arguments for the :func:`~asyncpg.connection.connect`\n        function.\n\n    :param Connection connection_class:\n        The class to use for connections.  Must be a subclass of\n        :class:`~asyncpg.connection.Connection`.\n\n    :param type record_class:\n        If specified, the class to use for records returned by queries on\n        the connections in this pool.  Must be a subclass of\n        :class:`~asyncpg.Record`.\n\n    :param int min_size:\n        Number of connection the pool will be initialized with.\n\n    :param int max_size:\n        Max number of connections in the pool.\n\n    :param int max_queries:\n        Number of queries after a connection is closed and replaced\n        with a new connection.\n\n    :param float max_inactive_connection_lifetime:\n        Number of seconds after which inactive connections in the\n        pool will be closed.  Pass ``0`` to disable this mechanism.\n\n    :param coroutine connect:\n        A coroutine that is called instead of\n        :func:`~asyncpg.connection.connect` whenever the pool needs to make a\n        new connection.  Must return an instance of type specified by\n        *connection_class* or :class:`~asyncpg.connection.Connection` if\n        *connection_class* was not specified.\n\n    :param coroutine setup:\n        A coroutine to prepare a connection right before it is returned\n        from :meth:`Pool.acquire()`.  An example use\n        case would be to automatically set up notifications listeners for\n        all connections of a pool.\n\n    :param coroutine init:\n        A coroutine to initialize a connection when it is created.\n        An example use case would be to setup type codecs with\n        :meth:`Connection.set_builtin_type_codec() <\\\n        asyncpg.connection.Connection.set_builtin_type_codec>`\n        or :meth:`Connection.set_type_codec() <\\\n        asyncpg.connection.Connection.set_type_codec>`.\n\n    :param coroutine reset:\n        A coroutine to reset a connection before it is returned to the pool by\n        :meth:`Pool.release()`.  The function is supposed\n        to reset any changes made to the database session so that the next\n        acquirer gets the connection in a well-defined state.\n\n        The default implementation calls :meth:`Connection.reset() <\\\n        asyncpg.connection.Connection.reset>`, which runs the following::\n\n            SELECT pg_advisory_unlock_all();\n            CLOSE ALL;\n            UNLISTEN *;\n            RESET ALL;\n\n        The exact reset query is determined by detected server capabilities,\n        and a custom *reset* implementation can obtain the default query\n        by calling :meth:`Connection.get_reset_query() <\\\n        asyncpg.connection.Connection.get_reset_query>`.\n\n    :param loop:\n        An asyncio event loop instance.  If ``None``, the default\n        event loop will be used.\n\n    :return: An instance of :class:`~asyncpg.pool.Pool`.\n\n    .. versionchanged:: 0.10.0\n       An :exc:`~asyncpg.exceptions.InterfaceError` will be raised on any\n       attempted operation on a released connection.\n\n    .. versionchanged:: 0.13.0\n       An :exc:`~asyncpg.exceptions.InterfaceError` will be raised on any\n       attempted operation on a prepared statement or a cursor created\n       on a connection that has been released to the pool.\n\n    .. versionchanged:: 0.13.0\n       An :exc:`~asyncpg.exceptions.InterfaceWarning` will be produced\n       if there are any active listeners (added via\n       :meth:`Connection.add_listener()\n       <asyncpg.connection.Connection.add_listener>`\n       or :meth:`Connection.add_log_listener()\n       <asyncpg.connection.Connection.add_log_listener>`) present on the\n       connection at the moment of its release to the pool.\n\n    .. versionchanged:: 0.22.0\n       Added the *record_class* parameter.\n\n    .. versionchanged:: 0.30.0\n       Added the *connect* and *reset* parameters.\n    \"\"\"\n    return Pool(\n        dsn,\n        connection_class=connection_class,\n        record_class=record_class,\n        min_size=min_size,\n        max_size=max_size,\n        max_queries=max_queries,\n        loop=loop,\n        connect=connect,\n        setup=setup,\n        init=init,\n        reset=reset,\n        max_inactive_connection_lifetime=max_inactive_connection_lifetime,\n        **connect_kwargs,\n    )\n"
  },
  {
    "path": "asyncpg/prepared_stmt.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport json\nimport typing\n\nfrom . import connresource\nfrom . import cursor\nfrom . import exceptions\n\n\nclass PreparedStatement(connresource.ConnectionResource):\n    \"\"\"A representation of a prepared statement.\"\"\"\n\n    __slots__ = ('_state', '_query', '_last_status')\n\n    def __init__(self, connection, query, state):\n        super().__init__(connection)\n        self._state = state\n        self._query = query\n        state.attach()\n        self._last_status = None\n\n    @connresource.guarded\n    def get_name(self) -> str:\n        \"\"\"Return the name of this prepared statement.\n\n        .. versionadded:: 0.25.0\n        \"\"\"\n        return self._state.name\n\n    @connresource.guarded\n    def get_query(self) -> str:\n        \"\"\"Return the text of the query for this prepared statement.\n\n        Example::\n\n            stmt = await connection.prepare('SELECT $1::int')\n            assert stmt.get_query() == \"SELECT $1::int\"\n        \"\"\"\n        return self._query\n\n    @connresource.guarded\n    def get_statusmsg(self) -> str:\n        \"\"\"Return the status of the executed command.\n\n        Example::\n\n            stmt = await connection.prepare('CREATE TABLE mytab (a int)')\n            await stmt.fetch()\n            assert stmt.get_statusmsg() == \"CREATE TABLE\"\n        \"\"\"\n        if self._last_status is None:\n            return self._last_status\n        return self._last_status.decode()\n\n    @connresource.guarded\n    def get_parameters(self):\n        \"\"\"Return a description of statement parameters types.\n\n        :return: A tuple of :class:`asyncpg.types.Type`.\n\n        Example::\n\n            stmt = await connection.prepare('SELECT ($1::int, $2::text)')\n            print(stmt.get_parameters())\n\n            # Will print:\n            #   (Type(oid=23, name='int4', kind='scalar', schema='pg_catalog'),\n            #    Type(oid=25, name='text', kind='scalar', schema='pg_catalog'))\n        \"\"\"\n        return self._state._get_parameters()\n\n    @connresource.guarded\n    def get_attributes(self):\n        \"\"\"Return a description of relation attributes (columns).\n\n        :return: A tuple of :class:`asyncpg.types.Attribute`.\n\n        Example::\n\n            st = await self.con.prepare('''\n                SELECT typname, typnamespace FROM pg_type\n            ''')\n            print(st.get_attributes())\n\n            # Will print:\n            #   (Attribute(\n            #       name='typname',\n            #       type=Type(oid=19, name='name', kind='scalar',\n            #                 schema='pg_catalog')),\n            #    Attribute(\n            #       name='typnamespace',\n            #       type=Type(oid=26, name='oid', kind='scalar',\n            #                 schema='pg_catalog')))\n        \"\"\"\n        return self._state._get_attributes()\n\n    @connresource.guarded\n    def cursor(self, *args, prefetch=None,\n               timeout=None) -> cursor.CursorFactory:\n        \"\"\"Return a *cursor factory* for the prepared statement.\n\n        :param args: Query arguments.\n        :param int prefetch: The number of rows the *cursor iterator*\n                             will prefetch (defaults to ``50``.)\n        :param float timeout: Optional timeout in seconds.\n\n        :return: A :class:`~cursor.CursorFactory` object.\n        \"\"\"\n        return cursor.CursorFactory(\n            self._connection,\n            self._query,\n            self._state,\n            args,\n            prefetch,\n            timeout,\n            self._state.record_class,\n        )\n\n    @connresource.guarded\n    async def explain(self, *args, analyze=False):\n        \"\"\"Return the execution plan of the statement.\n\n        :param args: Query arguments.\n        :param analyze: If ``True``, the statement will be executed and\n                        the run time statitics added to the return value.\n\n        :return: An object representing the execution plan.  This value\n                 is actually a deserialized JSON output of the SQL\n                 ``EXPLAIN`` command.\n        \"\"\"\n        query = 'EXPLAIN (FORMAT JSON, VERBOSE'\n        if analyze:\n            query += ', ANALYZE) '\n        else:\n            query += ') '\n        query += self._state.query\n\n        if analyze:\n            # From PostgreSQL docs:\n            # Important: Keep in mind that the statement is actually\n            # executed when the ANALYZE option is used. Although EXPLAIN\n            # will discard any output that a SELECT would return, other\n            # side effects of the statement will happen as usual. If you\n            # wish to use EXPLAIN ANALYZE on an INSERT, UPDATE, DELETE,\n            # MERGE, CREATE TABLE AS, or EXECUTE statement without letting\n            # the command affect your data, use this approach:\n            #     BEGIN;\n            #     EXPLAIN ANALYZE ...;\n            #     ROLLBACK;\n            tr = self._connection.transaction()\n            await tr.start()\n            try:\n                data = await self._connection.fetchval(query, *args)\n            finally:\n                await tr.rollback()\n        else:\n            data = await self._connection.fetchval(query, *args)\n\n        return json.loads(data)\n\n    @connresource.guarded\n    async def fetch(self, *args, timeout=None):\n        r\"\"\"Execute the statement and return a list of :class:`Record` objects.\n\n        :param str query: Query text\n        :param args: Query arguments\n        :param float timeout: Optional timeout value in seconds.\n\n        :return: A list of :class:`Record` instances.\n        \"\"\"\n        data = await self.__bind_execute(args, 0, timeout)\n        return data\n\n    @connresource.guarded\n    async def fetchval(self, *args, column=0, timeout=None):\n        \"\"\"Execute the statement and return a value in the first row.\n\n        :param args: Query arguments.\n        :param int column: Numeric index within the record of the value to\n                           return (defaults to 0).\n        :param float timeout: Optional timeout value in seconds.\n                            If not specified, defaults to the value of\n                            ``command_timeout`` argument to the ``Connection``\n                            instance constructor.\n\n        :return: The value of the specified column of the first record.\n        \"\"\"\n        data = await self.__bind_execute(args, 1, timeout)\n        if not data:\n            return None\n        return data[0][column]\n\n    @connresource.guarded\n    async def fetchrow(self, *args, timeout=None):\n        \"\"\"Execute the statement and return the first row.\n\n        :param str query: Query text\n        :param args: Query arguments\n        :param float timeout: Optional timeout value in seconds.\n\n        :return: The first row as a :class:`Record` instance.\n        \"\"\"\n        data = await self.__bind_execute(args, 1, timeout)\n        if not data:\n            return None\n        return data[0]\n\n    @connresource.guarded\n    async def fetchmany(self, args, *, timeout=None):\n        \"\"\"Execute the statement and return a list of :class:`Record` objects.\n\n        :param args: Query arguments.\n        :param float timeout: Optional timeout value in seconds.\n\n        :return: A list of :class:`Record` instances.\n\n        .. versionadded:: 0.30.0\n        \"\"\"\n        return await self.__do_execute(\n            lambda protocol: protocol.bind_execute_many(\n                self._state,\n                args,\n                portal_name='',\n                timeout=timeout,\n                return_rows=True,\n            )\n        )\n\n    @connresource.guarded\n    async def executemany(self, args, *, timeout: typing.Optional[float]=None):\n        \"\"\"Execute the statement for each sequence of arguments in *args*.\n\n        :param args: An iterable containing sequences of arguments.\n        :param float timeout: Optional timeout value in seconds.\n        :return None: This method discards the results of the operations.\n\n        .. versionadded:: 0.22.0\n        \"\"\"\n        return await self.__do_execute(\n            lambda protocol: protocol.bind_execute_many(\n                self._state,\n                args,\n                portal_name='',\n                timeout=timeout,\n                return_rows=False,\n            ))\n\n    async def __do_execute(self, executor):\n        protocol = self._connection._protocol\n        try:\n            return await executor(protocol)\n        except exceptions.OutdatedSchemaCacheError:\n            await self._connection.reload_schema_state()\n            # We can not find all manually created prepared statements, so just\n            # drop known cached ones in the `self._connection`.\n            # Other manually created prepared statements will fail and\n            # invalidate themselves (unfortunately, clearing caches again).\n            self._state.mark_closed()\n            raise\n\n    async def __bind_execute(self, args, limit, timeout):\n        data, status, _ = await self.__do_execute(\n            lambda protocol: protocol.bind_execute(\n                self._state, args, '', limit, True, timeout))\n        self._last_status = status\n        return data\n\n    def _check_open(self, meth_name):\n        if self._state.closed:\n            raise exceptions.InterfaceError(\n                'cannot call PreparedStmt.{}(): '\n                'the prepared statement is closed'.format(meth_name))\n\n    def _check_conn_validity(self, meth_name):\n        self._check_open(meth_name)\n        super()._check_conn_validity(meth_name)\n\n    def __del__(self):\n        self._state.detach()\n        self._connection._maybe_gc_stmt(self._state)\n"
  },
  {
    "path": "asyncpg/protocol/.gitignore",
    "content": "/*.c\n"
  },
  {
    "path": "asyncpg/protocol/__init__.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n# flake8: NOQA\n\nfrom __future__ import annotations\n\nfrom .protocol import Protocol, NO_TIMEOUT, BUILTIN_TYPE_NAME_MAP\nfrom .record import Record\n"
  },
  {
    "path": "asyncpg/protocol/codecs/__init__.py",
    "content": ""
  },
  {
    "path": "asyncpg/protocol/codecs/array.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom collections.abc import (Iterable as IterableABC,\n                             Mapping as MappingABC,\n                             Sized as SizedABC)\n\nfrom asyncpg import exceptions\n\n\nDEF ARRAY_MAXDIM = 6  # defined in postgresql/src/includes/c.h\n\n# \"NULL\"\ncdef Py_UCS4 *APG_NULL = [0x004E, 0x0055, 0x004C, 0x004C, 0x0000]\n\n\nctypedef object (*encode_func_ex)(ConnectionSettings settings,\n                                  WriteBuffer buf,\n                                  object obj,\n                                  const void *arg)\n\n\nctypedef object (*decode_func_ex)(ConnectionSettings settings,\n                                  FRBuffer *buf,\n                                  const void *arg)\n\n\ncdef inline bint _is_trivial_container(object obj):\n    return cpython.PyUnicode_Check(obj) or cpython.PyBytes_Check(obj) or \\\n            cpythonx.PyByteArray_Check(obj) or cpythonx.PyMemoryView_Check(obj)\n\n\ncdef inline _is_array_iterable(object obj):\n    return (\n        isinstance(obj, IterableABC) and\n        isinstance(obj, SizedABC) and\n        not _is_trivial_container(obj) and\n        not isinstance(obj, MappingABC)\n    )\n\n\ncdef inline _is_sub_array_iterable(object obj):\n    # Sub-arrays have a specialized check, because we treat\n    # nested tuples as records.\n    return _is_array_iterable(obj) and not cpython.PyTuple_Check(obj)\n\n\ncdef _get_array_shape(object obj, int32_t *dims, int32_t *ndims):\n    cdef:\n        ssize_t mylen = len(obj)\n        ssize_t elemlen = -2\n        object it\n\n    if mylen > _MAXINT32:\n        raise ValueError('too many elements in array value')\n\n    if ndims[0] > ARRAY_MAXDIM:\n        raise ValueError(\n            'number of array dimensions ({}) exceed the maximum expected ({})'.\n                format(ndims[0], ARRAY_MAXDIM))\n\n    dims[ndims[0] - 1] = <int32_t>mylen\n\n    for elem in obj:\n        if _is_sub_array_iterable(elem):\n            if elemlen == -2:\n                elemlen = len(elem)\n                if elemlen > _MAXINT32:\n                    raise ValueError('too many elements in array value')\n                ndims[0] += 1\n                _get_array_shape(elem, dims, ndims)\n            else:\n                if len(elem) != elemlen:\n                    raise ValueError('non-homogeneous array')\n        else:\n            if elemlen >= 0:\n                raise ValueError('non-homogeneous array')\n            else:\n                elemlen = -1\n\n\ncdef _write_array_data(ConnectionSettings settings, object obj, int32_t ndims,\n                       int32_t dim, WriteBuffer elem_data,\n                       encode_func_ex encoder, const void *encoder_arg):\n    if dim < ndims - 1:\n        for item in obj:\n            _write_array_data(settings, item, ndims, dim + 1, elem_data,\n                              encoder, encoder_arg)\n    else:\n        for item in obj:\n            if item is None:\n                elem_data.write_int32(-1)\n            else:\n                try:\n                    encoder(settings, elem_data, item, encoder_arg)\n                except TypeError as e:\n                    raise ValueError(\n                        'invalid array element: {}'.format(e.args[0])) from None\n\n\ncdef inline array_encode(ConnectionSettings settings, WriteBuffer buf,\n                         object obj, uint32_t elem_oid,\n                         encode_func_ex encoder, const void *encoder_arg):\n    cdef:\n        WriteBuffer elem_data\n        int32_t dims[ARRAY_MAXDIM]\n        int32_t ndims = 1\n        int32_t i\n\n    if not _is_array_iterable(obj):\n        raise TypeError(\n            'a sized iterable container expected (got type {!r})'.format(\n                type(obj).__name__))\n\n    _get_array_shape(obj, dims, &ndims)\n\n    elem_data = WriteBuffer.new()\n\n    if ndims > 1:\n        _write_array_data(settings, obj, ndims, 0, elem_data,\n                          encoder, encoder_arg)\n    else:\n        for i, item in enumerate(obj):\n            if item is None:\n                elem_data.write_int32(-1)\n            else:\n                try:\n                    encoder(settings, elem_data, item, encoder_arg)\n                except TypeError as e:\n                    raise ValueError(\n                        'invalid array element at index {}: {}'.format(\n                            i, e.args[0])) from None\n\n    buf.write_int32(12 + 8 * ndims + elem_data.len())\n    # Number of dimensions\n    buf.write_int32(ndims)\n    # flags\n    buf.write_int32(0)\n    # element type\n    buf.write_int32(<int32_t>elem_oid)\n    # upper / lower bounds\n    for i in range(ndims):\n        buf.write_int32(dims[i])\n        buf.write_int32(1)\n    # element data\n    buf.write_buffer(elem_data)\n\n\ncdef _write_textarray_data(ConnectionSettings settings, object obj,\n                           int32_t ndims, int32_t dim, WriteBuffer array_data,\n                           encode_func_ex encoder, const void *encoder_arg,\n                           Py_UCS4 typdelim):\n    cdef:\n        ssize_t i = 0\n        int8_t delim = <int8_t>typdelim\n        WriteBuffer elem_data\n        Py_buffer pybuf\n        const char *elem_str\n        char ch\n        ssize_t elem_len\n        ssize_t quoted_elem_len\n        bint need_quoting\n\n    array_data.write_byte(b'{')\n\n    if dim < ndims - 1:\n        for item in obj:\n            if i > 0:\n                array_data.write_byte(delim)\n                array_data.write_byte(b' ')\n            _write_textarray_data(settings, item, ndims, dim + 1, array_data,\n                                  encoder, encoder_arg, typdelim)\n            i += 1\n    else:\n        for item in obj:\n            elem_data = WriteBuffer.new()\n\n            if i > 0:\n                array_data.write_byte(delim)\n                array_data.write_byte(b' ')\n\n            if item is None:\n                array_data.write_bytes(b'NULL')\n                i += 1\n                continue\n            else:\n                try:\n                    encoder(settings, elem_data, item, encoder_arg)\n                except TypeError as e:\n                    raise ValueError(\n                        'invalid array element: {}'.format(\n                            e.args[0])) from None\n\n            # element string length (first four bytes are the encoded length.)\n            elem_len = elem_data.len() - 4\n\n            if elem_len == 0:\n                # Empty string\n                array_data.write_bytes(b'\"\"')\n            else:\n                cpython.PyObject_GetBuffer(\n                    elem_data, &pybuf, cpython.PyBUF_SIMPLE)\n\n                elem_str = <const char*>(pybuf.buf) + 4\n\n                try:\n                    if not apg_strcasecmp_char(elem_str, b'NULL'):\n                        array_data.write_byte(b'\"')\n                        array_data.write_cstr(elem_str, 4)\n                        array_data.write_byte(b'\"')\n                    else:\n                        quoted_elem_len = elem_len\n                        need_quoting = False\n\n                        for i in range(elem_len):\n                            ch = elem_str[i]\n                            if ch == b'\"' or ch == b'\\\\':\n                                # Quotes and backslashes need escaping.\n                                quoted_elem_len += 1\n                                need_quoting = True\n                            elif (ch == b'{' or ch == b'}' or ch == delim or\n                                    apg_ascii_isspace(<uint32_t>ch)):\n                                need_quoting = True\n\n                        if need_quoting:\n                            array_data.write_byte(b'\"')\n\n                            if quoted_elem_len == elem_len:\n                                array_data.write_cstr(elem_str, elem_len)\n                            else:\n                                # Escaping required.\n                                for i in range(elem_len):\n                                    ch = elem_str[i]\n                                    if ch == b'\"' or ch == b'\\\\':\n                                        array_data.write_byte(b'\\\\')\n                                    array_data.write_byte(ch)\n\n                            array_data.write_byte(b'\"')\n                        else:\n                            array_data.write_cstr(elem_str, elem_len)\n                finally:\n                    cpython.PyBuffer_Release(&pybuf)\n\n            i += 1\n\n    array_data.write_byte(b'}')\n\n\ncdef inline textarray_encode(ConnectionSettings settings, WriteBuffer buf,\n                             object obj, encode_func_ex encoder,\n                             const void *encoder_arg, Py_UCS4 typdelim):\n    cdef:\n        WriteBuffer array_data\n        int32_t dims[ARRAY_MAXDIM]\n        int32_t ndims = 1\n        int32_t i\n\n    if not _is_array_iterable(obj):\n        raise TypeError(\n            'a sized iterable container expected (got type {!r})'.format(\n                type(obj).__name__))\n\n    _get_array_shape(obj, dims, &ndims)\n\n    array_data = WriteBuffer.new()\n    _write_textarray_data(settings, obj, ndims, 0, array_data,\n                          encoder, encoder_arg, typdelim)\n    buf.write_int32(array_data.len())\n    buf.write_buffer(array_data)\n\n\ncdef inline array_decode(ConnectionSettings settings, FRBuffer *buf,\n                         decode_func_ex decoder, const void *decoder_arg):\n    cdef:\n        int32_t ndims = hton.unpack_int32(frb_read(buf, 4))\n        int32_t flags = hton.unpack_int32(frb_read(buf, 4))\n        uint32_t elem_oid = <uint32_t>hton.unpack_int32(frb_read(buf, 4))\n        list result\n        int i\n        int32_t elem_len\n        int32_t elem_count = 1\n        FRBuffer elem_buf\n        int32_t dims[ARRAY_MAXDIM]\n        Codec elem_codec\n\n    if ndims == 0:\n        return []\n\n    if ndims > ARRAY_MAXDIM:\n        raise exceptions.ProtocolError(\n            'number of array dimensions ({}) exceed the maximum expected ({})'.\n            format(ndims, ARRAY_MAXDIM))\n    elif ndims < 0:\n        raise exceptions.ProtocolError(\n            'unexpected array dimensions value: {}'.format(ndims))\n\n    for i in range(ndims):\n        dims[i] = hton.unpack_int32(frb_read(buf, 4))\n        if dims[i] < 0:\n            raise exceptions.ProtocolError(\n                'unexpected array dimension size: {}'.format(dims[i]))\n        # Ignore the lower bound information\n        frb_read(buf, 4)\n\n    if ndims == 1:\n        # Fast path for flat arrays\n        elem_count = dims[0]\n        result = cpython.PyList_New(elem_count)\n\n        for i in range(elem_count):\n            elem_len = hton.unpack_int32(frb_read(buf, 4))\n            if elem_len == -1:\n                elem = None\n            else:\n                frb_slice_from(&elem_buf, buf, elem_len)\n                elem = decoder(settings, &elem_buf, decoder_arg)\n\n            cpython.Py_INCREF(elem)\n            cpython.PyList_SET_ITEM(result, i, elem)\n\n    else:\n        result = _nested_array_decode(settings, buf,\n                                      decoder, decoder_arg, ndims, dims,\n                                      &elem_buf)\n\n    return result\n\n\ncdef _nested_array_decode(ConnectionSettings settings,\n                          FRBuffer *buf,\n                          decode_func_ex decoder,\n                          const void *decoder_arg,\n                          int32_t ndims, int32_t *dims,\n                          FRBuffer *elem_buf):\n\n    cdef:\n        int32_t elem_len\n        int64_t i, j\n        int64_t array_len = 1\n        object elem, stride\n        # An array of pointers to lists for each current array level.\n        void *strides[ARRAY_MAXDIM]\n        # An array of current positions at each array level.\n        int32_t indexes[ARRAY_MAXDIM]\n\n    for i in range(ndims):\n        array_len *= dims[i]\n        indexes[i] = 0\n        strides[i] = NULL\n\n    if array_len == 0:\n        # A multidimensional array with a zero-sized dimension?\n        return []\n\n    elif array_len < 0:\n        # Array length overflow\n        raise exceptions.ProtocolError('array length overflow')\n\n    for i in range(array_len):\n        # Decode the element.\n        elem_len = hton.unpack_int32(frb_read(buf, 4))\n        if elem_len == -1:\n            elem = None\n        else:\n            elem = decoder(settings,\n                           frb_slice_from(elem_buf, buf, elem_len),\n                           decoder_arg)\n\n        # Take an explicit reference for PyList_SET_ITEM in the below\n        # loop expects this.\n        cpython.Py_INCREF(elem)\n\n        # Iterate over array dimentions and put the element in\n        # the correctly nested sublist.\n        for j in reversed(range(ndims)):\n            if indexes[j] == 0:\n                # Allocate the list for this array level.\n                stride = cpython.PyList_New(dims[j])\n\n                strides[j] = <void*><cpython.PyObject>stride\n                # Take an explicit reference for PyList_SET_ITEM below\n                # expects this.\n                cpython.Py_INCREF(stride)\n\n            stride = <object><cpython.PyObject*>strides[j]\n            cpython.PyList_SET_ITEM(stride, indexes[j], elem)\n            indexes[j] += 1\n\n            if indexes[j] == dims[j] and j != 0:\n                # This array level is full, continue the\n                # ascent in the dimensions so that this level\n                # sublist will be appened to the parent list.\n                elem = stride\n                # Reset the index, this will cause the\n                # new list to be allocated on the next\n                # iteration on this array axis.\n                indexes[j] = 0\n            else:\n                break\n\n    stride = <object><cpython.PyObject*>strides[0]\n    # Since each element in strides has a refcount of 1,\n    # returning strides[0] will increment it to 2, so\n    # balance that.\n    cpython.Py_DECREF(stride)\n    return stride\n\n\ncdef textarray_decode(ConnectionSettings settings, FRBuffer *buf,\n                      decode_func_ex decoder, const void *decoder_arg,\n                      Py_UCS4 typdelim):\n    cdef:\n        Py_UCS4 *array_text\n        str s\n\n    # Make a copy of array data since we will be mutating it for\n    # the purposes of element decoding.\n    s = pgproto.text_decode(settings, buf)\n    array_text = cpythonx.PyUnicode_AsUCS4Copy(s)\n\n    try:\n        return _textarray_decode(\n            settings, array_text, decoder, decoder_arg, typdelim)\n    except ValueError as e:\n        raise exceptions.ProtocolError(\n            'malformed array literal {!r}: {}'.format(s, e.args[0]))\n    finally:\n        cpython.PyMem_Free(array_text)\n\n\ncdef _textarray_decode(ConnectionSettings settings,\n                       Py_UCS4 *array_text,\n                       decode_func_ex decoder,\n                       const void *decoder_arg,\n                       Py_UCS4 typdelim):\n\n    cdef:\n        bytearray array_bytes\n        list result\n        list new_stride\n        Py_UCS4 *ptr\n        int32_t ndims = 0\n        int32_t ubound = 0\n        int32_t lbound = 0\n        int32_t dims[ARRAY_MAXDIM]\n        int32_t inferred_dims[ARRAY_MAXDIM]\n        int32_t inferred_ndims = 0\n        void *strides[ARRAY_MAXDIM]\n        int32_t indexes[ARRAY_MAXDIM]\n        int32_t nest_level = 0\n        int32_t item_level = 0\n        bint end_of_array = False\n\n        bint end_of_item = False\n        bint has_quoting = False\n        bint strip_spaces = False\n        bint in_quotes = False\n        Py_UCS4 *item_start\n        Py_UCS4 *item_ptr\n        Py_UCS4 *item_end\n\n        int i\n        object item\n        str item_text\n        FRBuffer item_buf\n        char *pg_item_str\n        ssize_t pg_item_len\n\n    ptr = array_text\n\n    while True:\n        while apg_ascii_isspace(ptr[0]):\n            ptr += 1\n\n        if ptr[0] != '[':\n            # Finished parsing dimensions spec.\n            break\n\n        ptr += 1  # '['\n\n        if ndims > ARRAY_MAXDIM:\n            raise ValueError(\n                'number of array dimensions ({}) exceed the '\n                'maximum expected ({})'.format(ndims, ARRAY_MAXDIM))\n\n        ptr = apg_parse_int32(ptr, &ubound)\n        if ptr == NULL:\n            raise ValueError('missing array dimension value')\n\n        if ptr[0] == ':':\n            ptr += 1\n            lbound = ubound\n\n            # [lower:upper] spec.  We disregard the lbound for decoding.\n            ptr = apg_parse_int32(ptr, &ubound)\n            if ptr == NULL:\n                raise ValueError('missing array dimension value')\n        else:\n            lbound = 1\n\n        if ptr[0] != ']':\n            raise ValueError('missing \\']\\' after array dimensions')\n\n        ptr += 1  # ']'\n\n        dims[ndims] = ubound - lbound + 1\n        ndims += 1\n\n    if ndims != 0:\n        # If dimensions were given, the '=' token is expected.\n        if ptr[0] != '=':\n            raise ValueError('missing \\'=\\' after array dimensions')\n\n        ptr += 1  # '='\n\n        # Skip any whitespace after the '=', whitespace\n        # before was consumed in the above loop.\n        while apg_ascii_isspace(ptr[0]):\n            ptr += 1\n\n        # Infer the dimensions from the brace structure in the\n        # array literal body, and check that it matches the explicit\n        # spec.  This also validates that the array literal is sane.\n        _infer_array_dims(ptr, typdelim, inferred_dims, &inferred_ndims)\n\n        if inferred_ndims != ndims:\n            raise ValueError(\n                'specified array dimensions do not match array content')\n\n        for i in range(ndims):\n            if inferred_dims[i] != dims[i]:\n                raise ValueError(\n                    'specified array dimensions do not match array content')\n    else:\n        # Infer the dimensions from the brace structure in the array literal\n        # body.  This also validates that the array literal is sane.\n        _infer_array_dims(ptr, typdelim, dims, &ndims)\n\n    while not end_of_array:\n        # We iterate over the literal character by character\n        # and modify the string in-place removing the array-specific\n        # quoting and determining the boundaries of each element.\n        end_of_item = has_quoting = in_quotes = False\n        strip_spaces = True\n\n        # Pointers to array element start, end, and the current pointer\n        # tracking the position where characters are written when\n        # escaping is folded.\n        item_start = item_end = item_ptr = ptr\n        item_level = 0\n\n        while not end_of_item:\n            if ptr[0] == '\"':\n                in_quotes = not in_quotes\n                if in_quotes:\n                    strip_spaces = False\n                else:\n                    item_end = item_ptr\n                has_quoting = True\n\n            elif ptr[0] == '\\\\':\n                # Quoted character, collapse the backslash.\n                ptr += 1\n                has_quoting = True\n                item_ptr[0] = ptr[0]\n                item_ptr += 1\n                strip_spaces = False\n                item_end = item_ptr\n\n            elif in_quotes:\n                # Consume the string until we see the closing quote.\n                item_ptr[0] = ptr[0]\n                item_ptr += 1\n\n            elif ptr[0] == '{':\n                # Nesting level increase.\n                nest_level += 1\n\n                indexes[nest_level - 1] = 0\n                new_stride = cpython.PyList_New(dims[nest_level - 1])\n                strides[nest_level - 1] = \\\n                    <void*>(<cpython.PyObject>new_stride)\n\n                if nest_level > 1:\n                    cpython.Py_INCREF(new_stride)\n                    cpython.PyList_SET_ITEM(\n                        <object><cpython.PyObject*>strides[nest_level - 2],\n                        indexes[nest_level - 2],\n                        new_stride)\n                else:\n                    result = new_stride\n\n            elif ptr[0] == '}':\n                if item_level == 0:\n                    # Make sure we keep track of which nesting\n                    # level the item belongs to, as the loop\n                    # will continue to consume closing braces\n                    # until the delimiter or the end of input.\n                    item_level = nest_level\n\n                nest_level -= 1\n\n                if nest_level == 0:\n                    end_of_array = end_of_item = True\n\n            elif ptr[0] == typdelim:\n                # Array element delimiter,\n                end_of_item = True\n                if item_level == 0:\n                    item_level = nest_level\n\n            elif apg_ascii_isspace(ptr[0]):\n                if not strip_spaces:\n                    item_ptr[0] = ptr[0]\n                    item_ptr += 1\n                # Ignore the leading literal whitespace.\n\n            else:\n                item_ptr[0] = ptr[0]\n                item_ptr += 1\n                strip_spaces = False\n                item_end = item_ptr\n\n            ptr += 1\n\n        # end while not end_of_item\n\n        if item_end == item_start:\n            # Empty array\n            continue\n\n        item_end[0] = '\\0'\n\n        if not has_quoting and apg_strcasecmp(item_start, APG_NULL) == 0:\n            # NULL element.\n            item = None\n        else:\n            # XXX: find a way to avoid the redundant encode/decode\n            # cycle here.\n            item_text = cpythonx.PyUnicode_FromKindAndData(\n                cpythonx.PyUnicode_4BYTE_KIND,\n                <void *>item_start,\n                item_end - item_start)\n\n            # Prepare the element buffer and call the text decoder\n            # for the element type.\n            pgproto.as_pg_string_and_size(\n                settings, item_text, &pg_item_str, &pg_item_len)\n            frb_init(&item_buf, pg_item_str, pg_item_len)\n            item = decoder(settings, &item_buf, decoder_arg)\n\n        # Place the decoded element in the array.\n        cpython.Py_INCREF(item)\n        cpython.PyList_SET_ITEM(\n            <object><cpython.PyObject*>strides[item_level - 1],\n            indexes[item_level - 1],\n            item)\n\n        if nest_level > 0:\n            indexes[nest_level - 1] += 1\n\n    return result\n\n\ncdef enum _ArrayParseState:\n    APS_START = 1\n    APS_STRIDE_STARTED = 2\n    APS_STRIDE_DONE = 3\n    APS_STRIDE_DELIMITED = 4\n    APS_ELEM_STARTED = 5\n    APS_ELEM_DELIMITED = 6\n\n\ncdef _UnexpectedCharacter(const Py_UCS4 *array_text, const Py_UCS4 *ptr):\n    return ValueError('unexpected character {!r} at position {}'.format(\n        cpython.PyUnicode_FromOrdinal(<int>ptr[0]), ptr - array_text + 1))\n\n\ncdef _infer_array_dims(const Py_UCS4 *array_text,\n                       Py_UCS4 typdelim,\n                       int32_t *dims,\n                       int32_t *ndims):\n    cdef:\n        const Py_UCS4 *ptr = array_text\n        int i\n        int nest_level = 0\n        bint end_of_array = False\n        bint end_of_item = False\n        bint in_quotes = False\n        bint array_is_empty = True\n        int stride_len[ARRAY_MAXDIM]\n        int prev_stride_len[ARRAY_MAXDIM]\n        _ArrayParseState parse_state = APS_START\n\n    for i in range(ARRAY_MAXDIM):\n        dims[i] = prev_stride_len[i] = 0\n        stride_len[i] = 1\n\n    while not end_of_array:\n        end_of_item = False\n\n        while not end_of_item:\n            if ptr[0] == '\\0':\n                raise ValueError('unexpected end of string')\n\n            elif ptr[0] == '\"':\n                if (parse_state not in (APS_STRIDE_STARTED,\n                                        APS_ELEM_DELIMITED) and\n                        not (parse_state == APS_ELEM_STARTED and in_quotes)):\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                in_quotes = not in_quotes\n                if in_quotes:\n                    parse_state = APS_ELEM_STARTED\n                    array_is_empty = False\n\n            elif ptr[0] == '\\\\':\n                if parse_state not in (APS_STRIDE_STARTED,\n                                       APS_ELEM_STARTED,\n                                       APS_ELEM_DELIMITED):\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                parse_state = APS_ELEM_STARTED\n                array_is_empty = False\n\n                if ptr[1] != '\\0':\n                    ptr += 1\n                else:\n                    raise ValueError('unexpected end of string')\n\n            elif in_quotes:\n                # Ignore everything inside the quotes.\n                pass\n\n            elif ptr[0] == '{':\n                if parse_state not in (APS_START,\n                                       APS_STRIDE_STARTED,\n                                       APS_STRIDE_DELIMITED):\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                parse_state = APS_STRIDE_STARTED\n                if nest_level >= ARRAY_MAXDIM:\n                    raise ValueError(\n                        'number of array dimensions ({}) exceed the '\n                        'maximum expected ({})'.format(\n                            nest_level, ARRAY_MAXDIM))\n\n                dims[nest_level] = 0\n                nest_level += 1\n                if ndims[0] < nest_level:\n                    ndims[0] = nest_level\n\n            elif ptr[0] == '}':\n                if (parse_state not in (APS_ELEM_STARTED, APS_STRIDE_DONE) and\n                        not (nest_level == 1 and\n                             parse_state == APS_STRIDE_STARTED)):\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                parse_state = APS_STRIDE_DONE\n\n                if nest_level == 0:\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                nest_level -= 1\n\n                if (prev_stride_len[nest_level] != 0 and\n                        stride_len[nest_level] != prev_stride_len[nest_level]):\n                    raise ValueError(\n                        'inconsistent sub-array dimensions'\n                        ' at position {}'.format(\n                            ptr - array_text + 1))\n\n                prev_stride_len[nest_level] = stride_len[nest_level]\n                stride_len[nest_level] = 1\n                if nest_level == 0:\n                    end_of_array = end_of_item = True\n                else:\n                    dims[nest_level - 1] += 1\n\n            elif ptr[0] == typdelim:\n                if parse_state not in (APS_ELEM_STARTED, APS_STRIDE_DONE):\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                if parse_state == APS_STRIDE_DONE:\n                    parse_state = APS_STRIDE_DELIMITED\n                else:\n                    parse_state = APS_ELEM_DELIMITED\n                end_of_item = True\n                stride_len[nest_level - 1] += 1\n\n            elif not apg_ascii_isspace(ptr[0]):\n                if parse_state not in (APS_STRIDE_STARTED,\n                                       APS_ELEM_STARTED,\n                                       APS_ELEM_DELIMITED):\n                    raise _UnexpectedCharacter(array_text, ptr)\n\n                parse_state = APS_ELEM_STARTED\n                array_is_empty = False\n\n            if not end_of_item:\n                ptr += 1\n\n        if not array_is_empty:\n            dims[ndims[0] - 1] += 1\n\n        ptr += 1\n\n    # only whitespace is allowed after the closing brace\n    while ptr[0] != '\\0':\n        if not apg_ascii_isspace(ptr[0]):\n            raise _UnexpectedCharacter(array_text, ptr)\n\n        ptr += 1\n\n    if array_is_empty:\n        ndims[0] = 0\n\n\ncdef uint4_encode_ex(ConnectionSettings settings, WriteBuffer buf, object obj,\n                     const void *arg):\n    return pgproto.uint4_encode(settings, buf, obj)\n\n\ncdef uint4_decode_ex(ConnectionSettings settings, FRBuffer *buf,\n                     const void *arg):\n    return pgproto.uint4_decode(settings, buf)\n\n\ncdef arrayoid_encode(ConnectionSettings settings, WriteBuffer buf, items):\n    array_encode(settings, buf, items, OIDOID,\n                 <encode_func_ex>&uint4_encode_ex, NULL)\n\n\ncdef arrayoid_decode(ConnectionSettings settings, FRBuffer *buf):\n    return array_decode(settings, buf, <decode_func_ex>&uint4_decode_ex, NULL)\n\n\ncdef text_encode_ex(ConnectionSettings settings, WriteBuffer buf, object obj,\n                    const void *arg):\n    return pgproto.text_encode(settings, buf, obj)\n\n\ncdef text_decode_ex(ConnectionSettings settings, FRBuffer *buf,\n                    const void *arg):\n    return pgproto.text_decode(settings, buf)\n\n\ncdef arraytext_encode(ConnectionSettings settings, WriteBuffer buf, items):\n    array_encode(settings, buf, items, TEXTOID,\n                 <encode_func_ex>&text_encode_ex, NULL)\n\n\ncdef arraytext_decode(ConnectionSettings settings, FRBuffer *buf):\n    return array_decode(settings, buf, <decode_func_ex>&text_decode_ex, NULL)\n\n\ncdef init_array_codecs():\n    # oid[] and text[] are registered as core codecs\n    # to make type introspection query work\n    #\n    register_core_codec(_OIDOID,\n                        <encode_func>&arrayoid_encode,\n                        <decode_func>&arrayoid_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(_TEXTOID,\n                        <encode_func>&arraytext_encode,\n                        <decode_func>&arraytext_decode,\n                        PG_FORMAT_BINARY)\n\ninit_array_codecs()\n"
  },
  {
    "path": "asyncpg/protocol/codecs/base.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nctypedef object (*encode_func)(ConnectionSettings settings,\n                               WriteBuffer buf,\n                               object obj)\n\nctypedef object (*decode_func)(ConnectionSettings settings,\n                               FRBuffer *buf)\n\nctypedef object (*codec_encode_func)(Codec codec,\n                                     ConnectionSettings settings,\n                                     WriteBuffer buf,\n                                     object obj)\n\nctypedef object (*codec_decode_func)(Codec codec,\n                                     ConnectionSettings settings,\n                                     FRBuffer *buf)\n\n\ncdef class CodecMap:\n    cdef:\n        void** binary_codec_map\n        void** text_codec_map\n        dict extra_codecs\n\n    cdef inline void *get_binary_codec_ptr(self, uint32_t idx)\n    cdef inline void set_binary_codec_ptr(self, uint32_t idx, void *ptr)\n    cdef inline void *get_text_codec_ptr(self, uint32_t idx)\n    cdef inline void set_text_codec_ptr(self, uint32_t idx, void *ptr)\n\n\ncdef enum CodecType:\n    CODEC_UNDEFINED  = 0\n    CODEC_C          = 1\n    CODEC_PY         = 2\n    CODEC_ARRAY      = 3\n    CODEC_COMPOSITE  = 4\n    CODEC_RANGE      = 5\n    CODEC_MULTIRANGE = 6\n\n\ncdef enum ServerDataFormat:\n    PG_FORMAT_ANY = -1\n    PG_FORMAT_TEXT = 0\n    PG_FORMAT_BINARY = 1\n\n\ncdef enum ClientExchangeFormat:\n    PG_XFORMAT_OBJECT = 1\n    PG_XFORMAT_TUPLE = 2\n\n\ncdef class Codec:\n    cdef:\n        uint32_t        oid\n\n        str             name\n        str             schema\n        str             kind\n\n        CodecType       type\n        ServerDataFormat format\n        ClientExchangeFormat xformat\n\n        encode_func     c_encoder\n        decode_func     c_decoder\n        Codec           base_codec\n\n        object          py_encoder\n        object          py_decoder\n\n        # arrays\n        Codec           element_codec\n        Py_UCS4         element_delimiter\n\n        # composite types\n        tuple           element_type_oids\n        object          element_names\n        object          record_desc\n        list            element_codecs\n\n        # Pointers to actual encoder/decoder functions for this codec\n        codec_encode_func encoder\n        codec_decode_func decoder\n\n    cdef init(self, str name, str schema, str kind,\n              CodecType type, ServerDataFormat format,\n              ClientExchangeFormat xformat,\n              encode_func c_encoder, decode_func c_decoder,\n              Codec base_codec,\n              object py_encoder, object py_decoder,\n              Codec element_codec, tuple element_type_oids,\n              object element_names, list element_codecs,\n              Py_UCS4 element_delimiter)\n\n    cdef encode_scalar(self, ConnectionSettings settings, WriteBuffer buf,\n                       object obj)\n\n    cdef encode_array(self, ConnectionSettings settings, WriteBuffer buf,\n                      object obj)\n\n    cdef encode_array_text(self, ConnectionSettings settings, WriteBuffer buf,\n                           object obj)\n\n    cdef encode_range(self, ConnectionSettings settings, WriteBuffer buf,\n                      object obj)\n\n    cdef encode_multirange(self, ConnectionSettings settings, WriteBuffer buf,\n                           object obj)\n\n    cdef encode_composite(self, ConnectionSettings settings, WriteBuffer buf,\n                          object obj)\n\n    cdef encode_in_python(self, ConnectionSettings settings, WriteBuffer buf,\n                          object obj)\n\n    cdef decode_scalar(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef decode_array(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef decode_array_text(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef decode_range(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef decode_multirange(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef decode_composite(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef decode_in_python(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef inline encode(self,\n                       ConnectionSettings settings,\n                       WriteBuffer buf,\n                       object obj)\n\n    cdef inline decode(self, ConnectionSettings settings, FRBuffer *buf)\n\n    cdef has_encoder(self)\n    cdef has_decoder(self)\n    cdef is_binary(self)\n\n    cdef inline Codec copy(self)\n\n    @staticmethod\n    cdef Codec new_array_codec(uint32_t oid,\n                               str name,\n                               str schema,\n                               Codec element_codec,\n                               Py_UCS4 element_delimiter)\n\n    @staticmethod\n    cdef Codec new_range_codec(uint32_t oid,\n                               str name,\n                               str schema,\n                               Codec element_codec)\n\n    @staticmethod\n    cdef Codec new_multirange_codec(uint32_t oid,\n                                    str name,\n                                    str schema,\n                                    Codec element_codec)\n\n    @staticmethod\n    cdef Codec new_composite_codec(uint32_t oid,\n                                   str name,\n                                   str schema,\n                                   ServerDataFormat format,\n                                   list element_codecs,\n                                   tuple element_type_oids,\n                                   object element_names)\n\n    @staticmethod\n    cdef Codec new_python_codec(uint32_t oid,\n                                str name,\n                                str schema,\n                                str kind,\n                                object encoder,\n                                object decoder,\n                                encode_func c_encoder,\n                                decode_func c_decoder,\n                                Codec base_codec,\n                                ServerDataFormat format,\n                                ClientExchangeFormat xformat)\n\n\ncdef class DataCodecConfig:\n    cdef:\n        dict _derived_type_codecs\n        dict _custom_type_codecs\n\n    cdef inline Codec get_codec(self, uint32_t oid, ServerDataFormat format,\n                                bint ignore_custom_codec=*)\n    cdef inline Codec get_custom_codec(self, uint32_t oid,\n                                       ServerDataFormat format)\n"
  },
  {
    "path": "asyncpg/protocol/codecs/base.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom collections.abc import Mapping as MappingABC\n\nimport asyncpg\nfrom asyncpg import exceptions\n\n\n# The class indirection is needed because Cython\n# does not (as of 3.1.0) store global cdef variables\n# in module state.\n@cython.final\ncdef class CodecMap:\n\n    def __cinit__(self):\n        self.extra_codecs = {}\n        self.binary_codec_map = <void **>cpython.PyMem_Calloc(\n            (MAXSUPPORTEDOID + 1) * 2, sizeof(void *))\n        self.text_codec_map = <void **>cpython.PyMem_Calloc(\n            (MAXSUPPORTEDOID + 1) * 2, sizeof(void *))\n\n    cdef inline void *get_binary_codec_ptr(self, uint32_t idx):\n        return <void*>self.binary_codec_map[idx]\n\n    cdef inline void set_binary_codec_ptr(self, uint32_t idx, void *ptr):\n        self.binary_codec_map[idx] = ptr\n\n    cdef inline void *get_text_codec_ptr(self, uint32_t idx):\n        return <void*>self.text_codec_map[idx]\n\n    cdef inline void set_text_codec_ptr(self, uint32_t idx, void *ptr):\n        self.text_codec_map[idx] = ptr\n\n\ncodec_map = CodecMap()\n\n\n@cython.final\ncdef class Codec:\n\n    def __cinit__(self, uint32_t oid):\n        self.oid = oid\n        self.type = CODEC_UNDEFINED\n\n    cdef init(\n        self,\n        str name,\n        str schema,\n        str kind,\n        CodecType type,\n        ServerDataFormat format,\n        ClientExchangeFormat xformat,\n        encode_func c_encoder,\n        decode_func c_decoder,\n        Codec base_codec,\n        object py_encoder,\n        object py_decoder,\n        Codec element_codec,\n        tuple element_type_oids,\n        object element_names,\n        list element_codecs,\n        Py_UCS4 element_delimiter,\n    ):\n\n        self.name = name\n        self.schema = schema\n        self.kind = kind\n        self.type = type\n        self.format = format\n        self.xformat = xformat\n        self.c_encoder = c_encoder\n        self.c_decoder = c_decoder\n        self.base_codec = base_codec\n        self.py_encoder = py_encoder\n        self.py_decoder = py_decoder\n        self.element_codec = element_codec\n        self.element_type_oids = element_type_oids\n        self.element_codecs = element_codecs\n        self.element_delimiter = element_delimiter\n        self.element_names = element_names\n\n        if base_codec is not None:\n            if c_encoder != NULL or c_decoder != NULL:\n                raise exceptions.InternalClientError(\n                    'base_codec is mutually exclusive with c_encoder/c_decoder'\n                )\n\n        if element_names is not None:\n            self.record_desc = RecordDescriptor(\n                element_names, tuple(element_names))\n        else:\n            self.record_desc = None\n\n        if type == CODEC_C:\n            self.encoder = <codec_encode_func>&self.encode_scalar\n            self.decoder = <codec_decode_func>&self.decode_scalar\n        elif type == CODEC_ARRAY:\n            if format == PG_FORMAT_BINARY:\n                self.encoder = <codec_encode_func>&self.encode_array\n                self.decoder = <codec_decode_func>&self.decode_array\n            else:\n                self.encoder = <codec_encode_func>&self.encode_array_text\n                self.decoder = <codec_decode_func>&self.decode_array_text\n        elif type == CODEC_RANGE:\n            if format != PG_FORMAT_BINARY:\n                raise exceptions.UnsupportedClientFeatureError(\n                    'cannot decode type \"{}\".\"{}\": text encoding of '\n                    'range types is not supported'.format(schema, name))\n            self.encoder = <codec_encode_func>&self.encode_range\n            self.decoder = <codec_decode_func>&self.decode_range\n        elif type == CODEC_MULTIRANGE:\n            if format != PG_FORMAT_BINARY:\n                raise exceptions.UnsupportedClientFeatureError(\n                    'cannot decode type \"{}\".\"{}\": text encoding of '\n                    'range types is not supported'.format(schema, name))\n            self.encoder = <codec_encode_func>&self.encode_multirange\n            self.decoder = <codec_decode_func>&self.decode_multirange\n        elif type == CODEC_COMPOSITE:\n            if format != PG_FORMAT_BINARY:\n                raise exceptions.UnsupportedClientFeatureError(\n                    'cannot decode type \"{}\".\"{}\": text encoding of '\n                    'composite types is not supported'.format(schema, name))\n            self.encoder = <codec_encode_func>&self.encode_composite\n            self.decoder = <codec_decode_func>&self.decode_composite\n        elif type == CODEC_PY:\n            self.encoder = <codec_encode_func>&self.encode_in_python\n            self.decoder = <codec_decode_func>&self.decode_in_python\n        else:\n            raise exceptions.InternalClientError(\n                'unexpected codec type: {}'.format(type))\n\n    cdef Codec copy(self):\n        cdef Codec codec\n\n        codec = Codec(self.oid)\n        codec.init(self.name, self.schema, self.kind,\n                   self.type, self.format, self.xformat,\n                   self.c_encoder, self.c_decoder, self.base_codec,\n                   self.py_encoder, self.py_decoder,\n                   self.element_codec,\n                   self.element_type_oids, self.element_names,\n                   self.element_codecs, self.element_delimiter)\n\n        return codec\n\n    cdef encode_scalar(self, ConnectionSettings settings, WriteBuffer buf,\n                       object obj):\n        self.c_encoder(settings, buf, obj)\n\n    cdef encode_array(self, ConnectionSettings settings, WriteBuffer buf,\n                      object obj):\n        array_encode(settings, buf, obj, self.element_codec.oid,\n                     codec_encode_func_ex,\n                     <void*>(<cpython.PyObject>self.element_codec))\n\n    cdef encode_array_text(self, ConnectionSettings settings, WriteBuffer buf,\n                           object obj):\n        return textarray_encode(settings, buf, obj,\n                                codec_encode_func_ex,\n                                <void*>(<cpython.PyObject>self.element_codec),\n                                self.element_delimiter)\n\n    cdef encode_range(self, ConnectionSettings settings, WriteBuffer buf,\n                      object obj):\n        range_encode(settings, buf, obj, self.element_codec.oid,\n                     codec_encode_func_ex,\n                     <void*>(<cpython.PyObject>self.element_codec))\n\n    cdef encode_multirange(self, ConnectionSettings settings, WriteBuffer buf,\n                           object obj):\n        multirange_encode(settings, buf, obj, self.element_codec.oid,\n                          codec_encode_func_ex,\n                          <void*>(<cpython.PyObject>self.element_codec))\n\n    cdef encode_composite(self, ConnectionSettings settings, WriteBuffer buf,\n                          object obj):\n        cdef:\n            WriteBuffer elem_data\n            int i\n            list elem_codecs = self.element_codecs\n            ssize_t count\n            ssize_t composite_size\n            tuple rec\n\n        if isinstance(obj, MappingABC):\n            # Input is dict-like, form a tuple\n            composite_size = len(self.element_type_oids)\n            rec = cpython.PyTuple_New(composite_size)\n\n            for i in range(composite_size):\n                cpython.Py_INCREF(None)\n                cpython.PyTuple_SET_ITEM(rec, i, None)\n\n            for field in obj:\n                try:\n                    i = self.element_names[field]\n                except KeyError:\n                    raise ValueError(\n                        '{!r} is not a valid element of composite '\n                        'type {}'.format(field, self.name)) from None\n\n                item = obj[field]\n                cpython.Py_INCREF(item)\n                cpython.PyTuple_SET_ITEM(rec, i, item)\n\n            obj = rec\n\n        count = len(obj)\n        if count > _MAXINT32:\n            raise ValueError('too many elements in composite type record')\n\n        elem_data = WriteBuffer.new()\n        i = 0\n        for item in obj:\n            elem_data.write_int32(<int32_t>self.element_type_oids[i])\n            if item is None:\n                elem_data.write_int32(-1)\n            else:\n                (<Codec>elem_codecs[i]).encode(settings, elem_data, item)\n            i += 1\n\n        record_encode_frame(settings, buf, elem_data, <int32_t>count)\n\n    cdef encode_in_python(self, ConnectionSettings settings, WriteBuffer buf,\n                          object obj):\n        data = self.py_encoder(obj)\n        if self.xformat == PG_XFORMAT_OBJECT:\n            if self.format == PG_FORMAT_BINARY:\n                pgproto.bytea_encode(settings, buf, data)\n            elif self.format == PG_FORMAT_TEXT:\n                pgproto.text_encode(settings, buf, data)\n            else:\n                raise exceptions.InternalClientError(\n                    'unexpected data format: {}'.format(self.format))\n        elif self.xformat == PG_XFORMAT_TUPLE:\n            if self.base_codec is not None:\n                self.base_codec.encode(settings, buf, data)\n            else:\n                self.c_encoder(settings, buf, data)\n        else:\n            raise exceptions.InternalClientError(\n                'unexpected exchange format: {}'.format(self.xformat))\n\n    cdef encode(self, ConnectionSettings settings, WriteBuffer buf,\n                object obj):\n        return self.encoder(self, settings, buf, obj)\n\n    cdef decode_scalar(self, ConnectionSettings settings, FRBuffer *buf):\n        return self.c_decoder(settings, buf)\n\n    cdef decode_array(self, ConnectionSettings settings, FRBuffer *buf):\n        return array_decode(settings, buf, codec_decode_func_ex,\n                            <void*>(<cpython.PyObject>self.element_codec))\n\n    cdef decode_array_text(self, ConnectionSettings settings,\n                           FRBuffer *buf):\n        return textarray_decode(settings, buf, codec_decode_func_ex,\n                                <void*>(<cpython.PyObject>self.element_codec),\n                                self.element_delimiter)\n\n    cdef decode_range(self, ConnectionSettings settings, FRBuffer *buf):\n        return range_decode(settings, buf, codec_decode_func_ex,\n                            <void*>(<cpython.PyObject>self.element_codec))\n\n    cdef decode_multirange(self, ConnectionSettings settings, FRBuffer *buf):\n        return multirange_decode(settings, buf, codec_decode_func_ex,\n                                 <void*>(<cpython.PyObject>self.element_codec))\n\n    cdef decode_composite(self, ConnectionSettings settings,\n                          FRBuffer *buf):\n        cdef:\n            object result\n            ssize_t elem_count\n            ssize_t i\n            int32_t elem_len\n            uint32_t elem_typ\n            uint32_t received_elem_typ\n            Codec elem_codec\n            FRBuffer elem_buf\n\n        elem_count = <ssize_t><uint32_t>hton.unpack_int32(frb_read(buf, 4))\n        if elem_count != len(self.element_type_oids):\n            raise exceptions.OutdatedSchemaCacheError(\n                'unexpected number of attributes of composite type: '\n                '{}, expected {}'\n                    .format(\n                        elem_count,\n                        len(self.element_type_oids),\n                    ),\n                schema=self.schema,\n                data_type=self.name,\n            )\n        result = self.record_desc.make_record(asyncpg.Record, elem_count)\n        for i in range(elem_count):\n            elem_typ = self.element_type_oids[i]\n            received_elem_typ = <uint32_t>hton.unpack_int32(frb_read(buf, 4))\n\n            if received_elem_typ != elem_typ:\n                raise exceptions.OutdatedSchemaCacheError(\n                    'unexpected data type of composite type attribute {}: '\n                    '{!r}, expected {!r}'\n                        .format(\n                            i,\n                            BUILTIN_TYPE_OID_MAP.get(\n                                received_elem_typ, received_elem_typ),\n                            BUILTIN_TYPE_OID_MAP.get(\n                                elem_typ, elem_typ)\n                        ),\n                    schema=self.schema,\n                    data_type=self.name,\n                    position=i,\n                )\n\n            elem_len = hton.unpack_int32(frb_read(buf, 4))\n            if elem_len == -1:\n                elem = None\n            else:\n                elem_codec = self.element_codecs[i]\n                elem = elem_codec.decode(\n                    settings, frb_slice_from(&elem_buf, buf, elem_len))\n\n            cpython.Py_INCREF(elem)\n            recordcapi.ApgRecord_SET_ITEM(result, i, elem)\n\n        return result\n\n    cdef decode_in_python(self, ConnectionSettings settings,\n                          FRBuffer *buf):\n        if self.xformat == PG_XFORMAT_OBJECT:\n            if self.format == PG_FORMAT_BINARY:\n                data = pgproto.bytea_decode(settings, buf)\n            elif self.format == PG_FORMAT_TEXT:\n                data = pgproto.text_decode(settings, buf)\n            else:\n                raise exceptions.InternalClientError(\n                    'unexpected data format: {}'.format(self.format))\n        elif self.xformat == PG_XFORMAT_TUPLE:\n            if self.base_codec is not None:\n                data = self.base_codec.decode(settings, buf)\n            else:\n                data = self.c_decoder(settings, buf)\n        else:\n            raise exceptions.InternalClientError(\n                'unexpected exchange format: {}'.format(self.xformat))\n\n        return self.py_decoder(data)\n\n    cdef inline decode(self, ConnectionSettings settings, FRBuffer *buf):\n        return self.decoder(self, settings, buf)\n\n    cdef inline has_encoder(self):\n        cdef Codec elem_codec\n\n        if self.c_encoder is not NULL or self.py_encoder is not None:\n            return True\n\n        elif (\n            self.type == CODEC_ARRAY\n            or self.type == CODEC_RANGE\n            or self.type == CODEC_MULTIRANGE\n        ):\n            return self.element_codec.has_encoder()\n\n        elif self.type == CODEC_COMPOSITE:\n            for elem_codec in self.element_codecs:\n                if not elem_codec.has_encoder():\n                    return False\n            return True\n\n        else:\n            return False\n\n    cdef has_decoder(self):\n        cdef Codec elem_codec\n\n        if self.c_decoder is not NULL or self.py_decoder is not None:\n            return True\n\n        elif (\n            self.type == CODEC_ARRAY\n            or self.type == CODEC_RANGE\n            or self.type == CODEC_MULTIRANGE\n        ):\n            return self.element_codec.has_decoder()\n\n        elif self.type == CODEC_COMPOSITE:\n            for elem_codec in self.element_codecs:\n                if not elem_codec.has_decoder():\n                    return False\n            return True\n\n        else:\n            return False\n\n    cdef is_binary(self):\n        return self.format == PG_FORMAT_BINARY\n\n    def __repr__(self):\n        return '<Codec oid={} elem_oid={} core={}>'.format(\n            self.oid,\n            'NA' if self.element_codec is None else self.element_codec.oid,\n            has_core_codec(self.oid))\n\n    @staticmethod\n    cdef Codec new_array_codec(uint32_t oid,\n                               str name,\n                               str schema,\n                               Codec element_codec,\n                               Py_UCS4 element_delimiter):\n        cdef Codec codec\n        codec = Codec(oid)\n        codec.init(name, schema, 'array', CODEC_ARRAY, element_codec.format,\n                   PG_XFORMAT_OBJECT, NULL, NULL, None, None, None,\n                   element_codec, None, None, None, element_delimiter)\n        return codec\n\n    @staticmethod\n    cdef Codec new_range_codec(uint32_t oid,\n                               str name,\n                               str schema,\n                               Codec element_codec):\n        cdef Codec codec\n        codec = Codec(oid)\n        codec.init(name, schema, 'range', CODEC_RANGE, element_codec.format,\n                   PG_XFORMAT_OBJECT, NULL, NULL, None, None, None,\n                   element_codec, None, None, None, 0)\n        return codec\n\n    @staticmethod\n    cdef Codec new_multirange_codec(uint32_t oid,\n                                    str name,\n                                    str schema,\n                                    Codec element_codec):\n        cdef Codec codec\n        codec = Codec(oid)\n        codec.init(name, schema, 'multirange', CODEC_MULTIRANGE,\n                   element_codec.format, PG_XFORMAT_OBJECT, NULL, NULL, None,\n                   None, None, element_codec, None, None, None, 0)\n        return codec\n\n    @staticmethod\n    cdef Codec new_composite_codec(uint32_t oid,\n                                   str name,\n                                   str schema,\n                                   ServerDataFormat format,\n                                   list element_codecs,\n                                   tuple element_type_oids,\n                                   object element_names):\n        cdef Codec codec\n        codec = Codec(oid)\n        codec.init(name, schema, 'composite', CODEC_COMPOSITE,\n                   format, PG_XFORMAT_OBJECT, NULL, NULL, None, None, None,\n                   None, element_type_oids, element_names, element_codecs, 0)\n        return codec\n\n    @staticmethod\n    cdef Codec new_python_codec(uint32_t oid,\n                                str name,\n                                str schema,\n                                str kind,\n                                object encoder,\n                                object decoder,\n                                encode_func c_encoder,\n                                decode_func c_decoder,\n                                Codec base_codec,\n                                ServerDataFormat format,\n                                ClientExchangeFormat xformat):\n        cdef Codec codec\n        codec = Codec(oid)\n        codec.init(name, schema, kind, CODEC_PY, format, xformat,\n                   c_encoder, c_decoder, base_codec, encoder, decoder,\n                   None, None, None, None, 0)\n        return codec\n\n\n# Encode callback for arrays\ncdef codec_encode_func_ex(ConnectionSettings settings, WriteBuffer buf,\n                          object obj, const void *arg):\n    return (<Codec>arg).encode(settings, buf, obj)\n\n\n# Decode callback for arrays\ncdef codec_decode_func_ex(ConnectionSettings settings, FRBuffer *buf,\n                          const void *arg):\n    return (<Codec>arg).decode(settings, buf)\n\n\ncdef uint32_t pylong_as_oid(val) except? 0xFFFFFFFFl:\n    cdef:\n        int64_t oid = 0\n        bint overflow = False\n\n    try:\n        oid = cpython.PyLong_AsLongLong(val)\n    except OverflowError:\n        overflow = True\n\n    if overflow or (oid < 0 or oid > UINT32_MAX):\n        raise OverflowError('OID value too large: {!r}'.format(val))\n\n    return <uint32_t>val\n\n\ncdef class DataCodecConfig:\n    def __init__(self):\n        # Codec instance cache for derived types:\n        # composites, arrays, ranges, domains and their combinations.\n        self._derived_type_codecs = {}\n        # Codec instances set up by the user for the connection.\n        self._custom_type_codecs = {}\n\n    def add_types(self, types):\n        cdef:\n            Codec elem_codec\n            list comp_elem_codecs\n            ServerDataFormat format\n            ServerDataFormat elem_format\n            bint has_text_elements\n            Py_UCS4 elem_delim\n\n        for ti in types:\n            oid = ti['oid']\n\n            if self.get_codec(oid, PG_FORMAT_ANY) is not None:\n                continue\n\n            name = ti['name']\n            schema = ti['ns']\n            array_element_oid = ti['elemtype']\n            range_subtype_oid = ti['range_subtype']\n            if ti['attrtypoids']:\n                comp_type_attrs = tuple(ti['attrtypoids'])\n            else:\n                comp_type_attrs = None\n            base_type = ti['basetype']\n\n            if array_element_oid:\n                # Array type (note, there is no separate 'kind' for arrays)\n\n                # Canonicalize type name to \"elemtype[]\"\n                if name.startswith('_'):\n                    name = name[1:]\n                name = '{}[]'.format(name)\n\n                elem_codec = self.get_codec(array_element_oid, PG_FORMAT_ANY)\n                if elem_codec is None:\n                    elem_codec = self.declare_fallback_codec(\n                        array_element_oid, ti['elemtype_name'], schema)\n\n                elem_delim = <Py_UCS4>ti['elemdelim'][0]\n\n                self._derived_type_codecs[oid, elem_codec.format] = \\\n                    Codec.new_array_codec(\n                        oid, name, schema, elem_codec, elem_delim)\n\n            elif ti['kind'] == b'c':\n                # Composite type\n\n                if not comp_type_attrs:\n                    raise exceptions.InternalClientError(\n                        f'type record missing field types for composite {oid}')\n\n                comp_elem_codecs = []\n                has_text_elements = False\n\n                for typoid in comp_type_attrs:\n                    elem_codec = self.get_codec(typoid, PG_FORMAT_ANY)\n                    if elem_codec is None:\n                        raise exceptions.InternalClientError(\n                            f'no codec for composite attribute type {typoid}')\n                    if elem_codec.format is PG_FORMAT_TEXT:\n                        has_text_elements = True\n                    comp_elem_codecs.append(elem_codec)\n\n                element_names = collections.OrderedDict()\n                for i, attrname in enumerate(ti['attrnames']):\n                    element_names[attrname] = i\n\n                # If at least one element is text-encoded, we must\n                # encode the whole composite as text.\n                if has_text_elements:\n                    elem_format = PG_FORMAT_TEXT\n                else:\n                    elem_format = PG_FORMAT_BINARY\n\n                self._derived_type_codecs[oid, elem_format] = \\\n                    Codec.new_composite_codec(\n                        oid, name, schema, elem_format, comp_elem_codecs,\n                        comp_type_attrs, element_names)\n\n            elif ti['kind'] == b'd':\n                # Domain type\n\n                if not base_type:\n                    raise exceptions.InternalClientError(\n                        f'type record missing base type for domain {oid}')\n\n                elem_codec = self.get_codec(base_type, PG_FORMAT_ANY)\n                if elem_codec is None:\n                    elem_codec = self.declare_fallback_codec(\n                        base_type, ti['basetype_name'], schema)\n\n                self._derived_type_codecs[oid, elem_codec.format] = elem_codec\n\n            elif ti['kind'] == b'r':\n                # Range type\n\n                if not range_subtype_oid:\n                    raise exceptions.InternalClientError(\n                        f'type record missing base type for range {oid}')\n\n                elem_codec = self.get_codec(range_subtype_oid, PG_FORMAT_ANY)\n                if elem_codec is None:\n                    elem_codec = self.declare_fallback_codec(\n                        range_subtype_oid, ti['range_subtype_name'], schema)\n\n                self._derived_type_codecs[oid, elem_codec.format] = \\\n                    Codec.new_range_codec(oid, name, schema, elem_codec)\n\n            elif ti['kind'] == b'm':\n                # Multirange type\n\n                if not range_subtype_oid:\n                    raise exceptions.InternalClientError(\n                        f'type record missing base type for multirange {oid}')\n\n                elem_codec = self.get_codec(range_subtype_oid, PG_FORMAT_ANY)\n                if elem_codec is None:\n                    elem_codec = self.declare_fallback_codec(\n                        range_subtype_oid, ti['range_subtype_name'], schema)\n\n                self._derived_type_codecs[oid, elem_codec.format] = \\\n                    Codec.new_multirange_codec(oid, name, schema, elem_codec)\n\n            elif ti['kind'] == b'e':\n                # Enum types are essentially text\n                self._set_builtin_type_codec(oid, name, schema, 'scalar',\n                                             TEXTOID, PG_FORMAT_ANY)\n            else:\n                self.declare_fallback_codec(oid, name, schema)\n\n    def add_python_codec(self, typeoid, typename, typeschema, typekind,\n                         typeinfos, encoder, decoder, format, xformat):\n        cdef:\n            Codec core_codec = None\n            encode_func c_encoder = NULL\n            decode_func c_decoder = NULL\n            Codec base_codec = None\n            uint32_t oid = pylong_as_oid(typeoid)\n            bint codec_set = False\n\n        # Clear all previous overrides (this also clears type cache).\n        self.remove_python_codec(typeoid, typename, typeschema)\n\n        if typeinfos:\n            self.add_types(typeinfos)\n\n        if format == PG_FORMAT_ANY:\n            formats = (PG_FORMAT_TEXT, PG_FORMAT_BINARY)\n        else:\n            formats = (format,)\n\n        for fmt in formats:\n            if xformat == PG_XFORMAT_TUPLE:\n                if typekind == \"scalar\":\n                    core_codec = get_core_codec(oid, fmt, xformat)\n                    if core_codec is None:\n                        continue\n                    c_encoder = core_codec.c_encoder\n                    c_decoder = core_codec.c_decoder\n                elif typekind == \"composite\":\n                    base_codec = self.get_codec(oid, fmt)\n                    if base_codec is None:\n                        continue\n\n            self._custom_type_codecs[typeoid, fmt] = \\\n                Codec.new_python_codec(oid, typename, typeschema, typekind,\n                                       encoder, decoder, c_encoder, c_decoder,\n                                       base_codec, fmt, xformat)\n            codec_set = True\n\n        if not codec_set:\n            raise exceptions.InterfaceError(\n                \"{} type does not support the 'tuple' exchange format\".format(\n                    typename))\n\n    def remove_python_codec(self, typeoid, typename, typeschema):\n        for fmt in (PG_FORMAT_BINARY, PG_FORMAT_TEXT):\n            self._custom_type_codecs.pop((typeoid, fmt), None)\n        self.clear_type_cache()\n\n    def _set_builtin_type_codec(self, typeoid, typename, typeschema, typekind,\n                                alias_to, format=PG_FORMAT_ANY):\n        cdef:\n            Codec codec\n            Codec target_codec\n            uint32_t oid = pylong_as_oid(typeoid)\n            uint32_t alias_oid = 0\n            bint codec_set = False\n\n        if format == PG_FORMAT_ANY:\n            formats = (PG_FORMAT_BINARY, PG_FORMAT_TEXT)\n        else:\n            formats = (format,)\n\n        if isinstance(alias_to, int):\n            alias_oid = pylong_as_oid(alias_to)\n        else:\n            alias_oid = BUILTIN_TYPE_NAME_MAP.get(alias_to, 0)\n\n        for format in formats:\n            if alias_oid != 0:\n                target_codec = self.get_codec(alias_oid, format)\n            else:\n                target_codec = get_extra_codec(alias_to, format)\n\n            if target_codec is None:\n                continue\n\n            codec = target_codec.copy()\n            codec.oid = typeoid\n            codec.name = typename\n            codec.schema = typeschema\n            codec.kind = typekind\n\n            self._custom_type_codecs[typeoid, format] = codec\n            codec_set = True\n\n        if not codec_set:\n            if format == PG_FORMAT_BINARY:\n                codec_str = 'binary'\n            elif format == PG_FORMAT_TEXT:\n                codec_str = 'text'\n            else:\n                codec_str = 'text or binary'\n\n            raise exceptions.InterfaceError(\n                f'cannot alias {typename} to {alias_to}: '\n                f'there is no {codec_str} codec for {alias_to}')\n\n    def set_builtin_type_codec(self, typeoid, typename, typeschema, typekind,\n                               alias_to, format=PG_FORMAT_ANY):\n        self._set_builtin_type_codec(typeoid, typename, typeschema, typekind,\n                                     alias_to, format)\n        self.clear_type_cache()\n\n    def clear_type_cache(self):\n        self._derived_type_codecs.clear()\n\n    def declare_fallback_codec(self, uint32_t oid, str name, str schema):\n        cdef Codec codec\n\n        if oid <= MAXBUILTINOID:\n            # This is a BKI type, for which asyncpg has no\n            # defined codec.  This should only happen for newly\n            # added builtin types, for which this version of\n            # asyncpg is lacking support.\n            #\n            raise exceptions.UnsupportedClientFeatureError(\n                f'unhandled standard data type {name!r} (OID {oid})')\n        else:\n            # This is a non-BKI type, and as such, has no\n            # stable OID, so no possibility of a builtin codec.\n            # In this case, fallback to text format.  Applications\n            # can avoid this by specifying a codec for this type\n            # using Connection.set_type_codec().\n            #\n            self._set_builtin_type_codec(oid, name, schema, 'scalar',\n                                         TEXTOID, PG_FORMAT_TEXT)\n\n            codec = self.get_codec(oid, PG_FORMAT_TEXT)\n\n        return codec\n\n    cdef inline Codec get_codec(self, uint32_t oid, ServerDataFormat format,\n                                bint ignore_custom_codec=False):\n        cdef Codec codec\n\n        if format == PG_FORMAT_ANY:\n            codec = self.get_codec(\n                oid, PG_FORMAT_BINARY, ignore_custom_codec)\n            if codec is None:\n                codec = self.get_codec(\n                    oid, PG_FORMAT_TEXT, ignore_custom_codec)\n            return codec\n        else:\n            if not ignore_custom_codec:\n                codec = self.get_custom_codec(oid, PG_FORMAT_ANY)\n                if codec is not None:\n                    if codec.format != format:\n                        # The codec for this OID has been overridden by\n                        # set_{builtin}_type_codec with a different format.\n                        # We must respect that and not return a core codec.\n                        return None\n                    else:\n                        return codec\n\n            codec = get_core_codec(oid, format)\n            if codec is not None:\n                return codec\n            else:\n                try:\n                    return self._derived_type_codecs[oid, format]\n                except KeyError:\n                    return None\n\n    cdef inline Codec get_custom_codec(\n        self,\n        uint32_t oid,\n        ServerDataFormat format\n    ):\n        cdef Codec codec\n\n        if format == PG_FORMAT_ANY:\n            codec = self.get_custom_codec(oid, PG_FORMAT_BINARY)\n            if codec is None:\n                codec = self.get_custom_codec(oid, PG_FORMAT_TEXT)\n        else:\n            codec = self._custom_type_codecs.get((oid, format))\n\n        return codec\n\n\ncdef inline Codec get_core_codec(\n        uint32_t oid, ServerDataFormat format,\n        ClientExchangeFormat xformat=PG_XFORMAT_OBJECT):\n    cdef:\n        void *ptr = NULL\n\n    if oid > MAXSUPPORTEDOID:\n        return None\n    if format == PG_FORMAT_BINARY:\n        ptr = (<CodecMap>codec_map).get_binary_codec_ptr(oid * xformat)\n    elif format == PG_FORMAT_TEXT:\n        ptr = (<CodecMap>codec_map).get_text_codec_ptr(oid * xformat)\n\n    if ptr is NULL:\n        return None\n    else:\n        return <Codec>ptr\n\n\ncdef inline Codec get_any_core_codec(\n        uint32_t oid, ServerDataFormat format,\n        ClientExchangeFormat xformat=PG_XFORMAT_OBJECT):\n    \"\"\"A version of get_core_codec that accepts PG_FORMAT_ANY.\"\"\"\n    cdef:\n        Codec codec\n\n    if format == PG_FORMAT_ANY:\n        codec = get_core_codec(oid, PG_FORMAT_BINARY, xformat)\n        if codec is None:\n            codec = get_core_codec(oid, PG_FORMAT_TEXT, xformat)\n    else:\n        codec = get_core_codec(oid, format, xformat)\n\n    return codec\n\n\ncdef inline int has_core_codec(uint32_t oid):\n    return (\n        (<CodecMap>codec_map).get_binary_codec_ptr(oid) != NULL\n        or (<CodecMap>codec_map).get_text_codec_ptr(oid) != NULL\n    )\n\n\ncdef register_core_codec(uint32_t oid,\n                         encode_func encode,\n                         decode_func decode,\n                         ServerDataFormat format,\n                         ClientExchangeFormat xformat=PG_XFORMAT_OBJECT):\n\n    if oid > MAXSUPPORTEDOID:\n        raise exceptions.InternalClientError(\n            'cannot register core codec for OID {}: it is greater '\n            'than MAXSUPPORTEDOID ({})'.format(oid, MAXSUPPORTEDOID))\n\n    cdef:\n        Codec codec\n        str name\n        str kind\n\n    name = BUILTIN_TYPE_OID_MAP[oid]\n    kind = 'array' if oid in ARRAY_TYPES else 'scalar'\n\n    codec = Codec(oid)\n    codec.init(name, 'pg_catalog', kind, CODEC_C, format, xformat,\n               encode, decode, None, None, None, None, None, None, None, 0)\n    cpython.Py_INCREF(codec)  # immortalize\n\n    if format == PG_FORMAT_BINARY:\n        (<CodecMap>codec_map).set_binary_codec_ptr(oid * xformat, <void*>codec)\n    elif format == PG_FORMAT_TEXT:\n        (<CodecMap>codec_map).set_text_codec_ptr(oid * xformat, <void*>codec)\n    else:\n        raise exceptions.InternalClientError(\n            'invalid data format: {}'.format(format))\n\n\ncdef register_extra_codec(str name,\n                          encode_func encode,\n                          decode_func decode,\n                          ServerDataFormat format):\n    cdef:\n        Codec codec\n        str kind\n\n    kind = 'scalar'\n\n    codec = Codec(INVALIDOID)\n    codec.init(name, None, kind, CODEC_C, format, PG_XFORMAT_OBJECT,\n               encode, decode, None, None, None, None, None, None, None, 0)\n    (<CodecMap>codec_map).extra_codecs[name, format] = codec\n\n\ncdef inline Codec get_extra_codec(str name, ServerDataFormat format):\n    return (<CodecMap>codec_map).extra_codecs.get((name, format))\n"
  },
  {
    "path": "asyncpg/protocol/codecs/pgproto.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncdef init_bits_codecs():\n    register_core_codec(BITOID,\n                        <encode_func>pgproto.bits_encode,\n                        <decode_func>pgproto.bits_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(VARBITOID,\n                        <encode_func>pgproto.bits_encode,\n                        <decode_func>pgproto.bits_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_bytea_codecs():\n    register_core_codec(BYTEAOID,\n                        <encode_func>pgproto.bytea_encode,\n                        <decode_func>pgproto.bytea_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(CHAROID,\n                        <encode_func>pgproto.bytea_encode,\n                        <decode_func>pgproto.bytea_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_datetime_codecs():\n    register_core_codec(DATEOID,\n                        <encode_func>pgproto.date_encode,\n                        <decode_func>pgproto.date_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(DATEOID,\n                        <encode_func>pgproto.date_encode_tuple,\n                        <decode_func>pgproto.date_decode_tuple,\n                        PG_FORMAT_BINARY,\n                        PG_XFORMAT_TUPLE)\n\n    register_core_codec(TIMEOID,\n                        <encode_func>pgproto.time_encode,\n                        <decode_func>pgproto.time_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(TIMEOID,\n                        <encode_func>pgproto.time_encode_tuple,\n                        <decode_func>pgproto.time_decode_tuple,\n                        PG_FORMAT_BINARY,\n                        PG_XFORMAT_TUPLE)\n\n    register_core_codec(TIMETZOID,\n                        <encode_func>pgproto.timetz_encode,\n                        <decode_func>pgproto.timetz_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(TIMETZOID,\n                        <encode_func>pgproto.timetz_encode_tuple,\n                        <decode_func>pgproto.timetz_decode_tuple,\n                        PG_FORMAT_BINARY,\n                        PG_XFORMAT_TUPLE)\n\n    register_core_codec(TIMESTAMPOID,\n                        <encode_func>pgproto.timestamp_encode,\n                        <decode_func>pgproto.timestamp_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(TIMESTAMPOID,\n                        <encode_func>pgproto.timestamp_encode_tuple,\n                        <decode_func>pgproto.timestamp_decode_tuple,\n                        PG_FORMAT_BINARY,\n                        PG_XFORMAT_TUPLE)\n\n    register_core_codec(TIMESTAMPTZOID,\n                        <encode_func>pgproto.timestamptz_encode,\n                        <decode_func>pgproto.timestamptz_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(TIMESTAMPTZOID,\n                        <encode_func>pgproto.timestamp_encode_tuple,\n                        <decode_func>pgproto.timestamp_decode_tuple,\n                        PG_FORMAT_BINARY,\n                        PG_XFORMAT_TUPLE)\n\n    register_core_codec(INTERVALOID,\n                        <encode_func>pgproto.interval_encode,\n                        <decode_func>pgproto.interval_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(INTERVALOID,\n                        <encode_func>pgproto.interval_encode_tuple,\n                        <decode_func>pgproto.interval_decode_tuple,\n                        PG_FORMAT_BINARY,\n                        PG_XFORMAT_TUPLE)\n\n    # For obsolete abstime/reltime/tinterval, we do not bother to\n    # interpret the value, and simply return and pass it as text.\n    #\n    register_core_codec(ABSTIMEOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    register_core_codec(RELTIMEOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    register_core_codec(TINTERVALOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n\ncdef init_float_codecs():\n    register_core_codec(FLOAT4OID,\n                        <encode_func>pgproto.float4_encode,\n                        <decode_func>pgproto.float4_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(FLOAT8OID,\n                        <encode_func>pgproto.float8_encode,\n                        <decode_func>pgproto.float8_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_geometry_codecs():\n    register_core_codec(BOXOID,\n                        <encode_func>pgproto.box_encode,\n                        <decode_func>pgproto.box_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(LINEOID,\n                        <encode_func>pgproto.line_encode,\n                        <decode_func>pgproto.line_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(LSEGOID,\n                        <encode_func>pgproto.lseg_encode,\n                        <decode_func>pgproto.lseg_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(POINTOID,\n                        <encode_func>pgproto.point_encode,\n                        <decode_func>pgproto.point_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(PATHOID,\n                        <encode_func>pgproto.path_encode,\n                        <decode_func>pgproto.path_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(POLYGONOID,\n                        <encode_func>pgproto.poly_encode,\n                        <decode_func>pgproto.poly_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(CIRCLEOID,\n                        <encode_func>pgproto.circle_encode,\n                        <decode_func>pgproto.circle_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_hstore_codecs():\n    register_extra_codec('pg_contrib.hstore',\n                         <encode_func>pgproto.hstore_encode,\n                         <decode_func>pgproto.hstore_decode,\n                         PG_FORMAT_BINARY)\n\n\ncdef init_json_codecs():\n    register_core_codec(JSONOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_BINARY)\n    register_core_codec(JSONBOID,\n                        <encode_func>pgproto.jsonb_encode,\n                        <decode_func>pgproto.jsonb_decode,\n                        PG_FORMAT_BINARY)\n    register_core_codec(JSONPATHOID,\n                        <encode_func>pgproto.jsonpath_encode,\n                        <decode_func>pgproto.jsonpath_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_int_codecs():\n\n    register_core_codec(BOOLOID,\n                        <encode_func>pgproto.bool_encode,\n                        <decode_func>pgproto.bool_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(INT2OID,\n                        <encode_func>pgproto.int2_encode,\n                        <decode_func>pgproto.int2_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(INT4OID,\n                        <encode_func>pgproto.int4_encode,\n                        <decode_func>pgproto.int4_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(INT8OID,\n                        <encode_func>pgproto.int8_encode,\n                        <decode_func>pgproto.int8_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_pseudo_codecs():\n    # Void type is returned by SELECT void_returning_function()\n    register_core_codec(VOIDOID,\n                        <encode_func>pgproto.void_encode,\n                        <decode_func>pgproto.void_decode,\n                        PG_FORMAT_BINARY)\n\n    # Unknown type, always decoded as text\n    register_core_codec(UNKNOWNOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    # OID and friends\n    oid_types = [\n        OIDOID, XIDOID, CIDOID\n    ]\n\n    for oid_type in oid_types:\n        register_core_codec(oid_type,\n                            <encode_func>pgproto.uint4_encode,\n                            <decode_func>pgproto.uint4_decode,\n                            PG_FORMAT_BINARY)\n\n    # 64-bit OID types\n    oid8_types = [\n        XID8OID,\n    ]\n\n    for oid_type in oid8_types:\n        register_core_codec(oid_type,\n                            <encode_func>pgproto.uint8_encode,\n                            <decode_func>pgproto.uint8_decode,\n                            PG_FORMAT_BINARY)\n\n    # reg* types -- these are really system catalog OIDs, but\n    # allow the catalog object name as an input.  We could just\n    # decode these as OIDs, but handling them as text seems more\n    # useful.\n    #\n    reg_types = [\n        REGPROCOID, REGPROCEDUREOID, REGOPEROID, REGOPERATOROID,\n        REGCLASSOID, REGTYPEOID, REGCONFIGOID, REGDICTIONARYOID,\n        REGNAMESPACEOID, REGROLEOID, REFCURSOROID, REGCOLLATIONOID,\n    ]\n\n    for reg_type in reg_types:\n        register_core_codec(reg_type,\n                            <encode_func>pgproto.text_encode,\n                            <decode_func>pgproto.text_decode,\n                            PG_FORMAT_TEXT)\n\n    # cstring type is used by Postgres' I/O functions\n    register_core_codec(CSTRINGOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_BINARY)\n\n    # various system pseudotypes with no I/O\n    no_io_types = [\n        ANYOID, TRIGGEROID, EVENT_TRIGGEROID, LANGUAGE_HANDLEROID,\n        FDW_HANDLEROID, TSM_HANDLEROID, INTERNALOID, OPAQUEOID,\n        ANYELEMENTOID, ANYNONARRAYOID, ANYCOMPATIBLEOID,\n        ANYCOMPATIBLEARRAYOID, ANYCOMPATIBLENONARRAYOID,\n        ANYCOMPATIBLERANGEOID, ANYCOMPATIBLEMULTIRANGEOID,\n        ANYRANGEOID, ANYMULTIRANGEOID, ANYARRAYOID,\n        PG_DDL_COMMANDOID, INDEX_AM_HANDLEROID, TABLE_AM_HANDLEROID,\n    ]\n\n    register_core_codec(ANYENUMOID,\n                        NULL,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    for no_io_type in no_io_types:\n        register_core_codec(no_io_type,\n                            NULL,\n                            NULL,\n                            PG_FORMAT_BINARY)\n\n    # ACL specification string\n    register_core_codec(ACLITEMOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    # Postgres' serialized expression tree type\n    register_core_codec(PG_NODE_TREEOID,\n                        NULL,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    # pg_lsn type -- a pointer to a location in the XLOG.\n    register_core_codec(PG_LSNOID,\n                        <encode_func>pgproto.int8_encode,\n                        <decode_func>pgproto.int8_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(SMGROID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    # pg_dependencies and pg_ndistinct are special types\n    # used in pg_statistic_ext columns.\n    register_core_codec(PG_DEPENDENCIESOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    register_core_codec(PG_NDISTINCTOID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    # pg_mcv_list is a special type used in pg_statistic_ext_data\n    # system catalog\n    register_core_codec(PG_MCV_LISTOID,\n                        <encode_func>pgproto.bytea_encode,\n                        <decode_func>pgproto.bytea_decode,\n                        PG_FORMAT_BINARY)\n\n    # These two are internal to BRIN index support and are unlikely\n    # to be sent, but since I/O functions for these exist, add decoders\n    # nonetheless.\n    register_core_codec(PG_BRIN_BLOOM_SUMMARYOID,\n                        NULL,\n                        <decode_func>pgproto.bytea_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(PG_BRIN_MINMAX_MULTI_SUMMARYOID,\n                        NULL,\n                        <decode_func>pgproto.bytea_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_text_codecs():\n    textoids = [\n        NAMEOID,\n        BPCHAROID,\n        VARCHAROID,\n        TEXTOID,\n        XMLOID\n    ]\n\n    for oid in textoids:\n        register_core_codec(oid,\n                            <encode_func>pgproto.text_encode,\n                            <decode_func>pgproto.text_decode,\n                            PG_FORMAT_BINARY)\n\n        register_core_codec(oid,\n                            <encode_func>pgproto.text_encode,\n                            <decode_func>pgproto.text_decode,\n                            PG_FORMAT_TEXT)\n\n\ncdef init_tid_codecs():\n    register_core_codec(TIDOID,\n                        <encode_func>pgproto.tid_encode,\n                        <decode_func>pgproto.tid_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_txid_codecs():\n    register_core_codec(TXID_SNAPSHOTOID,\n                        <encode_func>pgproto.pg_snapshot_encode,\n                        <decode_func>pgproto.pg_snapshot_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(PG_SNAPSHOTOID,\n                        <encode_func>pgproto.pg_snapshot_encode,\n                        <decode_func>pgproto.pg_snapshot_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_tsearch_codecs():\n    ts_oids = [\n        TSQUERYOID,\n        TSVECTOROID,\n    ]\n\n    for oid in ts_oids:\n        register_core_codec(oid,\n                            <encode_func>pgproto.text_encode,\n                            <decode_func>pgproto.text_decode,\n                            PG_FORMAT_TEXT)\n\n    register_core_codec(GTSVECTOROID,\n                        NULL,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n\ncdef init_uuid_codecs():\n    register_core_codec(UUIDOID,\n                        <encode_func>pgproto.uuid_encode,\n                        <decode_func>pgproto.uuid_decode,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_numeric_codecs():\n    register_core_codec(NUMERICOID,\n                        <encode_func>pgproto.numeric_encode_text,\n                        <decode_func>pgproto.numeric_decode_text,\n                        PG_FORMAT_TEXT)\n\n    register_core_codec(NUMERICOID,\n                        <encode_func>pgproto.numeric_encode_binary,\n                        <decode_func>pgproto.numeric_decode_binary,\n                        PG_FORMAT_BINARY)\n\n\ncdef init_network_codecs():\n    register_core_codec(CIDROID,\n                        <encode_func>pgproto.cidr_encode,\n                        <decode_func>pgproto.cidr_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(INETOID,\n                        <encode_func>pgproto.inet_encode,\n                        <decode_func>pgproto.inet_decode,\n                        PG_FORMAT_BINARY)\n\n    register_core_codec(MACADDROID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n    register_core_codec(MACADDR8OID,\n                        <encode_func>pgproto.text_encode,\n                        <decode_func>pgproto.text_decode,\n                        PG_FORMAT_TEXT)\n\n\ncdef init_monetary_codecs():\n    moneyoids = [\n        MONEYOID,\n    ]\n\n    for oid in moneyoids:\n        register_core_codec(oid,\n                            <encode_func>pgproto.text_encode,\n                            <decode_func>pgproto.text_decode,\n                            PG_FORMAT_TEXT)\n\n\ncdef init_all_pgproto_codecs():\n    # Builtin types, in lexicographical order.\n    init_bits_codecs()\n    init_bytea_codecs()\n    init_datetime_codecs()\n    init_float_codecs()\n    init_geometry_codecs()\n    init_int_codecs()\n    init_json_codecs()\n    init_monetary_codecs()\n    init_network_codecs()\n    init_numeric_codecs()\n    init_text_codecs()\n    init_tid_codecs()\n    init_tsearch_codecs()\n    init_txid_codecs()\n    init_uuid_codecs()\n\n    # Various pseudotypes and system types\n    init_pseudo_codecs()\n\n    # contrib\n    init_hstore_codecs()\n\n\ninit_all_pgproto_codecs()\n"
  },
  {
    "path": "asyncpg/protocol/codecs/range.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom asyncpg import types as apg_types\n\nfrom collections.abc import Sequence as SequenceABC\n\n# defined in postgresql/src/include/utils/rangetypes.h\nDEF RANGE_EMPTY  = 0x01  # range is empty\nDEF RANGE_LB_INC = 0x02  # lower bound is inclusive\nDEF RANGE_UB_INC = 0x04  # upper bound is inclusive\nDEF RANGE_LB_INF = 0x08  # lower bound is -infinity\nDEF RANGE_UB_INF = 0x10  # upper bound is +infinity\n\n\ncdef enum _RangeArgumentType:\n    _RANGE_ARGUMENT_INVALID = 0\n    _RANGE_ARGUMENT_TUPLE = 1\n    _RANGE_ARGUMENT_RANGE = 2\n\n\ncdef inline bint _range_has_lbound(uint8_t flags):\n    return not (flags & (RANGE_EMPTY | RANGE_LB_INF))\n\n\ncdef inline bint _range_has_ubound(uint8_t flags):\n    return not (flags & (RANGE_EMPTY | RANGE_UB_INF))\n\n\ncdef inline _RangeArgumentType _range_type(object obj):\n    if cpython.PyTuple_Check(obj) or cpython.PyList_Check(obj):\n        return _RANGE_ARGUMENT_TUPLE\n    elif isinstance(obj, apg_types.Range):\n        return _RANGE_ARGUMENT_RANGE\n    else:\n        return _RANGE_ARGUMENT_INVALID\n\n\ncdef range_encode(ConnectionSettings settings, WriteBuffer buf,\n                  object obj, uint32_t elem_oid,\n                  encode_func_ex encoder, const void *encoder_arg):\n    cdef:\n        ssize_t obj_len\n        uint8_t flags = 0\n        object lower = None\n        object upper = None\n        WriteBuffer bounds_data = WriteBuffer.new()\n        _RangeArgumentType arg_type = _range_type(obj)\n\n    if arg_type == _RANGE_ARGUMENT_INVALID:\n        raise TypeError(\n            'list, tuple or Range object expected (got type {})'.format(\n                type(obj)))\n\n    elif arg_type == _RANGE_ARGUMENT_TUPLE:\n        obj_len = len(obj)\n        if obj_len == 2:\n            lower = obj[0]\n            upper = obj[1]\n\n            if lower is None:\n                flags |= RANGE_LB_INF\n\n            if upper is None:\n                flags |= RANGE_UB_INF\n\n            flags |= RANGE_LB_INC | RANGE_UB_INC\n\n        elif obj_len == 1:\n            lower = obj[0]\n            flags |= RANGE_LB_INC | RANGE_UB_INF\n\n        elif obj_len == 0:\n            flags |= RANGE_EMPTY\n\n        else:\n            raise ValueError(\n                'expected 0, 1 or 2 elements in range (got {})'.format(\n                    obj_len))\n\n    else:\n        if obj.isempty:\n            flags |= RANGE_EMPTY\n        else:\n            lower = obj.lower\n            upper = obj.upper\n\n            if obj.lower_inc:\n                flags |= RANGE_LB_INC\n            elif lower is None:\n                flags |= RANGE_LB_INF\n\n            if obj.upper_inc:\n                flags |= RANGE_UB_INC\n            elif upper is None:\n                flags |= RANGE_UB_INF\n\n    if _range_has_lbound(flags):\n        encoder(settings, bounds_data, lower, encoder_arg)\n\n    if _range_has_ubound(flags):\n        encoder(settings, bounds_data, upper, encoder_arg)\n\n    buf.write_int32(1 + bounds_data.len())\n    buf.write_byte(<int8_t>flags)\n    buf.write_buffer(bounds_data)\n\n\ncdef range_decode(ConnectionSettings settings, FRBuffer *buf,\n                  decode_func_ex decoder, const void *decoder_arg):\n    cdef:\n        uint8_t flags = <uint8_t>frb_read(buf, 1)[0]\n        int32_t bound_len\n        object lower = None\n        object upper = None\n        FRBuffer bound_buf\n\n    if _range_has_lbound(flags):\n        bound_len = hton.unpack_int32(frb_read(buf, 4))\n        if bound_len == -1:\n            lower = None\n        else:\n            frb_slice_from(&bound_buf, buf, bound_len)\n            lower = decoder(settings, &bound_buf, decoder_arg)\n\n    if _range_has_ubound(flags):\n        bound_len = hton.unpack_int32(frb_read(buf, 4))\n        if bound_len == -1:\n            upper = None\n        else:\n            frb_slice_from(&bound_buf, buf, bound_len)\n            upper = decoder(settings, &bound_buf, decoder_arg)\n\n    return apg_types.Range(lower=lower, upper=upper,\n                           lower_inc=(flags & RANGE_LB_INC) != 0,\n                           upper_inc=(flags & RANGE_UB_INC) != 0,\n                           empty=(flags & RANGE_EMPTY) != 0)\n\n\ncdef multirange_encode(ConnectionSettings settings, WriteBuffer buf,\n                       object obj, uint32_t elem_oid,\n                       encode_func_ex encoder, const void *encoder_arg):\n    cdef:\n        WriteBuffer elem_data\n        ssize_t elem_data_len\n        ssize_t elem_count\n\n    if not isinstance(obj, SequenceABC):\n        raise TypeError(\n            'expected a sequence (got type {!r})'.format(type(obj).__name__)\n        )\n\n    elem_data = WriteBuffer.new()\n\n    for elem in obj:\n        range_encode(settings, elem_data, elem, elem_oid, encoder, encoder_arg)\n\n    elem_count = len(obj)\n    if elem_count > INT32_MAX:\n        raise OverflowError(f'too many elements in multirange value')\n\n    elem_data_len = elem_data.len()\n    if elem_data_len > INT32_MAX - 4:\n        raise OverflowError(\n            f'size of encoded multirange datum exceeds the maximum allowed'\n            f' {INT32_MAX - 4} bytes')\n\n    # Datum length\n    buf.write_int32(4 + <int32_t>elem_data_len)\n    # Number of elements in multirange\n    buf.write_int32(<int32_t>elem_count)\n    buf.write_buffer(elem_data)\n\n\ncdef multirange_decode(ConnectionSettings settings, FRBuffer *buf,\n                       decode_func_ex decoder, const void *decoder_arg):\n    cdef:\n        int32_t nelems = hton.unpack_int32(frb_read(buf, 4))\n        FRBuffer elem_buf\n        int32_t elem_len\n        int i\n        list result\n\n    if nelems == 0:\n        return []\n\n    if nelems < 0:\n        raise exceptions.ProtocolError(\n            'unexpected multirange size value: {}'.format(nelems))\n\n    result = cpython.PyList_New(nelems)\n    for i in range(nelems):\n        elem_len = hton.unpack_int32(frb_read(buf, 4))\n        if elem_len == -1:\n            raise exceptions.ProtocolError(\n                'unexpected NULL element in multirange value')\n        else:\n            frb_slice_from(&elem_buf, buf, elem_len)\n        elem = range_decode(settings, &elem_buf, decoder, decoder_arg)\n        cpython.Py_INCREF(elem)\n        cpython.PyList_SET_ITEM(result, i, elem)\n\n    return result\n"
  },
  {
    "path": "asyncpg/protocol/codecs/record.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom asyncpg import exceptions\n\n\ncdef inline record_encode_frame(ConnectionSettings settings, WriteBuffer buf,\n                                WriteBuffer elem_data, int32_t elem_count):\n    buf.write_int32(4 + elem_data.len())\n    # attribute count\n    buf.write_int32(elem_count)\n    # encoded attribute data\n    buf.write_buffer(elem_data)\n\n\ncdef anonymous_record_decode(ConnectionSettings settings, FRBuffer *buf):\n    cdef:\n        tuple result\n        ssize_t elem_count\n        ssize_t i\n        int32_t elem_len\n        uint32_t elem_typ\n        Codec elem_codec\n        FRBuffer elem_buf\n\n    elem_count = <ssize_t><uint32_t>hton.unpack_int32(frb_read(buf, 4))\n    result = cpython.PyTuple_New(elem_count)\n\n    for i in range(elem_count):\n        elem_typ = <uint32_t>hton.unpack_int32(frb_read(buf, 4))\n        elem_len = hton.unpack_int32(frb_read(buf, 4))\n\n        if elem_len == -1:\n            elem = None\n        else:\n            elem_codec = settings.get_data_codec(elem_typ)\n            if elem_codec is None or not elem_codec.has_decoder():\n                raise exceptions.InternalClientError(\n                    'no decoder for composite type element in '\n                    'position {} of type OID {}'.format(i, elem_typ))\n            elem = elem_codec.decode(settings,\n                                     frb_slice_from(&elem_buf, buf, elem_len))\n\n        cpython.Py_INCREF(elem)\n        cpython.PyTuple_SET_ITEM(result, i, elem)\n\n    return result\n\n\ncdef anonymous_record_encode(ConnectionSettings settings, WriteBuffer buf, obj):\n    raise exceptions.UnsupportedClientFeatureError(\n        'input of anonymous composite types is not supported',\n        hint=(\n            'Consider declaring an explicit composite type and '\n            'using it to cast the argument.'\n        ),\n        detail='PostgreSQL does not implement anonymous composite type input.'\n    )\n\n\ncdef init_record_codecs():\n    register_core_codec(RECORDOID,\n                        <encode_func>anonymous_record_encode,\n                        <decode_func>anonymous_record_decode,\n                        PG_FORMAT_BINARY)\n\ninit_record_codecs()\n"
  },
  {
    "path": "asyncpg/protocol/codecs/textutils.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncdef inline uint32_t _apg_tolower(uint32_t c):\n    if c >= <uint32_t><Py_UCS4>'A' and c <= <uint32_t><Py_UCS4>'Z':\n        return c + <uint32_t><Py_UCS4>'a' - <uint32_t><Py_UCS4>'A'\n    else:\n        return c\n\n\ncdef int apg_strcasecmp(const Py_UCS4 *s1, const Py_UCS4 *s2):\n    cdef:\n        uint32_t c1\n        uint32_t c2\n        int i = 0\n\n    while True:\n        c1 = s1[i]\n        c2 = s2[i]\n\n        if c1 != c2:\n            c1 = _apg_tolower(c1)\n            c2 = _apg_tolower(c2)\n            if c1 != c2:\n                return <int32_t>c1 - <int32_t>c2\n\n        if c1 == 0 or c2 == 0:\n            break\n\n        i += 1\n\n    return 0\n\n\ncdef int apg_strcasecmp_char(const char *s1, const char *s2):\n    cdef:\n        uint8_t c1\n        uint8_t c2\n        int i = 0\n\n    while True:\n        c1 = <uint8_t>s1[i]\n        c2 = <uint8_t>s2[i]\n\n        if c1 != c2:\n            c1 = <uint8_t>_apg_tolower(c1)\n            c2 = <uint8_t>_apg_tolower(c2)\n            if c1 != c2:\n                return <int8_t>c1 - <int8_t>c2\n\n        if c1 == 0 or c2 == 0:\n            break\n\n        i += 1\n\n    return 0\n\n\ncdef inline bint apg_ascii_isspace(Py_UCS4 ch):\n    return (\n        ch == ' ' or\n        ch == '\\n' or\n        ch == '\\r' or\n        ch == '\\t' or\n        ch == '\\v' or\n        ch == '\\f'\n    )\n\n\ncdef Py_UCS4 *apg_parse_int32(Py_UCS4 *buf, int32_t *num):\n    cdef:\n        Py_UCS4 *p\n        int32_t n = 0\n        int32_t neg = 0\n\n    if buf[0] == '-':\n        neg = 1\n        buf += 1\n    elif buf[0] == '+':\n        buf += 1\n\n    p = buf\n    while <int>p[0] >= <int><Py_UCS4>'0' and <int>p[0] <= <int><Py_UCS4>'9':\n        n = 10 * n - (<int>p[0] - <int32_t><Py_UCS4>'0')\n        p += 1\n\n    if p == buf:\n        return NULL\n\n    if not neg:\n        n = -n\n\n    num[0] = n\n\n    return p\n"
  },
  {
    "path": "asyncpg/protocol/consts.pxi",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nDEF _MAXINT32 = 2**31 - 1\nDEF _COPY_BUFFER_SIZE = 524288\nDEF _COPY_SIGNATURE = b\"PGCOPY\\n\\377\\r\\n\\0\"\nDEF _EXECUTE_MANY_BUF_NUM = 4\nDEF _EXECUTE_MANY_BUF_SIZE = 32768\n"
  },
  {
    "path": "asyncpg/protocol/coreproto.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ninclude \"scram.pxd\"\n\n\ncdef enum ConnectionStatus:\n    CONNECTION_OK = 1\n    CONNECTION_BAD = 2\n    CONNECTION_STARTED = 3           # Waiting for connection to be made.\n\n\ncdef enum ProtocolState:\n    PROTOCOL_IDLE = 0\n\n    PROTOCOL_FAILED = 1\n    PROTOCOL_ERROR_CONSUME = 2\n    PROTOCOL_CANCELLED = 3\n    PROTOCOL_TERMINATING = 4\n\n    PROTOCOL_AUTH = 10\n    PROTOCOL_PREPARE = 11\n    PROTOCOL_BIND_EXECUTE = 12\n    PROTOCOL_BIND_EXECUTE_MANY = 13\n    PROTOCOL_CLOSE_STMT_PORTAL = 14\n    PROTOCOL_SIMPLE_QUERY = 15\n    PROTOCOL_EXECUTE = 16\n    PROTOCOL_BIND = 17\n    PROTOCOL_COPY_OUT = 18\n    PROTOCOL_COPY_OUT_DATA = 19\n    PROTOCOL_COPY_OUT_DONE = 20\n    PROTOCOL_COPY_IN = 21\n    PROTOCOL_COPY_IN_DATA = 22\n\n\ncdef enum AuthenticationMessage:\n    AUTH_SUCCESSFUL = 0\n    AUTH_REQUIRED_KERBEROS = 2\n    AUTH_REQUIRED_PASSWORD = 3\n    AUTH_REQUIRED_PASSWORDMD5 = 5\n    AUTH_REQUIRED_SCMCRED = 6\n    AUTH_REQUIRED_GSS = 7\n    AUTH_REQUIRED_GSS_CONTINUE = 8\n    AUTH_REQUIRED_SSPI = 9\n    AUTH_REQUIRED_SASL = 10\n    AUTH_SASL_CONTINUE = 11\n    AUTH_SASL_FINAL = 12\n\n\ncdef enum ResultType:\n    RESULT_OK = 1\n    RESULT_FAILED = 2\n\n\ncdef enum TransactionStatus:\n    PQTRANS_IDLE = 0                 # connection idle\n    PQTRANS_ACTIVE = 1               # command in progress\n    PQTRANS_INTRANS = 2              # idle, within transaction block\n    PQTRANS_INERROR = 3              # idle, within failed transaction\n    PQTRANS_UNKNOWN = 4              # cannot determine status\n\n\nctypedef object (*decode_row_method)(object, const char*, ssize_t)\n\n\ncdef class CoreProtocol:\n    cdef:\n        ReadBuffer buffer\n        bint _skip_discard\n        bint _discard_data\n\n        # executemany support data\n        object _execute_iter\n        str _execute_portal_name\n        str _execute_stmt_name\n\n        ConnectionStatus con_status\n        ProtocolState state\n        TransactionStatus xact_status\n\n        str encoding\n\n        object transport\n\n        object address\n        # Instance of _ConnectionParameters\n        object con_params\n        # Instance of SCRAMAuthentication\n        SCRAMAuthentication scram\n        # Instance of gssapi.SecurityContext or sspilib.SecurityContext\n        object gss_ctx\n\n        readonly int32_t backend_pid\n        readonly int32_t backend_secret\n\n        ## Result\n        ResultType result_type\n        object result\n        bytes result_param_desc\n        bytes result_row_desc\n        bytes result_status_msg\n\n        # True - completed, False - suspended\n        bint result_execute_completed\n\n    cpdef is_in_transaction(self)\n    cdef _process__auth(self, char mtype)\n    cdef _process__prepare(self, char mtype)\n    cdef _process__bind_execute(self, char mtype)\n    cdef _process__bind_execute_many(self, char mtype)\n    cdef _process__close_stmt_portal(self, char mtype)\n    cdef _process__simple_query(self, char mtype)\n    cdef _process__bind(self, char mtype)\n    cdef _process__copy_out(self, char mtype)\n    cdef _process__copy_out_data(self, char mtype)\n    cdef _process__copy_in(self, char mtype)\n    cdef _process__copy_in_data(self, char mtype)\n\n    cdef _parse_msg_authentication(self)\n    cdef _parse_msg_parameter_status(self)\n    cdef _parse_msg_notification(self)\n    cdef _parse_msg_backend_key_data(self)\n    cdef _parse_msg_ready_for_query(self)\n    cdef _parse_data_msgs(self)\n    cdef _parse_copy_data_msgs(self)\n    cdef _parse_msg_error_response(self, is_error)\n    cdef _parse_msg_command_complete(self)\n\n    cdef _write_copy_data_msg(self, object data)\n    cdef _write_copy_done_msg(self)\n    cdef _write_copy_fail_msg(self, str cause)\n\n    cdef _auth_password_message_cleartext(self)\n    cdef _auth_password_message_md5(self, bytes salt)\n    cdef _auth_password_message_sasl_initial(self, list sasl_auth_methods)\n    cdef _auth_password_message_sasl_continue(self, bytes server_response)\n    cdef _auth_gss_init_gssapi(self)\n    cdef _auth_gss_init_sspi(self, bint negotiate)\n    cdef _auth_gss_get_service(self)\n    cdef _auth_gss_step(self, bytes server_response)\n\n    cdef _write(self, buf)\n    cdef _writelines(self, list buffers)\n\n    cdef _read_server_messages(self)\n\n    cdef _push_result(self)\n    cdef _reset_result(self)\n    cdef _set_state(self, ProtocolState new_state)\n\n    cdef _ensure_connected(self)\n\n    cdef WriteBuffer _build_parse_message(self, str stmt_name, str query)\n    cdef WriteBuffer _build_bind_message(self, str portal_name,\n                                         str stmt_name,\n                                         WriteBuffer bind_data)\n    cdef WriteBuffer _build_empty_bind_data(self)\n    cdef WriteBuffer _build_execute_message(self, str portal_name,\n                                            int32_t limit)\n\n\n    cdef _connect(self)\n    cdef _prepare_and_describe(self, str stmt_name, str query)\n    cdef _send_parse_message(self, str stmt_name, str query)\n    cdef _send_bind_message(self, str portal_name, str stmt_name,\n                            WriteBuffer bind_data, int32_t limit)\n    cdef _bind_execute(self, str portal_name, str stmt_name,\n                       WriteBuffer bind_data, int32_t limit)\n    cdef bint _bind_execute_many(self, str portal_name, str stmt_name,\n                                 object bind_data, bint return_rows)\n    cdef bint _bind_execute_many_more(self, bint first=*)\n    cdef _bind_execute_many_fail(self, object error, bint first=*)\n    cdef _bind(self, str portal_name, str stmt_name,\n               WriteBuffer bind_data)\n    cdef _execute(self, str portal_name, int32_t limit)\n    cdef _close(self, str name, bint is_portal)\n    cdef _simple_query(self, str query)\n    cdef _copy_out(self, str copy_stmt)\n    cdef _copy_in(self, str copy_stmt)\n    cdef _terminate(self)\n\n    cdef _decode_row(self, const char* buf, ssize_t buf_len)\n\n    cdef _on_result(self)\n    cdef _on_notification(self, pid, channel, payload)\n    cdef _on_notice(self, parsed)\n    cdef _set_server_parameter(self, name, val)\n    cdef _on_connection_lost(self, exc)\n"
  },
  {
    "path": "asyncpg/protocol/coreproto.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport hashlib\n\n\ninclude \"scram.pyx\"\n\n\nAUTH_METHOD_NAME = {\n    AUTH_REQUIRED_KERBEROS: 'kerberosv5',\n    AUTH_REQUIRED_PASSWORD: 'password',\n    AUTH_REQUIRED_PASSWORDMD5: 'md5',\n    AUTH_REQUIRED_GSS: 'gss',\n    AUTH_REQUIRED_SASL: 'scram-sha-256',\n    AUTH_REQUIRED_SSPI: 'sspi',\n}\n\n\ncdef class CoreProtocol:\n\n    def __init__(self, addr, con_params):\n        self.address = addr\n        # type of `con_params` is `_ConnectionParameters`\n        self.buffer = ReadBuffer()\n        self.user = con_params.user\n        self.password = con_params.password\n        self.auth_msg = None\n        self.con_params = con_params\n        self.con_status = CONNECTION_BAD\n        self.state = PROTOCOL_IDLE\n        self.xact_status = PQTRANS_IDLE\n        self.encoding = 'utf-8'\n        # type of `scram` is `SCRAMAuthentcation`\n        self.scram = None\n        # type of `gss_ctx` is `gssapi.SecurityContext` or\n        # `sspilib.SecurityContext`\n        self.gss_ctx = None\n\n        self._reset_result()\n\n    cpdef is_in_transaction(self):\n        # PQTRANS_INTRANS = idle, within transaction block\n        # PQTRANS_INERROR = idle, within failed transaction\n        return self.xact_status in (PQTRANS_INTRANS, PQTRANS_INERROR)\n\n    cdef _read_server_messages(self):\n        cdef:\n            char mtype\n            ProtocolState state\n            pgproto.take_message_method take_message = \\\n                <pgproto.take_message_method>self.buffer.take_message\n            pgproto.get_message_type_method get_message_type= \\\n                <pgproto.get_message_type_method>self.buffer.get_message_type\n\n        while take_message(self.buffer) == 1:\n            mtype = get_message_type(self.buffer)\n            state = self.state\n\n            try:\n                if mtype == b'S':\n                    # ParameterStatus\n                    self._parse_msg_parameter_status()\n\n                elif mtype == b'A':\n                    # NotificationResponse\n                    self._parse_msg_notification()\n\n                elif mtype == b'N':\n                    # 'N' - NoticeResponse\n                    self._on_notice(self._parse_msg_error_response(False))\n\n                elif state == PROTOCOL_AUTH:\n                    self._process__auth(mtype)\n\n                elif state == PROTOCOL_PREPARE:\n                    self._process__prepare(mtype)\n\n                elif state == PROTOCOL_BIND_EXECUTE:\n                    self._process__bind_execute(mtype)\n\n                elif state == PROTOCOL_BIND_EXECUTE_MANY:\n                    self._process__bind_execute_many(mtype)\n\n                elif state == PROTOCOL_EXECUTE:\n                    self._process__bind_execute(mtype)\n\n                elif state == PROTOCOL_BIND:\n                    self._process__bind(mtype)\n\n                elif state == PROTOCOL_CLOSE_STMT_PORTAL:\n                    self._process__close_stmt_portal(mtype)\n\n                elif state == PROTOCOL_SIMPLE_QUERY:\n                    self._process__simple_query(mtype)\n\n                elif state == PROTOCOL_COPY_OUT:\n                    self._process__copy_out(mtype)\n\n                elif (state == PROTOCOL_COPY_OUT_DATA or\n                        state == PROTOCOL_COPY_OUT_DONE):\n                    self._process__copy_out_data(mtype)\n\n                elif state == PROTOCOL_COPY_IN:\n                    self._process__copy_in(mtype)\n\n                elif state == PROTOCOL_COPY_IN_DATA:\n                    self._process__copy_in_data(mtype)\n\n                elif state == PROTOCOL_CANCELLED:\n                    # discard all messages until the sync message\n                    if mtype == b'E':\n                        self._parse_msg_error_response(True)\n                    elif mtype == b'Z':\n                        self._parse_msg_ready_for_query()\n                        self._push_result()\n                    else:\n                        self.buffer.discard_message()\n\n                elif state == PROTOCOL_ERROR_CONSUME:\n                    # Error in protocol (on asyncpg side);\n                    # discard all messages until sync message\n\n                    if mtype == b'Z':\n                        # Sync point, self to push the result\n                        if self.result_type != RESULT_FAILED:\n                            self.result_type = RESULT_FAILED\n                            self.result = apg_exc.InternalClientError(\n                                'unknown error in protocol implementation')\n\n                        self._parse_msg_ready_for_query()\n                        self._push_result()\n\n                    else:\n                        self.buffer.discard_message()\n\n                elif state == PROTOCOL_TERMINATING:\n                    # The connection is being terminated.\n                    # discard all messages until connection\n                    # termination.\n                    self.buffer.discard_message()\n\n                else:\n                    raise apg_exc.InternalClientError(\n                        f'cannot process message {chr(mtype)!r}: '\n                        f'protocol is in an unexpected state {state!r}.')\n\n            except Exception as ex:\n                self.result_type = RESULT_FAILED\n                self.result = ex\n\n                if mtype == b'Z':\n                    self._push_result()\n                else:\n                    self.state = PROTOCOL_ERROR_CONSUME\n\n            finally:\n                self.buffer.finish_message()\n\n    cdef _process__auth(self, char mtype):\n        if mtype == b'R':\n            # Authentication...\n            try:\n                self._parse_msg_authentication()\n            except Exception as ex:\n                # Exception in authentication parsing code\n                # is usually either malformed authentication data\n                # or missing support for cryptographic primitives\n                # in the hashlib module.\n                self.result_type = RESULT_FAILED\n                self.result = apg_exc.InternalClientError(\n                    f\"unexpected error while performing authentication: {ex}\")\n                self.result.__cause__ = ex\n                self.con_status = CONNECTION_BAD\n                self._push_result()\n            else:\n                if self.result_type != RESULT_OK:\n                    self.con_status = CONNECTION_BAD\n                    self._push_result()\n\n                elif self.auth_msg is not None:\n                    # Server wants us to send auth data, so do that.\n                    self._write(self.auth_msg)\n                    self.auth_msg = None\n\n        elif mtype == b'K':\n            # BackendKeyData\n            self._parse_msg_backend_key_data()\n\n        elif mtype == b'E':\n            # ErrorResponse\n            self.con_status = CONNECTION_BAD\n            self._parse_msg_error_response(True)\n            self._push_result()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self.con_status = CONNECTION_OK\n            self._push_result()\n\n    cdef _process__prepare(self, char mtype):\n        if mtype == b't':\n            # Parameters description\n            self.result_param_desc = self.buffer.consume_message()\n\n        elif mtype == b'1':\n            # ParseComplete\n            self.buffer.discard_message()\n\n        elif mtype == b'T':\n            # Row description\n            self.result_row_desc = self.buffer.consume_message()\n            self._push_result()\n\n        elif mtype == b'E':\n            # ErrorResponse\n            self._parse_msg_error_response(True)\n            # we don't send a sync during the parse/describe sequence\n            # but send a FLUSH instead. If an error happens we need to\n            # send a SYNC explicitly in order to mark the end of the transaction.\n            # this effectively clears the error and we then wait until we get a\n            # ready for new query message\n            self._write(SYNC_MESSAGE)\n            self.state = PROTOCOL_ERROR_CONSUME\n\n        elif mtype == b'n':\n            # NoData\n            self.buffer.discard_message()\n            self._push_result()\n\n    cdef _process__bind_execute(self, char mtype):\n        if mtype == b'D':\n            # DataRow\n            self._parse_data_msgs()\n\n        elif mtype == b's':\n            # PortalSuspended\n            self.buffer.discard_message()\n\n        elif mtype == b'C':\n            # CommandComplete\n            self.result_execute_completed = True\n            self._parse_msg_command_complete()\n\n        elif mtype == b'E':\n            # ErrorResponse\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'1':\n            # ParseComplete, in case `_bind_execute()` is reparsing\n            self.buffer.discard_message()\n\n        elif mtype == b'2':\n            # BindComplete\n            self.buffer.discard_message()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n        elif mtype == b'I':\n            # EmptyQueryResponse\n            self.buffer.discard_message()\n\n    cdef _process__bind_execute_many(self, char mtype):\n        cdef WriteBuffer buf\n\n        if mtype == b'D':\n            # DataRow\n            self._parse_data_msgs()\n\n        elif mtype == b's':\n            # PortalSuspended\n            self.buffer.discard_message()\n\n        elif mtype == b'C':\n            # CommandComplete\n            self._parse_msg_command_complete()\n\n        elif mtype == b'E':\n            # ErrorResponse\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'1':\n            # ParseComplete, in case `_bind_execute_many()` is reparsing\n            self.buffer.discard_message()\n\n        elif mtype == b'2':\n            # BindComplete\n            self.buffer.discard_message()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n        elif mtype == b'I':\n            # EmptyQueryResponse\n            self.buffer.discard_message()\n\n        elif mtype == b'1':\n            # ParseComplete\n            self.buffer.discard_message()\n\n    cdef _process__bind(self, char mtype):\n        if mtype == b'E':\n            # ErrorResponse\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'2':\n            # BindComplete\n            self.buffer.discard_message()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n    cdef _process__close_stmt_portal(self, char mtype):\n        if mtype == b'E':\n            # ErrorResponse\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'3':\n            # CloseComplete\n            self.buffer.discard_message()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n    cdef _process__simple_query(self, char mtype):\n        if mtype in {b'D', b'I', b'T'}:\n            # 'D' - DataRow\n            # 'I' - EmptyQueryResponse\n            # 'T' - RowDescription\n            self.buffer.discard_message()\n\n        elif mtype == b'E':\n            # ErrorResponse\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n        elif mtype == b'C':\n            # CommandComplete\n            self._parse_msg_command_complete()\n\n        else:\n            # We don't really care about COPY IN etc\n            self.buffer.discard_message()\n\n    cdef _process__copy_out(self, char mtype):\n        if mtype == b'E':\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'H':\n            # CopyOutResponse\n            self._set_state(PROTOCOL_COPY_OUT_DATA)\n            self.buffer.discard_message()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n    cdef _process__copy_out_data(self, char mtype):\n        if mtype == b'E':\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'd':\n            # CopyData\n            self._parse_copy_data_msgs()\n\n        elif mtype == b'c':\n            # CopyDone\n            self.buffer.discard_message()\n            self._set_state(PROTOCOL_COPY_OUT_DONE)\n\n        elif mtype == b'C':\n            # CommandComplete\n            self._parse_msg_command_complete()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n    cdef _process__copy_in(self, char mtype):\n        if mtype == b'E':\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'G':\n            # CopyInResponse\n            self._set_state(PROTOCOL_COPY_IN_DATA)\n            self.buffer.discard_message()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n    cdef _process__copy_in_data(self, char mtype):\n        if mtype == b'E':\n            self._parse_msg_error_response(True)\n\n        elif mtype == b'C':\n            # CommandComplete\n            self._parse_msg_command_complete()\n\n        elif mtype == b'Z':\n            # ReadyForQuery\n            self._parse_msg_ready_for_query()\n            self._push_result()\n\n    cdef _parse_msg_command_complete(self):\n        cdef:\n            const char* cbuf\n            ssize_t cbuf_len\n\n        cbuf = self.buffer.try_consume_message(&cbuf_len)\n        if cbuf != NULL and cbuf_len > 0:\n            msg = cpython.PyBytes_FromStringAndSize(cbuf, cbuf_len - 1)\n        else:\n            msg = self.buffer.read_null_str()\n        self.result_status_msg = msg\n\n    cdef _parse_copy_data_msgs(self):\n        cdef:\n            ReadBuffer buf = self.buffer\n\n        self.result = buf.consume_messages(b'd')\n\n        # By this point we have consumed all CopyData messages\n        # in the inbound buffer.  If there are no messages left\n        # in the buffer, we need to push the accumulated data\n        # out to the caller in anticipation of the new CopyData\n        # batch.  If there _are_ non-CopyData messages left,\n        # we must not push the result here and let the\n        # _process__copy_out_data subprotocol do the job.\n        if not buf.take_message():\n            self._on_result()\n            self.result = None\n        else:\n            # If there is a message in the buffer, put it back to\n            # be processed by the next protocol iteration.\n            buf.put_message()\n\n    cdef _write_copy_data_msg(self, object data):\n        cdef:\n            WriteBuffer buf\n            object mview\n            Py_buffer *pybuf\n\n        mview = cpythonx.PyMemoryView_GetContiguous(\n            data, cpython.PyBUF_READ, b'C')\n\n        try:\n            pybuf = cpythonx.PyMemoryView_GET_BUFFER(mview)\n\n            buf = WriteBuffer.new_message(b'd')\n            buf.write_cstr(<const char *>pybuf.buf, pybuf.len)\n            buf.end_message()\n        finally:\n            mview.release()\n\n        self._write(buf)\n\n    cdef _write_copy_done_msg(self):\n        cdef:\n            WriteBuffer buf\n\n        buf = WriteBuffer.new_message(b'c')\n        buf.end_message()\n        self._write(buf)\n\n    cdef _write_copy_fail_msg(self, str cause):\n        cdef:\n            WriteBuffer buf\n\n        buf = WriteBuffer.new_message(b'f')\n        buf.write_str(cause or '', self.encoding)\n        buf.end_message()\n        self._write(buf)\n\n    cdef _parse_data_msgs(self):\n        cdef:\n            ReadBuffer buf = self.buffer\n            list rows\n\n            decode_row_method decoder = <decode_row_method>self._decode_row\n            pgproto.try_consume_message_method try_consume_message = \\\n                <pgproto.try_consume_message_method>buf.try_consume_message\n            pgproto.take_message_type_method take_message_type = \\\n                <pgproto.take_message_type_method>buf.take_message_type\n\n            const char* cbuf\n            ssize_t cbuf_len\n            object row\n            bytes mem\n\n        if PG_DEBUG:\n            if buf.get_message_type() != b'D':\n                raise apg_exc.InternalClientError(\n                    '_parse_data_msgs: first message is not \"D\"')\n\n        if self._discard_data:\n            while take_message_type(buf, b'D'):\n                buf.discard_message()\n            return\n\n        if PG_DEBUG:\n            if type(self.result) is not list:\n                raise apg_exc.InternalClientError(\n                    '_parse_data_msgs: result is not a list, but {!r}'.\n                    format(self.result))\n\n        rows = self.result\n        while take_message_type(buf, b'D'):\n            cbuf = try_consume_message(buf, &cbuf_len)\n            if cbuf != NULL:\n                row = decoder(self, cbuf, cbuf_len)\n            else:\n                mem = buf.consume_message()\n                row = decoder(\n                    self,\n                    cpython.PyBytes_AS_STRING(mem),\n                    cpython.PyBytes_GET_SIZE(mem))\n\n            cpython.PyList_Append(rows, row)\n\n    cdef _parse_msg_backend_key_data(self):\n        self.backend_pid = self.buffer.read_int32()\n        self.backend_secret = self.buffer.read_int32()\n\n    cdef _parse_msg_parameter_status(self):\n        name = self.buffer.read_null_str()\n        name = name.decode(self.encoding)\n\n        val = self.buffer.read_null_str()\n        val = val.decode(self.encoding)\n\n        self._set_server_parameter(name, val)\n\n    cdef _parse_msg_notification(self):\n        pid = self.buffer.read_int32()\n        channel = self.buffer.read_null_str().decode(self.encoding)\n        payload = self.buffer.read_null_str().decode(self.encoding)\n        self._on_notification(pid, channel, payload)\n\n    cdef _parse_msg_authentication(self):\n        cdef:\n            int32_t status\n            bytes md5_salt\n            list sasl_auth_methods\n            list unsupported_sasl_auth_methods\n\n        status = self.buffer.read_int32()\n\n        if status == AUTH_SUCCESSFUL:\n            # AuthenticationOk\n            self.result_type = RESULT_OK\n\n        elif status == AUTH_REQUIRED_PASSWORD:\n            # AuthenticationCleartextPassword\n            self.result_type = RESULT_OK\n            self.auth_msg = self._auth_password_message_cleartext()\n\n        elif status == AUTH_REQUIRED_PASSWORDMD5:\n            # AuthenticationMD5Password\n            # Note: MD5 salt is passed as a four-byte sequence\n            md5_salt = self.buffer.read_bytes(4)\n            self.auth_msg = self._auth_password_message_md5(md5_salt)\n\n        elif status == AUTH_REQUIRED_SASL:\n            # AuthenticationSASL\n            # This requires making additional requests to the server in order\n            # to follow the SCRAM protocol defined in RFC 5802.\n            # get the SASL authentication methods that the server is providing\n            sasl_auth_methods = []\n            unsupported_sasl_auth_methods = []\n            # determine if the advertised authentication methods are supported,\n            # and if so, add them to the list\n            auth_method = self.buffer.read_null_str()\n            while auth_method:\n                if auth_method in SCRAMAuthentication.AUTHENTICATION_METHODS:\n                    sasl_auth_methods.append(auth_method)\n                else:\n                    unsupported_sasl_auth_methods.append(auth_method)\n                auth_method = self.buffer.read_null_str()\n\n            # if none of the advertised authentication methods are supported,\n            # raise an error\n            # otherwise, initialize the SASL authentication exchange\n            if not sasl_auth_methods:\n                unsupported_sasl_auth_methods = [m.decode(\"ascii\")\n                    for m in unsupported_sasl_auth_methods]\n                self.result_type = RESULT_FAILED\n                self.result = apg_exc.InterfaceError(\n                    'unsupported SASL Authentication methods requested by the '\n                    'server: {!r}'.format(\n                        \", \".join(unsupported_sasl_auth_methods)))\n            else:\n                self.auth_msg = self._auth_password_message_sasl_initial(\n                    sasl_auth_methods)\n\n        elif status == AUTH_SASL_CONTINUE:\n            # AUTH_SASL_CONTINUE\n            # this requeires sending the second part of the SASL exchange, where\n            # the client parses information back from the server and determines\n            # if this is valid.\n            # The client builds a challenge response to the server\n            server_response = self.buffer.consume_message()\n            self.auth_msg = self._auth_password_message_sasl_continue(\n                server_response)\n\n        elif status == AUTH_SASL_FINAL:\n            # AUTH_SASL_FINAL\n            server_response = self.buffer.consume_message()\n            if not self.scram.verify_server_final_message(server_response):\n                self.result_type = RESULT_FAILED\n                self.result = apg_exc.InterfaceError(\n                    'could not verify server signature for '\n                    'SCRAM authentciation: scram-sha-256',\n                )\n            self.scram = None\n\n        elif status in (AUTH_REQUIRED_GSS, AUTH_REQUIRED_SSPI):\n            # AUTH_REQUIRED_SSPI is the same as AUTH_REQUIRED_GSS, except that\n            # it uses protocol negotiation with SSPI clients. Both methods use\n            # AUTH_REQUIRED_GSS_CONTINUE for subsequent authentication steps.\n            if self.gss_ctx is not None:\n                self.result_type = RESULT_FAILED\n                self.result = apg_exc.InterfaceError(\n                    'duplicate GSSAPI/SSPI authentication request')\n            else:\n                if self.con_params.gsslib == 'gssapi':\n                    self._auth_gss_init_gssapi()\n                else:\n                    self._auth_gss_init_sspi(status == AUTH_REQUIRED_SSPI)\n                self.auth_msg = self._auth_gss_step(None)\n\n        elif status == AUTH_REQUIRED_GSS_CONTINUE:\n            server_response = self.buffer.consume_message()\n            self.auth_msg = self._auth_gss_step(server_response)\n\n        else:\n            self.result_type = RESULT_FAILED\n            self.result = apg_exc.InterfaceError(\n                'unsupported authentication method requested by the '\n                'server: {!r}'.format(AUTH_METHOD_NAME.get(status, status)))\n\n        if status not in (AUTH_SASL_CONTINUE, AUTH_SASL_FINAL,\n                          AUTH_REQUIRED_GSS_CONTINUE):\n            self.buffer.discard_message()\n\n    cdef _auth_password_message_cleartext(self):\n        cdef:\n            WriteBuffer msg\n\n        msg = WriteBuffer.new_message(b'p')\n        msg.write_bytestring(self.password.encode(self.encoding))\n        msg.end_message()\n\n        return msg\n\n    cdef _auth_password_message_md5(self, bytes salt):\n        cdef:\n            WriteBuffer msg\n\n        msg = WriteBuffer.new_message(b'p')\n\n        # 'md5' + md5(md5(password + username) + salt))\n        userpass = (self.password or '') + (self.user or '')\n        md5_1 = hashlib.md5(userpass.encode(self.encoding)).hexdigest()\n        md5_2 = hashlib.md5(md5_1.encode('ascii') + salt).hexdigest()\n\n        msg.write_bytestring(b'md5' + md5_2.encode('ascii'))\n        msg.end_message()\n\n        return msg\n\n    cdef _auth_password_message_sasl_initial(self, list sasl_auth_methods):\n        cdef:\n            WriteBuffer msg\n\n        # use the first supported advertized mechanism\n        self.scram = SCRAMAuthentication(sasl_auth_methods[0])\n        # this involves a call and response with the server\n        msg = WriteBuffer.new_message(b'p')\n        msg.write_bytes(self.scram.create_client_first_message(self.user or ''))\n        msg.end_message()\n\n        return msg\n\n    cdef _auth_password_message_sasl_continue(self, bytes server_response):\n        cdef:\n            WriteBuffer msg\n\n        # determine if there is a valid server response\n        self.scram.parse_server_first_message(server_response)\n        # this involves a call and response with the server\n        msg = WriteBuffer.new_message(b'p')\n        client_final_message = self.scram.create_client_final_message(\n            self.password or '')\n        msg.write_bytes(client_final_message)\n        msg.end_message()\n\n        return msg\n\n    cdef _auth_gss_init_gssapi(self):\n        try:\n            import gssapi\n        except ModuleNotFoundError:\n            raise apg_exc.InterfaceError(\n                'gssapi module not found; please install asyncpg[gssauth] to '\n                'use asyncpg with Kerberos/GSSAPI/SSPI authentication'\n            ) from None\n\n        service_name, host = self._auth_gss_get_service()\n        self.gss_ctx = gssapi.SecurityContext(\n            name=gssapi.Name(\n                f'{service_name}@{host}', gssapi.NameType.hostbased_service),\n            usage='initiate')\n\n    cdef _auth_gss_init_sspi(self, bint negotiate):\n        try:\n            import sspilib\n        except ModuleNotFoundError:\n            raise apg_exc.InterfaceError(\n                'sspilib module not found; please install asyncpg[gssauth] to '\n                'use asyncpg with Kerberos/GSSAPI/SSPI authentication'\n            ) from None\n\n        service_name, host = self._auth_gss_get_service()\n        self.gss_ctx = sspilib.ClientSecurityContext(\n            target_name=f'{service_name}/{host}',\n            credential=sspilib.UserCredential(\n                protocol='Negotiate' if negotiate else 'Kerberos'))\n\n    cdef _auth_gss_get_service(self):\n        service_name = self.con_params.krbsrvname or 'postgres'\n        if isinstance(self.address, str):\n            raise apg_exc.InternalClientError(\n                'GSSAPI/SSPI authentication is only supported for TCP/IP '\n                'connections')\n\n        return service_name, self.address[0]\n\n    cdef _auth_gss_step(self, bytes server_response):\n        cdef:\n            WriteBuffer msg\n\n        token = self.gss_ctx.step(server_response)\n        if not token:\n            self.gss_ctx = None\n            return None\n        msg = WriteBuffer.new_message(b'p')\n        msg.write_bytes(token)\n        msg.end_message()\n\n        return msg\n\n    cdef _parse_msg_ready_for_query(self):\n        cdef char status = self.buffer.read_byte()\n\n        if status == b'I':\n            self.xact_status = PQTRANS_IDLE\n        elif status == b'T':\n            self.xact_status = PQTRANS_INTRANS\n        elif status == b'E':\n            self.xact_status = PQTRANS_INERROR\n        else:\n            self.xact_status = PQTRANS_UNKNOWN\n\n    cdef _parse_msg_error_response(self, is_error):\n        cdef:\n            char code\n            bytes message\n            dict parsed = {}\n\n        while True:\n            code = self.buffer.read_byte()\n            if code == 0:\n                break\n\n            message = self.buffer.read_null_str()\n\n            parsed[chr(code)] = message.decode()\n\n        if is_error:\n            self.result_type = RESULT_FAILED\n            self.result = parsed\n        else:\n            return parsed\n\n    cdef _push_result(self):\n        try:\n            self._on_result()\n        finally:\n            self._set_state(PROTOCOL_IDLE)\n            self._reset_result()\n\n    cdef _reset_result(self):\n        self.result_type = RESULT_OK\n        self.result = None\n        self.result_param_desc = None\n        self.result_row_desc = None\n        self.result_status_msg = None\n        self.result_execute_completed = False\n        self._discard_data = False\n\n        # executemany support data\n        self._execute_iter = None\n        self._execute_portal_name = None\n        self._execute_stmt_name = None\n\n    cdef _set_state(self, ProtocolState new_state):\n        if new_state == PROTOCOL_IDLE:\n            if self.state == PROTOCOL_FAILED:\n                raise apg_exc.InternalClientError(\n                    'cannot switch to \"idle\" state; '\n                    'protocol is in the \"failed\" state')\n            elif self.state == PROTOCOL_IDLE:\n                pass\n            else:\n                self.state = new_state\n\n        elif new_state == PROTOCOL_FAILED:\n            self.state = PROTOCOL_FAILED\n\n        elif new_state == PROTOCOL_CANCELLED:\n            self.state = PROTOCOL_CANCELLED\n\n        elif new_state == PROTOCOL_TERMINATING:\n            self.state = PROTOCOL_TERMINATING\n\n        else:\n            if self.state == PROTOCOL_IDLE:\n                self.state = new_state\n\n            elif (self.state == PROTOCOL_COPY_OUT and\n                    new_state == PROTOCOL_COPY_OUT_DATA):\n                self.state = new_state\n\n            elif (self.state == PROTOCOL_COPY_OUT_DATA and\n                    new_state == PROTOCOL_COPY_OUT_DONE):\n                self.state = new_state\n\n            elif (self.state == PROTOCOL_COPY_IN and\n                    new_state == PROTOCOL_COPY_IN_DATA):\n                self.state = new_state\n\n            elif self.state == PROTOCOL_FAILED:\n                raise apg_exc.InternalClientError(\n                    'cannot switch to state {}; '\n                    'protocol is in the \"failed\" state'.format(new_state))\n            else:\n                raise apg_exc.InternalClientError(\n                    'cannot switch to state {}; '\n                    'another operation ({}) is in progress'.format(\n                        new_state, self.state))\n\n    cdef _ensure_connected(self):\n        if self.con_status != CONNECTION_OK:\n            raise apg_exc.InternalClientError('not connected')\n\n    cdef WriteBuffer _build_parse_message(self, str stmt_name, str query):\n        cdef WriteBuffer buf\n\n        buf = WriteBuffer.new_message(b'P')\n        buf.write_str(stmt_name, self.encoding)\n        buf.write_str(query, self.encoding)\n        buf.write_int16(0)\n\n        buf.end_message()\n        return buf\n\n    cdef WriteBuffer _build_bind_message(self, str portal_name,\n                                         str stmt_name,\n                                         WriteBuffer bind_data):\n        cdef WriteBuffer buf\n\n        buf = WriteBuffer.new_message(b'B')\n        buf.write_str(portal_name, self.encoding)\n        buf.write_str(stmt_name, self.encoding)\n\n        # Arguments\n        buf.write_buffer(bind_data)\n\n        buf.end_message()\n        return buf\n\n    cdef WriteBuffer _build_empty_bind_data(self):\n        cdef WriteBuffer buf\n        buf = WriteBuffer.new()\n        buf.write_int16(0)  # The number of parameter format codes\n        buf.write_int16(0)  # The number of parameter values\n        buf.write_int16(0)  # The number of result-column format codes\n        return buf\n\n    cdef WriteBuffer _build_execute_message(self, str portal_name,\n                                            int32_t limit):\n        cdef WriteBuffer buf\n\n        buf = WriteBuffer.new_message(b'E')\n        buf.write_str(portal_name, self.encoding)  # name of the portal\n        buf.write_int32(limit)  # number of rows to return; 0 - all\n\n        buf.end_message()\n        return buf\n\n    # API for subclasses\n\n    cdef _connect(self):\n        cdef:\n            WriteBuffer buf\n            WriteBuffer outbuf\n\n        if self.con_status != CONNECTION_BAD:\n            raise apg_exc.InternalClientError('already connected')\n\n        self._set_state(PROTOCOL_AUTH)\n        self.con_status = CONNECTION_STARTED\n\n        # Assemble a startup message\n        buf = WriteBuffer()\n\n        # protocol version\n        buf.write_int16(3)\n        buf.write_int16(0)\n\n        buf.write_bytestring(b'client_encoding')\n        buf.write_bytestring(\"'{}'\".format(self.encoding).encode('ascii'))\n\n        buf.write_str('user', self.encoding)\n        buf.write_str(self.con_params.user, self.encoding)\n\n        buf.write_str('database', self.encoding)\n        buf.write_str(self.con_params.database, self.encoding)\n\n        if self.con_params.server_settings is not None:\n            for k, v in self.con_params.server_settings.items():\n                buf.write_str(k, self.encoding)\n                buf.write_str(v, self.encoding)\n\n        buf.write_bytestring(b'')\n\n        # Send the buffer\n        outbuf = WriteBuffer()\n        outbuf.write_int32(buf.len() + 4)\n        outbuf.write_buffer(buf)\n        self._write(outbuf)\n\n    cdef _send_parse_message(self, str stmt_name, str query):\n        cdef:\n            WriteBuffer msg\n\n        self._ensure_connected()\n        msg = self._build_parse_message(stmt_name, query)\n        self._write(msg)\n\n    cdef _prepare_and_describe(self, str stmt_name, str query):\n        cdef:\n            WriteBuffer packet\n            WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_PREPARE)\n\n        packet = self._build_parse_message(stmt_name, query)\n\n        buf = WriteBuffer.new_message(b'D')\n        buf.write_byte(b'S')\n        buf.write_str(stmt_name, self.encoding)\n        buf.end_message()\n        packet.write_buffer(buf)\n\n        packet.write_bytes(FLUSH_MESSAGE)\n\n        self._write(packet)\n\n    cdef _send_bind_message(self, str portal_name, str stmt_name,\n                            WriteBuffer bind_data, int32_t limit):\n\n        cdef:\n            WriteBuffer packet\n            WriteBuffer buf\n\n        buf = self._build_bind_message(portal_name, stmt_name, bind_data)\n        packet = buf\n\n        buf = self._build_execute_message(portal_name, limit)\n        packet.write_buffer(buf)\n\n        packet.write_bytes(SYNC_MESSAGE)\n\n        self._write(packet)\n\n    cdef _bind_execute(self, str portal_name, str stmt_name,\n                       WriteBuffer bind_data, int32_t limit):\n\n        cdef WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_BIND_EXECUTE)\n\n        self.result = []\n\n        self._send_bind_message(portal_name, stmt_name, bind_data, limit)\n\n    cdef bint _bind_execute_many(self, str portal_name, str stmt_name,\n                                 object bind_data, bint return_rows):\n        self._ensure_connected()\n        self._set_state(PROTOCOL_BIND_EXECUTE_MANY)\n\n        self.result = [] if return_rows else None\n        self._discard_data = not return_rows\n        self._execute_iter = bind_data\n        self._execute_portal_name = portal_name\n        self._execute_stmt_name = stmt_name\n        return self._bind_execute_many_more(True)\n\n    cdef bint _bind_execute_many_more(self, bint first=False):\n        cdef:\n            WriteBuffer packet\n            WriteBuffer buf\n            list buffers = []\n\n        # as we keep sending, the server may return an error early\n        if self.result_type == RESULT_FAILED:\n            self._write(SYNC_MESSAGE)\n            return False\n\n        # collect up to four 32KB buffers to send\n        # https://github.com/MagicStack/asyncpg/pull/289#issuecomment-391215051\n        while len(buffers) < _EXECUTE_MANY_BUF_NUM:\n            packet = WriteBuffer.new()\n\n            # fill one 32KB buffer\n            while packet.len() < _EXECUTE_MANY_BUF_SIZE:\n                try:\n                    # grab one item from the input\n                    buf = <WriteBuffer>next(self._execute_iter)\n\n                # reached the end of the input\n                except StopIteration:\n                    if first:\n                        # if we never send anything, simply set the result\n                        self._push_result()\n                    else:\n                        # otherwise, append SYNC and send the buffers\n                        packet.write_bytes(SYNC_MESSAGE)\n                        buffers.append(memoryview(packet))\n                        self._writelines(buffers)\n                    return False\n\n                # error in input, give up the buffers and cleanup\n                except Exception as ex:\n                    self._bind_execute_many_fail(ex, first)\n                    return False\n\n                # all good, write to the buffer\n                first = False\n                packet.write_buffer(\n                    self._build_bind_message(\n                        self._execute_portal_name,\n                        self._execute_stmt_name,\n                        buf,\n                    )\n                )\n                packet.write_buffer(\n                    self._build_execute_message(self._execute_portal_name, 0,\n                    )\n                )\n\n            # collected one buffer\n            buffers.append(memoryview(packet))\n\n        # write to the wire, and signal the caller for more to send\n        self._writelines(buffers)\n        return True\n\n    cdef _bind_execute_many_fail(self, object error, bint first=False):\n        cdef WriteBuffer buf\n\n        self.result_type = RESULT_FAILED\n        self.result = error\n        if first:\n            self._push_result()\n        elif self.is_in_transaction():\n            # we're in an explicit transaction, just SYNC\n            self._write(SYNC_MESSAGE)\n        else:\n            # In an implicit transaction, if `ignore_till_sync` is set,\n            # `ROLLBACK` will be ignored and `Sync` will restore the state;\n            # or the transaction will be rolled back with a warning saying\n            # that there was no transaction, but rollback is done anyway,\n            # so we could safely ignore this warning.\n            # GOTCHA: cannot use simple query message here, because it is\n            # ignored if `ignore_till_sync` is set.\n            buf = self._build_parse_message('', 'ROLLBACK')\n            buf.write_buffer(self._build_bind_message(\n                '', '', self._build_empty_bind_data()))\n            buf.write_buffer(self._build_execute_message('', 0))\n            buf.write_bytes(SYNC_MESSAGE)\n            self._write(buf)\n\n    cdef _execute(self, str portal_name, int32_t limit):\n        cdef WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_EXECUTE)\n\n        self.result = []\n\n        buf = self._build_execute_message(portal_name, limit)\n\n        buf.write_bytes(SYNC_MESSAGE)\n\n        self._write(buf)\n\n    cdef _bind(self, str portal_name, str stmt_name,\n               WriteBuffer bind_data):\n\n        cdef WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_BIND)\n\n        buf = self._build_bind_message(portal_name, stmt_name, bind_data)\n\n        buf.write_bytes(SYNC_MESSAGE)\n\n        self._write(buf)\n\n    cdef _close(self, str name, bint is_portal):\n        cdef WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_CLOSE_STMT_PORTAL)\n\n        buf = WriteBuffer.new_message(b'C')\n\n        if is_portal:\n            buf.write_byte(b'P')\n        else:\n            buf.write_byte(b'S')\n\n        buf.write_str(name, self.encoding)\n        buf.end_message()\n\n        buf.write_bytes(SYNC_MESSAGE)\n\n        self._write(buf)\n\n    cdef _simple_query(self, str query):\n        cdef WriteBuffer buf\n        self._ensure_connected()\n        self._set_state(PROTOCOL_SIMPLE_QUERY)\n        buf = WriteBuffer.new_message(b'Q')\n        buf.write_str(query, self.encoding)\n        buf.end_message()\n        self._write(buf)\n\n    cdef _copy_out(self, str copy_stmt):\n        cdef WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_COPY_OUT)\n\n        # Send the COPY .. TO STDOUT using the SimpleQuery protocol.\n        buf = WriteBuffer.new_message(b'Q')\n        buf.write_str(copy_stmt, self.encoding)\n        buf.end_message()\n        self._write(buf)\n\n    cdef _copy_in(self, str copy_stmt):\n        cdef WriteBuffer buf\n\n        self._ensure_connected()\n        self._set_state(PROTOCOL_COPY_IN)\n\n        buf = WriteBuffer.new_message(b'Q')\n        buf.write_str(copy_stmt, self.encoding)\n        buf.end_message()\n        self._write(buf)\n\n    cdef _terminate(self):\n        cdef WriteBuffer buf\n        self._ensure_connected()\n        self._set_state(PROTOCOL_TERMINATING)\n        buf = WriteBuffer.new_message(b'X')\n        buf.end_message()\n        self._write(buf)\n\n    cdef _write(self, buf):\n        raise NotImplementedError\n\n    cdef _writelines(self, list buffers):\n        raise NotImplementedError\n\n    cdef _decode_row(self, const char* buf, ssize_t buf_len):\n        pass\n\n    cdef _set_server_parameter(self, name, val):\n        pass\n\n    cdef _on_result(self):\n        pass\n\n    cdef _on_notice(self, parsed):\n        pass\n\n    cdef _on_notification(self, pid, channel, payload):\n        pass\n\n    cdef _on_connection_lost(self, exc):\n        pass\n\n\nSYNC_MESSAGE = bytes(WriteBuffer.new_message(b'S').end_message())\nFLUSH_MESSAGE = bytes(WriteBuffer.new_message(b'H').end_message())\n"
  },
  {
    "path": "asyncpg/protocol/cpythonx.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncdef extern from \"Python.h\":\n    int PyByteArray_Check(object)\n\n    int PyMemoryView_Check(object)\n    Py_buffer *PyMemoryView_GET_BUFFER(object)\n    object PyMemoryView_GetContiguous(object, int buffertype, char order)\n\n    Py_UCS4* PyUnicode_AsUCS4Copy(object) except NULL\n    object PyUnicode_FromKindAndData(\n        int kind, const void *buffer, Py_ssize_t size)\n\n    int PyUnicode_4BYTE_KIND\n"
  },
  {
    "path": "asyncpg/protocol/encodings.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\n'''Map PostgreSQL encoding names to Python encoding names\n\nhttps://www.postgresql.org/docs/current/static/multibyte.html#CHARSET-TABLE\n'''\n\nENCODINGS_MAP = {\n    'abc': 'cp1258',\n    'alt': 'cp866',\n    'euc_cn': 'euccn',\n    'euc_jp': 'eucjp',\n    'euc_kr': 'euckr',\n    'koi8r': 'koi8_r',\n    'koi8u': 'koi8_u',\n    'shift_jis_2004': 'euc_jis_2004',\n    'sjis': 'shift_jis',\n    'sql_ascii': 'ascii',\n    'vscii': 'cp1258',\n    'tcvn': 'cp1258',\n    'tcvn5712': 'cp1258',\n    'unicode': 'utf_8',\n    'win': 'cp1521',\n    'win1250': 'cp1250',\n    'win1251': 'cp1251',\n    'win1252': 'cp1252',\n    'win1253': 'cp1253',\n    'win1254': 'cp1254',\n    'win1255': 'cp1255',\n    'win1256': 'cp1256',\n    'win1257': 'cp1257',\n    'win1258': 'cp1258',\n    'win866': 'cp866',\n    'win874': 'cp874',\n    'win932': 'cp932',\n    'win936': 'cp936',\n    'win949': 'cp949',\n    'win950': 'cp950',\n    'windows1250': 'cp1250',\n    'windows1251': 'cp1251',\n    'windows1252': 'cp1252',\n    'windows1253': 'cp1253',\n    'windows1254': 'cp1254',\n    'windows1255': 'cp1255',\n    'windows1256': 'cp1256',\n    'windows1257': 'cp1257',\n    'windows1258': 'cp1258',\n    'windows866': 'cp866',\n    'windows874': 'cp874',\n    'windows932': 'cp932',\n    'windows936': 'cp936',\n    'windows949': 'cp949',\n    'windows950': 'cp950',\n}\n\n\ncdef get_python_encoding(pg_encoding):\n    return ENCODINGS_MAP.get(pg_encoding.lower(), pg_encoding.lower())\n"
  },
  {
    "path": "asyncpg/protocol/pgtypes.pxi",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\n# GENERATED FROM pg_catalog.pg_type\n# DO NOT MODIFY, use tools/generate_type_map.py to update\n\nDEF INVALIDOID = 0\nDEF MAXBUILTINOID = 9999\nDEF MAXSUPPORTEDOID = 5080\n\nDEF BOOLOID = 16\nDEF BYTEAOID = 17\nDEF CHAROID = 18\nDEF NAMEOID = 19\nDEF INT8OID = 20\nDEF INT2OID = 21\nDEF INT4OID = 23\nDEF REGPROCOID = 24\nDEF TEXTOID = 25\nDEF OIDOID = 26\nDEF TIDOID = 27\nDEF XIDOID = 28\nDEF CIDOID = 29\nDEF PG_DDL_COMMANDOID = 32\nDEF JSONOID = 114\nDEF XMLOID = 142\nDEF PG_NODE_TREEOID = 194\nDEF SMGROID = 210\nDEF TABLE_AM_HANDLEROID = 269\nDEF INDEX_AM_HANDLEROID = 325\nDEF POINTOID = 600\nDEF LSEGOID = 601\nDEF PATHOID = 602\nDEF BOXOID = 603\nDEF POLYGONOID = 604\nDEF LINEOID = 628\nDEF CIDROID = 650\nDEF FLOAT4OID = 700\nDEF FLOAT8OID = 701\nDEF ABSTIMEOID = 702\nDEF RELTIMEOID = 703\nDEF TINTERVALOID = 704\nDEF UNKNOWNOID = 705\nDEF CIRCLEOID = 718\nDEF MACADDR8OID = 774\nDEF MONEYOID = 790\nDEF MACADDROID = 829\nDEF INETOID = 869\nDEF _TEXTOID = 1009\nDEF _OIDOID = 1028\nDEF ACLITEMOID = 1033\nDEF BPCHAROID = 1042\nDEF VARCHAROID = 1043\nDEF DATEOID = 1082\nDEF TIMEOID = 1083\nDEF TIMESTAMPOID = 1114\nDEF TIMESTAMPTZOID = 1184\nDEF INTERVALOID = 1186\nDEF TIMETZOID = 1266\nDEF BITOID = 1560\nDEF VARBITOID = 1562\nDEF NUMERICOID = 1700\nDEF REFCURSOROID = 1790\nDEF REGPROCEDUREOID = 2202\nDEF REGOPEROID = 2203\nDEF REGOPERATOROID = 2204\nDEF REGCLASSOID = 2205\nDEF REGTYPEOID = 2206\nDEF RECORDOID = 2249\nDEF CSTRINGOID = 2275\nDEF ANYOID = 2276\nDEF ANYARRAYOID = 2277\nDEF VOIDOID = 2278\nDEF TRIGGEROID = 2279\nDEF LANGUAGE_HANDLEROID = 2280\nDEF INTERNALOID = 2281\nDEF OPAQUEOID = 2282\nDEF ANYELEMENTOID = 2283\nDEF ANYNONARRAYOID = 2776\nDEF UUIDOID = 2950\nDEF TXID_SNAPSHOTOID = 2970\nDEF FDW_HANDLEROID = 3115\nDEF PG_LSNOID = 3220\nDEF TSM_HANDLEROID = 3310\nDEF PG_NDISTINCTOID = 3361\nDEF PG_DEPENDENCIESOID = 3402\nDEF ANYENUMOID = 3500\nDEF TSVECTOROID = 3614\nDEF TSQUERYOID = 3615\nDEF GTSVECTOROID = 3642\nDEF REGCONFIGOID = 3734\nDEF REGDICTIONARYOID = 3769\nDEF JSONBOID = 3802\nDEF ANYRANGEOID = 3831\nDEF EVENT_TRIGGEROID = 3838\nDEF JSONPATHOID = 4072\nDEF REGNAMESPACEOID = 4089\nDEF REGROLEOID = 4096\nDEF REGCOLLATIONOID = 4191\nDEF ANYMULTIRANGEOID = 4537\nDEF ANYCOMPATIBLEMULTIRANGEOID = 4538\nDEF PG_BRIN_BLOOM_SUMMARYOID = 4600\nDEF PG_BRIN_MINMAX_MULTI_SUMMARYOID = 4601\nDEF PG_MCV_LISTOID = 5017\nDEF PG_SNAPSHOTOID = 5038\nDEF XID8OID = 5069\nDEF ANYCOMPATIBLEOID = 5077\nDEF ANYCOMPATIBLEARRAYOID = 5078\nDEF ANYCOMPATIBLENONARRAYOID = 5079\nDEF ANYCOMPATIBLERANGEOID = 5080\n\nARRAY_TYPES = {_TEXTOID, _OIDOID}\n\nBUILTIN_TYPE_OID_MAP = {\n    ABSTIMEOID: 'abstime',\n    ACLITEMOID: 'aclitem',\n    ANYARRAYOID: 'anyarray',\n    ANYCOMPATIBLEARRAYOID: 'anycompatiblearray',\n    ANYCOMPATIBLEMULTIRANGEOID: 'anycompatiblemultirange',\n    ANYCOMPATIBLENONARRAYOID: 'anycompatiblenonarray',\n    ANYCOMPATIBLEOID: 'anycompatible',\n    ANYCOMPATIBLERANGEOID: 'anycompatiblerange',\n    ANYELEMENTOID: 'anyelement',\n    ANYENUMOID: 'anyenum',\n    ANYMULTIRANGEOID: 'anymultirange',\n    ANYNONARRAYOID: 'anynonarray',\n    ANYOID: 'any',\n    ANYRANGEOID: 'anyrange',\n    BITOID: 'bit',\n    BOOLOID: 'bool',\n    BOXOID: 'box',\n    BPCHAROID: 'bpchar',\n    BYTEAOID: 'bytea',\n    CHAROID: 'char',\n    CIDOID: 'cid',\n    CIDROID: 'cidr',\n    CIRCLEOID: 'circle',\n    CSTRINGOID: 'cstring',\n    DATEOID: 'date',\n    EVENT_TRIGGEROID: 'event_trigger',\n    FDW_HANDLEROID: 'fdw_handler',\n    FLOAT4OID: 'float4',\n    FLOAT8OID: 'float8',\n    GTSVECTOROID: 'gtsvector',\n    INDEX_AM_HANDLEROID: 'index_am_handler',\n    INETOID: 'inet',\n    INT2OID: 'int2',\n    INT4OID: 'int4',\n    INT8OID: 'int8',\n    INTERNALOID: 'internal',\n    INTERVALOID: 'interval',\n    JSONBOID: 'jsonb',\n    JSONOID: 'json',\n    JSONPATHOID: 'jsonpath',\n    LANGUAGE_HANDLEROID: 'language_handler',\n    LINEOID: 'line',\n    LSEGOID: 'lseg',\n    MACADDR8OID: 'macaddr8',\n    MACADDROID: 'macaddr',\n    MONEYOID: 'money',\n    NAMEOID: 'name',\n    NUMERICOID: 'numeric',\n    OIDOID: 'oid',\n    OPAQUEOID: 'opaque',\n    PATHOID: 'path',\n    PG_BRIN_BLOOM_SUMMARYOID: 'pg_brin_bloom_summary',\n    PG_BRIN_MINMAX_MULTI_SUMMARYOID: 'pg_brin_minmax_multi_summary',\n    PG_DDL_COMMANDOID: 'pg_ddl_command',\n    PG_DEPENDENCIESOID: 'pg_dependencies',\n    PG_LSNOID: 'pg_lsn',\n    PG_MCV_LISTOID: 'pg_mcv_list',\n    PG_NDISTINCTOID: 'pg_ndistinct',\n    PG_NODE_TREEOID: 'pg_node_tree',\n    PG_SNAPSHOTOID: 'pg_snapshot',\n    POINTOID: 'point',\n    POLYGONOID: 'polygon',\n    RECORDOID: 'record',\n    REFCURSOROID: 'refcursor',\n    REGCLASSOID: 'regclass',\n    REGCOLLATIONOID: 'regcollation',\n    REGCONFIGOID: 'regconfig',\n    REGDICTIONARYOID: 'regdictionary',\n    REGNAMESPACEOID: 'regnamespace',\n    REGOPERATOROID: 'regoperator',\n    REGOPEROID: 'regoper',\n    REGPROCEDUREOID: 'regprocedure',\n    REGPROCOID: 'regproc',\n    REGROLEOID: 'regrole',\n    REGTYPEOID: 'regtype',\n    RELTIMEOID: 'reltime',\n    SMGROID: 'smgr',\n    TABLE_AM_HANDLEROID: 'table_am_handler',\n    TEXTOID: 'text',\n    TIDOID: 'tid',\n    TIMEOID: 'time',\n    TIMESTAMPOID: 'timestamp',\n    TIMESTAMPTZOID: 'timestamptz',\n    TIMETZOID: 'timetz',\n    TINTERVALOID: 'tinterval',\n    TRIGGEROID: 'trigger',\n    TSM_HANDLEROID: 'tsm_handler',\n    TSQUERYOID: 'tsquery',\n    TSVECTOROID: 'tsvector',\n    TXID_SNAPSHOTOID: 'txid_snapshot',\n    UNKNOWNOID: 'unknown',\n    UUIDOID: 'uuid',\n    VARBITOID: 'varbit',\n    VARCHAROID: 'varchar',\n    VOIDOID: 'void',\n    XID8OID: 'xid8',\n    XIDOID: 'xid',\n    XMLOID: 'xml',\n    _OIDOID: 'oid[]',\n    _TEXTOID: 'text[]'\n}\n\nBUILTIN_TYPE_NAME_MAP = {v: k for k, v in BUILTIN_TYPE_OID_MAP.items()}\n\nBUILTIN_TYPE_NAME_MAP['smallint'] = \\\n    BUILTIN_TYPE_NAME_MAP['int2']\n\nBUILTIN_TYPE_NAME_MAP['int'] = \\\n    BUILTIN_TYPE_NAME_MAP['int4']\n\nBUILTIN_TYPE_NAME_MAP['integer'] = \\\n    BUILTIN_TYPE_NAME_MAP['int4']\n\nBUILTIN_TYPE_NAME_MAP['bigint'] = \\\n    BUILTIN_TYPE_NAME_MAP['int8']\n\nBUILTIN_TYPE_NAME_MAP['decimal'] = \\\n    BUILTIN_TYPE_NAME_MAP['numeric']\n\nBUILTIN_TYPE_NAME_MAP['real'] = \\\n    BUILTIN_TYPE_NAME_MAP['float4']\n\nBUILTIN_TYPE_NAME_MAP['double precision'] = \\\n    BUILTIN_TYPE_NAME_MAP['float8']\n\nBUILTIN_TYPE_NAME_MAP['timestamp with timezone'] = \\\n    BUILTIN_TYPE_NAME_MAP['timestamptz']\n\nBUILTIN_TYPE_NAME_MAP['timestamp without timezone'] = \\\n    BUILTIN_TYPE_NAME_MAP['timestamp']\n\nBUILTIN_TYPE_NAME_MAP['time with timezone'] = \\\n    BUILTIN_TYPE_NAME_MAP['timetz']\n\nBUILTIN_TYPE_NAME_MAP['time without timezone'] = \\\n    BUILTIN_TYPE_NAME_MAP['time']\n\nBUILTIN_TYPE_NAME_MAP['char'] = \\\n    BUILTIN_TYPE_NAME_MAP['bpchar']\n\nBUILTIN_TYPE_NAME_MAP['character'] = \\\n    BUILTIN_TYPE_NAME_MAP['bpchar']\n\nBUILTIN_TYPE_NAME_MAP['character varying'] = \\\n    BUILTIN_TYPE_NAME_MAP['varchar']\n\nBUILTIN_TYPE_NAME_MAP['bit varying'] = \\\n    BUILTIN_TYPE_NAME_MAP['varbit']\n"
  },
  {
    "path": "asyncpg/protocol/prepared_stmt.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncdef class PreparedStatementState:\n    cdef:\n        readonly str name\n        readonly str query\n        readonly bint closed\n        readonly bint prepared\n        readonly int refs\n        readonly type record_class\n        readonly bint ignore_custom_codec\n\n\n        list         row_desc\n        list         parameters_desc\n\n        ConnectionSettings settings\n\n        int16_t      args_num\n        bint         have_text_args\n        tuple        args_codecs\n\n        int16_t      cols_num\n        object       cols_desc\n        bint         have_text_cols\n        tuple        rows_codecs\n\n    cdef _encode_bind_msg(self, args, int seqno = ?)\n    cpdef _init_codecs(self)\n    cdef _ensure_rows_decoder(self)\n    cdef _ensure_args_encoder(self)\n    cdef _set_row_desc(self, object desc)\n    cdef _set_args_desc(self, object desc)\n    cdef _decode_row(self, const char* cbuf, ssize_t buf_len)\n"
  },
  {
    "path": "asyncpg/protocol/prepared_stmt.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom asyncpg import exceptions\n\n\n@cython.final\ncdef class PreparedStatementState:\n\n    def __cinit__(\n        self,\n        str name,\n        str query,\n        BaseProtocol protocol,\n        type record_class,\n        bint ignore_custom_codec\n    ):\n        self.name = name\n        self.query = query\n        self.settings = protocol.settings\n        self.row_desc = self.parameters_desc = None\n        self.args_codecs = self.rows_codecs = None\n        self.args_num = self.cols_num = 0\n        self.cols_desc = None\n        self.closed = False\n        self.prepared = True\n        self.refs = 0\n        self.record_class = record_class\n        self.ignore_custom_codec = ignore_custom_codec\n\n    def _get_parameters(self):\n        cdef Codec codec\n\n        result = []\n        for oid in self.parameters_desc:\n            codec = self.settings.get_data_codec(oid)\n            if codec is None:\n                raise exceptions.InternalClientError(\n                    'missing codec information for OID {}'.format(oid))\n            result.append(apg_types.Type(\n                oid, codec.name, codec.kind, codec.schema))\n\n        return tuple(result)\n\n    def _get_attributes(self):\n        cdef Codec codec\n\n        if not self.row_desc:\n            return ()\n\n        result = []\n        for d in self.row_desc:\n            name = d[0]\n            oid = d[3]\n\n            codec = self.settings.get_data_codec(oid)\n            if codec is None:\n                raise exceptions.InternalClientError(\n                    'missing codec information for OID {}'.format(oid))\n\n            name = name.decode(self.settings._encoding)\n\n            result.append(\n                apg_types.Attribute(name,\n                    apg_types.Type(oid, codec.name, codec.kind, codec.schema)))\n\n        return tuple(result)\n\n    def _init_types(self):\n        cdef:\n            Codec codec\n            set missing = set()\n\n        if self.parameters_desc:\n            for p_oid in self.parameters_desc:\n                codec = self.settings.get_data_codec(<uint32_t>p_oid)\n                if codec is None or not codec.has_encoder():\n                    missing.add(p_oid)\n\n        if self.row_desc:\n            for rdesc in self.row_desc:\n                codec = self.settings.get_data_codec(<uint32_t>(rdesc[3]))\n                if codec is None or not codec.has_decoder():\n                    missing.add(rdesc[3])\n\n        return missing\n\n    cpdef _init_codecs(self):\n        self._ensure_args_encoder()\n        self._ensure_rows_decoder()\n\n    def attach(self):\n        self.refs += 1\n\n    def detach(self):\n        self.refs -= 1\n\n    def mark_closed(self):\n        self.closed = True\n\n    def mark_unprepared(self):\n        if self.name:\n            raise exceptions.InternalClientError(\n                \"named prepared statements cannot be marked unprepared\")\n        self.prepared = False\n\n    cdef _encode_bind_msg(self, args, int seqno = -1):\n        cdef:\n            int idx\n            WriteBuffer writer\n            Codec codec\n\n        if not cpython.PySequence_Check(args):\n            if seqno >= 0:\n                raise exceptions.DataError(\n                    f'invalid input in executemany() argument sequence '\n                    f'element #{seqno}: expected a sequence, got '\n                    f'{type(args).__name__}'\n                )\n            else:\n                # Non executemany() callers do not pass user input directly,\n                # so bad input is a bug.\n                raise exceptions.InternalClientError(\n                    f'Bind: expected a sequence, got {type(args).__name__}')\n\n        if len(args) > 32767:\n            raise exceptions.InterfaceError(\n                'the number of query arguments cannot exceed 32767')\n\n        writer = WriteBuffer.new()\n\n        num_args_passed = len(args)\n        if self.args_num != num_args_passed:\n            hint = 'Check the query against the passed list of arguments.'\n\n            if self.args_num == 0:\n                # If the server was expecting zero arguments, it is likely\n                # that the user tried to parametrize a statement that does\n                # not support parameters.\n                hint += (r'  Note that parameters are supported only in'\n                         r' SELECT, INSERT, UPDATE, DELETE, MERGE and VALUES'\n                         r' statements, and will *not* work in statements '\n                         r' like CREATE VIEW or DECLARE CURSOR.')\n\n            raise exceptions.InterfaceError(\n                'the server expects {x} argument{s} for this query, '\n                '{y} {w} passed'.format(\n                    x=self.args_num, s='s' if self.args_num != 1 else '',\n                    y=num_args_passed,\n                    w='was' if num_args_passed == 1 else 'were'),\n                hint=hint)\n\n        if self.have_text_args:\n            writer.write_int16(self.args_num)\n            for idx in range(self.args_num):\n                codec = <Codec>(self.args_codecs[idx])\n                writer.write_int16(<int16_t>codec.format)\n        else:\n            # All arguments are in binary format\n            writer.write_int32(0x00010001)\n\n        writer.write_int16(self.args_num)\n\n        for idx in range(self.args_num):\n            arg = args[idx]\n            if arg is None:\n                writer.write_int32(-1)\n            else:\n                codec = <Codec>(self.args_codecs[idx])\n                try:\n                    codec.encode(self.settings, writer, arg)\n                except (AssertionError, exceptions.InternalClientError):\n                    # These are internal errors and should raise as-is.\n                    raise\n                except exceptions.InterfaceError as e:\n                    # This is already a descriptive error, but annotate\n                    # with argument name for clarity.\n                    pos = f'${idx + 1}'\n                    if seqno >= 0:\n                        pos = (\n                            f'{pos} in element #{seqno} of'\n                            f' executemany() sequence'\n                        )\n                    raise e.with_msg(\n                        f'query argument {pos}: {e.args[0]}'\n                    ) from None\n                except Exception as e:\n                    # Everything else is assumed to be an encoding error\n                    # due to invalid input.\n                    pos = f'${idx + 1}'\n                    if seqno >= 0:\n                        pos = (\n                            f'{pos} in element #{seqno} of'\n                            f' executemany() sequence'\n                        )\n                    value_repr = repr(arg)\n                    if len(value_repr) > 40:\n                        value_repr = value_repr[:40] + '...'\n\n                    raise exceptions.DataError(\n                        f'invalid input for query argument'\n                        f' {pos}: {value_repr} ({e})'\n                    ) from e\n\n        if self.have_text_cols:\n            writer.write_int16(self.cols_num)\n            for idx in range(self.cols_num):\n                codec = <Codec>(self.rows_codecs[idx])\n                writer.write_int16(<int16_t>codec.format)\n        else:\n            # All columns are in binary format\n            writer.write_int32(0x00010001)\n\n        return writer\n\n    cdef _ensure_rows_decoder(self):\n        cdef:\n            list cols_names\n            object cols_mapping\n            tuple row\n            uint32_t oid\n            Codec codec\n            list codecs\n\n        if self.cols_desc is not None:\n            return\n\n        if self.cols_num == 0:\n            self.cols_desc = RecordDescriptor({}, ())\n            return\n\n        cols_mapping = collections.OrderedDict()\n        cols_names = []\n        codecs = []\n        for i from 0 <= i < self.cols_num:\n            row = self.row_desc[i]\n            col_name = row[0].decode(self.settings._encoding)\n            cols_mapping[col_name] = i\n            cols_names.append(col_name)\n            oid = row[3]\n            codec = self.settings.get_data_codec(\n                oid, ignore_custom_codec=self.ignore_custom_codec)\n            if codec is None or not codec.has_decoder():\n                raise exceptions.InternalClientError(\n                    'no decoder for OID {}'.format(oid))\n            if not codec.is_binary():\n                self.have_text_cols = True\n\n            codecs.append(codec)\n\n        self.cols_desc = RecordDescriptor(\n            cols_mapping, tuple(cols_names))\n\n        self.rows_codecs = tuple(codecs)\n\n    cdef _ensure_args_encoder(self):\n        cdef:\n            uint32_t p_oid\n            Codec codec\n            list codecs = []\n\n        if self.args_num == 0 or self.args_codecs is not None:\n            return\n\n        for i from 0 <= i < self.args_num:\n            p_oid = self.parameters_desc[i]\n            codec = self.settings.get_data_codec(\n                p_oid, ignore_custom_codec=self.ignore_custom_codec)\n            if codec is None or not codec.has_encoder():\n                raise exceptions.InternalClientError(\n                    'no encoder for OID {}'.format(p_oid))\n            if codec.type not in {}:\n                self.have_text_args = True\n\n            codecs.append(codec)\n\n        self.args_codecs = tuple(codecs)\n\n    cdef _set_row_desc(self, object desc):\n        self.row_desc = _decode_row_desc(desc)\n        self.cols_num = <int16_t>(len(self.row_desc))\n\n    cdef _set_args_desc(self, object desc):\n        self.parameters_desc = _decode_parameters_desc(desc)\n        self.args_num = <int16_t>(len(self.parameters_desc))\n\n    cdef _decode_row(self, const char* cbuf, ssize_t buf_len):\n        cdef:\n            Codec codec\n            int16_t fnum\n            int32_t flen\n            object dec_row\n            tuple rows_codecs = self.rows_codecs\n            ConnectionSettings settings = self.settings\n            int32_t i\n            FRBuffer rbuf\n            ssize_t bl\n\n        frb_init(&rbuf, cbuf, buf_len)\n\n        fnum = hton.unpack_int16(frb_read(&rbuf, 2))\n\n        if fnum != self.cols_num:\n            raise exceptions.ProtocolError(\n                'the number of columns in the result row ({}) is '\n                'different from what was described ({})'.format(\n                    fnum, self.cols_num))\n\n        dec_row = self.cols_desc.make_record(self.record_class, fnum)\n        for i in range(fnum):\n            flen = hton.unpack_int32(frb_read(&rbuf, 4))\n\n            if flen == -1:\n                val = None\n            else:\n                # Clamp buffer size to that of the reported field length\n                # to make sure that codecs can rely on read_all() working\n                # properly.\n                bl = frb_get_len(&rbuf)\n                if flen > bl:\n                    frb_check(&rbuf, flen)\n                frb_set_len(&rbuf, flen)\n                codec = <Codec>cpython.PyTuple_GET_ITEM(rows_codecs, i)\n                val = codec.decode(settings, &rbuf)\n                if frb_get_len(&rbuf) != 0:\n                    raise BufferError(\n                        'unexpected trailing {} bytes in buffer'.format(\n                            frb_get_len(&rbuf)))\n                frb_set_len(&rbuf, bl - flen)\n\n            cpython.Py_INCREF(val)\n            recordcapi.ApgRecord_SET_ITEM(dec_row, i, val)\n\n        if frb_get_len(&rbuf) != 0:\n            raise BufferError('unexpected trailing {} bytes in buffer'.format(\n                frb_get_len(&rbuf)))\n\n        return dec_row\n\n\ncdef _decode_parameters_desc(object desc):\n    cdef:\n        ReadBuffer reader\n        int16_t nparams\n        uint32_t p_oid\n        list result = []\n\n    reader = ReadBuffer.new_message_parser(desc)\n    nparams = reader.read_int16()\n\n    for i from 0 <= i < nparams:\n        p_oid = <uint32_t>reader.read_int32()\n        result.append(p_oid)\n\n    return result\n\n\ncdef _decode_row_desc(object desc):\n    cdef:\n        ReadBuffer reader\n\n        int16_t nfields\n\n        bytes f_name\n        uint32_t f_table_oid\n        int16_t f_column_num\n        uint32_t f_dt_oid\n        int16_t f_dt_size\n        int32_t f_dt_mod\n        int16_t f_format\n\n        list result\n\n    reader = ReadBuffer.new_message_parser(desc)\n    nfields = reader.read_int16()\n    result = []\n\n    for i from 0 <= i < nfields:\n        f_name = reader.read_null_str()\n        f_table_oid = <uint32_t>reader.read_int32()\n        f_column_num = reader.read_int16()\n        f_dt_oid = <uint32_t>reader.read_int32()\n        f_dt_size = reader.read_int16()\n        f_dt_mod = reader.read_int32()\n        f_format = reader.read_int16()\n\n        result.append(\n            (f_name, f_table_oid, f_column_num, f_dt_oid,\n             f_dt_size, f_dt_mod, f_format))\n\n    return result\n"
  },
  {
    "path": "asyncpg/protocol/protocol.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom libc.stdint cimport int16_t, int32_t, uint16_t, \\\n                         uint32_t, int64_t, uint64_t\n\nfrom asyncpg.pgproto.debug cimport PG_DEBUG\n\nfrom asyncpg.pgproto.pgproto cimport (\n    WriteBuffer,\n    ReadBuffer,\n    FRBuffer,\n)\n\nfrom asyncpg.pgproto cimport pgproto\n\ninclude \"consts.pxi\"\ninclude \"pgtypes.pxi\"\n\ninclude \"codecs/base.pxd\"\ninclude \"settings.pxd\"\ninclude \"coreproto.pxd\"\ninclude \"prepared_stmt.pxd\"\n\n\ncdef class BaseProtocol(CoreProtocol):\n\n    cdef:\n        object loop\n        ConnectionSettings settings\n        object cancel_sent_waiter\n        object cancel_waiter\n        object waiter\n        bint return_extra\n        object create_future\n        object timeout_handle\n        object conref\n        type record_class\n        bint is_reading\n\n        str last_query\n\n        bint writing_paused\n        bint closing\n\n        readonly uint64_t queries_count\n\n        bint _is_ssl\n\n        PreparedStatementState statement\n\n    cdef get_connection(self)\n\n    cdef _get_timeout_impl(self, timeout)\n    cdef _check_state(self)\n    cdef _new_waiter(self, timeout)\n    cdef _coreproto_error(self)\n\n    cdef _on_result__connect(self, object waiter)\n    cdef _on_result__prepare(self, object waiter)\n    cdef _on_result__bind_and_exec(self, object waiter)\n    cdef _on_result__close_stmt_or_portal(self, object waiter)\n    cdef _on_result__simple_query(self, object waiter)\n    cdef _on_result__bind(self, object waiter)\n    cdef _on_result__copy_out(self, object waiter)\n    cdef _on_result__copy_in(self, object waiter)\n\n    cdef _handle_waiter_on_connection_lost(self, cause)\n\n    cdef _dispatch_result(self)\n\n    cdef inline resume_reading(self)\n    cdef inline pause_reading(self)\n"
  },
  {
    "path": "asyncpg/protocol/protocol.pyi",
    "content": "import asyncio\nimport asyncio.protocols\nimport hmac\nfrom codecs import CodecInfo\nfrom collections.abc import Callable, Iterable, Sequence\nfrom hashlib import md5, sha256\nfrom typing import (\n    Any,\n    ClassVar,\n    Final,\n    Generic,\n    Literal,\n    NewType,\n    TypeVar,\n    final,\n    overload,\n)\nfrom typing_extensions import TypeAlias\n\nimport asyncpg.pgproto.pgproto\n\nfrom ..connect_utils import _ConnectionParameters\nfrom ..pgproto.pgproto import WriteBuffer\nfrom ..types import Attribute, Type\nfrom .record import Record\n\n_Record = TypeVar('_Record', bound=Record)\n_OtherRecord = TypeVar('_OtherRecord', bound=Record)\n_PreparedStatementState = TypeVar(\n    '_PreparedStatementState', bound=PreparedStatementState[Any]\n)\n\n_NoTimeoutType = NewType('_NoTimeoutType', object)\n_TimeoutType: TypeAlias = float | None | _NoTimeoutType\n\nBUILTIN_TYPE_NAME_MAP: Final[dict[str, int]]\nBUILTIN_TYPE_OID_MAP: Final[dict[int, str]]\nNO_TIMEOUT: Final[_NoTimeoutType]\n\nhashlib_md5 = md5\n\n@final\nclass ConnectionSettings(asyncpg.pgproto.pgproto.CodecContext):\n    __pyx_vtable__: Any\n    def __init__(self, conn_key: object) -> None: ...\n    def add_python_codec(\n        self,\n        typeoid: int,\n        typename: str,\n        typeschema: str,\n        typeinfos: Iterable[object],\n        typekind: str,\n        encoder: Callable[[Any], Any],\n        decoder: Callable[[Any], Any],\n        format: object,\n    ) -> Any: ...\n    def clear_type_cache(self) -> None: ...\n    def get_data_codec(\n        self, oid: int, format: object = ..., ignore_custom_codec: bool = ...\n    ) -> Any: ...\n    def get_text_codec(self) -> CodecInfo: ...\n    def register_data_types(self, types: Iterable[object]) -> None: ...\n    def remove_python_codec(\n        self, typeoid: int, typename: str, typeschema: str\n    ) -> None: ...\n    def set_builtin_type_codec(\n        self,\n        typeoid: int,\n        typename: str,\n        typeschema: str,\n        typekind: str,\n        alias_to: str,\n        format: object = ...,\n    ) -> Any: ...\n    def __getattr__(self, name: str) -> Any: ...\n    def __reduce__(self) -> Any: ...\n\n@final\nclass PreparedStatementState(Generic[_Record]):\n    closed: bool\n    prepared: bool\n    name: str\n    query: str\n    refs: int\n    record_class: type[_Record]\n    ignore_custom_codec: bool\n    __pyx_vtable__: Any\n    def __init__(\n        self,\n        name: str,\n        query: str,\n        protocol: BaseProtocol[Any],\n        record_class: type[_Record],\n        ignore_custom_codec: bool,\n    ) -> None: ...\n    def _get_parameters(self) -> tuple[Type, ...]: ...\n    def _get_attributes(self) -> tuple[Attribute, ...]: ...\n    def _init_types(self) -> set[int]: ...\n    def _init_codecs(self) -> None: ...\n    def attach(self) -> None: ...\n    def detach(self) -> None: ...\n    def mark_closed(self) -> None: ...\n    def mark_unprepared(self) -> None: ...\n    def __reduce__(self) -> Any: ...\n\nclass CoreProtocol:\n    backend_pid: Any\n    backend_secret: Any\n    __pyx_vtable__: Any\n    def __init__(self, addr: object, con_params: _ConnectionParameters) -> None: ...\n    def is_in_transaction(self) -> bool: ...\n    def __reduce__(self) -> Any: ...\n\nclass BaseProtocol(CoreProtocol, Generic[_Record]):\n    queries_count: Any\n    is_ssl: bool\n    __pyx_vtable__: Any\n    def __init__(\n        self,\n        addr: object,\n        connected_fut: object,\n        con_params: _ConnectionParameters,\n        record_class: type[_Record],\n        loop: object,\n    ) -> None: ...\n    def set_connection(self, connection: object) -> None: ...\n    def get_server_pid(self, *args: object, **kwargs: object) -> int: ...\n    def get_settings(self, *args: object, **kwargs: object) -> ConnectionSettings: ...\n    def get_record_class(self) -> type[_Record]: ...\n    def abort(self) -> None: ...\n    async def bind(\n        self,\n        state: PreparedStatementState[_OtherRecord],\n        args: Sequence[object],\n        portal_name: str,\n        timeout: _TimeoutType,\n    ) -> Any: ...\n    @overload\n    async def bind_execute(\n        self,\n        state: PreparedStatementState[_OtherRecord],\n        args: Sequence[object],\n        portal_name: str,\n        limit: int,\n        return_extra: Literal[False],\n        timeout: _TimeoutType,\n    ) -> list[_OtherRecord]: ...\n    @overload\n    async def bind_execute(\n        self,\n        state: PreparedStatementState[_OtherRecord],\n        args: Sequence[object],\n        portal_name: str,\n        limit: int,\n        return_extra: Literal[True],\n        timeout: _TimeoutType,\n    ) -> tuple[list[_OtherRecord], bytes, bool]: ...\n    @overload\n    async def bind_execute(\n        self,\n        state: PreparedStatementState[_OtherRecord],\n        args: Sequence[object],\n        portal_name: str,\n        limit: int,\n        return_extra: bool,\n        timeout: _TimeoutType,\n    ) -> list[_OtherRecord] | tuple[list[_OtherRecord], bytes, bool]: ...\n    async def bind_execute_many(\n        self,\n        state: PreparedStatementState[_OtherRecord],\n        args: Iterable[Sequence[object]],\n        portal_name: str,\n        timeout: _TimeoutType,\n    ) -> None: ...\n    async def close(self, timeout: _TimeoutType) -> None: ...\n    def _get_timeout(self, timeout: _TimeoutType) -> float | None: ...\n    def _is_cancelling(self) -> bool: ...\n    async def _wait_for_cancellation(self) -> None: ...\n    async def close_statement(\n        self, state: PreparedStatementState[_OtherRecord], timeout: _TimeoutType\n    ) -> Any: ...\n    async def copy_in(self, *args: object, **kwargs: object) -> str: ...\n    async def copy_out(self, *args: object, **kwargs: object) -> str: ...\n    async def execute(self, *args: object, **kwargs: object) -> Any: ...\n    def is_closed(self, *args: object, **kwargs: object) -> Any: ...\n    def is_connected(self, *args: object, **kwargs: object) -> Any: ...\n    def data_received(self, data: object) -> None: ...\n    def connection_made(self, transport: object) -> None: ...\n    def connection_lost(self, exc: Exception | None) -> None: ...\n    def pause_writing(self, *args: object, **kwargs: object) -> Any: ...\n    @overload\n    async def prepare(\n        self,\n        stmt_name: str,\n        query: str,\n        timeout: float | None = ...,\n        *,\n        state: _PreparedStatementState,\n        ignore_custom_codec: bool = ...,\n        record_class: None,\n    ) -> _PreparedStatementState: ...\n    @overload\n    async def prepare(\n        self,\n        stmt_name: str,\n        query: str,\n        timeout: float | None = ...,\n        *,\n        state: None = ...,\n        ignore_custom_codec: bool = ...,\n        record_class: type[_OtherRecord],\n    ) -> PreparedStatementState[_OtherRecord]: ...\n    async def close_portal(self, portal_name: str, timeout: _TimeoutType) -> None: ...\n    async def query(self, *args: object, **kwargs: object) -> str: ...\n    def resume_writing(self, *args: object, **kwargs: object) -> Any: ...\n    def __reduce__(self) -> Any: ...\n\n@final\nclass Codec:\n    __pyx_vtable__: Any\n    def __reduce__(self) -> Any: ...\n\nclass DataCodecConfig:\n    __pyx_vtable__: Any\n    def __init__(self) -> None: ...\n    def add_python_codec(\n        self,\n        typeoid: int,\n        typename: str,\n        typeschema: str,\n        typekind: str,\n        typeinfos: Iterable[object],\n        encoder: Callable[[ConnectionSettings, WriteBuffer, object], object],\n        decoder: Callable[..., object],\n        format: object,\n        xformat: object,\n    ) -> Any: ...\n    def add_types(self, types: Iterable[object]) -> Any: ...\n    def clear_type_cache(self) -> None: ...\n    def declare_fallback_codec(self, oid: int, name: str, schema: str) -> Codec: ...\n    def remove_python_codec(\n        self, typeoid: int, typename: str, typeschema: str\n    ) -> Any: ...\n    def set_builtin_type_codec(\n        self,\n        typeoid: int,\n        typename: str,\n        typeschema: str,\n        typekind: str,\n        alias_to: str,\n        format: object = ...,\n    ) -> Any: ...\n    def __reduce__(self) -> Any: ...\n\nclass Protocol(BaseProtocol[_Record], asyncio.protocols.Protocol): ...\n\nclass Timer:\n    def __init__(self, budget: float | None) -> None: ...\n    def __enter__(self) -> None: ...\n    def __exit__(self, et: object, e: object, tb: object) -> None: ...\n    def get_remaining_budget(self) -> float: ...\n    def has_budget_greater_than(self, amount: float) -> bool: ...\n\n@final\nclass SCRAMAuthentication:\n    AUTHENTICATION_METHODS: ClassVar[list[str]]\n    DEFAULT_CLIENT_NONCE_BYTES: ClassVar[int]\n    DIGEST = sha256\n    REQUIREMENTS_CLIENT_FINAL_MESSAGE: ClassVar[list[str]]\n    REQUIREMENTS_CLIENT_PROOF: ClassVar[list[str]]\n    SASLPREP_PROHIBITED: ClassVar[tuple[Callable[[str], bool], ...]]\n    authentication_method: bytes\n    authorization_message: bytes | None\n    client_channel_binding: bytes\n    client_first_message_bare: bytes | None\n    client_nonce: bytes | None\n    client_proof: bytes | None\n    password_salt: bytes | None\n    password_iterations: int\n    server_first_message: bytes | None\n    server_key: hmac.HMAC | None\n    server_nonce: bytes | None\n"
  },
  {
    "path": "asyncpg/protocol/protocol.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\n# cython: language_level=3\n\ncimport cython\ncimport cpython\n\nimport asyncio\nimport builtins\nimport codecs\nimport collections.abc\nimport socket\nimport time\nimport weakref\n\nfrom asyncpg.pgproto.pgproto cimport (\n    WriteBuffer,\n    ReadBuffer,\n\n    FRBuffer,\n    frb_init,\n    frb_read,\n    frb_read_all,\n    frb_slice_from,\n    frb_check,\n    frb_set_len,\n    frb_get_len,\n)\n\nfrom asyncpg.pgproto cimport pgproto\nfrom asyncpg.protocol cimport cpythonx\nfrom asyncpg.protocol cimport recordcapi\n\nfrom libc.stdint cimport int8_t, uint8_t, int16_t, uint16_t, \\\n                         int32_t, uint32_t, int64_t, uint64_t, \\\n                         INT32_MAX, UINT32_MAX\n\nfrom asyncpg.exceptions import _base as apg_exc_base\nfrom asyncpg import compat\nfrom asyncpg import types as apg_types\nfrom asyncpg import exceptions as apg_exc\n\nfrom asyncpg.pgproto cimport hton\nfrom asyncpg.protocol.record import Record, RecordDescriptor\n\n\ninclude \"consts.pxi\"\ninclude \"pgtypes.pxi\"\n\ninclude \"encodings.pyx\"\ninclude \"settings.pyx\"\n\ninclude \"codecs/base.pyx\"\ninclude \"codecs/textutils.pyx\"\n\n# register codecs provided by pgproto\ninclude \"codecs/pgproto.pyx\"\n\n# nonscalar\ninclude \"codecs/array.pyx\"\ninclude \"codecs/range.pyx\"\ninclude \"codecs/record.pyx\"\n\ninclude \"coreproto.pyx\"\ninclude \"prepared_stmt.pyx\"\n\n\nNO_TIMEOUT = object()\n\n\ncdef class BaseProtocol(CoreProtocol):\n    def __init__(self, addr, connected_fut, con_params, record_class: type, loop):\n        # type of `con_params` is `_ConnectionParameters`\n        CoreProtocol.__init__(self, addr, con_params)\n\n        self.loop = loop\n        self.transport = None\n        self.waiter = connected_fut\n        self.cancel_waiter = None\n        self.cancel_sent_waiter = None\n\n        self.settings = ConnectionSettings((addr, con_params.database))\n        self.record_class = record_class\n\n        self.statement = None\n        self.return_extra = False\n\n        self.last_query = None\n\n        self.closing = False\n        self.is_reading = True\n        self.writing_allowed = asyncio.Event()\n        self.writing_allowed.set()\n\n        self.timeout_handle = None\n\n        self.queries_count = 0\n\n        self._is_ssl = False\n\n        try:\n            self.create_future = loop.create_future\n        except AttributeError:\n            self.create_future = self._create_future_fallback\n\n    def set_connection(self, connection):\n        self.conref = weakref.ref(connection)\n\n    cdef get_connection(self):\n        if self.conref is not None:\n            return self.conref()\n        else:\n            return None\n\n    def get_server_pid(self):\n        return self.backend_pid\n\n    def get_settings(self):\n        return self.settings\n\n    def get_record_class(self):\n        return self.record_class\n\n    cdef inline resume_reading(self):\n        if not self.is_reading:\n            self.is_reading = True\n            self.transport.resume_reading()\n\n    cdef inline pause_reading(self):\n        if self.is_reading:\n            self.is_reading = False\n            self.transport.pause_reading()\n\n    async def prepare(self, stmt_name, query, timeout,\n                      *,\n                      PreparedStatementState state=None,\n                      ignore_custom_codec=False,\n                      record_class):\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        timeout = self._get_timeout_impl(timeout)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            self._prepare_and_describe(stmt_name, query)  # network op\n            self.last_query = query\n            if state is None:\n                state = PreparedStatementState(\n                    stmt_name, query, self, record_class, ignore_custom_codec)\n            self.statement = state\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def bind_execute(\n        self,\n        state: PreparedStatementState,\n        args,\n        portal_name: str,\n        limit: int,\n        return_extra: bool,\n        timeout,\n    ):\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        timeout = self._get_timeout_impl(timeout)\n        args_buf = state._encode_bind_msg(args)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            if not state.prepared:\n                self._send_parse_message(state.name, state.query)\n\n            self._bind_execute(\n                portal_name,\n                state.name,\n                args_buf,\n                limit)  # network op\n\n            self.last_query = state.query\n            self.statement = state\n            self.return_extra = return_extra\n            self.queries_count += 1\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def bind_execute_many(\n        self,\n        state: PreparedStatementState,\n        args,\n        portal_name: str,\n        timeout,\n        return_rows: bool,\n    ):\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        timeout = self._get_timeout_impl(timeout)\n        timer = Timer(timeout)\n\n        # Make sure the argument sequence is encoded lazily with\n        # this generator expression to keep the memory pressure under\n        # control.\n        data_gen = (state._encode_bind_msg(b, i) for i, b in enumerate(args))\n        arg_bufs = iter(data_gen)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            if not state.prepared:\n                self._send_parse_message(state.name, state.query)\n\n            more = self._bind_execute_many(\n                portal_name,\n                state.name,\n                arg_bufs,\n                return_rows)  # network op\n\n            self.last_query = state.query\n            self.statement = state\n            self.return_extra = False\n            self.queries_count += 1\n\n            while more:\n                with timer:\n                    await compat.wait_for(\n                        self.writing_allowed.wait(),\n                        timeout=timer.get_remaining_budget())\n                    # On Windows the above event somehow won't allow context\n                    # switch, so forcing one with sleep(0) here\n                    await asyncio.sleep(0)\n                if not timer.has_budget_greater_than(0):\n                    raise asyncio.TimeoutError\n                more = self._bind_execute_many_more()  # network op\n\n        except asyncio.TimeoutError as e:\n            self._bind_execute_many_fail(e)  # network op\n\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def bind(self, PreparedStatementState state, args,\n                   str portal_name, timeout):\n\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        timeout = self._get_timeout_impl(timeout)\n        args_buf = state._encode_bind_msg(args)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            self._bind(\n                portal_name,\n                state.name,\n                args_buf)  # network op\n\n            self.last_query = state.query\n            self.statement = state\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def execute(self, PreparedStatementState state,\n                      str portal_name, int limit, return_extra,\n                      timeout):\n\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        timeout = self._get_timeout_impl(timeout)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            self._execute(\n                portal_name,\n                limit)  # network op\n\n            self.last_query = state.query\n            self.statement = state\n            self.return_extra = return_extra\n            self.queries_count += 1\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def close_portal(self, str portal_name, timeout):\n\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        timeout = self._get_timeout_impl(timeout)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            self._close(\n                portal_name,\n                True)  # network op\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def query(self, query, timeout):\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n        # query() needs to call _get_timeout instead of _get_timeout_impl\n        # for consistent validation, as it is called differently from\n        # prepare/bind/execute methods.\n        timeout = self._get_timeout(timeout)\n\n        waiter = self._new_waiter(timeout)\n        try:\n            self._simple_query(query)  # network op\n            self.last_query = query\n            self.queries_count += 1\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    async def copy_out(self, copy_stmt, sink, timeout):\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n\n        timeout = self._get_timeout_impl(timeout)\n        timer = Timer(timeout)\n\n        # The copy operation is guarded by a single timeout\n        # on the top level.\n        waiter = self._new_waiter(timer.get_remaining_budget())\n\n        self._copy_out(copy_stmt)\n\n        try:\n            while True:\n                self.resume_reading()\n\n                with timer:\n                    buffer, done, status_msg = await waiter\n\n                # buffer will be empty if CopyDone was received apart from\n                # the last CopyData message.\n                if buffer:\n                    try:\n                        with timer:\n                            await compat.wait_for(\n                                sink(buffer),\n                                timeout=timer.get_remaining_budget())\n                    except (Exception, asyncio.CancelledError) as ex:\n                        # Abort the COPY operation on any error in\n                        # output sink.\n                        self._request_cancel()\n                        # Make asyncio shut up about unretrieved\n                        # QueryCanceledError\n                        waiter.add_done_callback(lambda f: f.exception())\n                        raise\n\n                # done will be True upon receipt of CopyDone.\n                if done:\n                    break\n\n                waiter = self._new_waiter(timer.get_remaining_budget())\n\n        finally:\n            self.resume_reading()\n\n        return status_msg\n\n    async def copy_in(self, copy_stmt, reader, data,\n                      records, PreparedStatementState record_stmt, timeout):\n        cdef:\n            WriteBuffer wbuf\n            ssize_t num_cols\n            Codec codec\n\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n\n        timeout = self._get_timeout_impl(timeout)\n        timer = Timer(timeout)\n\n        waiter = self._new_waiter(timer.get_remaining_budget())\n\n        # Initiate COPY IN.\n        self._copy_in(copy_stmt)\n\n        try:\n            if record_stmt is not None:\n                # copy_in_records in binary mode\n                wbuf = WriteBuffer.new()\n                # Signature\n                wbuf.write_bytes(_COPY_SIGNATURE)\n                # Flags field\n                wbuf.write_int32(0)\n                # No header extension\n                wbuf.write_int32(0)\n\n                record_stmt._ensure_rows_decoder()\n                codecs = record_stmt.rows_codecs\n                num_cols = len(codecs)\n                settings = self.settings\n\n                for codec in codecs:\n                    if (not codec.has_encoder() or\n                            codec.format != PG_FORMAT_BINARY):\n                        raise apg_exc.InternalClientError(\n                            'no binary format encoder for '\n                            'type {} (OID {})'.format(codec.name, codec.oid))\n\n                if isinstance(records, collections.abc.AsyncIterable):\n                    async for row in records:\n                        # Tuple header\n                        wbuf.write_int16(<int16_t>num_cols)\n                        # Tuple data\n                        for i in range(num_cols):\n                            item = row[i]\n                            if item is None:\n                                wbuf.write_int32(-1)\n                            else:\n                                codec = <Codec>cpython.PyTuple_GET_ITEM(\n                                    codecs, i)\n                                codec.encode(settings, wbuf, item)\n\n                        if wbuf.len() >= _COPY_BUFFER_SIZE:\n                            with timer:\n                                await self.writing_allowed.wait()\n                            self._write_copy_data_msg(wbuf)\n                            wbuf = WriteBuffer.new()\n                else:\n                    for row in records:\n                        # Tuple header\n                        wbuf.write_int16(<int16_t>num_cols)\n                        # Tuple data\n                        for i in range(num_cols):\n                            item = row[i]\n                            if item is None:\n                                wbuf.write_int32(-1)\n                            else:\n                                codec = <Codec>cpython.PyTuple_GET_ITEM(\n                                    codecs, i)\n                                codec.encode(settings, wbuf, item)\n\n                        if wbuf.len() >= _COPY_BUFFER_SIZE:\n                            with timer:\n                                await self.writing_allowed.wait()\n                            self._write_copy_data_msg(wbuf)\n                            wbuf = WriteBuffer.new()\n\n                # End of binary copy.\n                wbuf.write_int16(-1)\n                self._write_copy_data_msg(wbuf)\n\n            elif reader is not None:\n                try:\n                    aiter = reader.__aiter__\n                except AttributeError:\n                    raise TypeError('reader is not an asynchronous iterable')\n                else:\n                    iterator = aiter()\n\n                try:\n                    while True:\n                        # We rely on protocol flow control to moderate the\n                        # rate of data messages.\n                        with timer:\n                            await self.writing_allowed.wait()\n                        with timer:\n                            chunk = await compat.wait_for(\n                                iterator.__anext__(),\n                                timeout=timer.get_remaining_budget())\n                        self._write_copy_data_msg(chunk)\n                except builtins.StopAsyncIteration:\n                    pass\n            else:\n                # Buffer passed in directly.\n                await self.writing_allowed.wait()\n                self._write_copy_data_msg(data)\n\n        except asyncio.TimeoutError:\n            self._write_copy_fail_msg('TimeoutError')\n            self._on_timeout(self.waiter)\n            try:\n                await waiter\n            except TimeoutError:\n                raise\n            else:\n                raise apg_exc.InternalClientError('TimoutError was not raised')\n\n        except (Exception, asyncio.CancelledError) as e:\n            self._write_copy_fail_msg(str(e))\n            self._request_cancel()\n            # Make asyncio shut up about unretrieved QueryCanceledError\n            waiter.add_done_callback(lambda f: f.exception())\n            raise\n\n        self._write_copy_done_msg()\n\n        status_msg = await waiter\n\n        return status_msg\n\n    async def close_statement(self, PreparedStatementState state, timeout):\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        self._check_state()\n\n        if state.refs != 0:\n            raise apg_exc.InternalClientError(\n                'cannot close prepared statement; refs == {} != 0'.format(\n                    state.refs))\n\n        timeout = self._get_timeout_impl(timeout)\n        waiter = self._new_waiter(timeout)\n        try:\n            self._close(state.name, False)  # network op\n            state.closed = True\n        except Exception as ex:\n            waiter.set_exception(ex)\n            self._coreproto_error()\n        finally:\n            return await waiter\n\n    def is_closed(self):\n        return self.closing\n\n    def is_connected(self):\n        return not self.closing and self.con_status == CONNECTION_OK\n\n    def abort(self):\n        if self.closing:\n            return\n        self.closing = True\n        self._handle_waiter_on_connection_lost(None)\n        self._terminate()\n        self.transport.abort()\n        self.transport = None\n\n    async def close(self, timeout):\n        if self.closing:\n            return\n\n        self.closing = True\n\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n\n        if self.waiter is not None:\n            # If there is a query running, cancel it\n            self._request_cancel()\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n            if self.cancel_waiter is not None:\n                await self.cancel_waiter\n\n        assert self.waiter is None\n\n        timeout = self._get_timeout_impl(timeout)\n\n        # Ask the server to terminate the connection and wait for it\n        # to drop.\n        self.waiter = self._new_waiter(timeout)\n        self._terminate()\n        try:\n            await self.waiter\n        except ConnectionResetError:\n            # There appears to be a difference in behaviour of asyncio\n            # in Windows, where, instead of calling protocol.connection_lost()\n            # a ConnectionResetError will be thrown into the task.\n            pass\n        finally:\n            self.waiter = None\n            self.transport.abort()\n\n    def _request_cancel(self):\n        self.cancel_waiter = self.create_future()\n        self.cancel_sent_waiter = self.create_future()\n\n        con = self.get_connection()\n        if con is not None:\n            # if 'con' is None it means that the connection object has been\n            # garbage collected and that the transport will soon be aborted.\n            con._cancel_current_command(self.cancel_sent_waiter)\n        else:\n            self.loop.call_exception_handler({\n                'message': 'asyncpg.Protocol has no reference to its '\n                           'Connection object and yet a cancellation '\n                           'was requested. Please report this at '\n                           'github.com/magicstack/asyncpg.'\n            })\n            self.abort()\n\n        if self.state == PROTOCOL_PREPARE:\n            # we need to send a SYNC to server if we cancel during the PREPARE phase\n            # because the PREPARE sequence does not send a SYNC itself.\n            # we cannot send this extra SYNC if we are not in PREPARE phase,\n            # because then we would issue two SYNCs and we would get two ReadyForQuery\n            # replies, which our current state machine implementation cannot handle\n            self._write(SYNC_MESSAGE)\n        self._set_state(PROTOCOL_CANCELLED)\n\n    def _on_timeout(self, fut):\n        if self.waiter is not fut or fut.done() or \\\n                self.cancel_waiter is not None or \\\n                self.timeout_handle is None:\n            return\n        self._request_cancel()\n        self.waiter.set_exception(asyncio.TimeoutError())\n\n    def _on_waiter_completed(self, fut):\n        if self.timeout_handle:\n            self.timeout_handle.cancel()\n            self.timeout_handle = None\n        if fut is not self.waiter or self.cancel_waiter is not None:\n            return\n        if fut.cancelled():\n            self._request_cancel()\n\n    def _create_future_fallback(self):\n        return asyncio.Future(loop=self.loop)\n\n    cdef _handle_waiter_on_connection_lost(self, cause):\n        if self.waiter is not None and not self.waiter.done():\n            exc = apg_exc.ConnectionDoesNotExistError(\n                'connection was closed in the middle of '\n                'operation')\n            if cause is not None:\n                exc.__cause__ = cause\n            self.waiter.set_exception(exc)\n        self.waiter = None\n\n    cdef _set_server_parameter(self, name, val):\n        self.settings.add_setting(name, val)\n\n    def _get_timeout(self, timeout):\n        if timeout is not None:\n            try:\n                if type(timeout) is bool:\n                    raise ValueError\n                timeout = float(timeout)\n            except ValueError:\n                raise ValueError(\n                    'invalid timeout value: expected non-negative float '\n                    '(got {!r})'.format(timeout)) from None\n\n        return self._get_timeout_impl(timeout)\n\n    cdef inline _get_timeout_impl(self, timeout):\n        if timeout is None:\n            timeout = self.get_connection()._config.command_timeout\n        elif timeout is NO_TIMEOUT:\n            timeout = None\n        else:\n            timeout = float(timeout)\n\n        if timeout is not None and timeout <= 0:\n            raise asyncio.TimeoutError()\n        return timeout\n\n    cdef _check_state(self):\n        if self.cancel_waiter is not None:\n            raise apg_exc.InterfaceError(\n                'cannot perform operation: another operation is cancelling')\n        if self.closing:\n            raise apg_exc.InterfaceError(\n                'cannot perform operation: connection is closed')\n        if self.waiter is not None or self.timeout_handle is not None:\n            raise apg_exc.InterfaceError(\n                'cannot perform operation: another operation is in progress')\n\n    def _is_cancelling(self):\n        return (\n            self.cancel_waiter is not None or\n            self.cancel_sent_waiter is not None\n        )\n\n    async def _wait_for_cancellation(self):\n        if self.cancel_sent_waiter is not None:\n            await self.cancel_sent_waiter\n            self.cancel_sent_waiter = None\n        if self.cancel_waiter is not None:\n            await self.cancel_waiter\n\n    cdef _coreproto_error(self):\n        try:\n            if self.waiter is not None:\n                if not self.waiter.done():\n                    raise apg_exc.InternalClientError(\n                        'waiter is not done while handling critical '\n                        'protocol error')\n                self.waiter = None\n        finally:\n            self.abort()\n\n    cdef _new_waiter(self, timeout):\n        if self.waiter is not None:\n            raise apg_exc.InterfaceError(\n                'cannot perform operation: another operation is in progress')\n        self.waiter = self.create_future()\n        if timeout is not None:\n            self.timeout_handle = self.loop.call_later(\n                timeout, self._on_timeout, self.waiter)\n        self.waiter.add_done_callback(self._on_waiter_completed)\n        return self.waiter\n\n    cdef _on_result__connect(self, object waiter):\n        waiter.set_result(True)\n\n    cdef _on_result__prepare(self, object waiter):\n        if PG_DEBUG:\n            if self.statement is None:\n                raise apg_exc.InternalClientError(\n                    '_on_result__prepare: statement is None')\n\n        if self.result_param_desc is not None:\n            self.statement._set_args_desc(self.result_param_desc)\n        if self.result_row_desc is not None:\n            self.statement._set_row_desc(self.result_row_desc)\n        waiter.set_result(self.statement)\n\n    cdef _on_result__bind_and_exec(self, object waiter):\n        if self.return_extra:\n            waiter.set_result((\n                self.result,\n                self.result_status_msg,\n                self.result_execute_completed))\n        else:\n            waiter.set_result(self.result)\n\n    cdef _on_result__bind(self, object waiter):\n        waiter.set_result(self.result)\n\n    cdef _on_result__close_stmt_or_portal(self, object waiter):\n        waiter.set_result(self.result)\n\n    cdef _on_result__simple_query(self, object waiter):\n        waiter.set_result(self.result_status_msg.decode(self.encoding))\n\n    cdef _on_result__copy_out(self, object waiter):\n        cdef bint copy_done = self.state == PROTOCOL_COPY_OUT_DONE\n        if copy_done:\n            status_msg = self.result_status_msg.decode(self.encoding)\n        else:\n            status_msg = None\n\n        # We need to put some backpressure on Postgres\n        # here in case the sink is slow to process the output.\n        self.pause_reading()\n\n        waiter.set_result((self.result, copy_done, status_msg))\n\n    cdef _on_result__copy_in(self, object waiter):\n        status_msg = self.result_status_msg.decode(self.encoding)\n        waiter.set_result(status_msg)\n\n    cdef _decode_row(self, const char* buf, ssize_t buf_len):\n        if PG_DEBUG:\n            if self.statement is None:\n                raise apg_exc.InternalClientError(\n                    '_decode_row: statement is None')\n\n        return self.statement._decode_row(buf, buf_len)\n\n    cdef _dispatch_result(self):\n        waiter = self.waiter\n        self.waiter = None\n\n        if PG_DEBUG:\n            if waiter is None:\n                raise apg_exc.InternalClientError('_on_result: waiter is None')\n\n        if waiter.cancelled():\n            return\n\n        if waiter.done():\n            raise apg_exc.InternalClientError('_on_result: waiter is done')\n\n        if self.result_type == RESULT_FAILED:\n            if isinstance(self.result, dict):\n                exc = apg_exc_base.PostgresError.new(\n                    self.result, query=self.last_query)\n            else:\n                exc = self.result\n            waiter.set_exception(exc)\n            return\n\n        try:\n            if self.state == PROTOCOL_AUTH:\n                self._on_result__connect(waiter)\n\n            elif self.state == PROTOCOL_PREPARE:\n                self._on_result__prepare(waiter)\n\n            elif self.state == PROTOCOL_BIND_EXECUTE:\n                self._on_result__bind_and_exec(waiter)\n\n            elif self.state == PROTOCOL_BIND_EXECUTE_MANY:\n                self._on_result__bind_and_exec(waiter)\n\n            elif self.state == PROTOCOL_EXECUTE:\n                self._on_result__bind_and_exec(waiter)\n\n            elif self.state == PROTOCOL_BIND:\n                self._on_result__bind(waiter)\n\n            elif self.state == PROTOCOL_CLOSE_STMT_PORTAL:\n                self._on_result__close_stmt_or_portal(waiter)\n\n            elif self.state == PROTOCOL_SIMPLE_QUERY:\n                self._on_result__simple_query(waiter)\n\n            elif (self.state == PROTOCOL_COPY_OUT_DATA or\n                    self.state == PROTOCOL_COPY_OUT_DONE):\n                self._on_result__copy_out(waiter)\n\n            elif self.state == PROTOCOL_COPY_IN_DATA:\n                self._on_result__copy_in(waiter)\n\n            elif self.state == PROTOCOL_TERMINATING:\n                # We are waiting for the connection to drop, so\n                # ignore any stray results at this point.\n                pass\n\n            else:\n                raise apg_exc.InternalClientError(\n                    'got result for unknown protocol state {}'.\n                    format(self.state))\n\n        except Exception as exc:\n            waiter.set_exception(exc)\n\n    cdef _on_result(self):\n        if self.timeout_handle is not None:\n            self.timeout_handle.cancel()\n            self.timeout_handle = None\n\n        if self.cancel_waiter is not None:\n            # We have received the result of a cancelled command.\n            if not self.cancel_waiter.done():\n                # The cancellation future might have been cancelled\n                # by the cancellation of the entire task running the query.\n                self.cancel_waiter.set_result(None)\n            self.cancel_waiter = None\n            if self.waiter is not None and self.waiter.done():\n                self.waiter = None\n            if self.waiter is None:\n                return\n\n        try:\n            self._dispatch_result()\n        finally:\n            self.statement = None\n            self.last_query = None\n            self.return_extra = False\n\n    cdef _on_notice(self, parsed):\n        con = self.get_connection()\n        if con is not None:\n            con._process_log_message(parsed, self.last_query)\n\n    cdef _on_notification(self, pid, channel, payload):\n        con = self.get_connection()\n        if con is not None:\n            con._process_notification(pid, channel, payload)\n\n    cdef _on_connection_lost(self, exc):\n        if self.closing:\n            # The connection was lost because\n            # Protocol.close() was called\n            if self.waiter is not None and not self.waiter.done():\n                if exc is None:\n                    self.waiter.set_result(None)\n                else:\n                    self.waiter.set_exception(exc)\n            self.waiter = None\n        else:\n            # The connection was lost because it was\n            # terminated or due to another error;\n            # Throw an error in any awaiting waiter.\n            self.closing = True\n            # Cleanup the connection resources, including, possibly,\n            # releasing the pool holder.\n            con = self.get_connection()\n            if con is not None:\n                con._cleanup()\n            self._handle_waiter_on_connection_lost(exc)\n\n    cdef _write(self, buf):\n        self.transport.write(memoryview(buf))\n\n    cdef _writelines(self, list buffers):\n        self.transport.writelines(buffers)\n\n    # asyncio callbacks:\n\n    def data_received(self, data):\n        self.buffer.feed_data(data)\n        self._read_server_messages()\n\n    def connection_made(self, transport):\n        self.transport = transport\n\n        sock = transport.get_extra_info('socket')\n        if (sock is not None and\n              (not hasattr(socket, 'AF_UNIX')\n               or sock.family != socket.AF_UNIX)):\n            sock.setsockopt(socket.IPPROTO_TCP,\n                            socket.TCP_NODELAY, 1)\n\n        try:\n            self._connect()\n        except Exception as ex:\n            transport.abort()\n            self.con_status = CONNECTION_BAD\n            self._set_state(PROTOCOL_FAILED)\n            self._on_error(ex)\n\n    def connection_lost(self, exc):\n        self.con_status = CONNECTION_BAD\n        self._set_state(PROTOCOL_FAILED)\n        self._on_connection_lost(exc)\n\n    def pause_writing(self):\n        self.writing_allowed.clear()\n\n    def resume_writing(self):\n        self.writing_allowed.set()\n\n    @property\n    def is_ssl(self):\n        return self._is_ssl\n\n    @is_ssl.setter\n    def is_ssl(self, value):\n        self._is_ssl = value\n\n\nclass Timer:\n    def __init__(self, budget):\n        self._budget = budget\n        self._started = 0\n\n    def __enter__(self):\n        if self._budget is not None:\n            self._started = time.monotonic()\n\n    def __exit__(self, et, e, tb):\n        if self._budget is not None:\n            self._budget -= time.monotonic() - self._started\n\n    def get_remaining_budget(self):\n        return self._budget\n\n    def has_budget_greater_than(self, amount):\n        if self._budget is None:\n            # Unlimited budget.\n            return True\n        else:\n            return self._budget > amount\n\n\nclass Protocol(BaseProtocol, asyncio.Protocol):\n    pass\n\n\ndef _create_record(object mapping, tuple elems):\n    # Exposed only for testing purposes.\n\n    cdef:\n        object rec\n        int32_t i\n\n    if mapping is None:\n        desc = RecordDescriptor({}, ())\n    else:\n        desc = RecordDescriptor(\n            mapping, tuple(mapping) if mapping else ())\n\n    rec = desc.make_record(Record, len(elems))\n    for i in range(len(elems)):\n        elem = elems[i]\n        cpython.Py_INCREF(elem)\n        recordcapi.ApgRecord_SET_ITEM(rec, i, elem)\n    return rec\n"
  },
  {
    "path": "asyncpg/protocol/record/pythoncapi_compat.h",
    "content": "// Header file providing new C API functions to old Python versions.\n//\n// File distributed under the Zero Clause BSD (0BSD) license.\n// Copyright Contributors to the pythoncapi_compat project.\n//\n// Homepage:\n// https://github.com/python/pythoncapi_compat\n//\n// Latest version:\n// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h\n//\n// SPDX-License-Identifier: 0BSD\n\n#ifndef PYTHONCAPI_COMPAT\n#define PYTHONCAPI_COMPAT\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <Python.h>\n#include <stddef.h>               // offsetof()\n\n// Python 3.11.0b4 added PyFrame_Back() to Python.h\n#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION)\n#  include \"frameobject.h\"        // PyFrameObject, PyFrame_GetBack()\n#endif\n#if PY_VERSION_HEX < 0x030C00A3\n#  include <structmember.h>       // T_SHORT, READONLY\n#endif\n\n\n#ifndef _Py_CAST\n#  define _Py_CAST(type, expr) ((type)(expr))\n#endif\n\n// Static inline functions should use _Py_NULL rather than using directly NULL\n// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer,\n// _Py_NULL is defined as nullptr.\n#ifndef _Py_NULL\n#  if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \\\n          || (defined(__cplusplus) && __cplusplus >= 201103)\n#    define _Py_NULL nullptr\n#  else\n#    define _Py_NULL NULL\n#  endif\n#endif\n\n// Cast argument to PyObject* type.\n#ifndef _PyObject_CAST\n#  define _PyObject_CAST(op) _Py_CAST(PyObject*, op)\n#endif\n\n#ifndef Py_BUILD_ASSERT\n#  define Py_BUILD_ASSERT(cond) \\\n        do { \\\n            (void)sizeof(char [1 - 2 * !(cond)]); \\\n        } while(0)\n#endif\n\n\n// bpo-42262 added Py_NewRef() to Python 3.10.0a3\n#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef)\nstatic inline PyObject* _Py_NewRef(PyObject *obj)\n{\n    Py_INCREF(obj);\n    return obj;\n}\n#define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj))\n#endif\n\n\n// bpo-42262 added Py_XNewRef() to Python 3.10.0a3\n#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef)\nstatic inline PyObject* _Py_XNewRef(PyObject *obj)\n{\n    Py_XINCREF(obj);\n    return obj;\n}\n#define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj))\n#endif\n\n\n// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4\n#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT)\nstatic inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)\n{\n    ob->ob_refcnt = refcnt;\n}\n#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)\n#endif\n\n\n// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2.\n// It is excluded from the limited C API.\n#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API)\n#define Py_SETREF(dst, src)                                     \\\n    do {                                                        \\\n        PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \\\n        PyObject *_tmp_dst = (*_tmp_dst_ptr);                   \\\n        *_tmp_dst_ptr = _PyObject_CAST(src);                    \\\n        Py_DECREF(_tmp_dst);                                    \\\n    } while (0)\n\n#define Py_XSETREF(dst, src)                                    \\\n    do {                                                        \\\n        PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \\\n        PyObject *_tmp_dst = (*_tmp_dst_ptr);                   \\\n        *_tmp_dst_ptr = _PyObject_CAST(src);                    \\\n        Py_XDECREF(_tmp_dst);                                   \\\n    } while (0)\n#endif\n\n\n// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse()\n// to Python 3.10.0b1.\n#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is)\n#  define Py_Is(x, y) ((x) == (y))\n#endif\n#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone)\n#  define Py_IsNone(x) Py_Is(x, Py_None)\n#endif\n#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue)\n#  define Py_IsTrue(x) Py_Is(x, Py_True)\n#endif\n#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse)\n#  define Py_IsFalse(x) Py_Is(x, Py_False)\n#endif\n\n\n// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4\n#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)\nstatic inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type)\n{\n    ob->ob_type = type;\n}\n#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type)\n#endif\n\n\n// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4\n#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE)\nstatic inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size)\n{\n    ob->ob_size = size;\n}\n#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size)\n#endif\n\n\n// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1\n#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION)\nstatic inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame)\n{\n    assert(frame != _Py_NULL);\n    assert(frame->f_code != _Py_NULL);\n    return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code));\n}\n#endif\n\nstatic inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame)\n{\n    PyCodeObject *code = PyFrame_GetCode(frame);\n    Py_DECREF(code);\n    return code;\n}\n\n\n// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1\n#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)\nstatic inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame)\n{\n    assert(frame != _Py_NULL);\n    return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back));\n}\n#endif\n\n#if !defined(PYPY_VERSION)\nstatic inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame)\n{\n    PyFrameObject *back = PyFrame_GetBack(frame);\n    Py_XDECREF(back);\n    return back;\n}\n#endif\n\n\n// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7\n#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyFrame_GetLocals(PyFrameObject *frame)\n{\n#if PY_VERSION_HEX >= 0x030400B1\n    if (PyFrame_FastToLocalsWithError(frame) < 0) {\n        return NULL;\n    }\n#else\n    PyFrame_FastToLocals(frame);\n#endif\n    return Py_NewRef(frame->f_locals);\n}\n#endif\n\n\n// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7\n#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame)\n{\n    return Py_NewRef(frame->f_globals);\n}\n#endif\n\n\n// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7\n#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame)\n{\n    return Py_NewRef(frame->f_builtins);\n}\n#endif\n\n\n// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1\n#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION)\nstatic inline int PyFrame_GetLasti(PyFrameObject *frame)\n{\n#if PY_VERSION_HEX >= 0x030A00A7\n    // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset,\n    // not a bytes offset anymore. Python uses 16-bit \"wordcode\" (2 bytes)\n    // instructions.\n    if (frame->f_lasti < 0) {\n        return -1;\n    }\n    return frame->f_lasti * 2;\n#else\n    return frame->f_lasti;\n#endif\n}\n#endif\n\n\n// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2\n#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name)\n{\n    PyObject *locals, *value;\n\n    locals = PyFrame_GetLocals(frame);\n    if (locals == NULL) {\n        return NULL;\n    }\n#if PY_VERSION_HEX >= 0x03000000\n    value = PyDict_GetItemWithError(locals, name);\n#else\n    value = _PyDict_GetItemWithError(locals, name);\n#endif\n    Py_DECREF(locals);\n\n    if (value == NULL) {\n        if (PyErr_Occurred()) {\n            return NULL;\n        }\n#if PY_VERSION_HEX >= 0x03000000\n        PyErr_Format(PyExc_NameError, \"variable %R does not exist\", name);\n#else\n        PyErr_SetString(PyExc_NameError, \"variable does not exist\");\n#endif\n        return NULL;\n    }\n    return Py_NewRef(value);\n}\n#endif\n\n\n// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2\n#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION)\nstatic inline PyObject*\nPyFrame_GetVarString(PyFrameObject *frame, const char *name)\n{\n    PyObject *name_obj, *value;\n#if PY_VERSION_HEX >= 0x03000000\n    name_obj = PyUnicode_FromString(name);\n#else\n    name_obj = PyString_FromString(name);\n#endif\n    if (name_obj == NULL) {\n        return NULL;\n    }\n    value = PyFrame_GetVar(frame, name_obj);\n    Py_DECREF(name_obj);\n    return value;\n}\n#endif\n\n\n// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5\n#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000)\nstatic inline PyInterpreterState *\nPyThreadState_GetInterpreter(PyThreadState *tstate)\n{\n    assert(tstate != _Py_NULL);\n    return tstate->interp;\n}\n#endif\n\n\n// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1\n#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)\nstatic inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate)\n{\n    assert(tstate != _Py_NULL);\n    return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame));\n}\n#endif\n\n#if !defined(PYPY_VERSION)\nstatic inline PyFrameObject*\n_PyThreadState_GetFrameBorrow(PyThreadState *tstate)\n{\n    PyFrameObject *frame = PyThreadState_GetFrame(tstate);\n    Py_XDECREF(frame);\n    return frame;\n}\n#endif\n\n\n// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5\n#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION)\nstatic inline PyInterpreterState* PyInterpreterState_Get(void)\n{\n    PyThreadState *tstate;\n    PyInterpreterState *interp;\n\n    tstate = PyThreadState_GET();\n    if (tstate == _Py_NULL) {\n        Py_FatalError(\"GIL released (tstate is NULL)\");\n    }\n    interp = tstate->interp;\n    if (interp == _Py_NULL) {\n        Py_FatalError(\"no current interpreter\");\n    }\n    return interp;\n}\n#endif\n\n\n// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6\n#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)\nstatic inline uint64_t PyThreadState_GetID(PyThreadState *tstate)\n{\n    assert(tstate != _Py_NULL);\n    return tstate->id;\n}\n#endif\n\n// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2\n#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)\nstatic inline void PyThreadState_EnterTracing(PyThreadState *tstate)\n{\n    tstate->tracing++;\n#if PY_VERSION_HEX >= 0x030A00A1\n    tstate->cframe->use_tracing = 0;\n#else\n    tstate->use_tracing = 0;\n#endif\n}\n#endif\n\n// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2\n#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)\nstatic inline void PyThreadState_LeaveTracing(PyThreadState *tstate)\n{\n    int use_tracing = (tstate->c_tracefunc != _Py_NULL\n                       || tstate->c_profilefunc != _Py_NULL);\n    tstate->tracing--;\n#if PY_VERSION_HEX >= 0x030A00A1\n    tstate->cframe->use_tracing = use_tracing;\n#else\n    tstate->use_tracing = use_tracing;\n#endif\n}\n#endif\n\n\n// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1\n// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11\n#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1\nstatic inline PyObject* PyObject_CallNoArgs(PyObject *func)\n{\n    return PyObject_CallFunctionObjArgs(func, NULL);\n}\n#endif\n\n\n// bpo-39245 made PyObject_CallOneArg() public (previously called\n// _PyObject_CallOneArg) in Python 3.9.0a4\n// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11\n#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4\nstatic inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg)\n{\n    return PyObject_CallFunctionObjArgs(func, arg, NULL);\n}\n#endif\n\n\n// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3\n#if PY_VERSION_HEX < 0x030A00A3\nstatic inline int\nPyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)\n{\n    int res;\n\n    if (!value && !PyErr_Occurred()) {\n        // PyModule_AddObject() raises TypeError in this case\n        PyErr_SetString(PyExc_SystemError,\n                        \"PyModule_AddObjectRef() must be called \"\n                        \"with an exception raised if value is NULL\");\n        return -1;\n    }\n\n    Py_XINCREF(value);\n    res = PyModule_AddObject(module, name, value);\n    if (res < 0) {\n        Py_XDECREF(value);\n    }\n    return res;\n}\n#endif\n\n\n// bpo-40024 added PyModule_AddType() to Python 3.9.0a5\n#if PY_VERSION_HEX < 0x030900A5\nstatic inline int PyModule_AddType(PyObject *module, PyTypeObject *type)\n{\n    const char *name, *dot;\n\n    if (PyType_Ready(type) < 0) {\n        return -1;\n    }\n\n    // inline _PyType_Name()\n    name = type->tp_name;\n    assert(name != _Py_NULL);\n    dot = strrchr(name, '.');\n    if (dot != _Py_NULL) {\n        name = dot + 1;\n    }\n\n    return PyModule_AddObjectRef(module, name, _PyObject_CAST(type));\n}\n#endif\n\n\n// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6.\n// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2.\n#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)\nstatic inline int PyObject_GC_IsTracked(PyObject* obj)\n{\n    return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj));\n}\n#endif\n\n// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6.\n// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final.\n#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION)\nstatic inline int PyObject_GC_IsFinalized(PyObject *obj)\n{\n    PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1;\n    return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc));\n}\n#endif\n\n\n// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4\n#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)\nstatic inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {\n    return Py_TYPE(ob) == type;\n}\n#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type)\n#endif\n\n\n// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7.\n// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1.\n// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal\n// C API: Python 3.11a2-3.11a6 versions are not supported.\n#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)\nstatic inline int PyFloat_Pack2(double x, char *p, int le)\n{ return _PyFloat_Pack2(x, (unsigned char*)p, le); }\n\nstatic inline double PyFloat_Unpack2(const char *p, int le)\n{ return _PyFloat_Unpack2((const unsigned char *)p, le); }\n#endif\n\n\n// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and\n// PyFloat_Unpack8() to Python 3.11a7.\n// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4()\n// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions\n// are not supported.\n#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)\nstatic inline int PyFloat_Pack4(double x, char *p, int le)\n{ return _PyFloat_Pack4(x, (unsigned char*)p, le); }\n\nstatic inline int PyFloat_Pack8(double x, char *p, int le)\n{ return _PyFloat_Pack8(x, (unsigned char*)p, le); }\n\nstatic inline double PyFloat_Unpack4(const char *p, int le)\n{ return _PyFloat_Unpack4((const unsigned char *)p, le); }\n\nstatic inline double PyFloat_Unpack8(const char *p, int le)\n{ return _PyFloat_Unpack8((const unsigned char *)p, le); }\n#endif\n\n\n// gh-92154 added PyCode_GetCode() to Python 3.11.0b1\n#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyCode_GetCode(PyCodeObject *code)\n{\n    return Py_NewRef(code->co_code);\n}\n#endif\n\n\n// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1\n#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyCode_GetVarnames(PyCodeObject *code)\n{\n    return Py_NewRef(code->co_varnames);\n}\n#endif\n\n// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1\n#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyCode_GetFreevars(PyCodeObject *code)\n{\n    return Py_NewRef(code->co_freevars);\n}\n#endif\n\n// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1\n#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION)\nstatic inline PyObject* PyCode_GetCellvars(PyCodeObject *code)\n{\n    return Py_NewRef(code->co_cellvars);\n}\n#endif\n\n\n// Py_UNUSED() was added to Python 3.4.0b2.\n#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED)\n#  if defined(__GNUC__) || defined(__clang__)\n#    define Py_UNUSED(name) _unused_ ## name __attribute__((unused))\n#  else\n#    define Py_UNUSED(name) _unused_ ## name\n#  endif\n#endif\n\n\n// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A0\nstatic inline PyObject* PyImport_AddModuleRef(const char *name)\n{\n    return Py_XNewRef(PyImport_AddModule(name));\n}\n#endif\n\n\n// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D0000\nstatic inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)\n{\n    PyObject *obj;\n    if (ref != NULL && !PyWeakref_Check(ref)) {\n        *pobj = NULL;\n        PyErr_SetString(PyExc_TypeError, \"expected a weakref\");\n        return -1;\n    }\n    obj = PyWeakref_GetObject(ref);\n    if (obj == NULL) {\n        // SystemError if ref is NULL\n        *pobj = NULL;\n        return -1;\n    }\n    if (obj == Py_None) {\n        *pobj = NULL;\n        return 0;\n    }\n    *pobj = Py_NewRef(obj);\n    return 1;\n}\n#endif\n\n\n// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1\n#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET\n#  define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1))\n#endif\n\n// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1\n#if PY_VERSION_HEX < 0x030800B1\nstatic inline Py_ssize_t PyVectorcall_NARGS(size_t n)\n{\n    return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;\n}\n#endif\n\n\n// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4\n#if PY_VERSION_HEX < 0x030900A4\nstatic inline PyObject*\nPyObject_Vectorcall(PyObject *callable, PyObject *const *args,\n                     size_t nargsf, PyObject *kwnames)\n{\n#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION)\n    // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1\n    return _PyObject_Vectorcall(callable, args, nargsf, kwnames);\n#else\n    PyObject *posargs = NULL, *kwargs = NULL;\n    PyObject *res;\n    Py_ssize_t nposargs, nkwargs, i;\n\n    if (nargsf != 0 && args == NULL) {\n        PyErr_BadInternalCall();\n        goto error;\n    }\n    if (kwnames != NULL && !PyTuple_Check(kwnames)) {\n        PyErr_BadInternalCall();\n        goto error;\n    }\n\n    nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf);\n    if (kwnames) {\n        nkwargs = PyTuple_GET_SIZE(kwnames);\n    }\n    else {\n        nkwargs = 0;\n    }\n\n    posargs = PyTuple_New(nposargs);\n    if (posargs == NULL) {\n        goto error;\n    }\n    if (nposargs) {\n        for (i=0; i < nposargs; i++) {\n            PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args));\n            args++;\n        }\n    }\n\n    if (nkwargs) {\n        kwargs = PyDict_New();\n        if (kwargs == NULL) {\n            goto error;\n        }\n\n        for (i = 0; i < nkwargs; i++) {\n            PyObject *key = PyTuple_GET_ITEM(kwnames, i);\n            PyObject *value = *args;\n            args++;\n            if (PyDict_SetItem(kwargs, key, value) < 0) {\n                goto error;\n            }\n        }\n    }\n    else {\n        kwargs = NULL;\n    }\n\n    res = PyObject_Call(callable, posargs, kwargs);\n    Py_DECREF(posargs);\n    Py_XDECREF(kwargs);\n    return res;\n\nerror:\n    Py_DECREF(posargs);\n    Py_XDECREF(kwargs);\n    return NULL;\n#endif\n}\n#endif\n\n\n// gh-106521 added PyObject_GetOptionalAttr() and\n// PyObject_GetOptionalAttrString() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result)\n{\n    // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1\n#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION)\n    return _PyObject_LookupAttr(obj, attr_name, result);\n#else\n    *result = PyObject_GetAttr(obj, attr_name);\n    if (*result != NULL) {\n        return 1;\n    }\n    if (!PyErr_Occurred()) {\n        return 0;\n    }\n    if (PyErr_ExceptionMatches(PyExc_AttributeError)) {\n        PyErr_Clear();\n        return 0;\n    }\n    return -1;\n#endif\n}\n\nstatic inline int\nPyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result)\n{\n    PyObject *name_obj;\n    int rc;\n#if PY_VERSION_HEX >= 0x03000000\n    name_obj = PyUnicode_FromString(attr_name);\n#else\n    name_obj = PyString_FromString(attr_name);\n#endif\n    if (name_obj == NULL) {\n        *result = NULL;\n        return -1;\n    }\n    rc = PyObject_GetOptionalAttr(obj, name_obj, result);\n    Py_DECREF(name_obj);\n    return rc;\n}\n#endif\n\n\n// gh-106307 added PyObject_GetOptionalAttr() and\n// PyMapping_GetOptionalItemString() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result)\n{\n    *result = PyObject_GetItem(obj, key);\n    if (*result) {\n        return 1;\n    }\n    if (!PyErr_ExceptionMatches(PyExc_KeyError)) {\n        return -1;\n    }\n    PyErr_Clear();\n    return 0;\n}\n\nstatic inline int\nPyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result)\n{\n    PyObject *key_obj;\n    int rc;\n#if PY_VERSION_HEX >= 0x03000000\n    key_obj = PyUnicode_FromString(key);\n#else\n    key_obj = PyString_FromString(key);\n#endif\n    if (key_obj == NULL) {\n        *result = NULL;\n        return -1;\n    }\n    rc = PyMapping_GetOptionalItem(obj, key_obj, result);\n    Py_DECREF(key_obj);\n    return rc;\n}\n#endif\n\n// gh-108511 added PyMapping_HasKeyWithError() and\n// PyMapping_HasKeyStringWithError() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyMapping_HasKeyWithError(PyObject *obj, PyObject *key)\n{\n    PyObject *res;\n    int rc = PyMapping_GetOptionalItem(obj, key, &res);\n    Py_XDECREF(res);\n    return rc;\n}\n\nstatic inline int\nPyMapping_HasKeyStringWithError(PyObject *obj, const char *key)\n{\n    PyObject *res;\n    int rc = PyMapping_GetOptionalItemString(obj, key, &res);\n    Py_XDECREF(res);\n    return rc;\n}\n#endif\n\n\n// gh-108511 added PyObject_HasAttrWithError() and\n// PyObject_HasAttrStringWithError() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyObject_HasAttrWithError(PyObject *obj, PyObject *attr)\n{\n    PyObject *res;\n    int rc = PyObject_GetOptionalAttr(obj, attr, &res);\n    Py_XDECREF(res);\n    return rc;\n}\n\nstatic inline int\nPyObject_HasAttrStringWithError(PyObject *obj, const char *attr)\n{\n    PyObject *res;\n    int rc = PyObject_GetOptionalAttrString(obj, attr, &res);\n    Py_XDECREF(res);\n    return rc;\n}\n#endif\n\n\n// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef()\n// to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result)\n{\n#if PY_VERSION_HEX >= 0x03000000\n    PyObject *item = PyDict_GetItemWithError(mp, key);\n#else\n    PyObject *item = _PyDict_GetItemWithError(mp, key);\n#endif\n    if (item != NULL) {\n        *result = Py_NewRef(item);\n        return 1;  // found\n    }\n    if (!PyErr_Occurred()) {\n        *result = NULL;\n        return 0;  // not found\n    }\n    *result = NULL;\n    return -1;\n}\n\nstatic inline int\nPyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result)\n{\n    int res;\n#if PY_VERSION_HEX >= 0x03000000\n    PyObject *key_obj = PyUnicode_FromString(key);\n#else\n    PyObject *key_obj = PyString_FromString(key);\n#endif\n    if (key_obj == NULL) {\n        *result = NULL;\n        return -1;\n    }\n    res = PyDict_GetItemRef(mp, key_obj, result);\n    Py_DECREF(key_obj);\n    return res;\n}\n#endif\n\n\n// gh-106307 added PyModule_Add() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyModule_Add(PyObject *mod, const char *name, PyObject *value)\n{\n    int res = PyModule_AddObjectRef(mod, name, value);\n    Py_XDECREF(value);\n    return res;\n}\n#endif\n\n\n// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1\n// bpo-1856 added _Py_Finalizing to Python 3.2.1b1.\n// _Py_IsFinalizing() was added to PyPy 7.3.0.\n#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \\\n        && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000)\nstatic inline int Py_IsFinalizing(void)\n{\n#if PY_VERSION_HEX >= 0x030700A1\n    // _Py_IsFinalizing() was added to Python 3.7.0a1.\n    return _Py_IsFinalizing();\n#else\n    return (_Py_Finalizing != NULL);\n#endif\n}\n#endif\n\n\n// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int PyDict_ContainsString(PyObject *op, const char *key)\n{\n    PyObject *key_obj = PyUnicode_FromString(key);\n    if (key_obj == NULL) {\n        return -1;\n    }\n    int res = PyDict_Contains(op, key_obj);\n    Py_DECREF(key_obj);\n    return res;\n}\n#endif\n\n\n// gh-108445 added PyLong_AsInt() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int PyLong_AsInt(PyObject *obj)\n{\n#ifdef PYPY_VERSION\n    long value = PyLong_AsLong(obj);\n    if (value == -1 && PyErr_Occurred()) {\n        return -1;\n    }\n    if (value < (long)INT_MIN || (long)INT_MAX < value) {\n        PyErr_SetString(PyExc_OverflowError,\n                        \"Python int too large to convert to C int\");\n        return -1;\n    }\n    return (int)value;\n#else\n    return _PyLong_AsInt(obj);\n#endif\n}\n#endif\n\n\n// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)\n{\n    PyObject **dict = _PyObject_GetDictPtr(obj);\n    if (dict == NULL || *dict == NULL) {\n        return -1;\n    }\n    Py_VISIT(*dict);\n    return 0;\n}\n\nstatic inline void\nPyObject_ClearManagedDict(PyObject *obj)\n{\n    PyObject **dict = _PyObject_GetDictPtr(obj);\n    if (dict == NULL || *dict == NULL) {\n        return;\n    }\n    Py_CLEAR(*dict);\n}\n#endif\n\n// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1\n// Python 3.5.2 added _PyThreadState_UncheckedGet().\n#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1\nstatic inline PyThreadState*\nPyThreadState_GetUnchecked(void)\n{\n    return _PyThreadState_UncheckedGet();\n}\n#endif\n\n// gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize()\n// to Python 3.13.0a1\n#if PY_VERSION_HEX < 0x030D00A1\nstatic inline int\nPyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len)\n{\n    Py_ssize_t len;\n    const void *utf8;\n    PyObject *exc_type, *exc_value, *exc_tb;\n    int res;\n\n    // API cannot report errors so save/restore the exception\n    PyErr_Fetch(&exc_type, &exc_value, &exc_tb);\n\n    // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize()\n#if PY_VERSION_HEX >= 0x030300A1\n    if (PyUnicode_IS_ASCII(unicode)) {\n        utf8 = PyUnicode_DATA(unicode);\n        len = PyUnicode_GET_LENGTH(unicode);\n    }\n    else {\n        utf8 = PyUnicode_AsUTF8AndSize(unicode, &len);\n        if (utf8 == NULL) {\n            // Memory allocation failure. The API cannot report error,\n            // so ignore the exception and return 0.\n            res = 0;\n            goto done;\n        }\n    }\n\n    if (len != str_len) {\n        res = 0;\n        goto done;\n    }\n    res = (memcmp(utf8, str, (size_t)len) == 0);\n#else\n    PyObject *bytes = PyUnicode_AsUTF8String(unicode);\n    if (bytes == NULL) {\n        // Memory allocation failure. The API cannot report error,\n        // so ignore the exception and return 0.\n        res = 0;\n        goto done;\n    }\n\n#if PY_VERSION_HEX >= 0x03000000\n    len = PyBytes_GET_SIZE(bytes);\n    utf8 = PyBytes_AS_STRING(bytes);\n#else\n    len = PyString_GET_SIZE(bytes);\n    utf8 = PyString_AS_STRING(bytes);\n#endif\n    if (len != str_len) {\n        Py_DECREF(bytes);\n        res = 0;\n        goto done;\n    }\n\n    res = (memcmp(utf8, str, (size_t)len) == 0);\n    Py_DECREF(bytes);\n#endif\n\ndone:\n    PyErr_Restore(exc_type, exc_value, exc_tb);\n    return res;\n}\n\nstatic inline int\nPyUnicode_EqualToUTF8(PyObject *unicode, const char *str)\n{\n    return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str));\n}\n#endif\n\n\n// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2\n#if PY_VERSION_HEX < 0x030D00A2\nstatic inline int\nPyList_Extend(PyObject *list, PyObject *iterable)\n{\n    return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable);\n}\n\nstatic inline int\nPyList_Clear(PyObject *list)\n{\n    return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL);\n}\n#endif\n\n// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2\n#if PY_VERSION_HEX < 0x030D00A2\nstatic inline int\nPyDict_Pop(PyObject *dict, PyObject *key, PyObject **result)\n{\n    PyObject *value;\n\n    if (!PyDict_Check(dict)) {\n        PyErr_BadInternalCall();\n        if (result) {\n            *result = NULL;\n        }\n        return -1;\n    }\n\n    // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2.\n    // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*.\n    // Python 3.13.0a1 removed _PyDict_Pop().\n#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000\n    value = PyObject_CallMethod(dict, \"pop\", \"O\", key);\n#elif PY_VERSION_HEX < 0x030600b3\n    value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL);\n#else\n    value = _PyDict_Pop(dict, key, NULL);\n#endif\n    if (value == NULL) {\n        if (result) {\n            *result = NULL;\n        }\n        if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) {\n            return -1;\n        }\n        PyErr_Clear();\n        return 0;\n    }\n    if (result) {\n        *result = value;\n    }\n    else {\n        Py_DECREF(value);\n    }\n    return 1;\n}\n\nstatic inline int\nPyDict_PopString(PyObject *dict, const char *key, PyObject **result)\n{\n    PyObject *key_obj = PyUnicode_FromString(key);\n    if (key_obj == NULL) {\n        if (result != NULL) {\n            *result = NULL;\n        }\n        return -1;\n    }\n\n    int res = PyDict_Pop(dict, key_obj, result);\n    Py_DECREF(key_obj);\n    return res;\n}\n#endif\n\n\n#if PY_VERSION_HEX < 0x030200A4\n// Python 3.2.0a4 added Py_hash_t type\ntypedef Py_ssize_t Py_hash_t;\n#endif\n\n\n// gh-111545 added Py_HashPointer() to Python 3.13.0a3\n#if PY_VERSION_HEX < 0x030D00A3\nstatic inline Py_hash_t Py_HashPointer(const void *ptr)\n{\n#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION)\n    return _Py_HashPointer(ptr);\n#else\n    return _Py_HashPointer(_Py_CAST(void*, ptr));\n#endif\n}\n#endif\n\n\n// Python 3.13a4 added a PyTime API.\n// Use the private API added to Python 3.5.\n#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX  >= 0x03050000\ntypedef _PyTime_t PyTime_t;\n#define PyTime_MIN _PyTime_MIN\n#define PyTime_MAX _PyTime_MAX\n\nstatic inline double PyTime_AsSecondsDouble(PyTime_t t)\n{ return _PyTime_AsSecondsDouble(t); }\n\nstatic inline int PyTime_Monotonic(PyTime_t *result)\n{ return _PyTime_GetMonotonicClockWithInfo(result, NULL); }\n\nstatic inline int PyTime_Time(PyTime_t *result)\n{ return _PyTime_GetSystemClockWithInfo(result, NULL); }\n\nstatic inline int PyTime_PerfCounter(PyTime_t *result)\n{\n#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION)\n    return _PyTime_GetPerfCounterWithInfo(result, NULL);\n#elif PY_VERSION_HEX >= 0x03070000\n    // Call time.perf_counter_ns() and convert Python int object to PyTime_t.\n    // Cache time.perf_counter_ns() function for best performance.\n    static PyObject *func = NULL;\n    if (func == NULL) {\n        PyObject *mod = PyImport_ImportModule(\"time\");\n        if (mod == NULL) {\n            return -1;\n        }\n\n        func = PyObject_GetAttrString(mod, \"perf_counter_ns\");\n        Py_DECREF(mod);\n        if (func == NULL) {\n            return -1;\n        }\n    }\n\n    PyObject *res = PyObject_CallNoArgs(func);\n    if (res == NULL) {\n        return -1;\n    }\n    long long value = PyLong_AsLongLong(res);\n    Py_DECREF(res);\n\n    if (value == -1 && PyErr_Occurred()) {\n        return -1;\n    }\n\n    Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t));\n    *result = (PyTime_t)value;\n    return 0;\n#else\n    // Call time.perf_counter() and convert C double to PyTime_t.\n    // Cache time.perf_counter() function for best performance.\n    static PyObject *func = NULL;\n    if (func == NULL) {\n        PyObject *mod = PyImport_ImportModule(\"time\");\n        if (mod == NULL) {\n            return -1;\n        }\n\n        func = PyObject_GetAttrString(mod, \"perf_counter\");\n        Py_DECREF(mod);\n        if (func == NULL) {\n            return -1;\n        }\n    }\n\n    PyObject *res = PyObject_CallNoArgs(func);\n    if (res == NULL) {\n        return -1;\n    }\n    double d = PyFloat_AsDouble(res);\n    Py_DECREF(res);\n\n    if (d == -1.0 && PyErr_Occurred()) {\n        return -1;\n    }\n\n    // Avoid floor() to avoid having to link to libm\n    *result = (PyTime_t)(d * 1e9);\n    return 0;\n#endif\n}\n\n#endif\n\n// gh-111389 added hash constants to Python 3.13.0a5. These constants were\n// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8.\n#if (!defined(PyHASH_BITS) \\\n     && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \\\n         || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \\\n             && PYPY_VERSION_NUM >= 0x07030800)))\n#  define PyHASH_BITS _PyHASH_BITS\n#  define PyHASH_MODULUS _PyHASH_MODULUS\n#  define PyHASH_INF _PyHASH_INF\n#  define PyHASH_IMAG _PyHASH_IMAG\n#endif\n\n\n// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed()\n// to Python 3.13.0a6\n#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE)\n\n#define Py_CONSTANT_NONE 0\n#define Py_CONSTANT_FALSE 1\n#define Py_CONSTANT_TRUE 2\n#define Py_CONSTANT_ELLIPSIS 3\n#define Py_CONSTANT_NOT_IMPLEMENTED 4\n#define Py_CONSTANT_ZERO 5\n#define Py_CONSTANT_ONE 6\n#define Py_CONSTANT_EMPTY_STR 7\n#define Py_CONSTANT_EMPTY_BYTES 8\n#define Py_CONSTANT_EMPTY_TUPLE 9\n\nstatic inline PyObject* Py_GetConstant(unsigned int constant_id)\n{\n    static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL};\n\n    if (constants[Py_CONSTANT_NONE] == NULL) {\n        constants[Py_CONSTANT_NONE] = Py_None;\n        constants[Py_CONSTANT_FALSE] = Py_False;\n        constants[Py_CONSTANT_TRUE] = Py_True;\n        constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis;\n        constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented;\n\n        constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0);\n        if (constants[Py_CONSTANT_ZERO] == NULL) {\n            goto fatal_error;\n        }\n\n        constants[Py_CONSTANT_ONE] = PyLong_FromLong(1);\n        if (constants[Py_CONSTANT_ONE] == NULL) {\n            goto fatal_error;\n        }\n\n        constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize(\"\", 0);\n        if (constants[Py_CONSTANT_EMPTY_STR] == NULL) {\n            goto fatal_error;\n        }\n\n        constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize(\"\", 0);\n        if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) {\n            goto fatal_error;\n        }\n\n        constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0);\n        if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) {\n            goto fatal_error;\n        }\n        // goto dance to avoid compiler warnings about Py_FatalError()\n        goto init_done;\n\nfatal_error:\n        // This case should never happen\n        Py_FatalError(\"Py_GetConstant() failed to get constants\");\n    }\n\ninit_done:\n    if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) {\n        return Py_NewRef(constants[constant_id]);\n    }\n    else {\n        PyErr_BadInternalCall();\n        return NULL;\n    }\n}\n\nstatic inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id)\n{\n    PyObject *obj = Py_GetConstant(constant_id);\n    Py_XDECREF(obj);\n    return obj;\n}\n#endif\n\n\n// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4\n#if PY_VERSION_HEX < 0x030D00A4\nstatic inline PyObject *\nPyList_GetItemRef(PyObject *op, Py_ssize_t index)\n{\n    PyObject *item = PyList_GetItem(op, index);\n    Py_XINCREF(item);\n    return item;\n}\n#endif\n\n\n// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4\n#if PY_VERSION_HEX < 0x030D00A4\nstatic inline int\nPyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value,\n                     PyObject **result)\n{\n    PyObject *value;\n    if (PyDict_GetItemRef(d, key, &value) < 0) {\n        // get error\n        if (result) {\n            *result = NULL;\n        }\n        return -1;\n    }\n    if (value != NULL) {\n        // present\n        if (result) {\n            *result = value;\n        }\n        else {\n            Py_DECREF(value);\n        }\n        return 1;\n    }\n\n    // missing: set the item\n    if (PyDict_SetItem(d, key, default_value) < 0) {\n        // set error\n        if (result) {\n            *result = NULL;\n        }\n        return -1;\n    }\n    if (result) {\n        *result = Py_NewRef(default_value);\n    }\n    return 0;\n}\n#endif\n\n#if PY_VERSION_HEX < 0x030D00B3\n#  define Py_BEGIN_CRITICAL_SECTION(op) {\n#  define Py_END_CRITICAL_SECTION() }\n#  define Py_BEGIN_CRITICAL_SECTION2(a, b) {\n#  define Py_END_CRITICAL_SECTION2() }\n#endif\n\n#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION)\ntypedef struct PyUnicodeWriter PyUnicodeWriter;\n\nstatic inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer)\n{\n    _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer);\n    PyMem_Free(writer);\n}\n\nstatic inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length)\n{\n    if (length < 0) {\n        PyErr_SetString(PyExc_ValueError,\n                        \"length must be positive\");\n        return NULL;\n    }\n\n    const size_t size = sizeof(_PyUnicodeWriter);\n    PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size);\n    if (pub_writer == _Py_NULL) {\n        PyErr_NoMemory();\n        return _Py_NULL;\n    }\n    _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer;\n\n    _PyUnicodeWriter_Init(writer);\n    if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) {\n        PyUnicodeWriter_Discard(pub_writer);\n        return NULL;\n    }\n    writer->overallocate = 1;\n    return pub_writer;\n}\n\nstatic inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer)\n{\n    PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer);\n    assert(((_PyUnicodeWriter*)writer)->buffer == NULL);\n    PyMem_Free(writer);\n    return str;\n}\n\nstatic inline int\nPyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch)\n{\n    if (ch > 0x10ffff) {\n        PyErr_SetString(PyExc_ValueError,\n                        \"character must be in range(0x110000)\");\n        return -1;\n    }\n\n    return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch);\n}\n\nstatic inline int\nPyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj)\n{\n    PyObject *str = PyObject_Str(obj);\n    if (str == NULL) {\n        return -1;\n    }\n\n    int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str);\n    Py_DECREF(str);\n    return res;\n}\n\nstatic inline int\nPyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj)\n{\n    PyObject *str = PyObject_Repr(obj);\n    if (str == NULL) {\n        return -1;\n    }\n\n    int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str);\n    Py_DECREF(str);\n    return res;\n}\n\nstatic inline int\nPyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer,\n                          const char *str, Py_ssize_t size)\n{\n    if (size < 0) {\n        size = (Py_ssize_t)strlen(str);\n    }\n\n    PyObject *str_obj = PyUnicode_FromStringAndSize(str, size);\n    if (str_obj == _Py_NULL) {\n        return -1;\n    }\n\n    int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj);\n    Py_DECREF(str_obj);\n    return res;\n}\n\nstatic inline int\nPyUnicodeWriter_WriteASCII(PyUnicodeWriter *writer,\n                           const char *str, Py_ssize_t size)\n{\n    if (size < 0) {\n        size = (Py_ssize_t)strlen(str);\n    }\n\n    return _PyUnicodeWriter_WriteASCIIString((_PyUnicodeWriter*)writer,\n                                             str, size);\n}\n\nstatic inline int\nPyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer,\n                              const wchar_t *str, Py_ssize_t size)\n{\n    if (size < 0) {\n        size = (Py_ssize_t)wcslen(str);\n    }\n\n    PyObject *str_obj = PyUnicode_FromWideChar(str, size);\n    if (str_obj == _Py_NULL) {\n        return -1;\n    }\n\n    int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj);\n    Py_DECREF(str_obj);\n    return res;\n}\n\nstatic inline int\nPyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str,\n                               Py_ssize_t start, Py_ssize_t end)\n{\n    if (!PyUnicode_Check(str)) {\n        PyErr_Format(PyExc_TypeError, \"expect str, not %s\",\n                     Py_TYPE(str)->tp_name);\n        return -1;\n    }\n    if (start < 0 || start > end) {\n        PyErr_Format(PyExc_ValueError, \"invalid start argument\");\n        return -1;\n    }\n    if (end > PyUnicode_GET_LENGTH(str)) {\n        PyErr_Format(PyExc_ValueError, \"invalid end argument\");\n        return -1;\n    }\n\n    return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str,\n                                           start, end);\n}\n\nstatic inline int\nPyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...)\n{\n    va_list vargs;\n    va_start(vargs, format);\n    PyObject *str = PyUnicode_FromFormatV(format, vargs);\n    va_end(vargs);\n    if (str == _Py_NULL) {\n        return -1;\n    }\n\n    int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str);\n    Py_DECREF(str);\n    return res;\n}\n#endif  // PY_VERSION_HEX < 0x030E0000\n\n// gh-116560 added PyLong_GetSign() to Python 3.14.0a0\n#if PY_VERSION_HEX < 0x030E00A0\nstatic inline int PyLong_GetSign(PyObject *obj, int *sign)\n{\n    if (!PyLong_Check(obj)) {\n        PyErr_Format(PyExc_TypeError, \"expect int, got %s\", Py_TYPE(obj)->tp_name);\n        return -1;\n    }\n\n    *sign = _PyLong_Sign(obj);\n    return 0;\n}\n#endif\n\n// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2\n#if PY_VERSION_HEX < 0x030E00A2\nstatic inline int PyLong_IsPositive(PyObject *obj)\n{\n    if (!PyLong_Check(obj)) {\n        PyErr_Format(PyExc_TypeError, \"expected int, got %s\", Py_TYPE(obj)->tp_name);\n        return -1;\n    }\n    return _PyLong_Sign(obj) == 1;\n}\n\nstatic inline int PyLong_IsNegative(PyObject *obj)\n{\n    if (!PyLong_Check(obj)) {\n        PyErr_Format(PyExc_TypeError, \"expected int, got %s\", Py_TYPE(obj)->tp_name);\n        return -1;\n    }\n    return _PyLong_Sign(obj) == -1;\n}\n\nstatic inline int PyLong_IsZero(PyObject *obj)\n{\n    if (!PyLong_Check(obj)) {\n        PyErr_Format(PyExc_TypeError, \"expected int, got %s\", Py_TYPE(obj)->tp_name);\n        return -1;\n    }\n    return _PyLong_Sign(obj) == 0;\n}\n#endif\n\n\n// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0\n#if PY_VERSION_HEX < 0x030E00A0\nstatic inline int PyUnicode_Equal(PyObject *str1, PyObject *str2)\n{\n    if (!PyUnicode_Check(str1)) {\n        PyErr_Format(PyExc_TypeError, \"first argument must be str, not %s\",\n                     Py_TYPE(str1)->tp_name);\n        return -1;\n    }\n    if (!PyUnicode_Check(str2)) {\n        PyErr_Format(PyExc_TypeError, \"second argument must be str, not %s\",\n                     Py_TYPE(str2)->tp_name);\n        return -1;\n    }\n\n#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION)\n    PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2);\n\n    return _PyUnicode_Equal(str1, str2);\n#elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION)\n    return _PyUnicode_EQ(str1, str2);\n#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION)\n    return _PyUnicode_EQ(str1, str2);\n#else\n    return (PyUnicode_Compare(str1, str2) == 0);\n#endif\n}\n#endif\n\n\n// gh-121645 added PyBytes_Join() to Python 3.14.0a0\n#if PY_VERSION_HEX < 0x030E00A0\nstatic inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable)\n{\n    return _PyBytes_Join(sep, iterable);\n}\n#endif\n\n\n#if PY_VERSION_HEX < 0x030E00A0\nstatic inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len)\n{\n#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)\n    PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len);\n\n    return _Py_HashBytes(ptr, len);\n#else\n    Py_hash_t hash;\n    PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len);\n    if (bytes == NULL) {\n        return -1;\n    }\n    hash = PyObject_Hash(bytes);\n    Py_DECREF(bytes);\n    return hash;\n#endif\n}\n#endif\n\n\n#if PY_VERSION_HEX < 0x030E00A0\nstatic inline int PyIter_NextItem(PyObject *iter, PyObject **item)\n{\n    iternextfunc tp_iternext;\n\n    assert(iter != NULL);\n    assert(item != NULL);\n\n    tp_iternext = Py_TYPE(iter)->tp_iternext;\n    if (tp_iternext == NULL) {\n        *item = NULL;\n        PyErr_Format(PyExc_TypeError, \"expected an iterator, got '%s'\",\n                     Py_TYPE(iter)->tp_name);\n        return -1;\n    }\n\n    if ((*item = tp_iternext(iter))) {\n        return 1;\n    }\n    if (!PyErr_Occurred()) {\n        return 0;\n    }\n    if (PyErr_ExceptionMatches(PyExc_StopIteration)) {\n        PyErr_Clear();\n        return 0;\n    }\n    return -1;\n}\n#endif\n\n\n#if PY_VERSION_HEX < 0x030E00A0\nstatic inline PyObject* PyLong_FromInt32(int32_t value)\n{\n    Py_BUILD_ASSERT(sizeof(long) >= 4);\n    return PyLong_FromLong(value);\n}\n\nstatic inline PyObject* PyLong_FromInt64(int64_t value)\n{\n    Py_BUILD_ASSERT(sizeof(long long) >= 8);\n    return PyLong_FromLongLong(value);\n}\n\nstatic inline PyObject* PyLong_FromUInt32(uint32_t value)\n{\n    Py_BUILD_ASSERT(sizeof(unsigned long) >= 4);\n    return PyLong_FromUnsignedLong(value);\n}\n\nstatic inline PyObject* PyLong_FromUInt64(uint64_t value)\n{\n    Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8);\n    return PyLong_FromUnsignedLongLong(value);\n}\n\nstatic inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue)\n{\n    Py_BUILD_ASSERT(sizeof(int) == 4);\n    int value = PyLong_AsInt(obj);\n    if (value == -1 && PyErr_Occurred()) {\n        return -1;\n    }\n    *pvalue = (int32_t)value;\n    return 0;\n}\n\nstatic inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue)\n{\n    Py_BUILD_ASSERT(sizeof(long long) == 8);\n    long long value = PyLong_AsLongLong(obj);\n    if (value == -1 && PyErr_Occurred()) {\n        return -1;\n    }\n    *pvalue = (int64_t)value;\n    return 0;\n}\n\nstatic inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue)\n{\n    Py_BUILD_ASSERT(sizeof(long) >= 4);\n    unsigned long value = PyLong_AsUnsignedLong(obj);\n    if (value == (unsigned long)-1 && PyErr_Occurred()) {\n        return -1;\n    }\n#if SIZEOF_LONG > 4\n    if ((unsigned long)UINT32_MAX < value) {\n        PyErr_SetString(PyExc_OverflowError,\n                        \"Python int too large to convert to C uint32_t\");\n        return -1;\n    }\n#endif\n    *pvalue = (uint32_t)value;\n    return 0;\n}\n\nstatic inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)\n{\n    Py_BUILD_ASSERT(sizeof(long long) == 8);\n    unsigned long long value = PyLong_AsUnsignedLongLong(obj);\n    if (value == (unsigned long long)-1 && PyErr_Occurred()) {\n        return -1;\n    }\n    *pvalue = (uint64_t)value;\n    return 0;\n}\n#endif\n\n\n// gh-102471 added import and export API for integers to 3.14.0a2.\n#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)\n// Helpers to access PyLongObject internals.\nstatic inline void\n_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)\n{\n#if PY_VERSION_HEX >= 0x030C0000\n    op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3);\n#elif PY_VERSION_HEX >= 0x030900A4\n    Py_SET_SIZE(op, sign * size);\n#else\n    Py_SIZE(op) = sign * size;\n#endif\n}\n\nstatic inline Py_ssize_t\n_PyLong_DigitCount(const PyLongObject *op)\n{\n#if PY_VERSION_HEX >= 0x030C0000\n    return (Py_ssize_t)(op->long_value.lv_tag >> 3);\n#else\n    return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op);\n#endif\n}\n\nstatic inline digit*\n_PyLong_GetDigits(const PyLongObject *op)\n{\n#if PY_VERSION_HEX >= 0x030C0000\n    return (digit*)(op->long_value.ob_digit);\n#else\n    return (digit*)(op->ob_digit);\n#endif\n}\n\ntypedef struct PyLongLayout {\n    uint8_t bits_per_digit;\n    uint8_t digit_size;\n    int8_t digits_order;\n    int8_t digit_endianness;\n} PyLongLayout;\n\ntypedef struct PyLongExport {\n    int64_t value;\n    uint8_t negative;\n    Py_ssize_t ndigits;\n    const void *digits;\n    Py_uintptr_t _reserved;\n} PyLongExport;\n\ntypedef struct PyLongWriter PyLongWriter;\n\nstatic inline const PyLongLayout*\nPyLong_GetNativeLayout(void)\n{\n    static const PyLongLayout PyLong_LAYOUT = {\n        PyLong_SHIFT,\n        sizeof(digit),\n        -1,  // least significant first\n        PY_LITTLE_ENDIAN ? -1 : 1,\n    };\n\n    return &PyLong_LAYOUT;\n}\n\nstatic inline int\nPyLong_Export(PyObject *obj, PyLongExport *export_long)\n{\n    if (!PyLong_Check(obj)) {\n        memset(export_long, 0, sizeof(*export_long));\n        PyErr_Format(PyExc_TypeError, \"expected int, got %s\",\n                     Py_TYPE(obj)->tp_name);\n        return -1;\n    }\n\n    // Fast-path: try to convert to a int64_t\n    PyLongObject *self = (PyLongObject*)obj;\n    int overflow;\n#if SIZEOF_LONG == 8\n    long value = PyLong_AsLongAndOverflow(obj, &overflow);\n#else\n    // Windows has 32-bit long, so use 64-bit long long instead\n    long long value = PyLong_AsLongLongAndOverflow(obj, &overflow);\n#endif\n    Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t));\n    // the function cannot fail since obj is a PyLongObject\n    assert(!(value == -1 && PyErr_Occurred()));\n\n    if (!overflow) {\n        export_long->value = value;\n        export_long->negative = 0;\n        export_long->ndigits = 0;\n        export_long->digits = 0;\n        export_long->_reserved = 0;\n    }\n    else {\n        export_long->value = 0;\n        export_long->negative = _PyLong_Sign(obj) < 0;\n        export_long->ndigits = _PyLong_DigitCount(self);\n        if (export_long->ndigits == 0) {\n            export_long->ndigits = 1;\n        }\n        export_long->digits = _PyLong_GetDigits(self);\n        export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj);\n    }\n    return 0;\n}\n\nstatic inline void\nPyLong_FreeExport(PyLongExport *export_long)\n{\n    PyObject *obj = (PyObject*)export_long->_reserved;\n\n    if (obj) {\n        export_long->_reserved = 0;\n        Py_DECREF(obj);\n    }\n}\n\nstatic inline PyLongWriter*\nPyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits)\n{\n    if (ndigits <= 0) {\n        PyErr_SetString(PyExc_ValueError, \"ndigits must be positive\");\n        return NULL;\n    }\n    assert(digits != NULL);\n\n    PyLongObject *obj = _PyLong_New(ndigits);\n    if (obj == NULL) {\n        return NULL;\n    }\n    _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits);\n\n    *digits = _PyLong_GetDigits(obj);\n    return (PyLongWriter*)obj;\n}\n\nstatic inline void\nPyLongWriter_Discard(PyLongWriter *writer)\n{\n    PyLongObject *obj = (PyLongObject *)writer;\n\n    assert(Py_REFCNT(obj) == 1);\n    Py_DECREF(obj);\n}\n\nstatic inline PyObject*\nPyLongWriter_Finish(PyLongWriter *writer)\n{\n    PyObject *obj = (PyObject *)writer;\n    PyLongObject *self = (PyLongObject*)obj;\n    Py_ssize_t j = _PyLong_DigitCount(self);\n    Py_ssize_t i = j;\n    int sign = _PyLong_Sign(obj);\n\n    assert(Py_REFCNT(obj) == 1);\n\n    // Normalize and get singleton if possible\n    while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) {\n        --i;\n    }\n    if (i != j) {\n        if (i == 0) {\n            sign = 0;\n        }\n        _PyLong_SetSignAndDigitCount(self, sign, i);\n    }\n    if (i <= 1) {\n        long val = sign * (long)(_PyLong_GetDigits(self)[0]);\n        Py_DECREF(obj);\n        return PyLong_FromLong(val);\n    }\n\n    return obj;\n}\n#endif\n\n\n#if PY_VERSION_HEX < 0x030C00A3\n#  define Py_T_SHORT      T_SHORT\n#  define Py_T_INT        T_INT\n#  define Py_T_LONG       T_LONG\n#  define Py_T_FLOAT      T_FLOAT\n#  define Py_T_DOUBLE     T_DOUBLE\n#  define Py_T_STRING     T_STRING\n#  define _Py_T_OBJECT    T_OBJECT\n#  define Py_T_CHAR       T_CHAR\n#  define Py_T_BYTE       T_BYTE\n#  define Py_T_UBYTE      T_UBYTE\n#  define Py_T_USHORT     T_USHORT\n#  define Py_T_UINT       T_UINT\n#  define Py_T_ULONG      T_ULONG\n#  define Py_T_STRING_INPLACE  T_STRING_INPLACE\n#  define Py_T_BOOL       T_BOOL\n#  define Py_T_OBJECT_EX  T_OBJECT_EX\n#  define Py_T_LONGLONG   T_LONGLONG\n#  define Py_T_ULONGLONG  T_ULONGLONG\n#  define Py_T_PYSSIZET   T_PYSSIZET\n\n#  if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)\n#    define _Py_T_NONE      T_NONE\n#  endif\n\n#  define Py_READONLY            READONLY\n#  define Py_AUDIT_READ          READ_RESTRICTED\n#  define _Py_WRITE_RESTRICTED   PY_WRITE_RESTRICTED\n#endif\n\n\n// gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4\n#if PY_VERSION_HEX < 0x030E00A4\nstatic inline FILE* Py_fopen(PyObject *path, const char *mode)\n{\n#if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION)\n    PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode);\n\n    return _Py_fopen_obj(path, mode);\n#else\n    FILE *f;\n    PyObject *bytes;\n#if PY_VERSION_HEX >= 0x03000000\n    if (!PyUnicode_FSConverter(path, &bytes)) {\n        return NULL;\n    }\n#else\n    if (!PyString_Check(path)) {\n        PyErr_SetString(PyExc_TypeError, \"except str\");\n        return NULL;\n    }\n    bytes = Py_NewRef(path);\n#endif\n    const char *path_bytes = PyBytes_AS_STRING(bytes);\n\n    f = fopen(path_bytes, mode);\n    Py_DECREF(bytes);\n\n    if (f == NULL) {\n        PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);\n        return NULL;\n    }\n    return f;\n#endif\n}\n\nstatic inline int Py_fclose(FILE *file)\n{\n    return fclose(file);\n}\n#endif\n\n\n#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION)\nstatic inline PyObject*\nPyConfig_Get(const char *name)\n{\n    typedef enum {\n        _PyConfig_MEMBER_INT,\n        _PyConfig_MEMBER_UINT,\n        _PyConfig_MEMBER_ULONG,\n        _PyConfig_MEMBER_BOOL,\n        _PyConfig_MEMBER_WSTR,\n        _PyConfig_MEMBER_WSTR_OPT,\n        _PyConfig_MEMBER_WSTR_LIST,\n    } PyConfigMemberType;\n\n    typedef struct {\n        const char *name;\n        size_t offset;\n        PyConfigMemberType type;\n        const char *sys_attr;\n    } PyConfigSpec;\n\n#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \\\n    {#MEMBER, offsetof(PyConfig, MEMBER), \\\n     _PyConfig_MEMBER_##TYPE, sys_attr}\n\n    static const PyConfigSpec config_spec[] = {\n        PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, \"argv\"),\n        PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, \"base_exec_prefix\"),\n        PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, \"_base_executable\"),\n        PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, \"base_prefix\"),\n        PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, \"exec_prefix\"),\n        PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, \"executable\"),\n        PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL),\n#if 0x030C0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, \"path\"),\n        PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, \"platlibdir\"),\n        PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, \"prefix\"),\n        PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, \"pycache_prefix\"),\n        PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL),\n#if 0x030B0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, \"_stdlib_dir\"),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, \"warnoptions\"),\n        PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, \"_xoptions\"),\n        PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL),\n#if 0x030B0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL),\n#if 0x030D0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL),\n#if 0x030B0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL),\n#endif\n#ifdef Py_GIL_DISABLED\n        PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL),\n#ifdef MS_WINDOWS\n        PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL),\n#if 0x030A0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, \"orig_argv\"),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL),\n#if 0x030C0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL),\n#if 0x030B0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL),\n#if 0x030B0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL),\n#endif\n        PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL),\n        PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL),\n#if 0x030A0000 <= PY_VERSION_HEX\n        PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL),\n#endif\n    };\n\n#undef PYTHONCAPI_COMPAT_SPEC\n\n    const PyConfigSpec *spec;\n    int found = 0;\n    for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) {\n        spec = &config_spec[i];\n        if (strcmp(spec->name, name) == 0) {\n            found = 1;\n            break;\n        }\n    }\n    if (found) {\n        if (spec->sys_attr != NULL) {\n            PyObject *value = PySys_GetObject(spec->sys_attr);\n            if (value == NULL) {\n                PyErr_Format(PyExc_RuntimeError, \"lost sys.%s\", spec->sys_attr);\n                return NULL;\n            }\n            return Py_NewRef(value);\n        }\n\n        PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void);\n\n        const PyConfig *config = _Py_GetConfig();\n        void *member = (char *)config + spec->offset;\n        switch (spec->type) {\n        case _PyConfig_MEMBER_INT:\n        case _PyConfig_MEMBER_UINT:\n        {\n            int value = *(int *)member;\n            return PyLong_FromLong(value);\n        }\n        case _PyConfig_MEMBER_BOOL:\n        {\n            int value = *(int *)member;\n            return PyBool_FromLong(value != 0);\n        }\n        case _PyConfig_MEMBER_ULONG:\n        {\n            unsigned long value = *(unsigned long *)member;\n            return PyLong_FromUnsignedLong(value);\n        }\n        case _PyConfig_MEMBER_WSTR:\n        case _PyConfig_MEMBER_WSTR_OPT:\n        {\n            wchar_t *wstr = *(wchar_t **)member;\n            if (wstr != NULL) {\n                return PyUnicode_FromWideChar(wstr, -1);\n            }\n            else {\n                return Py_NewRef(Py_None);\n            }\n        }\n        case _PyConfig_MEMBER_WSTR_LIST:\n        {\n            const PyWideStringList *list = (const PyWideStringList *)member;\n            PyObject *tuple = PyTuple_New(list->length);\n            if (tuple == NULL) {\n                return NULL;\n            }\n\n            for (Py_ssize_t i = 0; i < list->length; i++) {\n                PyObject *item = PyUnicode_FromWideChar(list->items[i], -1);\n                if (item == NULL) {\n                    Py_DECREF(tuple);\n                    return NULL;\n                }\n                PyTuple_SET_ITEM(tuple, i, item);\n            }\n            return tuple;\n        }\n        default:\n            Py_UNREACHABLE();\n        }\n    }\n\n    PyErr_Format(PyExc_ValueError, \"unknown config option name: %s\", name);\n    return NULL;\n}\n\nstatic inline int\nPyConfig_GetInt(const char *name, int *value)\n{\n    PyObject *obj = PyConfig_Get(name);\n    if (obj == NULL) {\n        return -1;\n    }\n\n    if (!PyLong_Check(obj)) {\n        Py_DECREF(obj);\n        PyErr_Format(PyExc_TypeError, \"config option %s is not an int\", name);\n        return -1;\n    }\n\n    int as_int = PyLong_AsInt(obj);\n    Py_DECREF(obj);\n    if (as_int == -1 && PyErr_Occurred()) {\n        PyErr_Format(PyExc_OverflowError,\n                     \"config option %s value does not fit into a C int\", name);\n        return -1;\n    }\n\n    *value = as_int;\n    return 0;\n}\n#endif  // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)\n\n// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14.0b1.\n// Adapted from  _PyObject_IsUniquelyReferenced() implementation.\n#if PY_VERSION_HEX < 0x030E00B0\nstatic inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj)\n{\n#if !defined(Py_GIL_DISABLED)\n    return Py_REFCNT(obj) == 1;\n#else\n    // NOTE: the entire ob_ref_shared field must be zero, including flags, to\n    // ensure that other threads cannot concurrently create new references to\n    // this object.\n    return (_Py_IsOwnedByCurrentThread(obj) &&\n            _Py_atomic_load_uint32_relaxed(&obj->ob_ref_local) == 1 &&\n            _Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared) == 0);\n#endif\n}\n#endif\n\n\n#if PY_VERSION_HEX < 0x030F0000\nstatic inline PyObject*\nPySys_GetAttrString(const char *name)\n{\n#if PY_VERSION_HEX >= 0x03000000\n    PyObject *value = Py_XNewRef(PySys_GetObject(name));\n#else\n    PyObject *value = Py_XNewRef(PySys_GetObject((char*)name));\n#endif\n    if (value != NULL) {\n        return value;\n    }\n    if (!PyErr_Occurred()) {\n        PyErr_Format(PyExc_RuntimeError, \"lost sys.%s\", name);\n    }\n    return NULL;\n}\n\nstatic inline PyObject*\nPySys_GetAttr(PyObject *name)\n{\n#if PY_VERSION_HEX >= 0x03000000\n    const char *name_str = PyUnicode_AsUTF8(name);\n#else\n    const char *name_str = PyString_AsString(name);\n#endif\n    if (name_str == NULL) {\n        return NULL;\n    }\n\n    return PySys_GetAttrString(name_str);\n}\n\nstatic inline int\nPySys_GetOptionalAttrString(const char *name, PyObject **value)\n{\n#if PY_VERSION_HEX >= 0x03000000\n    *value = Py_XNewRef(PySys_GetObject(name));\n#else\n    *value = Py_XNewRef(PySys_GetObject((char*)name));\n#endif\n    if (*value != NULL) {\n        return 1;\n    }\n    return 0;\n}\n\nstatic inline int\nPySys_GetOptionalAttr(PyObject *name, PyObject **value)\n{\n#if PY_VERSION_HEX >= 0x03000000\n    const char *name_str = PyUnicode_AsUTF8(name);\n#else\n    const char *name_str = PyString_AsString(name);\n#endif\n    if (name_str == NULL) {\n        *value = NULL;\n        return -1;\n    }\n\n    return PySys_GetOptionalAttrString(name_str, value);\n}\n#endif  // PY_VERSION_HEX < 0x030F00A1\n\n\n#if PY_VERSION_HEX < 0x030F00A1\ntypedef struct PyBytesWriter {\n    char small_buffer[256];\n    PyObject *obj;\n    Py_ssize_t size;\n} PyBytesWriter;\n\nstatic inline Py_ssize_t\n_PyBytesWriter_GetAllocated(PyBytesWriter *writer)\n{\n    if (writer->obj == NULL) {\n        return sizeof(writer->small_buffer);\n    }\n    else {\n        return PyBytes_GET_SIZE(writer->obj);\n    }\n}\n\n\nstatic inline int\n_PyBytesWriter_Resize_impl(PyBytesWriter *writer, Py_ssize_t size,\n                           int resize)\n{\n    int overallocate = resize;\n    assert(size >= 0);\n\n    if (size <= _PyBytesWriter_GetAllocated(writer)) {\n        return 0;\n    }\n\n    if (overallocate) {\n#ifdef MS_WINDOWS\n        /* On Windows, overallocate by 50% is the best factor */\n        if (size <= (PY_SSIZE_T_MAX - size / 2)) {\n            size += size / 2;\n        }\n#else\n        /* On Linux, overallocate by 25% is the best factor */\n        if (size <= (PY_SSIZE_T_MAX - size / 4)) {\n            size += size / 4;\n        }\n#endif\n    }\n\n    if (writer->obj != NULL) {\n        if (_PyBytes_Resize(&writer->obj, size)) {\n            return -1;\n        }\n        assert(writer->obj != NULL);\n    }\n    else {\n        writer->obj = PyBytes_FromStringAndSize(NULL, size);\n        if (writer->obj == NULL) {\n            return -1;\n        }\n\n        if (resize) {\n            assert((size_t)size > sizeof(writer->small_buffer));\n            memcpy(PyBytes_AS_STRING(writer->obj),\n                   writer->small_buffer,\n                   sizeof(writer->small_buffer));\n        }\n    }\n    return 0;\n}\n\nstatic inline void*\nPyBytesWriter_GetData(PyBytesWriter *writer)\n{\n    if (writer->obj == NULL) {\n        return writer->small_buffer;\n    }\n    else {\n        return PyBytes_AS_STRING(writer->obj);\n    }\n}\n\nstatic inline Py_ssize_t\nPyBytesWriter_GetSize(PyBytesWriter *writer)\n{\n    return writer->size;\n}\n\nstatic inline void\nPyBytesWriter_Discard(PyBytesWriter *writer)\n{\n    if (writer == NULL) {\n        return;\n    }\n\n    Py_XDECREF(writer->obj);\n    PyMem_Free(writer);\n}\n\nstatic inline PyBytesWriter*\nPyBytesWriter_Create(Py_ssize_t size)\n{\n    if (size < 0) {\n        PyErr_SetString(PyExc_ValueError, \"size must be >= 0\");\n        return NULL;\n    }\n\n    PyBytesWriter *writer = (PyBytesWriter*)PyMem_Malloc(sizeof(PyBytesWriter));\n    if (writer == NULL) {\n        PyErr_NoMemory();\n        return NULL;\n    }\n\n    writer->obj = NULL;\n    writer->size = 0;\n\n    if (size >= 1) {\n        if (_PyBytesWriter_Resize_impl(writer, size, 0) < 0) {\n            PyBytesWriter_Discard(writer);\n            return NULL;\n        }\n        writer->size = size;\n    }\n    return writer;\n}\n\nstatic inline PyObject*\nPyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size)\n{\n    PyObject *result;\n    if (size == 0) {\n        result = PyBytes_FromStringAndSize(\"\", 0);\n    }\n    else if (writer->obj != NULL) {\n        if (size != PyBytes_GET_SIZE(writer->obj)) {\n            if (_PyBytes_Resize(&writer->obj, size)) {\n                goto error;\n            }\n        }\n        result = writer->obj;\n        writer->obj = NULL;\n    }\n    else {\n        result = PyBytes_FromStringAndSize(writer->small_buffer, size);\n    }\n    PyBytesWriter_Discard(writer);\n    return result;\n\nerror:\n    PyBytesWriter_Discard(writer);\n    return NULL;\n}\n\nstatic inline PyObject*\nPyBytesWriter_Finish(PyBytesWriter *writer)\n{\n    return PyBytesWriter_FinishWithSize(writer, writer->size);\n}\n\nstatic inline PyObject*\nPyBytesWriter_FinishWithPointer(PyBytesWriter *writer, void *buf)\n{\n    Py_ssize_t size = (char*)buf - (char*)PyBytesWriter_GetData(writer);\n    if (size < 0 || size > _PyBytesWriter_GetAllocated(writer)) {\n        PyBytesWriter_Discard(writer);\n        PyErr_SetString(PyExc_ValueError, \"invalid end pointer\");\n        return NULL;\n    }\n\n    return PyBytesWriter_FinishWithSize(writer, size);\n}\n\nstatic inline int\nPyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size)\n{\n    if (size < 0) {\n        PyErr_SetString(PyExc_ValueError, \"size must be >= 0\");\n        return -1;\n    }\n    if (_PyBytesWriter_Resize_impl(writer, size, 1) < 0) {\n        return -1;\n    }\n    writer->size = size;\n    return 0;\n}\n\nstatic inline int\nPyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t size)\n{\n    if (size < 0 && writer->size + size < 0) {\n        PyErr_SetString(PyExc_ValueError, \"invalid size\");\n        return -1;\n    }\n    if (size > PY_SSIZE_T_MAX - writer->size) {\n        PyErr_NoMemory();\n        return -1;\n    }\n    size = writer->size + size;\n\n    if (_PyBytesWriter_Resize_impl(writer, size, 1) < 0) {\n        return -1;\n    }\n    writer->size = size;\n    return 0;\n}\n\nstatic inline void*\nPyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer,\n                                   Py_ssize_t size, void *buf)\n{\n    Py_ssize_t pos = (char*)buf - (char*)PyBytesWriter_GetData(writer);\n    if (PyBytesWriter_Grow(writer, size) < 0) {\n        return NULL;\n    }\n    return (char*)PyBytesWriter_GetData(writer) + pos;\n}\n\nstatic inline int\nPyBytesWriter_WriteBytes(PyBytesWriter *writer,\n                         const void *bytes, Py_ssize_t size)\n{\n    if (size < 0) {\n        size_t len = strlen((const char*)bytes);\n        if (len > (size_t)PY_SSIZE_T_MAX) {\n            PyErr_NoMemory();\n            return -1;\n        }\n        size = (Py_ssize_t)len;\n    }\n\n    Py_ssize_t pos = writer->size;\n    if (PyBytesWriter_Grow(writer, size) < 0) {\n        return -1;\n    }\n    char *buf = (char*)PyBytesWriter_GetData(writer);\n    memcpy(buf + pos, bytes, (size_t)size);\n    return 0;\n}\n\nstatic inline int\nPyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...)\n                     Py_GCC_ATTRIBUTE((format(printf, 2, 3)));\n\nstatic inline int\nPyBytesWriter_Format(PyBytesWriter *writer, const char *format, ...)\n{\n    va_list vargs;\n    va_start(vargs, format);\n    PyObject *str = PyBytes_FromFormatV(format, vargs);\n    va_end(vargs);\n\n    if (str == NULL) {\n        return -1;\n    }\n    int res = PyBytesWriter_WriteBytes(writer,\n                                       PyBytes_AS_STRING(str),\n                                       PyBytes_GET_SIZE(str));\n    Py_DECREF(str);\n    return res;\n}\n#endif  // PY_VERSION_HEX < 0x030F00A1\n\n\n#ifdef __cplusplus\n}\n#endif\n#endif  // PYTHONCAPI_COMPAT\n"
  },
  {
    "path": "asyncpg/protocol/record/pythoncapi_compat_extras.h",
    "content": "#ifndef PYTHONCAPI_COMPAT_EXTRAS\n#define PYTHONCAPI_COMPAT_EXTRAS\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <Python.h>\n\n// Python 3.11.0a6 added PyType_GetModuleByDef() to Python.h\n#if PY_VERSION_HEX < 0x030b00A6\nPyObject *\nPyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)\n{\n    assert(PyType_Check(type));\n\n    if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {\n        // type_ready_mro() ensures that no heap type is\n        // contained in a static type MRO.\n        goto error;\n    }\n    else {\n        PyHeapTypeObject *ht = (PyHeapTypeObject*)type;\n        PyObject *module = ht->ht_module;\n        if (module && PyModule_GetDef(module) == def) {\n            return module;\n        }\n    }\n\n    PyObject *res = NULL;\n    PyObject *mro = type->tp_mro;\n    // The type must be ready\n    assert(mro != NULL);\n    assert(PyTuple_Check(mro));\n    // mro_invoke() ensures that the type MRO cannot be empty.\n    assert(PyTuple_GET_SIZE(mro) >= 1);\n    // Also, the first item in the MRO is the type itself, which\n    // we already checked above. We skip it in the loop.\n    assert(PyTuple_GET_ITEM(mro, 0) == (PyObject *)type);\n\n    Py_ssize_t n = PyTuple_GET_SIZE(mro);\n    for (Py_ssize_t i = 1; i < n; i++) {\n        PyObject *super = PyTuple_GET_ITEM(mro, i);\n        if (!PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) {\n            // Static types in the MRO need to be skipped\n            continue;\n        }\n\n        PyHeapTypeObject *ht = (PyHeapTypeObject*)super;\n        PyObject *module = ht->ht_module;\n        if (module && PyModule_GetDef(module) == def) {\n            res = module;\n            break;\n        }\n    }\n\n    if (res != NULL) {\n        return res;\n    }\nerror:\n    PyErr_Format(\n        PyExc_TypeError,\n        \"PyType_GetModuleByDef: No superclass of '%s' has the given module\",\n        type->tp_name);\n    return NULL;\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n#endif  // PYTHONCAPI_COMPAT_EXTRAS\n"
  },
  {
    "path": "asyncpg/protocol/record/recordobj.c",
    "content": "/* Big parts of this file are copied (with modifications) from\n   CPython/Objects/tupleobject.c.\n\n   Portions Copyright (c) PSF (and other CPython copyright holders).\n   Portions Copyright (c) 2016-present MagicStack Inc.\n   License: PSFL v2; see CPython/LICENSE for details.\n*/\n\n#include <stdint.h>\n#include <Python.h>\n#include \"pythoncapi_compat.h\"\n#include \"pythoncapi_compat_extras.h\"\n\n#include \"recordobj.h\"\n\n#ifndef _PyCFunction_CAST\n#define _PyCFunction_CAST(func) ((PyCFunction)(void (*)(void))(func))\n#endif\n\nstatic size_t ApgRecord_MAXSIZE =\n    (((size_t)PY_SSIZE_T_MAX - sizeof(ApgRecordObject) - sizeof(PyObject *)) /\n     sizeof(PyObject *));\n\n/* Largest record to save on free list */\n#define ApgRecord_MAXSAVESIZE 20\n\n/* Maximum number of records of each size to save */\n#define ApgRecord_MAXFREELIST 2000\n\ntypedef struct {\n    ApgRecordObject *freelist[ApgRecord_MAXSAVESIZE];\n    int numfree[ApgRecord_MAXSAVESIZE];\n} record_freelist_state;\n\ntypedef struct {\n    PyTypeObject *ApgRecord_Type;\n    PyTypeObject *ApgRecordDesc_Type;\n    PyTypeObject *ApgRecordIter_Type;\n    PyTypeObject *ApgRecordItems_Type;\n\n    Py_tss_t freelist_key;  // TSS key for per-thread record_freelist_state\n} record_module_state;\n\nstatic inline record_module_state *\nget_module_state(PyObject *module)\n{\n    void *state = PyModule_GetState(module);\n    if (state == NULL) {\n        PyErr_SetString(PyExc_SystemError, \"failed to get record module state\");\n        return NULL;\n    }\n    return (record_module_state *)state;\n}\n\nstatic inline record_module_state *\nget_module_state_from_type(PyTypeObject *type)\n{\n    void *state = PyType_GetModuleState(type);\n    if (state != NULL) {\n        return (record_module_state *)state;\n    }\n\n    PyErr_Format(PyExc_SystemError, \"could not get record module state from '%.100s'\",\n                 type->tp_name);\n    return NULL;\n}\n\nstatic struct PyModuleDef _recordmodule;\n\nstatic inline record_module_state *\nfind_module_state_by_def(PyTypeObject *type)\n{\n    PyObject *mod = PyType_GetModuleByDef(type, &_recordmodule);\n    if (mod == NULL)\n        return NULL;\n    return get_module_state(mod);\n}\n\nstatic inline record_freelist_state *\nget_freelist_state(record_module_state *state)\n{\n    record_freelist_state *freelist;\n\n    freelist = (record_freelist_state *)PyThread_tss_get(&state->freelist_key);\n    if (freelist == NULL) {\n        freelist = (record_freelist_state *)PyMem_Calloc(\n            1, sizeof(record_freelist_state));\n        if (freelist == NULL) {\n            PyErr_NoMemory();\n            return NULL;\n        }\n        if (PyThread_tss_set(&state->freelist_key, (void *)freelist) != 0) {\n            PyMem_Free(freelist);\n            PyErr_SetString(\n                PyExc_SystemError, \"failed to set thread-specific data\");\n            return NULL;\n        }\n    }\n    return freelist;\n}\n\nPyObject *\nmake_record(PyTypeObject *type, PyObject *desc, Py_ssize_t size,\n            record_module_state *state)\n{\n    ApgRecordObject *o;\n    Py_ssize_t i;\n    int need_gc_track = 0;\n\n    if (size < 0 || desc == NULL ||\n        Py_TYPE(desc) != state->ApgRecordDesc_Type) {\n        PyErr_BadInternalCall();\n        return NULL;\n    }\n\n    if (type == state->ApgRecord_Type) {\n        record_freelist_state *freelist = NULL;\n\n        if (size < ApgRecord_MAXSAVESIZE) {\n            freelist = get_freelist_state(state);\n            if (freelist != NULL && freelist->freelist[size] != NULL) {\n                o = freelist->freelist[size];\n                freelist->freelist[size] = (ApgRecordObject *)o->ob_item[0];\n                freelist->numfree[size]--;\n                _Py_NewReference((PyObject *)o);\n            }\n            else {\n                freelist = NULL;\n            }\n        }\n\n        if (freelist == NULL) {\n            if ((size_t)size > ApgRecord_MAXSIZE) {\n                return PyErr_NoMemory();\n            }\n            o = PyObject_GC_NewVar(ApgRecordObject, state->ApgRecord_Type, size);\n            if (o == NULL) {\n                return NULL;\n            }\n        }\n\n        need_gc_track = 1;\n    }\n    else {\n        assert(PyType_IsSubtype(type, state->ApgRecord_Type));\n\n        if ((size_t)size > ApgRecord_MAXSIZE) {\n            return PyErr_NoMemory();\n        }\n        o = (ApgRecordObject *)type->tp_alloc(type, size);\n        if (!PyObject_GC_IsTracked((PyObject *)o)) {\n            PyErr_SetString(PyExc_TypeError, \"record subclass is not tracked by GC\");\n            return NULL;\n        }\n    }\n\n    for (i = 0; i < size; i++) {\n        o->ob_item[i] = NULL;\n    }\n\n    Py_INCREF(desc);\n    o->desc = (ApgRecordDescObject *)desc;\n    o->self_hash = -1;\n    if (need_gc_track) {\n        PyObject_GC_Track(o);\n    }\n    return (PyObject *)o;\n}\n\nstatic void\nrecord_dealloc(PyObject *self)\n{\n    ApgRecordObject *o = (ApgRecordObject *)self;\n    Py_ssize_t i;\n    Py_ssize_t len = Py_SIZE(o);\n    PyTypeObject *tp = Py_TYPE(o);\n    record_module_state *state;\n    int skip_dealloc = 0;\n\n    state = find_module_state_by_def(tp);\n    if (state == NULL) {\n        return;\n    }\n\n    PyObject_GC_UnTrack(o);\n\n    o->self_hash = -1;\n\n    Py_CLEAR(o->desc);\n\n    Py_TRASHCAN_BEGIN(o, record_dealloc)\n\n    i = len;\n    while (--i >= 0) {\n        Py_XDECREF(o->ob_item[i]);\n    }\n\n    if (len < ApgRecord_MAXSAVESIZE && tp == state->ApgRecord_Type) {\n        record_freelist_state *freelist = get_freelist_state(state);\n        if (freelist != NULL && freelist->numfree[len] < ApgRecord_MAXFREELIST) {\n            o->ob_item[0] = (PyObject *)freelist->freelist[len];\n            freelist->numfree[len]++;\n            freelist->freelist[len] = o;\n            skip_dealloc = 1;\n        }\n    }\n\n    if (!skip_dealloc) {\n        tp->tp_free(self);\n        Py_DECREF(tp);\n    }\n\n    Py_TRASHCAN_END\n}\n\nstatic int\nrecord_traverse(PyObject *self, visitproc visit, void *arg)\n{\n    ApgRecordObject *o = (ApgRecordObject *)self;\n    for (Py_ssize_t i = Py_SIZE(o); --i >= 0;) {\n        Py_VISIT(o->ob_item[i]);\n    }\n    return 0;\n}\n\n/* Below are the official constants from the xxHash specification. Optimizing\n   compilers should emit a single \"rotate\" instruction for the\n   _PyTuple_HASH_XXROTATE() expansion. If that doesn't happen for some important\n   platform, the macro could be changed to expand to a platform-specific rotate\n   spelling instead.\n*/\n#if SIZEOF_PY_UHASH_T > 4\n#define _ApgRecord_HASH_XXPRIME_1 ((Py_uhash_t)11400714785074694791ULL)\n#define _ApgRecord_HASH_XXPRIME_2 ((Py_uhash_t)14029467366897019727ULL)\n#define _ApgRecord_HASH_XXPRIME_5 ((Py_uhash_t)2870177450012600261ULL)\n#define _ApgRecord_HASH_XXROTATE(x) ((x << 31) | (x >> 33)) /* Rotate left 31 bits */\n#else\n#define _ApgRecord_HASH_XXPRIME_1 ((Py_uhash_t)2654435761UL)\n#define _ApgRecord_HASH_XXPRIME_2 ((Py_uhash_t)2246822519UL)\n#define _ApgRecord_HASH_XXPRIME_5 ((Py_uhash_t)374761393UL)\n#define _ApgRecord_HASH_XXROTATE(x) ((x << 13) | (x >> 19)) /* Rotate left 13 bits */\n#endif\n\nstatic Py_hash_t\nrecord_hash(PyObject *op)\n{\n    ApgRecordObject *v = (ApgRecordObject *)op;\n    Py_uhash_t acc;\n    Py_ssize_t len = Py_SIZE(v);\n    PyObject **item = v->ob_item;\n    acc = _ApgRecord_HASH_XXPRIME_5;\n    for (Py_ssize_t i = 0; i < len; i++) {\n        Py_uhash_t lane = (Py_uhash_t)PyObject_Hash(item[i]);\n        if (lane == (Py_uhash_t)-1) {\n            return -1;\n        }\n        acc += lane * _ApgRecord_HASH_XXPRIME_2;\n        acc = _ApgRecord_HASH_XXROTATE(acc);\n        acc *= _ApgRecord_HASH_XXPRIME_1;\n    }\n\n    /* Add input length, mangled to keep the historical value of hash(()). */\n    acc += (Py_uhash_t)len ^ (_ApgRecord_HASH_XXPRIME_5 ^ 3527539UL);\n\n    if (acc == (Py_uhash_t)-1) {\n        acc = 1546275796;\n    }\n\n    return (Py_hash_t)acc;\n}\n\nstatic Py_ssize_t\nrecord_length(PyObject *self)\n{\n    ApgRecordObject *a = (ApgRecordObject *)self;\n    return Py_SIZE(a);\n}\n\nstatic int\nrecord_contains(PyObject *self, PyObject *el)\n{\n    ApgRecordObject *a = (ApgRecordObject *)self;\n    if (a->desc == NULL || a->desc->keys == NULL) {\n        return 0;\n    }\n    return PySequence_Contains(a->desc->keys, el);\n}\n\nstatic PyObject *\nrecord_item(ApgRecordObject *op, Py_ssize_t i)\n{\n    ApgRecordObject *a = (ApgRecordObject *)op;\n    if (i < 0 || i >= Py_SIZE(a)) {\n        PyErr_SetString(PyExc_IndexError, \"record index out of range\");\n        return NULL;\n    }\n    return Py_NewRef(a->ob_item[i]);\n}\n\nstatic PyObject *\nrecord_richcompare(PyObject *v, PyObject *w, int op)\n{\n    Py_ssize_t i;\n    Py_ssize_t vlen, wlen;\n    int v_is_tuple = 0;\n    int w_is_tuple = 0;\n    int v_is_record = 0;\n    int w_is_record = 0;\n    int comp;\n    PyTypeObject *v_type = Py_TYPE(v);\n    PyTypeObject *w_type = Py_TYPE(w);\n\n    record_module_state *state;\n\n    state = find_module_state_by_def(v_type);\n    if (state == NULL) {\n        PyErr_Clear();\n        state = find_module_state_by_def(w_type);\n    }\n    if (PyTuple_Check(v)) {\n        v_is_tuple = 1;\n    }\n    else if (v_type == state->ApgRecord_Type) {\n        v_is_record = 1;\n    }\n    else if (!PyObject_TypeCheck(v, state->ApgRecord_Type)) {\n        Py_RETURN_NOTIMPLEMENTED;\n    }\n\n    if (PyTuple_Check(w)) {\n        w_is_tuple = 1;\n    }\n    else if (w_type == state->ApgRecord_Type) {\n        w_is_record = 1;\n    }\n    else if (!PyObject_TypeCheck(w, state->ApgRecord_Type)) {\n        Py_RETURN_NOTIMPLEMENTED;\n    }\n\n#define V_ITEM(i)                        \\\n    (v_is_tuple ? PyTuple_GET_ITEM(v, i) \\\n                : (v_is_record ? ApgRecord_GET_ITEM(v, i) : PySequence_GetItem(v, i)))\n#define W_ITEM(i)                        \\\n    (w_is_tuple ? PyTuple_GET_ITEM(w, i) \\\n                : (w_is_record ? ApgRecord_GET_ITEM(w, i) : PySequence_GetItem(w, i)))\n\n    vlen = Py_SIZE(v);\n    wlen = Py_SIZE(w);\n\n    if (op == Py_EQ && vlen != wlen) {\n        /* Checking if v == w, but len(v) != len(w): return False */\n        Py_RETURN_FALSE;\n    }\n\n    if (op == Py_NE && vlen != wlen) {\n        /* Checking if v != w, and len(v) != len(w): return True */\n        Py_RETURN_TRUE;\n    }\n\n    /* Search for the first index where items are different.\n     * Note that because tuples are immutable, it's safe to reuse\n     * vlen and wlen across the comparison calls.\n     */\n    for (i = 0; i < vlen && i < wlen; i++) {\n        comp = PyObject_RichCompareBool(V_ITEM(i), W_ITEM(i), Py_EQ);\n        if (comp < 0) {\n            return NULL;\n        }\n        if (!comp) {\n            break;\n        }\n    }\n\n    if (i >= vlen || i >= wlen) {\n        /* No more items to compare -- compare sizes */\n        int cmp;\n        switch (op) {\n            case Py_LT:\n                cmp = vlen < wlen;\n                break;\n            case Py_LE:\n                cmp = vlen <= wlen;\n                break;\n            case Py_EQ:\n                cmp = vlen == wlen;\n                break;\n            case Py_NE:\n                cmp = vlen != wlen;\n                break;\n            case Py_GT:\n                cmp = vlen > wlen;\n                break;\n            case Py_GE:\n                cmp = vlen >= wlen;\n                break;\n            default:\n                Py_UNREACHABLE();\n        }\n        if (cmp) {\n            Py_RETURN_TRUE;\n        }\n        else {\n            Py_RETURN_FALSE;\n        }\n    }\n\n    /* We have an item that differs -- shortcuts for EQ/NE */\n    if (op == Py_EQ) {\n        Py_RETURN_FALSE;\n    }\n    if (op == Py_NE) {\n        Py_RETURN_TRUE;\n    }\n\n    /* Compare the final item again using the proper operator */\n    return PyObject_RichCompare(V_ITEM(i), W_ITEM(i), op);\n\n#undef V_ITEM\n#undef W_ITEM\n}\n\ntypedef enum item_by_name_result {\n    APG_ITEM_FOUND = 0,\n    APG_ERROR = -1,\n    APG_ITEM_NOT_FOUND = -2\n} item_by_name_result_t;\n\n/* Lookup a record value by its name.  Return 0 on success, -2 if the\n * value was not found (with KeyError set), and -1 on all other errors.\n */\nstatic item_by_name_result_t\nrecord_item_by_name(ApgRecordObject *o, PyObject *item, PyObject **result)\n{\n    PyObject *mapped;\n    PyObject *val;\n    Py_ssize_t i;\n\n    mapped = PyObject_GetItem(o->desc->mapping, item);\n    if (mapped == NULL) {\n        goto noitem;\n    }\n\n    if (!PyIndex_Check(mapped)) {\n        Py_DECREF(mapped);\n        goto error;\n    }\n\n    i = PyNumber_AsSsize_t(mapped, PyExc_IndexError);\n    Py_DECREF(mapped);\n\n    if (i < 0) {\n        if (PyErr_Occurred())\n            PyErr_Clear();\n        goto error;\n    }\n\n    val = record_item(o, i);\n    if (val == NULL) {\n        PyErr_Clear();\n        goto error;\n    }\n\n    *result = val;\n\n    return APG_ITEM_FOUND;\n\nnoitem:\n    PyErr_SetObject(PyExc_KeyError, item);\n    return APG_ITEM_NOT_FOUND;\n\nerror:\n    PyErr_SetString(PyExc_RuntimeError, \"invalid record descriptor\");\n    return APG_ERROR;\n}\n\nstatic PyObject *\nrecord_subscript(PyObject *op, PyObject *item)\n{\n    ApgRecordObject *self = (ApgRecordObject *)op;\n\n    if (PyIndex_Check(item)) {\n        Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);\n        if (i == -1 && PyErr_Occurred())\n            return NULL;\n        if (i < 0) {\n            i += Py_SIZE(self);\n        }\n        return record_item(self, i);\n    }\n    else if (PySlice_Check(item)) {\n        Py_ssize_t start, stop, step, cur, slicelength, i;\n        PyObject *it;\n        PyObject **src, **dest;\n\n        if (PySlice_Unpack(item, &start, &stop, &step) < 0) {\n            return NULL;\n        }\n        slicelength = PySlice_AdjustIndices(Py_SIZE(self), &start, &stop, step);\n\n        if (slicelength <= 0) {\n            return PyTuple_New(0);\n        }\n        else if (start == 0 && step == 1 && slicelength == Py_SIZE(self) &&\n                 PyTuple_CheckExact(self)) {\n            return Py_NewRef(self);\n        }\n        else {\n            PyTupleObject *result = (PyTupleObject *)PyTuple_New(slicelength);\n            if (!result)\n                return NULL;\n\n            src = self->ob_item;\n            dest = result->ob_item;\n            for (cur = start, i = 0; i < slicelength; cur += step, i++) {\n                it = Py_NewRef(src[cur]);\n                dest[i] = it;\n            }\n\n            return (PyObject *)result;\n        }\n    }\n    else {\n        PyObject *result;\n\n        if (record_item_by_name(self, item, &result) < 0)\n            return NULL;\n        else\n            return result;\n    }\n}\n\nstatic const char *\nget_typename(PyTypeObject *type)\n{\n    assert(type->tp_name != NULL);\n    const char *s = strrchr(type->tp_name, '.');\n    if (s == NULL) {\n        s = type->tp_name;\n    }\n    else {\n        s++;\n    }\n    return s;\n}\n\nstatic PyObject *\nrecord_repr(PyObject *self)\n{\n    ApgRecordObject *v = (ApgRecordObject *)self;\n    Py_ssize_t i, n;\n    PyObject *keys_iter;\n    PyUnicodeWriter *writer;\n\n    n = Py_SIZE(v);\n    if (n == 0) {\n        return PyUnicode_FromFormat(\"<%s>\", get_typename(Py_TYPE(v)));\n    }\n\n    keys_iter = PyObject_GetIter(v->desc->keys);\n    if (keys_iter == NULL) {\n        return NULL;\n    }\n\n    i = Py_ReprEnter((PyObject *)v);\n    if (i != 0) {\n        Py_DECREF(keys_iter);\n        if (i > 0) {\n            return PyUnicode_FromFormat(\"<%s ...>\", get_typename(Py_TYPE(v)));\n        }\n        return NULL;\n    }\n    writer = PyUnicodeWriter_Create(12); /* <Record a=1> */\n\n    if (PyUnicodeWriter_Format(writer, \"<%s \", get_typename(Py_TYPE(v))) < 0) {\n        goto error;\n    }\n\n    for (i = 0; i < n; ++i) {\n        int res;\n        PyObject *key;\n\n        if (i > 0)\n            if (PyUnicodeWriter_WriteChar(writer, ' ') < 0)\n                goto error;\n\n        key = PyIter_Next(keys_iter);\n        if (key == NULL) {\n            PyErr_SetString(PyExc_RuntimeError, \"invalid record mapping\");\n            goto error;\n        }\n\n        res = PyUnicodeWriter_WriteStr(writer, key);\n        Py_DECREF(key);\n        if (res < 0)\n            goto error;\n\n        if (PyUnicodeWriter_WriteChar(writer, '=') < 0)\n            goto error;\n\n        if (Py_EnterRecursiveCall(\" while getting the repr of a record\"))\n            goto error;\n        res = PyUnicodeWriter_WriteRepr(writer, v->ob_item[i]);\n        Py_LeaveRecursiveCall();\n        if (res < 0)\n            goto error;\n    }\n\n    if (PyUnicodeWriter_WriteChar(writer, '>') < 0)\n        goto error;\n\n    Py_DECREF(keys_iter);\n    Py_ReprLeave((PyObject *)v);\n    return PyUnicodeWriter_Finish(writer);\n\nerror:\n    Py_DECREF(keys_iter);\n    PyUnicodeWriter_Discard(writer);\n    Py_ReprLeave((PyObject *)v);\n    return NULL;\n}\n\nstatic PyObject *\nrecord_new_iter(ApgRecordObject *, const record_module_state *);\n\nstatic PyObject *\nrecord_iter(PyObject *seq)\n{\n    ApgRecordObject *r = (ApgRecordObject *)seq;\n    record_module_state *state;\n\n    state = find_module_state_by_def(Py_TYPE(seq));\n    if (state == NULL) {\n        return NULL;\n    }\n\n    return record_new_iter(r, state);\n}\n\nstatic PyObject *\nrecord_values(PyObject *self, PyTypeObject *defcls, PyObject *const *args,\n              size_t nargsf, PyObject *kwnames)\n{\n    ApgRecordObject *r = (ApgRecordObject *)self;\n    record_module_state *state = get_module_state_from_type(defcls);\n\n    if (state == NULL)\n        return NULL;\n\n    return record_new_iter(r, state);\n}\n\nstatic PyObject *\nrecord_keys(PyObject *self, PyTypeObject *defcls, PyObject *const *args,\n            size_t nargsf, PyObject *kwnames)\n{\n    ApgRecordObject *r = (ApgRecordObject *)self;\n    return PyObject_GetIter(r->desc->keys);\n}\n\nstatic PyObject *\nrecord_new_items_iter(ApgRecordObject *, const record_module_state *);\n\nstatic PyObject *\nrecord_items(PyObject *self, PyTypeObject *defcls, PyObject *const *args,\n             size_t nargsf, PyObject *kwnames)\n{\n    ApgRecordObject *r = (ApgRecordObject *)self;\n    record_module_state *state = get_module_state_from_type(defcls);\n\n    if (state == NULL)\n        return NULL;\n\n    return record_new_items_iter(r, state);\n}\n\nstatic PyObject *\nrecord_get(PyObject *self, PyTypeObject *defcls, PyObject *const *args,\n           size_t nargsf, PyObject *kwnames)\n{\n    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);\n    PyObject *key;\n    PyObject *defval = Py_None;\n    PyObject *val = NULL;\n    int res;\n\n    if (nargs == 2) {\n        key = args[0];\n        defval = args[1];\n    } else if (nargs == 1) {\n        key = args[0];\n    } else {\n        PyErr_Format(PyExc_TypeError,\n                     \"Record.get() expected 1 or 2 arguments, got %zd\",\n                     nargs);\n    }\n\n    if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {\n        PyErr_SetString(PyExc_TypeError, \"Record.get() takes no keyword arguments\");\n        return NULL;\n    }\n\n    res = record_item_by_name((ApgRecordObject *)self, key, &val);\n    if (res == APG_ITEM_NOT_FOUND) {\n        PyErr_Clear();\n        Py_INCREF(defval);\n        val = defval;\n    }\n\n    return val;\n}\n\nstatic PyObject *\nrecord_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)\n{\n    record_module_state *state;\n\n    state = get_module_state_from_type(type);\n    if (state == NULL) {\n        return NULL;\n    }\n\n    if (type == state->ApgRecord_Type) {\n        PyErr_Format(PyExc_TypeError, \"cannot create '%.100s' instances\", type->tp_name);\n        return NULL;\n    }\n\n    /* For subclasses, use the default allocation */\n    return type->tp_alloc(type, 0);\n}\n\nstatic PyMethodDef record_methods[] = {\n    {\"values\", _PyCFunction_CAST(record_values), METH_METHOD | METH_FASTCALL | METH_KEYWORDS},\n    {\"keys\", _PyCFunction_CAST(record_keys), METH_METHOD | METH_FASTCALL | METH_KEYWORDS},\n    {\"items\", _PyCFunction_CAST(record_items), METH_METHOD | METH_FASTCALL | METH_KEYWORDS},\n    {\"get\", _PyCFunction_CAST(record_get), METH_METHOD | METH_FASTCALL | METH_KEYWORDS},\n    {NULL, NULL} /* sentinel */\n};\n\nstatic PyType_Slot ApgRecord_TypeSlots[] = {\n    {Py_tp_dealloc, record_dealloc},\n    {Py_tp_repr, record_repr},\n    {Py_tp_hash, record_hash},\n    {Py_tp_getattro, PyObject_GenericGetAttr},\n    {Py_tp_traverse, record_traverse},\n    {Py_tp_richcompare, record_richcompare},\n    {Py_tp_iter, record_iter},\n    {Py_tp_methods, record_methods},\n    {Py_tp_new, record_new},\n    {Py_tp_free, PyObject_GC_Del},\n    {Py_sq_length, record_length},\n    {Py_sq_item, record_item},\n    {Py_sq_contains, record_contains},\n    {Py_mp_length, record_length},\n    {Py_mp_subscript, record_subscript},\n    {0, NULL},\n};\n\n#ifndef Py_TPFLAGS_IMMUTABLETYPE\n#define Py_TPFLAGS_IMMUTABLETYPE 0\n#endif\n\nstatic PyType_Spec ApgRecord_TypeSpec = {\n    .name = \"asyncpg.protocol.record.Record\",\n    .basicsize = sizeof(ApgRecordObject) - sizeof(PyObject *),\n    .itemsize = sizeof(PyObject *),\n    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE |\n              Py_TPFLAGS_IMMUTABLETYPE),\n    .slots = ApgRecord_TypeSlots,\n};\n\n/* Record Iterator */\n\ntypedef struct {\n    PyObject_HEAD Py_ssize_t it_index;\n    ApgRecordObject *it_seq; /* Set to NULL when iterator is exhausted */\n} ApgRecordIterObject;\n\nstatic void\nrecord_iter_dealloc(ApgRecordIterObject *it)\n{\n    PyTypeObject *tp = Py_TYPE(it);\n    PyObject_GC_UnTrack(it);\n    Py_CLEAR(it->it_seq);\n    PyObject_GC_Del(it);\n    Py_DECREF(tp);\n}\n\nstatic int\nrecord_iter_traverse(ApgRecordIterObject *it, visitproc visit, void *arg)\n{\n    Py_VISIT(it->it_seq);\n    return 0;\n}\n\nstatic PyObject *\nrecord_iter_next(ApgRecordIterObject *it)\n{\n    ApgRecordObject *seq;\n    PyObject *item;\n\n    assert(it != NULL);\n    seq = it->it_seq;\n    if (seq == NULL)\n        return NULL;\n\n    if (it->it_index < Py_SIZE(seq)) {\n        item = ApgRecord_GET_ITEM(seq, it->it_index);\n        ++it->it_index;\n        Py_INCREF(item);\n        return item;\n    }\n\n    it->it_seq = NULL;\n    Py_DECREF(seq);\n    return NULL;\n}\n\nstatic PyObject *\nrecord_iter_len(ApgRecordIterObject *it)\n{\n    Py_ssize_t len = 0;\n    if (it->it_seq) {\n        len = Py_SIZE(it->it_seq) - it->it_index;\n    }\n    return PyLong_FromSsize_t(len);\n}\n\nPyDoc_STRVAR(record_iter_len_doc, \"Private method returning an estimate of len(list(it)).\");\n\nstatic PyMethodDef record_iter_methods[] = {\n    {\"__length_hint__\", (PyCFunction)record_iter_len, METH_NOARGS, record_iter_len_doc},\n    {NULL, NULL} /* sentinel */\n};\n\nstatic PyType_Slot ApgRecordIter_TypeSlots[] = {\n    {Py_tp_dealloc, (destructor)record_iter_dealloc},\n    {Py_tp_getattro, PyObject_GenericGetAttr},\n    {Py_tp_traverse, (traverseproc)record_iter_traverse},\n    {Py_tp_iter, PyObject_SelfIter},\n    {Py_tp_iternext, (iternextfunc)record_iter_next},\n    {Py_tp_methods, record_iter_methods},\n    {0, NULL},\n};\n\nstatic PyType_Spec ApgRecordIter_TypeSpec = {\n    .name = \"asyncpg.protocol.record.RecordIterator\",\n    .basicsize = sizeof(ApgRecordIterObject),\n    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,\n    .slots = ApgRecordIter_TypeSlots,\n};\n\nstatic PyObject *\nrecord_new_iter(ApgRecordObject *r, const record_module_state *state)\n{\n    ApgRecordIterObject *it;\n    it = PyObject_GC_New(ApgRecordIterObject, state->ApgRecordIter_Type);\n    if (it == NULL)\n        return NULL;\n    it->it_index = 0;\n    Py_INCREF(r);\n    it->it_seq = r;\n    PyObject_GC_Track(it);\n    return (PyObject *)it;\n}\n\n/* Record Items Iterator */\n\ntypedef struct {\n    PyObject_HEAD Py_ssize_t it_index;\n    PyObject *it_key_iter;\n    ApgRecordObject *it_seq; /* Set to NULL when iterator is exhausted */\n} ApgRecordItemsObject;\n\nstatic void\nrecord_items_dealloc(ApgRecordItemsObject *it)\n{\n    PyTypeObject *tp = Py_TYPE(it);\n    PyObject_GC_UnTrack(it);\n    Py_CLEAR(it->it_key_iter);\n    Py_CLEAR(it->it_seq);\n    PyObject_GC_Del(it);\n    Py_DECREF(tp);\n}\n\nstatic int\nrecord_items_traverse(ApgRecordItemsObject *it, visitproc visit, void *arg)\n{\n    Py_VISIT(it->it_key_iter);\n    Py_VISIT(it->it_seq);\n    return 0;\n}\n\nstatic PyObject *\nrecord_items_next(ApgRecordItemsObject *it)\n{\n    ApgRecordObject *seq;\n    PyObject *key;\n    PyObject *val;\n    PyObject *tup;\n\n    assert(it != NULL);\n    seq = it->it_seq;\n    if (seq == NULL) {\n        return NULL;\n    }\n    assert(it->it_key_iter != NULL);\n\n    key = PyIter_Next(it->it_key_iter);\n    if (key == NULL) {\n        /* likely it_key_iter had less items than seq has values */\n        goto exhausted;\n    }\n\n    if (it->it_index < Py_SIZE(seq)) {\n        val = ApgRecord_GET_ITEM(seq, it->it_index);\n        ++it->it_index;\n        Py_INCREF(val);\n    }\n    else {\n        /* it_key_iter had more items than seq has values */\n        Py_DECREF(key);\n        goto exhausted;\n    }\n\n    tup = PyTuple_New(2);\n    if (tup == NULL) {\n        Py_DECREF(val);\n        Py_DECREF(key);\n        goto exhausted;\n    }\n\n    PyTuple_SET_ITEM(tup, 0, key);\n    PyTuple_SET_ITEM(tup, 1, val);\n    return tup;\n\nexhausted:\n    Py_CLEAR(it->it_key_iter);\n    Py_CLEAR(it->it_seq);\n    return NULL;\n}\n\nstatic PyObject *\nrecord_items_len(ApgRecordItemsObject *it)\n{\n    Py_ssize_t len = 0;\n    if (it->it_seq) {\n        len = Py_SIZE(it->it_seq) - it->it_index;\n    }\n    return PyLong_FromSsize_t(len);\n}\n\nPyDoc_STRVAR(record_items_len_doc, \"Private method returning an estimate of len(list(it())).\");\n\nstatic PyMethodDef record_items_methods[] = {\n    {\"__length_hint__\", (PyCFunction)record_items_len, METH_NOARGS, record_items_len_doc},\n    {NULL, NULL} /* sentinel */\n};\n\nstatic PyType_Slot ApgRecordItems_TypeSlots[] = {\n    {Py_tp_dealloc, (destructor)record_items_dealloc},\n    {Py_tp_getattro, PyObject_GenericGetAttr},\n    {Py_tp_traverse, (traverseproc)record_items_traverse},\n    {Py_tp_iter, PyObject_SelfIter},\n    {Py_tp_iternext, (iternextfunc)record_items_next},\n    {Py_tp_methods, record_items_methods},\n    {0, NULL},\n};\n\nstatic PyType_Spec ApgRecordItems_TypeSpec = {\n    .name = \"asyncpg.protocol.record.RecordItemsIterator\",\n    .basicsize = sizeof(ApgRecordItemsObject),\n    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,\n    .slots = ApgRecordItems_TypeSlots,\n};\n\nstatic PyObject *\nrecord_new_items_iter(ApgRecordObject *r, const record_module_state *state)\n{\n    ApgRecordItemsObject *it;\n    PyObject *key_iter;\n\n    key_iter = PyObject_GetIter(r->desc->keys);\n    if (key_iter == NULL)\n        return NULL;\n\n    it = PyObject_GC_New(ApgRecordItemsObject, state->ApgRecordItems_Type);\n    if (it == NULL) {\n        Py_DECREF(key_iter);\n        return NULL;\n    }\n\n    it->it_key_iter = key_iter;\n    it->it_index = 0;\n    Py_INCREF(r);\n    it->it_seq = r;\n    PyObject_GC_Track(it);\n\n    return (PyObject *)it;\n}\n\n/* ----------------- */\n\nstatic void\nrecord_desc_dealloc(ApgRecordDescObject *o)\n{\n    PyTypeObject *tp = Py_TYPE(o);\n    PyObject_GC_UnTrack(o);\n    Py_CLEAR(o->mapping);\n    Py_CLEAR(o->keys);\n    PyObject_GC_Del(o);\n    Py_DECREF(tp);\n}\n\nstatic int\nrecord_desc_traverse(ApgRecordDescObject *o, visitproc visit, void *arg)\n{\n    Py_VISIT(o->mapping);\n    Py_VISIT(o->keys);\n    return 0;\n}\n\nstatic PyObject *\nrecord_desc_vectorcall(PyObject *type, PyObject *const *args, size_t nargsf,\n                       PyObject *kwnames)\n{\n    PyObject *mapping;\n    PyObject *keys;\n    ApgRecordDescObject *o;\n    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);\n\n    if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {\n        PyErr_SetString(PyExc_TypeError, \"RecordDescriptor() takes no keyword arguments\");\n        return NULL;\n    }\n\n    if (nargs != 2) {\n        PyErr_Format(PyExc_TypeError,\n                     \"RecordDescriptor() takes exactly 2 arguments (%zd given)\", nargs);\n        return NULL;\n    }\n\n    mapping = args[0];\n    keys = args[1];\n\n    if (!PyTuple_CheckExact(keys)) {\n        PyErr_SetString(PyExc_TypeError, \"keys must be a tuple\");\n        return NULL;\n    }\n\n    o = PyObject_GC_New(ApgRecordDescObject, (PyTypeObject *)type);\n    if (o == NULL) {\n        return NULL;\n    }\n\n    Py_INCREF(mapping);\n    o->mapping = mapping;\n\n    Py_INCREF(keys);\n    o->keys = keys;\n\n    PyObject_GC_Track(o);\n    return (PyObject *)o;\n}\n\n/* Fallback wrapper for when there is no vectorcall support */\nstatic PyObject *\nrecord_desc_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)\n{\n    PyObject *const *args_array;\n    size_t nargsf;\n    PyObject *kwnames = NULL;\n\n    if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) {\n        PyErr_SetString(PyExc_TypeError,\n                        \"RecordDescriptor() takes no keyword arguments\");\n        return NULL;\n    }\n\n    if (!PyTuple_Check(args)) {\n        PyErr_SetString(PyExc_TypeError,\n                        \"args must be a tuple\");\n        return NULL;\n    }\n\n    nargsf = (size_t)PyTuple_GET_SIZE(args);\n    args_array = &PyTuple_GET_ITEM(args, 0);\n\n    return record_desc_vectorcall((PyObject *)type, args_array, nargsf, kwnames);\n}\n\nstatic PyObject *\nrecord_desc_make_record(PyObject *desc, PyTypeObject *desc_type,\n                        PyObject *const *args, Py_ssize_t nargs,\n                        PyObject *kwnames)\n{\n    PyObject *type_obj;\n    Py_ssize_t size;\n    record_module_state *state = get_module_state_from_type(desc_type);\n\n    if (state == NULL) {\n        return NULL;\n    }\n\n    if (nargs != 2) {\n        PyErr_Format(PyExc_TypeError,\n                     \"RecordDescriptor.make_record() takes exactly 2 arguments (%zd given)\",\n                     nargs);\n        return NULL;\n    }\n\n    if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {\n        PyErr_SetString(PyExc_TypeError,\n                        \"RecordDescriptor.make_record() takes no keyword arguments\");\n        return NULL;\n    }\n\n    type_obj = args[0];\n    size = PyLong_AsSsize_t(args[1]);\n    if (size == -1 && PyErr_Occurred()) {\n        return NULL;\n    }\n\n    if (!PyType_Check(type_obj)) {\n        PyErr_SetString(PyExc_TypeError,\n                        \"RecordDescriptor.make_record(): first argument must be a type\");\n        return NULL;\n    }\n\n    return make_record((PyTypeObject *)type_obj, desc, size, state);\n}\n\nstatic PyMethodDef record_desc_methods[] = {\n    {\"make_record\", _PyCFunction_CAST(record_desc_make_record),\n     METH_FASTCALL | METH_METHOD | METH_KEYWORDS},\n    {NULL, NULL} /* sentinel */\n};\n\nstatic PyType_Slot ApgRecordDesc_TypeSlots[] = {\n#ifdef Py_tp_vectorcall\n    {Py_tp_vectorcall, (vectorcallfunc)record_desc_vectorcall},\n#endif\n    {Py_tp_new, (newfunc)record_desc_new},\n    {Py_tp_dealloc, (destructor)record_desc_dealloc},\n    {Py_tp_getattro, PyObject_GenericGetAttr},\n    {Py_tp_traverse, (traverseproc)record_desc_traverse},\n    {Py_tp_methods, record_desc_methods},\n    {0, NULL},\n};\n\nstatic PyType_Spec ApgRecordDesc_TypeSpec = {\n    .name = \"asyncpg.protocol.record.RecordDescriptor\",\n    .basicsize = sizeof(ApgRecordDescObject),\n    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE,\n    .slots = ApgRecordDesc_TypeSlots,\n};\n\n/*\n * Module init\n */\n\nstatic PyMethodDef record_module_methods[] = {{NULL, NULL, 0, NULL}};\n\nstatic int\nrecord_module_exec(PyObject *module)\n{\n    record_module_state *state = get_module_state(module);\n    if (state == NULL) {\n        return -1;\n    }\n\n    if (PyThread_tss_create(&state->freelist_key) != 0) {\n        PyErr_SetString(\n            PyExc_SystemError,\n            \"failed to create TSS key for record freelist\");\n        return -1;\n    }\n\n#define CREATE_TYPE(m, tp, spec)                                      \\\n    do {                                                              \\\n        tp = (PyTypeObject *)PyType_FromModuleAndSpec(m, spec, NULL); \\\n        if (tp == NULL)                                               \\\n            goto error;                                               \\\n        if (PyModule_AddType(m, tp) < 0)                              \\\n            goto error;                                               \\\n    } while (0)\n\n    CREATE_TYPE(module, state->ApgRecord_Type, &ApgRecord_TypeSpec);\n    CREATE_TYPE(module, state->ApgRecordDesc_Type, &ApgRecordDesc_TypeSpec);\n    CREATE_TYPE(module, state->ApgRecordIter_Type, &ApgRecordIter_TypeSpec);\n    CREATE_TYPE(module, state->ApgRecordItems_Type, &ApgRecordItems_TypeSpec);\n\n#undef CREATE_TYPE\n\n    return 0;\n\nerror:\n    Py_CLEAR(state->ApgRecord_Type);\n    Py_CLEAR(state->ApgRecordDesc_Type);\n    Py_CLEAR(state->ApgRecordIter_Type);\n    Py_CLEAR(state->ApgRecordItems_Type);\n    return -1;\n}\n\nstatic int\nrecord_module_traverse(PyObject *module, visitproc visit, void *arg)\n{\n    record_module_state *state = get_module_state(module);\n    if (state == NULL) {\n        return 0;\n    }\n\n    Py_VISIT(state->ApgRecord_Type);\n    Py_VISIT(state->ApgRecordDesc_Type);\n    Py_VISIT(state->ApgRecordIter_Type);\n    Py_VISIT(state->ApgRecordItems_Type);\n\n    return 0;\n}\n\nstatic int\nrecord_module_clear(PyObject *module)\n{\n    record_module_state *state = get_module_state(module);\n    if (state == NULL) {\n        return 0;\n    }\n\n    if (PyThread_tss_is_created(&state->freelist_key)) {\n        record_freelist_state *freelist =\n            (record_freelist_state *)PyThread_tss_get(&state->freelist_key);\n        if (freelist != NULL) {\n            for (int i = 0; i < ApgRecord_MAXSAVESIZE; i++) {\n                ApgRecordObject *op = freelist->freelist[i];\n                while (op != NULL) {\n                    ApgRecordObject *next = (ApgRecordObject *)(op->ob_item[0]);\n                    PyObject_GC_Del(op);\n                    op = next;\n                }\n                freelist->freelist[i] = NULL;\n                freelist->numfree[i] = 0;\n            }\n            PyMem_Free(freelist);\n            PyThread_tss_set(&state->freelist_key, NULL);\n        }\n\n        PyThread_tss_delete(&state->freelist_key);\n    }\n\n    Py_CLEAR(state->ApgRecord_Type);\n    Py_CLEAR(state->ApgRecordDesc_Type);\n    Py_CLEAR(state->ApgRecordIter_Type);\n    Py_CLEAR(state->ApgRecordItems_Type);\n\n    return 0;\n}\n\nstatic void\nrecord_module_free(void *module)\n{\n    record_module_clear((PyObject *)module);\n}\n\nstatic PyModuleDef_Slot record_module_slots[] = {\n    {Py_mod_exec, record_module_exec},\n#ifdef Py_mod_multiple_interpreters\n    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},\n#endif\n#ifdef Py_mod_gil\n    {Py_mod_gil, Py_MOD_GIL_NOT_USED},\n#endif\n    {0, NULL},\n};\n\nstatic struct PyModuleDef _recordmodule = {\n    PyModuleDef_HEAD_INIT,\n    .m_name = \"asyncpg.protocol.record\",\n    .m_size = sizeof(record_module_state),\n    .m_methods = record_module_methods,\n    .m_slots = record_module_slots,\n    .m_traverse = record_module_traverse,\n    .m_clear = record_module_clear,\n    .m_free = record_module_free,\n};\n\nPyMODINIT_FUNC\nPyInit_record(void)\n{\n    return PyModuleDef_Init(&_recordmodule);\n}\n"
  },
  {
    "path": "asyncpg/protocol/record/recordobj.h",
    "content": "#ifndef APG_RECORDOBJ_H\n#define APG_RECORDOBJ_H\n\n#include <Python.h>\n\n\ntypedef struct {\n    PyObject_HEAD\n    PyObject *mapping;\n    PyObject *keys;\n} ApgRecordDescObject;\n\n\ntypedef struct {\n    PyObject_VAR_HEAD\n    Py_hash_t self_hash;\n    ApgRecordDescObject *desc;\n    PyObject *ob_item[1];\n\n    /* ob_item contains space for 'ob_size' elements.\n     * Items must normally not be NULL, except during construction when\n     * the record is not yet visible outside the function that builds it.\n     */\n} ApgRecordObject;\n\n\n#define ApgRecord_SET_ITEM(op, i, v) \\\n\t\t\t(((ApgRecordObject *)(op))->ob_item[i] = v)\n\n#define ApgRecord_GET_ITEM(op, i) \\\n\t\t\t(((ApgRecordObject *)(op))->ob_item[i])\n\n#endif\n"
  },
  {
    "path": "asyncpg/protocol/record.pyi",
    "content": "from typing import (\n    Any,\n    TypeVar,\n    overload,\n)\n\nfrom collections.abc import Iterator\n\n\n_T = TypeVar(\"_T\")\n\n\nclass Record:\n    @overload\n    def get(self, key: str) -> Any | None: ...\n    @overload\n    def get(self, key: str, default: _T) -> Any | _T: ...\n    def items(self) -> Iterator[tuple[str, Any]]: ...\n    def keys(self) -> Iterator[str]: ...\n    def values(self) -> Iterator[Any]: ...\n    @overload\n    def __getitem__(self, index: str) -> Any: ...\n    @overload\n    def __getitem__(self, index: int) -> Any: ...\n    @overload\n    def __getitem__(self, index: slice) -> tuple[Any, ...]: ...\n    def __iter__(self) -> Iterator[Any]: ...\n    def __contains__(self, x: object) -> bool: ...\n    def __len__(self) -> int: ...\n"
  },
  {
    "path": "asyncpg/protocol/recordcapi.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncimport cpython\n\n\ncdef extern from \"record/recordobj.h\":\n\n\tvoid ApgRecord_SET_ITEM(object, int, object)\n\tobject RecordDescriptor(object, object)\n"
  },
  {
    "path": "asyncpg/protocol/scram.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncdef class SCRAMAuthentication:\n    cdef:\n        readonly bytes authentication_method\n        readonly bytes authorization_message\n        readonly bytes client_channel_binding\n        readonly bytes client_first_message_bare\n        readonly bytes client_nonce\n        readonly bytes client_proof\n        readonly bytes password_salt\n        readonly int   password_iterations\n        readonly bytes server_first_message\n        # server_key is an instance of hmac.HAMC\n        readonly object server_key\n        readonly bytes server_nonce\n\n    cdef create_client_first_message(self, str username)\n    cdef create_client_final_message(self, str password)\n    cdef parse_server_first_message(self, bytes server_response)\n    cdef verify_server_final_message(self, bytes server_final_message)\n    cdef _bytes_xor(self, bytes a, bytes b)\n    cdef _generate_client_nonce(self, int num_bytes)\n    cdef _generate_client_proof(self, str password)\n    cdef _generate_salted_password(self, str password, bytes salt, int iterations)\n    cdef _normalize_password(self, str original_password)\n"
  },
  {
    "path": "asyncpg/protocol/scram.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport base64\nimport hashlib\nimport hmac\nimport re\nimport secrets\nimport stringprep\nimport unicodedata\n\n\n@cython.final\ncdef class SCRAMAuthentication:\n    \"\"\"Contains the protocol for generating and a SCRAM hashed password.\n\n    Since PostgreSQL 10, the option to hash passwords using the SCRAM-SHA-256\n    method was added. This module follows the defined protocol, which can be\n    referenced from here:\n\n    https://www.postgresql.org/docs/current/sasl-authentication.html#SASL-SCRAM-SHA-256\n\n    libpq references the following RFCs that it uses for implementation:\n\n        * RFC 5802\n        * RFC 5803\n        * RFC 7677\n\n    The protocol works as such:\n\n    - A client connets to the server. The server requests the client to begin\n    SASL authentication using SCRAM and presents a client with the methods it\n    supports. At present, those are SCRAM-SHA-256, and, on servers that are\n    built with OpenSSL and\n    are PG11+, SCRAM-SHA-256-PLUS (which supports channel binding, more on that\n    below)\n\n    - The client sends a \"first message\" to the server, where it chooses which\n    method to authenticate with, and sends, along with the method, an indication\n    of channel binding (we disable for now), a nonce, and the username.\n    (Technically, PostgreSQL ignores the username as it already has it from the\n    initical connection, but we add it for completeness)\n\n    - The server responds with a \"first message\" in which it extends the nonce,\n    as well as a password salt and the number of iterations to hash the password\n    with. The client validates that the new nonce contains the first part of the\n    client's original nonce\n\n    - The client generates a salted password, but does not sent this up to the\n    server. Instead, the client follows the SCRAM algorithm (RFC5802) to\n    generate a proof. This proof is sent aspart of a client \"final message\" to\n    the server for it to validate.\n\n    - The server validates the proof. If it is valid, the server sends a\n    verification code for the client to verify that the server came to the same\n    proof the client did. PostgreSQL immediately sends an AuthenticationOK\n    response right after a valid negotiation. If the password the client\n    provided was invalid, then authentication fails.\n\n    (The beauty of this is that the salted password is never transmitted over\n    the wire!)\n\n    PostgreSQL 11 added support for the channel binding (i.e.\n    SCRAM-SHA-256-PLUS) but to do some ongoing discussion, there is a conscious\n    decision by several driver authors to not support it as of yet. As such, the\n    channel binding parameter is hard-coded to \"n\" for now, but can be updated\n    to support other channel binding methos in the future\n    \"\"\"\n    AUTHENTICATION_METHODS = [b\"SCRAM-SHA-256\"]\n    DEFAULT_CLIENT_NONCE_BYTES = 24\n    DIGEST = hashlib.sha256\n    REQUIREMENTS_CLIENT_FINAL_MESSAGE = ['client_channel_binding',\n        'server_nonce']\n    REQUIREMENTS_CLIENT_PROOF = ['password_iterations', 'password_salt',\n        'server_first_message', 'server_nonce']\n    SASLPREP_PROHIBITED = (\n        stringprep.in_table_a1, # PostgreSQL treats this as prohibited\n        stringprep.in_table_c12,\n        stringprep.in_table_c21_c22,\n        stringprep.in_table_c3,\n        stringprep.in_table_c4,\n        stringprep.in_table_c5,\n        stringprep.in_table_c6,\n        stringprep.in_table_c7,\n        stringprep.in_table_c8,\n        stringprep.in_table_c9,\n    )\n\n    def __cinit__(self, bytes authentication_method):\n        self.authentication_method = authentication_method\n        self.authorization_message = None\n        # channel binding is turned off for the time being\n        self.client_channel_binding = b\"n,,\"\n        self.client_first_message_bare = None\n        self.client_nonce = None\n        self.client_proof = None\n        self.password_salt = None\n        # self.password_iterations = None\n        self.server_first_message = None\n        self.server_key = None\n        self.server_nonce = None\n\n    cdef create_client_first_message(self, str username):\n        \"\"\"Create the initial client message for SCRAM authentication\"\"\"\n        cdef:\n            bytes msg\n            bytes client_first_message\n\n        self.client_nonce = \\\n            self._generate_client_nonce(self.DEFAULT_CLIENT_NONCE_BYTES)\n        # set the client first message bare here, as it's used in a later step\n        self.client_first_message_bare =  b\"n=\" + username.encode(\"utf-8\") + \\\n            b\",r=\" + self.client_nonce\n        # put together the full message here\n        msg = bytes()\n        msg += self.authentication_method + b\"\\0\"\n        client_first_message = self.client_channel_binding + \\\n            self.client_first_message_bare\n        msg += (len(client_first_message)).to_bytes(4, byteorder='big') + \\\n            client_first_message\n        return msg\n\n    cdef create_client_final_message(self, str password):\n        \"\"\"Create the final client message as part of SCRAM authentication\"\"\"\n        cdef:\n            bytes msg\n\n        if any([getattr(self, val) is None for val in\n                self.REQUIREMENTS_CLIENT_FINAL_MESSAGE]):\n            raise Exception(\n                \"you need values from server to generate a client proof\")\n\n        # normalize the password using the SASLprep algorithm in RFC 4013\n        password = self._normalize_password(password)\n\n        # generate the client proof\n        self.client_proof = self._generate_client_proof(password=password)\n        msg = bytes()\n        msg += b\"c=\" + base64.b64encode(self.client_channel_binding) + \\\n            b\",r=\" + self.server_nonce + \\\n            b\",p=\" + base64.b64encode(self.client_proof)\n        return msg\n\n    cdef parse_server_first_message(self, bytes server_response):\n        \"\"\"Parse the response from the first message from the server\"\"\"\n        self.server_first_message = server_response\n        try:\n            self.server_nonce = re.search(b'r=([^,]+),',\n                self.server_first_message).group(1)\n        except IndexError:\n            raise Exception(\"could not get nonce\")\n        if not self.server_nonce.startswith(self.client_nonce):\n            raise Exception(\"invalid nonce\")\n        try:\n            self.password_salt = re.search(b',s=([^,]+),',\n                self.server_first_message).group(1)\n        except IndexError:\n            raise Exception(\"could not get salt\")\n        try:\n            self.password_iterations = int(re.search(b',i=(\\d+),?',\n                self.server_first_message).group(1))\n        except (IndexError, TypeError, ValueError):\n            raise Exception(\"could not get iterations\")\n\n    cdef verify_server_final_message(self, bytes server_final_message):\n        \"\"\"Verify the final message from the server\"\"\"\n        cdef:\n            bytes server_signature\n\n        try:\n            server_signature = re.search(b'v=([^,]+)',\n                server_final_message).group(1)\n        except IndexError:\n            raise Exception(\"could not get server signature\")\n\n        verify_server_signature = hmac.new(self.server_key.digest(),\n            self.authorization_message, self.DIGEST)\n        # validate the server signature against the verifier\n        return server_signature == base64.b64encode(\n            verify_server_signature.digest())\n\n    cdef _bytes_xor(self, bytes a, bytes b):\n        \"\"\"XOR two bytestrings together\"\"\"\n        return bytes(a_i ^ b_i for a_i, b_i in zip(a, b))\n\n    cdef _generate_client_nonce(self, int num_bytes):\n        cdef:\n            bytes token\n\n        token = secrets.token_bytes(num_bytes)\n\n        return base64.b64encode(token)\n\n    cdef _generate_client_proof(self, str password):\n        \"\"\"need to ensure a server response exists, i.e. \"\"\"\n        cdef:\n            bytes salted_password\n\n        if any([getattr(self, val) is None for val in\n                self.REQUIREMENTS_CLIENT_PROOF]):\n            raise Exception(\n                \"you need values from server to generate a client proof\")\n        # generate a salt password\n        salted_password = self._generate_salted_password(password,\n            self.password_salt, self.password_iterations)\n        # client key is derived from the salted password\n        client_key = hmac.new(salted_password, b\"Client Key\", self.DIGEST)\n        # this allows us to compute the stored key that is residing on the server\n        stored_key = self.DIGEST(client_key.digest())\n        # as well as compute the server key\n        self.server_key = hmac.new(salted_password, b\"Server Key\", self.DIGEST)\n        # build the authorization message that will be used in the\n        # client signature\n        # the \"c=\" portion is for the channel binding, but this is not\n        # presently implemented\n        self.authorization_message = self.client_first_message_bare + b\",\" + \\\n            self.server_first_message + b\",c=\" + \\\n            base64.b64encode(self.client_channel_binding) + \\\n            b\",r=\" +  self.server_nonce\n        # sign!\n        client_signature = hmac.new(stored_key.digest(),\n            self.authorization_message, self.DIGEST)\n        # and the proof\n        return self._bytes_xor(client_key.digest(), client_signature.digest())\n\n    cdef _generate_salted_password(self, str password, bytes salt, int iterations):\n        \"\"\"This follows the \"Hi\" algorithm specified in RFC5802\"\"\"\n        cdef:\n            bytes p\n            bytes s\n            bytes u\n\n        # convert the password to a binary string - UTF8 is safe for SASL\n        # (though there are SASLPrep rules)\n        p = password.encode(\"utf8\")\n        # the salt needs to be base64 decoded -- full binary must be used\n        s = base64.b64decode(salt)\n        # the initial signature is the salt with a terminator of a 32-bit string\n        # ending in 1\n        ui = hmac.new(p, s + b'\\x00\\x00\\x00\\x01', self.DIGEST)\n        # grab the initial digest\n        u = ui.digest()\n        # for X number of iterations, recompute the HMAC signature against the\n        # password and the latest iteration of the hash, and XOR it with the\n        # previous version\n        for x in range(iterations - 1):\n            ui = hmac.new(p, ui.digest(), hashlib.sha256)\n            # this is a fancy way of XORing two byte strings together\n            u = self._bytes_xor(u, ui.digest())\n        return u\n\n    cdef _normalize_password(self, str original_password):\n        \"\"\"Normalize the password using the SASLprep from RFC4013\"\"\"\n        cdef:\n            str normalized_password\n\n        # Note: Per the PostgreSQL documentation, PostgreSWL does not require\n        # UTF-8 to be used for the password, but will perform SASLprep on the\n        # password regardless.\n        # If the password is not valid UTF-8, PostgreSQL will then **not** use\n        # SASLprep processing.\n        # If the password fails SASLprep, the password should still be sent\n        # See: https://www.postgresql.org/docs/current/sasl-authentication.html\n        # and\n        # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/common/saslprep.c\n        # using the `pg_saslprep` function\n        normalized_password = original_password\n        # if the original password is an ASCII string or fails to encode as a\n        # UTF-8 string, then no further action is needed\n        try:\n            original_password.encode(\"ascii\")\n        except UnicodeEncodeError:\n            pass\n        else:\n            return original_password\n\n        # Step 1 of SASLPrep: Map. Per the algorithm, we map non-ascii space\n        # characters to ASCII spaces (\\x20 or \\u0020, but we will use ' ') and\n        # commonly mapped to nothing characters are removed\n        # Table C.1.2 -- non-ASCII spaces\n        # Table B.1 -- \"Commonly mapped to nothing\"\n        normalized_password = u\"\".join(\n            ' ' if stringprep.in_table_c12(c) else c\n            for c in tuple(normalized_password) if not stringprep.in_table_b1(c)\n        )\n\n        # If at this point the password is empty, PostgreSQL uses the original\n        # password\n        if not normalized_password:\n            return original_password\n\n        # Step 2 of SASLPrep: Normalize. Normalize the password using the\n        # Unicode normalization algorithm to NFKC form\n        normalized_password = unicodedata.normalize('NFKC', normalized_password)\n\n        # If the password is not empty, PostgreSQL uses the original password\n        if not normalized_password:\n            return original_password\n\n        normalized_password_tuple = tuple(normalized_password)\n\n        # Step 3 of SASLPrep: Prohobited characters. If PostgreSQL detects any\n        # of the prohibited characters in SASLPrep, it will use the original\n        # password\n        # We also include \"unassigned code points\" in the prohibited character\n        # category as PostgreSQL does the same\n        for c in normalized_password_tuple:\n            if any(\n                in_prohibited_table(c)\n                for in_prohibited_table in self.SASLPREP_PROHIBITED\n            ):\n                return original_password\n\n        # Step 4 of SASLPrep: Bi-directional characters. PostgreSQL follows the\n        # rules for bi-directional characters laid on in RFC3454 Sec. 6 which\n        # are:\n        # 1. Characters in RFC 3454 Sec 5.8 are prohibited (C.8)\n        # 2. If a string contains a RandALCat character, it cannot containy any\n        #    LCat character\n        # 3. If the string contains any RandALCat character, an RandALCat\n        #    character must be the first and last character of the string\n        # RandALCat characters are found in table D.1, whereas LCat are in D.2\n        if any(stringprep.in_table_d1(c) for c in normalized_password_tuple):\n            # if the first character or the last character are not in D.1,\n            # return the original password\n            if not (stringprep.in_table_d1(normalized_password_tuple[0]) and\n                    stringprep.in_table_d1(normalized_password_tuple[-1])):\n                return original_password\n\n            # if any characters are in D.2, use the original password\n            if any(\n                stringprep.in_table_d2(c) for c in normalized_password_tuple\n            ):\n                return original_password\n\n        # return the normalized password\n        return normalized_password\n"
  },
  {
    "path": "asyncpg/protocol/settings.pxd",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\ncdef class ConnectionSettings(pgproto.CodecContext):\n    cdef:\n        str _encoding\n        object _codec\n        dict _settings\n        bint _is_utf8\n        DataCodecConfig _data_codecs\n\n    cdef add_setting(self, str name, str val)\n    cdef is_encoding_utf8(self)\n    cpdef get_text_codec(self)\n    cpdef inline register_data_types(self, types)\n    cpdef inline add_python_codec(\n        self, typeoid, typename, typeschema, typeinfos, typekind, encoder,\n        decoder, format)\n    cpdef inline remove_python_codec(\n        self, typeoid, typename, typeschema)\n    cpdef inline clear_type_cache(self)\n    cpdef inline set_builtin_type_codec(\n        self, typeoid, typename, typeschema, typekind, alias_to, format)\n    cpdef inline Codec get_data_codec(\n        self, uint32_t oid, ServerDataFormat format=*,\n        bint ignore_custom_codec=*)\n"
  },
  {
    "path": "asyncpg/protocol/settings.pyx",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nfrom asyncpg import exceptions\n\n\n@cython.final\ncdef class ConnectionSettings(pgproto.CodecContext):\n\n    def __cinit__(self):\n        self._encoding = 'utf-8'\n        self._is_utf8 = True\n        self._settings = {}\n        self._codec = codecs.lookup('utf-8')\n        self._data_codecs = DataCodecConfig()\n\n    cdef add_setting(self, str name, str val):\n        self._settings[name] = val\n        if name == 'client_encoding':\n            py_enc = get_python_encoding(val)\n            self._codec = codecs.lookup(py_enc)\n            self._encoding = self._codec.name\n            self._is_utf8 = self._encoding == 'utf-8'\n\n    cdef is_encoding_utf8(self):\n        return self._is_utf8\n\n    cpdef get_text_codec(self):\n        return self._codec\n\n    cpdef inline register_data_types(self, types):\n        self._data_codecs.add_types(types)\n\n    cpdef inline add_python_codec(self, typeoid, typename, typeschema,\n                                  typeinfos, typekind, encoder, decoder,\n                                  format):\n        cdef:\n            ServerDataFormat _format\n            ClientExchangeFormat xformat\n\n        if format == 'binary':\n            _format = PG_FORMAT_BINARY\n            xformat = PG_XFORMAT_OBJECT\n        elif format == 'text':\n            _format = PG_FORMAT_TEXT\n            xformat = PG_XFORMAT_OBJECT\n        elif format == 'tuple':\n            _format = PG_FORMAT_ANY\n            xformat = PG_XFORMAT_TUPLE\n        else:\n            raise exceptions.InterfaceError(\n                'invalid `format` argument, expected {}, got {!r}'.format(\n                    \"'text', 'binary' or 'tuple'\", format\n                ))\n\n        self._data_codecs.add_python_codec(typeoid, typename, typeschema,\n                                           typekind, typeinfos,\n                                           encoder, decoder,\n                                           _format, xformat)\n\n    cpdef inline remove_python_codec(self, typeoid, typename, typeschema):\n        self._data_codecs.remove_python_codec(typeoid, typename, typeschema)\n\n    cpdef inline clear_type_cache(self):\n        self._data_codecs.clear_type_cache()\n\n    cpdef inline set_builtin_type_codec(self, typeoid, typename, typeschema,\n                                        typekind, alias_to, format):\n        cdef:\n            ServerDataFormat _format\n\n        if format is None:\n            _format = PG_FORMAT_ANY\n        elif format == 'binary':\n            _format = PG_FORMAT_BINARY\n        elif format == 'text':\n            _format = PG_FORMAT_TEXT\n        else:\n            raise exceptions.InterfaceError(\n                'invalid `format` argument, expected {}, got {!r}'.format(\n                    \"'text' or 'binary'\", format\n                ))\n\n        self._data_codecs.set_builtin_type_codec(typeoid, typename, typeschema,\n                                                 typekind, alias_to, _format)\n\n    cpdef inline Codec get_data_codec(self, uint32_t oid,\n                                      ServerDataFormat format=PG_FORMAT_ANY,\n                                      bint ignore_custom_codec=False):\n        return self._data_codecs.get_codec(oid, format, ignore_custom_codec)\n\n    def __getattr__(self, name):\n        if not name.startswith('_'):\n            try:\n                return self._settings[name]\n            except KeyError:\n                raise AttributeError(name) from None\n\n        return object.__getattribute__(self, name)\n\n    def __repr__(self):\n        return '<ConnectionSettings {!r}>'.format(self._settings)\n"
  },
  {
    "path": "asyncpg/serverversion.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nimport re\nimport typing\n\nfrom .types import ServerVersion\n\nversion_regex: typing.Final = re.compile(\n    r\"(Postgre[^\\s]*)?\\s*\"\n    r\"(?P<major>[0-9]+)\\.?\"\n    r\"((?P<minor>[0-9]+)\\.?)?\"\n    r\"(?P<micro>[0-9]+)?\"\n    r\"(?P<releaselevel>[a-z]+)?\"\n    r\"(?P<serial>[0-9]+)?\"\n)\n\n\nclass _VersionDict(typing.TypedDict):\n    major: int\n    minor: int | None\n    micro: int | None\n    releaselevel: str | None\n    serial: int | None\n\n\ndef split_server_version_string(version_string: str) -> ServerVersion:\n    version_match = version_regex.search(version_string)\n\n    if version_match is None:\n        raise ValueError(\n            \"Unable to parse Postgres \"\n            f'version from \"{version_string}\"'\n        )\n\n    version: _VersionDict = version_match.groupdict()  # type: ignore[assignment]  # noqa: E501\n    for ver_key, ver_value in version.items():\n        # Cast all possible versions parts to int\n        try:\n            version[ver_key] = int(ver_value)  # type: ignore[literal-required, call-overload]  # noqa: E501\n        except (TypeError, ValueError):\n            pass\n\n    if version[\"major\"] < 10:\n        return ServerVersion(\n            version[\"major\"],\n            version.get(\"minor\") or 0,\n            version.get(\"micro\") or 0,\n            version.get(\"releaselevel\") or \"final\",\n            version.get(\"serial\") or 0,\n        )\n\n    # Since PostgreSQL 10 the versioning scheme has changed.\n    # 10.x really means 10.0.x.  While parsing 10.1\n    # as (10, 1) may seem less confusing, in practice most\n    # version checks are written as version[:2], and we\n    # want to keep that behaviour consistent, i.e not fail\n    # a major version check due to a bugfix release.\n    return ServerVersion(\n        version[\"major\"],\n        0,\n        version.get(\"minor\") or 0,\n        version.get(\"releaselevel\") or \"final\",\n        version.get(\"serial\") or 0,\n    )\n"
  },
  {
    "path": "asyncpg/transaction.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport enum\n\nfrom . import connresource\nfrom . import exceptions as apg_errors\n\n\nclass TransactionState(enum.Enum):\n    NEW = 0\n    STARTED = 1\n    COMMITTED = 2\n    ROLLEDBACK = 3\n    FAILED = 4\n\n\nISOLATION_LEVELS = {\n    'read_committed',\n    'read_uncommitted',\n    'serializable',\n    'repeatable_read',\n}\nISOLATION_LEVELS_BY_VALUE = {\n    'read committed': 'read_committed',\n    'read uncommitted': 'read_uncommitted',\n    'serializable': 'serializable',\n    'repeatable read': 'repeatable_read',\n}\n\n\nclass Transaction(connresource.ConnectionResource):\n    \"\"\"Represents a transaction or savepoint block.\n\n    Transactions are created by calling the\n    :meth:`Connection.transaction() <connection.Connection.transaction>`\n    function.\n    \"\"\"\n\n    __slots__ = ('_connection', '_isolation', '_readonly', '_deferrable',\n                 '_state', '_nested', '_id', '_managed')\n\n    def __init__(self, connection, isolation, readonly, deferrable):\n        super().__init__(connection)\n\n        if isolation and isolation not in ISOLATION_LEVELS:\n            raise ValueError(\n                'isolation is expected to be either of {}, '\n                'got {!r}'.format(ISOLATION_LEVELS, isolation))\n\n        self._isolation = isolation\n        self._readonly = readonly\n        self._deferrable = deferrable\n        self._state = TransactionState.NEW\n        self._nested = False\n        self._id = None\n        self._managed = False\n\n    async def __aenter__(self):\n        if self._managed:\n            raise apg_errors.InterfaceError(\n                'cannot enter context: already in an `async with` block')\n        self._managed = True\n        await self.start()\n\n    async def __aexit__(self, extype, ex, tb):\n        try:\n            self._check_conn_validity('__aexit__')\n        except apg_errors.InterfaceError:\n            if extype is GeneratorExit:\n                # When a PoolAcquireContext is being exited, and there\n                # is an open transaction in an async generator that has\n                # not been iterated fully, there is a possibility that\n                # Pool.release() would race with this __aexit__(), since\n                # both would be in concurrent tasks.  In such case we\n                # yield to Pool.release() to do the ROLLBACK for us.\n                # See https://github.com/MagicStack/asyncpg/issues/232\n                # for an example.\n                return\n            else:\n                raise\n\n        try:\n            if extype is not None:\n                await self.__rollback()\n            else:\n                await self.__commit()\n        finally:\n            self._managed = False\n\n    @connresource.guarded\n    async def start(self):\n        \"\"\"Enter the transaction or savepoint block.\"\"\"\n        self.__check_state_base('start')\n        if self._state is TransactionState.STARTED:\n            raise apg_errors.InterfaceError(\n                'cannot start; the transaction is already started')\n\n        con = self._connection\n\n        if con._top_xact is None:\n            if con._protocol.is_in_transaction():\n                raise apg_errors.InterfaceError(\n                    'cannot use Connection.transaction() in '\n                    'a manually started transaction')\n            con._top_xact = self\n        else:\n            # Nested transaction block\n            if self._isolation:\n                top_xact_isolation = con._top_xact._isolation\n                if top_xact_isolation is None:\n                    top_xact_isolation = ISOLATION_LEVELS_BY_VALUE[\n                        await self._connection.fetchval(\n                            'SHOW transaction_isolation;')]\n                if self._isolation != top_xact_isolation:\n                    raise apg_errors.InterfaceError(\n                        'nested transaction has a different isolation level: '\n                        'current {!r} != outer {!r}'.format(\n                            self._isolation, top_xact_isolation))\n            self._nested = True\n\n        if self._nested:\n            self._id = con._get_unique_id('savepoint')\n            query = 'SAVEPOINT {};'.format(self._id)\n        else:\n            query = 'BEGIN'\n            if self._isolation == 'read_committed':\n                query += ' ISOLATION LEVEL READ COMMITTED'\n            elif self._isolation == 'read_uncommitted':\n                query += ' ISOLATION LEVEL READ UNCOMMITTED'\n            elif self._isolation == 'repeatable_read':\n                query += ' ISOLATION LEVEL REPEATABLE READ'\n            elif self._isolation == 'serializable':\n                query += ' ISOLATION LEVEL SERIALIZABLE'\n            if self._readonly:\n                query += ' READ ONLY'\n            if self._deferrable:\n                query += ' DEFERRABLE'\n            query += ';'\n\n        try:\n            await self._connection.execute(query)\n        except BaseException:\n            self._state = TransactionState.FAILED\n            raise\n        else:\n            self._state = TransactionState.STARTED\n\n    def __check_state_base(self, opname):\n        if self._state is TransactionState.COMMITTED:\n            raise apg_errors.InterfaceError(\n                'cannot {}; the transaction is already committed'.format(\n                    opname))\n        if self._state is TransactionState.ROLLEDBACK:\n            raise apg_errors.InterfaceError(\n                'cannot {}; the transaction is already rolled back'.format(\n                    opname))\n        if self._state is TransactionState.FAILED:\n            raise apg_errors.InterfaceError(\n                'cannot {}; the transaction is in error state'.format(\n                    opname))\n\n    def __check_state(self, opname):\n        if self._state is not TransactionState.STARTED:\n            if self._state is TransactionState.NEW:\n                raise apg_errors.InterfaceError(\n                    'cannot {}; the transaction is not yet started'.format(\n                        opname))\n            self.__check_state_base(opname)\n\n    async def __commit(self):\n        self.__check_state('commit')\n\n        if self._connection._top_xact is self:\n            self._connection._top_xact = None\n\n        if self._nested:\n            query = 'RELEASE SAVEPOINT {};'.format(self._id)\n        else:\n            query = 'COMMIT;'\n\n        try:\n            await self._connection.execute(query)\n        except BaseException:\n            self._state = TransactionState.FAILED\n            raise\n        else:\n            self._state = TransactionState.COMMITTED\n\n    async def __rollback(self):\n        self.__check_state('rollback')\n\n        if self._connection._top_xact is self:\n            self._connection._top_xact = None\n\n        if self._nested:\n            query = 'ROLLBACK TO {};'.format(self._id)\n        else:\n            query = 'ROLLBACK;'\n\n        try:\n            await self._connection.execute(query)\n        except BaseException:\n            self._state = TransactionState.FAILED\n            raise\n        else:\n            self._state = TransactionState.ROLLEDBACK\n\n    @connresource.guarded\n    async def commit(self):\n        \"\"\"Exit the transaction or savepoint block and commit changes.\"\"\"\n        if self._managed:\n            raise apg_errors.InterfaceError(\n                'cannot manually commit from within an `async with` block')\n        await self.__commit()\n\n    @connresource.guarded\n    async def rollback(self):\n        \"\"\"Exit the transaction or savepoint block and rollback changes.\"\"\"\n        if self._managed:\n            raise apg_errors.InterfaceError(\n                'cannot manually rollback from within an `async with` block')\n        await self.__rollback()\n\n    def __repr__(self):\n        attrs = []\n        attrs.append('state:{}'.format(self._state.name.lower()))\n\n        if self._isolation is not None:\n            attrs.append(self._isolation)\n        if self._readonly:\n            attrs.append('readonly')\n        if self._deferrable:\n            attrs.append('deferrable')\n\n        if self.__class__.__module__.startswith('asyncpg.'):\n            mod = 'asyncpg'\n        else:\n            mod = self.__class__.__module__\n\n        return '<{}.{} {} {:#x}>'.format(\n            mod, self.__class__.__name__, ' '.join(attrs), id(self))\n"
  },
  {
    "path": "asyncpg/types.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom __future__ import annotations\n\nimport typing\n\nfrom asyncpg.pgproto.types import (\n    BitString, Point, Path, Polygon,\n    Box, Line, LineSegment, Circle,\n)\n\nif typing.TYPE_CHECKING:\n    from typing_extensions import Self\n\n\n__all__ = (\n    'Type', 'Attribute', 'Range', 'BitString', 'Point', 'Path', 'Polygon',\n    'Box', 'Line', 'LineSegment', 'Circle', 'ServerVersion',\n)\n\n\nclass Type(typing.NamedTuple):\n    oid: int\n    name: str\n    kind: str\n    schema: str\n\n\nType.__doc__ = 'Database data type.'\nType.oid.__doc__ = 'OID of the type.'\nType.name.__doc__ = 'Type name.  For example \"int2\".'\nType.kind.__doc__ = \\\n    'Type kind.  Can be \"scalar\", \"array\", \"composite\" or \"range\".'\nType.schema.__doc__ = 'Name of the database schema that defines the type.'\n\n\nclass Attribute(typing.NamedTuple):\n    name: str\n    type: Type\n\n\nAttribute.__doc__ = 'Database relation attribute.'\nAttribute.name.__doc__ = 'Attribute name.'\nAttribute.type.__doc__ = 'Attribute data type :class:`asyncpg.types.Type`.'\n\n\nclass ServerVersion(typing.NamedTuple):\n    major: int\n    minor: int\n    micro: int\n    releaselevel: str\n    serial: int\n\n\nServerVersion.__doc__ = 'PostgreSQL server version tuple.'\n\n\nclass _RangeValue(typing.Protocol):\n    def __eq__(self, __value: object) -> bool:\n        ...\n\n    def __lt__(self, __other: Self, /) -> bool:\n        ...\n\n    def __gt__(self, __other: Self, /) -> bool:\n        ...\n\n\n_RV = typing.TypeVar('_RV', bound=_RangeValue)\n\n\nclass Range(typing.Generic[_RV]):\n    \"\"\"Immutable representation of PostgreSQL `range` type.\"\"\"\n\n    __slots__ = ('_lower', '_upper', '_lower_inc', '_upper_inc', '_empty')\n\n    _lower: _RV | None\n    _upper: _RV | None\n    _lower_inc: bool\n    _upper_inc: bool\n    _empty: bool\n\n    def __init__(\n        self,\n        lower: _RV | None = None,\n        upper: _RV | None = None,\n        *,\n        lower_inc: bool = True,\n        upper_inc: bool = False,\n        empty: bool = False\n    ) -> None:\n        self._empty = empty\n        if empty:\n            self._lower = self._upper = None\n            self._lower_inc = self._upper_inc = False\n        else:\n            self._lower = lower\n            self._upper = upper\n            self._lower_inc = lower is not None and lower_inc\n            self._upper_inc = upper is not None and upper_inc\n\n    @property\n    def lower(self) -> _RV | None:\n        return self._lower\n\n    @property\n    def lower_inc(self) -> bool:\n        return self._lower_inc\n\n    @property\n    def lower_inf(self) -> bool:\n        return self._lower is None and not self._empty\n\n    @property\n    def upper(self) -> _RV | None:\n        return self._upper\n\n    @property\n    def upper_inc(self) -> bool:\n        return self._upper_inc\n\n    @property\n    def upper_inf(self) -> bool:\n        return self._upper is None and not self._empty\n\n    @property\n    def isempty(self) -> bool:\n        return self._empty\n\n    def _issubset_lower(self, other: Self) -> bool:\n        if other._lower is None:\n            return True\n        if self._lower is None:\n            return False\n\n        return self._lower > other._lower or (\n            self._lower == other._lower\n            and (other._lower_inc or not self._lower_inc)\n        )\n\n    def _issubset_upper(self, other: Self) -> bool:\n        if other._upper is None:\n            return True\n        if self._upper is None:\n            return False\n\n        return self._upper < other._upper or (\n            self._upper == other._upper\n            and (other._upper_inc or not self._upper_inc)\n        )\n\n    def issubset(self, other: Self) -> bool:\n        if self._empty:\n            return True\n        if other._empty:\n            return False\n\n        return self._issubset_lower(other) and self._issubset_upper(other)\n\n    def issuperset(self, other: Self) -> bool:\n        return other.issubset(self)\n\n    def __bool__(self) -> bool:\n        return not self._empty\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, Range):\n            return NotImplemented\n\n        return (\n            self._lower,\n            self._upper,\n            self._lower_inc,\n            self._upper_inc,\n            self._empty\n        ) == (\n            other._lower,  # pyright: ignore [reportUnknownMemberType]\n            other._upper,  # pyright: ignore [reportUnknownMemberType]\n            other._lower_inc,\n            other._upper_inc,\n            other._empty\n        )\n\n    def __hash__(self) -> int:\n        return hash((\n            self._lower,\n            self._upper,\n            self._lower_inc,\n            self._upper_inc,\n            self._empty\n        ))\n\n    def __repr__(self) -> str:\n        if self._empty:\n            desc = 'empty'\n        else:\n            if self._lower is None or not self._lower_inc:\n                lb = '('\n            else:\n                lb = '['\n\n            if self._lower is not None:\n                lb += repr(self._lower)\n\n            if self._upper is not None:\n                ub = repr(self._upper)\n            else:\n                ub = ''\n\n            if self._upper is None or not self._upper_inc:\n                ub += ')'\n            else:\n                ub += ']'\n\n            desc = '{}, {}'.format(lb, ub)\n\n        return '<Range {}>'.format(desc)\n\n    __str__ = __repr__\n"
  },
  {
    "path": "asyncpg/utils.py",
    "content": "# Copyright (C) 2016-present the ayncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport re\n\n\ndef _quote_ident(ident):\n    return '\"{}\"'.format(ident.replace('\"', '\"\"'))\n\n\ndef _quote_literal(string):\n    return \"'{}'\".format(string.replace(\"'\", \"''\"))\n\n\nasync def _mogrify(conn, query, args):\n    \"\"\"Safely inline arguments to query text.\"\"\"\n    # Introspect the target query for argument types and\n    # build a list of safely-quoted fully-qualified type names.\n    ps = await conn.prepare(query)\n    paramtypes = []\n    for t in ps.get_parameters():\n        if t.name.endswith('[]'):\n            pname = '_' + t.name[:-2]\n        else:\n            pname = t.name\n\n        paramtypes.append('{}.{}'.format(\n            _quote_ident(t.schema), _quote_ident(pname)))\n    del ps\n\n    # Use Postgres to convert arguments to text representation\n    # by casting each value to text.\n    cols = ['quote_literal(${}::{}::text)'.format(i, t)\n            for i, t in enumerate(paramtypes, start=1)]\n\n    textified = await conn.fetchrow(\n        'SELECT {cols}'.format(cols=', '.join(cols)), *args)\n\n    # Finally, replace $n references with text values.\n    return re.sub(\n        r\"\\$(\\d+)\\b\",\n        lambda m: (\n            textified[int(m.group(1)) - 1]\n            if textified[int(m.group(1)) - 1] is not None\n            else \"NULL\"\n        ),\n        query,\n    )\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "_build\n_templates\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = python -m sphinx\nPAPER         =\nBUILDDIR      = _build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  applehelp  to make an Apple Help Book\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  epub3      to make an epub3\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\t@echo \"  coverage   to run coverage check of the documentation (if enabled)\"\n\t@echo \"  dummy      to check syntax errors of document sources\"\n\n.PHONY: clean\nclean:\n\trm -rf $(BUILDDIR)/*\n\n.PHONY: html\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\n.PHONY: dirhtml\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\n.PHONY: singlehtml\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\n.PHONY: pickle\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\n.PHONY: json\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\n.PHONY: htmlhelp\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\n.PHONY: qthelp\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/asyncpg.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/asyncpg.qhc\"\n\n.PHONY: applehelp\napplehelp:\n\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp\n\t@echo\n\t@echo \"Build finished. The help book is in $(BUILDDIR)/applehelp.\"\n\t@echo \"N.B. You won't be able to view it unless you put it in\" \\\n\t      \"~/Library/Documentation/Help or install it in your application\" \\\n\t      \"bundle.\"\n\n.PHONY: devhelp\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/asyncpg\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/asyncpg\"\n\t@echo \"# devhelp\"\n\n.PHONY: epub\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\n.PHONY: epub3\nepub3:\n\t$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3\n\t@echo\n\t@echo \"Build finished. The epub3 file is in $(BUILDDIR)/epub3.\"\n\n.PHONY: latex\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\n.PHONY: latexpdf\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: latexpdfja\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\n.PHONY: text\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\n.PHONY: man\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\n.PHONY: texinfo\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\n.PHONY: info\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\n.PHONY: gettext\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\n.PHONY: changes\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\n.PHONY: linkcheck\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\n.PHONY: doctest\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\n.PHONY: coverage\ncoverage:\n\t$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage\n\t@echo \"Testing of coverage in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/coverage/python.txt.\"\n\n.PHONY: xml\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\n.PHONY: pseudoxml\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n\n.PHONY: dummy\ndummy:\n\t$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy\n\t@echo\n\t@echo \"Build finished. Dummy builder generates no files.\"\n"
  },
  {
    "path": "docs/_static/theme_overrides.css",
    "content": "/* override table width restrictions */\n@media screen and (min-width: 767px) {\n\n   .wy-table-responsive table td {\n      white-space: normal !important;\n      vertical-align: top !important;\n   }\n\n   .wy-table-responsive {\n      overflow: visible !important;\n   }\n}\n"
  },
  {
    "path": "docs/api/index.rst",
    "content": ".. _asyncpg-api-reference:\n\n=============\nAPI Reference\n=============\n\n.. module:: asyncpg\n    :synopsis: A fast PostgreSQL Database Client Library for Python/asyncio\n\n.. currentmodule:: asyncpg\n\n\n.. _asyncpg-api-connection:\n\nConnection\n==========\n\n.. autofunction:: asyncpg.connection.connect\n\n\n.. autoclass:: asyncpg.connection.Connection\n   :members:\n\n\n.. _asyncpg-api-prepared-stmt:\n\nPrepared Statements\n===================\n\nPrepared statements are a PostgreSQL feature that can be used to optimize the\nperformance of queries that are executed more than once.  When a query\nis *prepared* by a call to :meth:`Connection.prepare`, the server parses,\nanalyzes and compiles the query allowing to reuse that work once there is\na need to run the same query again.\n\n.. code-block:: pycon\n\n   >>> import asyncpg, asyncio\n   >>> async def run():\n   ...     conn = await asyncpg.connect()\n   ...     stmt = await conn.prepare('''SELECT 2 ^ $1''')\n   ...     print(await stmt.fetchval(10))\n   ...     print(await stmt.fetchval(20))\n   ...\n   >>> asyncio.run(run())\n   1024.0\n   1048576.0\n\n.. note::\n\n   asyncpg automatically maintains a small LRU cache for queries executed\n   during calls to the :meth:`~Connection.fetch`, :meth:`~Connection.fetchrow`,\n   or :meth:`~Connection.fetchval` methods.\n\n.. warning::\n\n   If you are using pgbouncer with ``pool_mode`` set to ``transaction`` or\n   ``statement``, prepared statements will not work correctly.  See\n   :ref:`asyncpg-prepared-stmt-errors` for more information.\n\n\n.. autoclass:: asyncpg.prepared_stmt.PreparedStatement()\n   :members:\n\n\n.. _asyncpg-api-transaction:\n\nTransactions\n============\n\nThe most common way to use transactions is through an ``async with`` statement:\n\n.. code-block:: python\n\n   async with connection.transaction():\n       await connection.execute(\"INSERT INTO mytable VALUES(1, 2, 3)\")\n\n\nasyncpg supports nested transactions (a nested transaction context will create\na `savepoint`_.):\n\n.. code-block:: python\n\n   async with connection.transaction():\n       await connection.execute('CREATE TABLE mytab (a int)')\n\n       try:\n           # Create a nested transaction:\n           async with connection.transaction():\n               await connection.execute('INSERT INTO mytab (a) VALUES (1), (2)')\n               # This nested transaction will be automatically rolled back:\n               raise Exception\n       except:\n           # Ignore exception\n           pass\n\n       # Because the nested transaction was rolled back, there\n       # will be nothing in `mytab`.\n       assert await connection.fetch('SELECT a FROM mytab') == []\n\nAlternatively, transactions can be used without an ``async with`` block:\n\n.. code-block:: python\n\n    tr = connection.transaction()\n    await tr.start()\n    try:\n        ...\n    except:\n        await tr.rollback()\n        raise\n    else:\n        await tr.commit()\n\n\nSee also the\n:meth:`Connection.transaction() <asyncpg.connection.Connection.transaction>`\nfunction.\n\n.. _savepoint: https://www.postgresql.org/docs/current/static/sql-savepoint.html\n\n\n.. autoclass:: asyncpg.transaction.Transaction()\n   :members:\n\n   .. describe:: async with c:\n\n      start and commit/rollback the transaction or savepoint block\n      automatically when entering and exiting the code inside the\n      context manager block.\n\n\n.. _asyncpg-api-cursor:\n\nCursors\n=======\n\nCursors are useful when there is a need to iterate over the results of\na large query without fetching all rows at once.  The cursor interface\nprovided by asyncpg supports *asynchronous iteration* via the ``async for``\nstatement, and also a way to read row chunks and skip forward over the\nresult set.\n\nTo iterate over a cursor using a connection object use\n:meth:`Connection.cursor() <asyncpg.connection.Connection.cursor>`.\nTo make the iteration efficient, the cursor will prefetch records to\nreduce the number of queries sent to the server:\n\n.. code-block:: python\n\n    async def iterate(con: Connection):\n        async with con.transaction():\n            # Postgres requires non-scrollable cursors to be created\n            # and used in a transaction.\n            async for record in con.cursor('SELECT generate_series(0, 100)'):\n                print(record)\n\nOr, alternatively, you can iterate over the cursor manually (cursor\nwon't be prefetching any rows):\n\n.. code-block:: python\n\n    async def iterate(con: Connection):\n        async with con.transaction():\n            # Postgres requires non-scrollable cursors to be created\n            # and used in a transaction.\n\n            # Create a Cursor object\n            cur = await con.cursor('SELECT generate_series(0, 100)')\n\n            # Move the cursor 10 rows forward\n            await cur.forward(10)\n\n            # Fetch one row and print it\n            print(await cur.fetchrow())\n\n            # Fetch a list of 5 rows and print it\n            print(await cur.fetch(5))\n\nIt's also possible to create cursors from prepared statements:\n\n.. code-block:: python\n\n    async def iterate(con: Connection):\n        # Create a prepared statement that will accept one argument\n        stmt = await con.prepare('SELECT generate_series(0, $1)')\n\n        async with con.transaction():\n            # Postgres requires non-scrollable cursors to be created\n            # and used in a transaction.\n\n            # Execute the prepared statement passing `10` as the\n            # argument -- that will generate a series or records\n            # from 0..10.  Iterate over all of them and print every\n            # record.\n            async for record in stmt.cursor(10):\n                print(record)\n\n\n.. note::\n\n   Cursors created by a call to\n   :meth:`Connection.cursor() <asyncpg.connection.Connection.cursor>` or\n   :meth:`PreparedStatement.cursor() <asyncpg.prepared_stmt.PreparedStatement.cursor>`\n   are *non-scrollable*: they can only be read forwards.  To create a scrollable\n   cursor, use the ``DECLARE ... SCROLL CURSOR`` SQL statement directly.\n\n.. warning::\n\n   Cursors created by a call to\n   :meth:`Connection.cursor() <asyncpg.connection.Connection.cursor>` or\n   :meth:`PreparedStatement.cursor() <asyncpg.prepared_stmt.PreparedStatement.cursor>`\n   cannot be used outside of a transaction.  Any such attempt will result in\n   :exc:`~asyncpg.exceptions.InterfaceError`.\n\n   To create a cursor usable outside of a transaction, use the\n   ``DECLARE ... CURSOR WITH HOLD`` SQL statement directly.\n\n\n.. autoclass:: asyncpg.cursor.CursorFactory()\n   :members:\n\n   .. describe:: async for row in c\n\n      Execute the statement and iterate over the results asynchronously.\n\n   .. describe:: await c\n\n      Execute the statement and return an instance of\n      :class:`~asyncpg.cursor.Cursor` which can be used to navigate over and\n      fetch subsets of the query results.\n\n\n.. autoclass:: asyncpg.cursor.Cursor()\n   :members:\n\n\n.. _asyncpg-api-pool:\n\nConnection Pools\n================\n\n.. autofunction:: asyncpg.pool.create_pool\n\n\n.. autoclass:: asyncpg.pool.Pool()\n   :members:\n\n\n.. _asyncpg-api-record:\n\nRecord Objects\n==============\n\nEach row (or composite type value) returned by calls to ``fetch*`` methods\nis represented by an instance of the :class:`~asyncpg.Record` object.\n``Record`` objects are a tuple-/dict-like hybrid, and allow addressing of\nitems either by a numeric index or by a field name:\n\n.. code-block:: pycon\n\n    >>> import asyncpg\n    >>> import asyncio\n    >>> loop = asyncio.get_event_loop()\n    >>> conn = loop.run_until_complete(asyncpg.connect())\n    >>> r = loop.run_until_complete(conn.fetchrow('''\n    ...     SELECT oid, rolname, rolsuper FROM pg_roles WHERE rolname = user'''))\n    >>> r\n    <Record oid=16388 rolname='elvis' rolsuper=True>\n    >>> r['oid']\n    16388\n    >>> r[0]\n    16388\n    >>> dict(r)\n    {'oid': 16388, 'rolname': 'elvis', 'rolsuper': True}\n    >>> tuple(r)\n    (16388, 'elvis', True)\n\n.. note::\n\n   ``Record`` objects currently cannot be created from Python code.\n\n.. class:: Record()\n\n   A read-only representation of PostgreSQL row.\n\n   .. describe:: len(r)\n\n      Return the number of fields in record *r*.\n\n   .. describe:: r[field]\n\n      Return the field of *r* with field name or index *field*.\n\n   .. describe:: name in r\n\n      Return ``True`` if record *r* has a field named *name*.\n\n   .. describe:: iter(r)\n\n      Return an iterator over the *values* of the record *r*.\n\n   .. describe:: get(name[, default])\n\n      Return the value for *name* if the record has a field named *name*,\n      else return *default*. If *default* is not given, return ``None``.\n\n      .. versionadded:: 0.18\n\n   .. method:: values()\n\n      Return an iterator over the record values.\n\n   .. method:: keys()\n\n      Return an iterator over the record field names.\n\n   .. method:: items()\n\n      Return an iterator over ``(field, value)`` pairs.\n\n\n.. class:: ConnectionSettings()\n\n    A read-only collection of Connection settings.\n\n    .. describe:: settings.setting_name\n\n       Return the value of the \"setting_name\" setting.  Raises an\n       ``AttributeError`` if the setting is not defined.\n\n       Example:\n\n       .. code-block:: pycon\n\n           >>> connection.get_settings().client_encoding\n           'UTF8'\n\n\nData Types\n==========\n\n.. automodule:: asyncpg.types\n   :members:\n"
  },
  {
    "path": "docs/conf.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport sys\n\nsys.path.insert(0, os.path.abspath('..'))\n\nversion_file = os.path.join(os.path.dirname(os.path.dirname(__file__)),\n                            'asyncpg', '_version.py')\n\nwith open(version_file, 'r') as f:\n    for line in f:\n        if line.startswith('__version__: typing.Final ='):\n            _, _, version = line.partition('=')\n            version = version.strip(\" \\n'\\\"\")\n            break\n    else:\n        raise RuntimeError(\n            'unable to read the version from asyncpg/_version.py')\n\n# -- General configuration ------------------------------------------------\n\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.doctest',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.githubpages',\n    'sphinx.ext.intersphinx',\n]\n\nadd_module_names = False\n\ntemplates_path = ['_templates']\nsource_suffix = '.rst'\nmaster_doc = 'index'\nproject = 'asyncpg'\ncopyright = '2016-present, the asyncpg authors and contributors'\nauthor = '<See AUTHORS file>'\nrelease = version\nlanguage = \"en\"\nexclude_patterns = ['_build']\npygments_style = 'sphinx'\ntodo_include_todos = False\nsuppress_warnings = ['image.nonlocal_uri']\n\n# -- Options for HTML output ----------------------------------------------\n\nhtml_theme = 'sphinx_rtd_theme'\nhtml_title = 'asyncpg Documentation'\nhtml_short_title = 'asyncpg'\nhtml_static_path = ['_static']\nhtml_sidebars = {\n    '**': [\n        'about.html',\n        'navigation.html',\n    ]\n}\nhtml_show_sourcelink = False\nhtml_show_sphinx = False\nhtml_show_copyright = True\nhtmlhelp_basename = 'asyncpgdoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {}\n\nlatex_documents = [\n    (master_doc, 'asyncpg.tex', 'asyncpg Documentation',\n     author, 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\nman_pages = [\n    (master_doc, 'asyncpg', 'asyncpg Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\ntexinfo_documents = [\n    (master_doc, 'asyncpg', 'asyncpg Documentation',\n     author, 'asyncpg',\n     'asyncpg is a fast PostgreSQL client library for the '\n     'Python asyncio framework',\n     'Miscellaneous'),\n]\n\n# -- Options for intersphinx ----------------------------------------------\n\nintersphinx_mapping = {'python': ('https://docs.python.org/3', None)}\n"
  },
  {
    "path": "docs/faq.rst",
    "content": ".. _asyncpg-faq:\n\n\nFrequently Asked Questions\n==========================\n\nDoes asyncpg support DB-API?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNo.  DB-API is a synchronous API, while asyncpg is based\naround an asynchronous I/O model.  Thus, full drop-in compatibility\nwith DB-API is not possible and we decided to design asyncpg API\nin a way that is better aligned with PostgreSQL architecture and\nterminology.  We will release a synchronous DB-API-compatible version\nof asyncpg at some point in the future.\n\n\nCan I use asyncpg with SQLAlchemy ORM?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYes.  SQLAlchemy version 1.4 and later supports the asyncpg dialect natively.\nPlease refer to its documentation for details.  Older SQLAlchemy versions\nmay be used in tandem with a third-party adapter such as\nasyncpgsa_ or databases_.\n\n\nCan I use dot-notation with :class:`asyncpg.Record`?  It looks cleaner.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe decided against making :class:`asyncpg.Record` a named tuple\nbecause we want to keep the ``Record`` method namespace separate\nfrom the column namespace.  That said, you can provide a custom ``Record``\nclass that implements dot-notation via the ``record_class`` argument to\n:func:`connect() <asyncpg.connection.connect>` or any of the Record-returning\nmethods.\n\n.. code-block:: python\n\n    class MyRecord(asyncpg.Record):\n        def __getattr__(self, name):\n            return self[name]\n\n\nWhy can't I use a :ref:`cursor <asyncpg-api-cursor>` outside of a transaction?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCursors created by a call to\n:meth:`Connection.cursor() <asyncpg.connection.Connection.cursor>` or\n:meth:`PreparedStatement.cursor() \\\n<asyncpg.prepared_stmt.PreparedStatement.cursor>`\ncannot be used outside of a transaction.  Any such attempt will result in\n``InterfaceError``.\nTo create a cursor usable outside of a transaction, use the\n``DECLARE ... CURSOR WITH HOLD`` SQL statement directly.\n\n\n.. _asyncpg-prepared-stmt-errors:\n\nWhy am I getting prepared statement errors?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you are getting intermittent ``prepared statement \"__asyncpg_stmt_xx__\"\ndoes not exist`` or ``prepared statement “__asyncpg_stmt_xx__”\nalready exists`` errors, you are most likely not connecting to the\nPostgreSQL server directly, but via\n`pgbouncer <https://pgbouncer.github.io/>`_.  pgbouncer, when\nin the ``\"transaction\"`` or ``\"statement\"`` pooling mode, does not support\nprepared statements.  You have several options:\n\n* if you are using pgbouncer only to reduce the cost of new connections\n  (as opposed to using pgbouncer for connection pooling from\n  a large number of clients in the interest of better scalability),\n  switch to the :ref:`connection pool <asyncpg-connection-pool>`\n  functionality provided by asyncpg, it is a much better option for this\n  purpose;\n\n* disable automatic use of prepared statements by passing\n  ``statement_cache_size=0``\n  to :func:`asyncpg.connect() <asyncpg.connection.connect>` and\n  :func:`asyncpg.create_pool() <asyncpg.pool.create_pool>`\n  (and, obviously, avoid the use of\n  :meth:`Connection.prepare() <asyncpg.connection.Connection.prepare>`);\n\n* switch pgbouncer's ``pool_mode`` to ``session``.\n\n\nWhy do I get ``PostgresSyntaxError`` when using ``expression IN $1``?\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n``expression IN $1`` is not a valid PostgreSQL syntax.  To check\na value against a sequence use ``expression = any($1::mytype[])``,\nwhere ``mytype`` is the array element type.\n\n.. _asyncpgsa: https://github.com/CanopyTax/asyncpgsa\n.. _databases: https://github.com/encode/databases\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. image:: https://github.com/MagicStack/asyncpg/workflows/Tests/badge.svg\n   :target: https://github.com/MagicStack/asyncpg/actions?query=workflow%3ATests+branch%3Amaster\n   :alt: GitHub Actions status\n\n.. image:: https://img.shields.io/pypi/status/asyncpg.svg?maxAge=2592000?style=plastic\n    :target: https://pypi.python.org/pypi/asyncpg\n\n\n=======\nasyncpg\n=======\n\n**asyncpg** is a database interface library designed specifically for\nPostgreSQL and Python/asyncio.  asyncpg is an efficient, clean implementation\nof PostgreSQL server binary protocol for use with Python's ``asyncio``\nframework.\n\n**asyncpg** requires Python 3.9 or later and is supported for PostgreSQL\nversions 9.5 to 18.  Other PostgreSQL versions or other databases implementing\nthe PostgreSQL protocol *may* work, but are not being actively tested.\n\nContents\n--------\n\n.. toctree::\n   :maxdepth: 2\n\n   installation\n   usage\n   api/index\n   faq\n"
  },
  {
    "path": "docs/installation.rst",
    "content": ".. _asyncpg-installation:\n\n\nInstallation\n============\n\n**asyncpg** has no external dependencies when not using GSSAPI/SSPI\nauthentication.  The recommended way to install it is to use **pip**:\n\n.. code-block:: bash\n\n    $ pip install asyncpg\n\nIf you need GSSAPI/SSPI authentication, the recommended way is to use\n\n.. code-block:: bash\n\n    $ pip install 'asyncpg[gssauth]'\n\nThis installs SSPI support on Windows and GSSAPI support on non-Windows\nplatforms.  SSPI and GSSAPI interoperate as clients and servers: an SSPI\nclient can authenticate to a GSSAPI server and vice versa.\n\nOn Linux installing GSSAPI requires a working C compiler and Kerberos 5\ndevelopment files.  The latter can be obtained by installing **libkrb5-dev**\npackage on Debian/Ubuntu or **krb5-devel** on RHEL/Fedora.  (This is needed\nbecause PyPI does not have Linux wheels for **gssapi**. See `here for the\ndetails <https://github.com/pythongssapi/python-gssapi/issues/200#issuecomment-1032934269>`_.)\n\nIt is also possible to use GSSAPI on Windows:\n\n  * `pip install gssapi`\n  * Install `Kerberos for Windows <https://web.mit.edu/kerberos/dist/>`_.\n  * Set the ``gsslib`` parameter or the ``PGGSSLIB`` environment variable to\n    `gssapi` when connecting.\n\n\nBuilding from source\n--------------------\n\nIf you want to build **asyncpg** from a Git checkout you will need:\n\n  * To have cloned the repo with `--recurse-submodules`.\n  * A working C compiler.\n  * CPython header files.  These can usually be obtained by installing\n    the relevant Python development package: **python3-dev** on Debian/Ubuntu,\n    **python3-devel** on RHEL/Fedora.\n\nOnce the above requirements are satisfied, run the following command\nin the root of the source checkout:\n\n.. code-block:: bash\n\n    $ pip install -e .\n\nA debug build containing more runtime checks can be created by setting\nthe ``ASYNCPG_DEBUG`` environment variable when building:\n\n.. code-block:: bash\n\n    $ env ASYNCPG_DEBUG=1 pip install -e .\n\n\nRunning tests\n-------------\n\n\nIf you want to run tests you must have PostgreSQL installed.\n\nTo execute the testsuite run:\n\n.. code-block:: bash\n\n    $ python setup.py test\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "sphinxcontrib-asyncio\nsphinx_rtd_theme\n"
  },
  {
    "path": "docs/usage.rst",
    "content": ".. _asyncpg-examples:\n\n\nasyncpg Usage\n=============\n\nThe interaction with the database normally starts with a call to\n:func:`connect() <asyncpg.connection.connect>`, which establishes\na new database session and returns a new\n:class:`Connection <asyncpg.connection.Connection>` instance,\nwhich provides methods to run queries and manage transactions.\n\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n    import datetime\n\n    async def main():\n        # Establish a connection to an existing database named \"test\"\n        # as a \"postgres\" user.\n        conn = await asyncpg.connect('postgresql://postgres@localhost/test')\n        # Execute a statement to create a new table.\n        await conn.execute('''\n            CREATE TABLE users(\n                id serial PRIMARY KEY,\n                name text,\n                dob date\n            )\n        ''')\n\n        # Insert a record into the created table.\n        await conn.execute('''\n            INSERT INTO users(name, dob) VALUES($1, $2)\n        ''', 'Bob', datetime.date(1984, 3, 1))\n\n        # Select a row from the table.\n        row = await conn.fetchrow(\n            'SELECT * FROM users WHERE name = $1', 'Bob')\n        # *row* now contains\n        # asyncpg.Record(id=1, name='Bob', dob=datetime.date(1984, 3, 1))\n\n        # Close the connection.\n        await conn.close()\n\n    asyncio.run(main())\n\n\n.. note::\n\n   asyncpg uses the native PostgreSQL syntax for query arguments: ``$n``.\n\n\n\nType Conversion\n---------------\n\nasyncpg automatically converts PostgreSQL types to the corresponding Python\ntypes and vice versa.  All standard data types are supported out of the box,\nincluding arrays, composite types, range types, enumerations and any\ncombination of them.  It is possible to supply codecs for non-standard\ntypes or override standard codecs.  See :ref:`asyncpg-custom-codecs` for\nmore information.\n\nThe table below shows the correspondence between PostgreSQL and Python types.\n\n+----------------------+-----------------------------------------------------+\n| PostgreSQL Type      |  Python Type                                        |\n+======================+=====================================================+\n| ``anyarray``         | :class:`list <python:list>`                         |\n+----------------------+-----------------------------------------------------+\n| ``anyenum``          | :class:`str <python:str>`                           |\n+----------------------+-----------------------------------------------------+\n| ``anyrange``         | :class:`asyncpg.Range <asyncpg.types.Range>`,       |\n|                      | :class:`tuple <python:tuple>`                       |\n+----------------------+-----------------------------------------------------+\n| ``anymultirange``    | ``list[``:class:`asyncpg.Range\\                     |\n|                      | <asyncpg.types.Range>` ``]``,                       |\n|                      | ``list[``:class:`tuple <python:tuple>` ``]`` [#f1]_ |\n+----------------------+-----------------------------------------------------+\n| ``record``           | :class:`asyncpg.Record`,                            |\n|                      | :class:`tuple <python:tuple>`,                      |\n|                      | :class:`Mapping <python:collections.abc.Mapping>`   |\n+----------------------+-----------------------------------------------------+\n| ``bit``, ``varbit``  | :class:`asyncpg.BitString <asyncpg.types.BitString>`|\n+----------------------+-----------------------------------------------------+\n| ``bool``             | :class:`bool <python:bool>`                         |\n+----------------------+-----------------------------------------------------+\n| ``box``              | :class:`asyncpg.Box <asyncpg.types.Box>`            |\n+----------------------+-----------------------------------------------------+\n| ``bytea``            | :class:`bytes <python:bytes>`                       |\n+----------------------+-----------------------------------------------------+\n| ``char``, ``name``,  | :class:`str <python:str>`                           |\n| ``varchar``,         |                                                     |\n| ``text``,            |                                                     |\n| ``xml``              |                                                     |\n+----------------------+-----------------------------------------------------+\n| ``cidr``             | :class:`ipaddress.IPv4Network\\                      |\n|                      | <python:ipaddress.IPv4Network>`,                    |\n|                      | :class:`ipaddress.IPv6Network\\                      |\n|                      | <python:ipaddress.IPv6Network>`                     |\n+----------------------+-----------------------------------------------------+\n| ``inet``             | :class:`ipaddress.IPv4Interface\\                    |\n|                      | <python:ipaddress.IPv4Interface>`,                  |\n|                      | :class:`ipaddress.IPv6Interface\\                    |\n|                      | <python:ipaddress.IPv6Interface>`,                  |\n|                      | :class:`ipaddress.IPv4Address\\                      |\n|                      | <python:ipaddress.IPv4Address>`,                    |\n|                      | :class:`ipaddress.IPv6Address\\                      |\n|                      | <python:ipaddress.IPv6Address>` [#f2]_              |\n+----------------------+-----------------------------------------------------+\n| ``macaddr``          | :class:`str <python:str>`                           |\n+----------------------+-----------------------------------------------------+\n| ``circle``           | :class:`asyncpg.Circle <asyncpg.types.Circle>`      |\n+----------------------+-----------------------------------------------------+\n| ``date``             | :class:`datetime.date <python:datetime.date>`       |\n+----------------------+-----------------------------------------------------+\n| ``time``             | offset-naïve :class:`datetime.time \\                |\n|                      | <python:datetime.time>`                             |\n+----------------------+-----------------------------------------------------+\n| ``time with          | offset-aware :class:`datetime.time \\                |\n| time zone``          | <python:datetime.time>`                             |\n+----------------------+-----------------------------------------------------+\n| ``timestamp``        | offset-naïve :class:`datetime.datetime \\            |\n|                      | <python:datetime.datetime>`                         |\n+----------------------+-----------------------------------------------------+\n| ``timestamp with     | offset-aware :class:`datetime.datetime \\            |\n| time zone``          | <python:datetime.datetime>`                         |\n+----------------------+-----------------------------------------------------+\n| ``interval``         | :class:`datetime.timedelta \\                        |\n|                      | <python:datetime.timedelta>`                        |\n+----------------------+-----------------------------------------------------+\n| ``float``,           | :class:`float <python:float>` [#f3]_                |\n| ``double precision`` |                                                     |\n+----------------------+-----------------------------------------------------+\n| ``smallint``,        | :class:`int <python:int>`                           |\n| ``integer``,         |                                                     |\n| ``bigint``           |                                                     |\n+----------------------+-----------------------------------------------------+\n| ``numeric``          | :class:`Decimal <python:decimal.Decimal>`           |\n+----------------------+-----------------------------------------------------+\n| ``json``, ``jsonb``  | :class:`str <python:str>`                           |\n+----------------------+-----------------------------------------------------+\n| ``line``             | :class:`asyncpg.Line <asyncpg.types.Line>`          |\n+----------------------+-----------------------------------------------------+\n| ``lseg``             | :class:`asyncpg.LineSegment \\                       |\n|                      | <asyncpg.types.LineSegment>`                        |\n+----------------------+-----------------------------------------------------+\n| ``money``            | :class:`str <python:str>`                           |\n+----------------------+-----------------------------------------------------+\n| ``path``             | :class:`asyncpg.Path <asyncpg.types.Path>`          |\n+----------------------+-----------------------------------------------------+\n| ``point``            | :class:`asyncpg.Point <asyncpg.types.Point>`        |\n+----------------------+-----------------------------------------------------+\n| ``polygon``          | :class:`asyncpg.Polygon <asyncpg.types.Polygon>`    |\n+----------------------+-----------------------------------------------------+\n| ``uuid``             | :class:`uuid.UUID <python:uuid.UUID>`               |\n+----------------------+-----------------------------------------------------+\n| ``tid``              | :class:`tuple <python:tuple>`                       |\n+----------------------+-----------------------------------------------------+\n\nAll other types are encoded and decoded as text by default.\n\n.. [#f1] Since version 0.25.0\n\n.. [#f2] Prior to version 0.20.0, asyncpg erroneously treated ``inet`` values\n         with prefix as ``IPvXNetwork`` instead of ``IPvXInterface``.\n\n.. [#f3] Inexact single-precision ``float`` values may have a different\n         representation when decoded into a Python float.  This is inherent\n         to the implementation of limited-precision floating point types.\n         If you need the decimal representation to match, cast the expression\n         to ``double`` or ``numeric`` in your query.\n\n.. _asyncpg-custom-codecs:\n\nCustom Type Conversions\n-----------------------\n\nasyncpg allows defining custom type conversion functions both for standard\nand user-defined types using the :meth:`Connection.set_type_codec() \\\n<asyncpg.connection.Connection.set_type_codec>` and\n:meth:`Connection.set_builtin_type_codec() \\\n<asyncpg.connection.Connection.set_builtin_type_codec>` methods.\n\n\nExample: automatic JSON conversion\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe example below shows how to configure asyncpg to encode and decode\nJSON values using the :mod:`json <python:json>` module.\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n    import json\n\n\n    async def main():\n        conn = await asyncpg.connect()\n\n        try:\n            await conn.set_type_codec(\n                'json',\n                encoder=json.dumps,\n                decoder=json.loads,\n                schema='pg_catalog'\n            )\n\n            data = {'foo': 'bar', 'spam': 1}\n            res = await conn.fetchval('SELECT $1::json', data)\n\n        finally:\n            await conn.close()\n\n    asyncio.run(main())\n\n\nExample: complex types\n~~~~~~~~~~~~~~~~~~~~~~\n\nThe example below shows how to configure asyncpg to encode and decode\nPython :class:`complex <python:complex>` values to a custom composite\ntype in PostgreSQL.\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n\n\n    async def main():\n        conn = await asyncpg.connect()\n\n        try:\n            await conn.execute(\n                '''\n                CREATE TYPE mycomplex AS (\n                    r float,\n                    i float\n                );'''\n            )\n            await conn.set_type_codec(\n                'complex',\n                encoder=lambda x: (x.real, x.imag),\n                decoder=lambda t: complex(t[0], t[1]),\n                format='tuple',\n            )\n\n            res = await conn.fetchval('SELECT $1::mycomplex', (1+2j))\n\n        finally:\n            await conn.close()\n\n    asyncio.run(main())\n\n\nExample: automatic conversion of PostGIS types\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe example below shows how to configure asyncpg to encode and decode\nthe PostGIS ``geometry`` type.  It works for any Python object that\nconforms to the `geo interface specification`_ and relies on Shapely_,\nalthough any library that supports reading and writing the WKB format\nwill work.\n\n.. _Shapely: https://github.com/Toblerity/Shapely\n.. _geo interface specification: https://gist.github.com/sgillies/2217756\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n\n    import shapely.geometry\n    import shapely.wkb\n    from shapely.geometry.base import BaseGeometry\n\n\n    async def main():\n        conn = await asyncpg.connect()\n\n        try:\n            def encode_geometry(geometry):\n                if not hasattr(geometry, '__geo_interface__'):\n                    raise TypeError('{g} does not conform to '\n                                    'the geo interface'.format(g=geometry))\n                shape = shapely.geometry.shape(geometry)\n                return shapely.wkb.dumps(shape)\n\n            def decode_geometry(wkb):\n                return shapely.wkb.loads(wkb)\n\n            await conn.set_type_codec(\n                'geometry',  # also works for 'geography'\n                encoder=encode_geometry,\n                decoder=decode_geometry,\n                format='binary',\n            )\n\n            data = shapely.geometry.Point(-73.985661, 40.748447)\n            res = await conn.fetchrow(\n                '''SELECT 'Empire State Building' AS name,\n                          $1::geometry            AS coordinates\n                ''',\n                data)\n\n            print(res)\n\n        finally:\n            await conn.close()\n\n    asyncio.run(main())\n\n\nExample: decoding numeric columns as floats\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBy default asyncpg decodes numeric columns as Python\n:class:`Decimal <python:decimal.Decimal>` instances.  The example below\nshows how to instruct asyncpg to use floats instead.\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n\n\n    async def main():\n        conn = await asyncpg.connect()\n\n        try:\n            await conn.set_type_codec(\n                'numeric', encoder=str, decoder=float,\n                schema='pg_catalog', format='text'\n            )\n\n            res = await conn.fetchval(\"SELECT $1::numeric\", 11.123)\n            print(res, type(res))\n\n        finally:\n            await conn.close()\n\n    asyncio.run(main())\n\n\nExample: decoding hstore values\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nhstore_ is an extension data type used for storing key/value pairs.\nasyncpg includes a codec to decode and encode hstore values as ``dict``\nobjects.  Because ``hstore`` is not a builtin type, the codec must\nbe registered on a connection using :meth:`Connection.set_builtin_type_codec()\n<asyncpg.connection.Connection.set_builtin_type_codec>`:\n\n.. code-block:: python\n\n    import asyncpg\n    import asyncio\n\n    async def run():\n        conn = await asyncpg.connect()\n        # Assuming the hstore extension exists in the public schema.\n        await conn.set_builtin_type_codec(\n            'hstore', codec_name='pg_contrib.hstore')\n        result = await conn.fetchval(\"SELECT 'a=>1,b=>2,c=>NULL'::hstore\")\n        assert result == {'a': '1', 'b': '2', 'c': None}\n\n    asyncio.run(run())\n\n.. _hstore: https://www.postgresql.org/docs/current/static/hstore.html\n\n\nTransactions\n------------\n\nTo create transactions, the\n:meth:`Connection.transaction() <asyncpg.connection.Connection>` method\nshould be used.\n\nThe most common way to use transactions is through an ``async with`` statement:\n\n.. code-block:: python\n\n   async with connection.transaction():\n       await connection.execute(\"INSERT INTO mytable VALUES(1, 2, 3)\")\n\n.. note::\n\n   When not in an explicit transaction block, any changes to the database\n   will be applied immediately.  This is also known as *auto-commit*.\n\nSee the :ref:`asyncpg-api-transaction` API documentation for more information.\n\n\n.. _asyncpg-connection-pool:\n\nConnection Pools\n----------------\n\nFor server-type type applications, that handle frequent requests and need\nthe database connection for a short period time while handling a request,\nthe use of a connection pool is recommended.  asyncpg provides an advanced\npool implementation, which eliminates the need to use an external connection\npooler such as PgBouncer.\n\nTo create a connection pool, use the\n:func:`asyncpg.create_pool() <asyncpg.pool.create_pool>` function.\nThe resulting :class:`Pool <asyncpg.pool.Pool>` object can then be used\nto borrow connections from the pool.\n\nBelow is an example of how **asyncpg** can be used to implement a simple\nWeb service that computes the requested power of two.\n\n\n.. code-block:: python\n\n    import asyncio\n    import asyncpg\n    from aiohttp import web\n\n\n    async def handle(request):\n        \"\"\"Handle incoming requests.\"\"\"\n        pool = request.app['pool']\n        power = int(request.match_info.get('power', 10))\n\n        # Take a connection from the pool.\n        async with pool.acquire() as connection:\n            # Open a transaction.\n            async with connection.transaction():\n                # Run the query passing the request argument.\n                result = await connection.fetchval('select 2 ^ $1', power)\n                return web.Response(\n                    text=\"2 ^ {} is {}\".format(power, result))\n\n\n    async def init_db(app):\n        \"\"\"Initialize a connection pool.\"\"\"\n         app['pool'] = await asyncpg.create_pool(database='postgres',\n                                                 user='postgres')\n         yield\n         await app['pool'].close()\n\n \n    def init_app():\n        \"\"\"Initialize the application server.\"\"\"\n        app = web.Application()\n        # Create a database context\n        app.cleanup_ctx.append(init_db)\n        # Configure service routes\n        app.router.add_route('GET', '/{power:\\d+}', handle)\n        app.router.add_route('GET', '/', handle)\n        return app\n\n\n    app = init_app()\n    web.run_app(app)\n\nSee :ref:`asyncpg-api-pool` API documentation for more information.\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"asyncpg\"\ndescription = \"An asyncio PostgreSQL driver\"\nauthors = [{name = \"MagicStack Inc\", email = \"hello@magic.io\"}]\nrequires-python = '>=3.9.0'\nreadme = \"README.rst\"\nlicense = \"Apache-2.0\"\nlicense-files = [\"LICENSE\"]\ndynamic = [\"version\"]\nkeywords = [\n    \"database\",\n    \"postgres\",\n]\nclassifiers = [\n    \"Development Status :: 5 - Production/Stable\",\n    \"Framework :: AsyncIO\",\n    \"Intended Audience :: Developers\",\n    \"Operating System :: POSIX\",\n    \"Operating System :: MacOS :: MacOS X\",\n    \"Operating System :: Microsoft :: Windows\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"Programming Language :: Python :: 3.9\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n    \"Programming Language :: Python :: 3.12\",\n    \"Programming Language :: Python :: 3.13\",\n    \"Programming Language :: Python :: 3.14\",\n    \"Programming Language :: Python :: Implementation :: CPython\",\n    \"Programming Language :: Python :: Free Threading :: 2 - Beta\",\n    \"Topic :: Database :: Front-Ends\",\n]\ndependencies = [\n    'async_timeout>=4.0.3; python_version < \"3.11.0\"',\n]\n\n[project.urls]\ngithub = \"https://github.com/MagicStack/asyncpg\"\n\n[project.optional-dependencies]\ngssauth = [\n    'gssapi; platform_system != \"Windows\"',\n    'sspilib; platform_system == \"Windows\"',\n]\n\n[dependency-groups]\ntest = [\n    'flake8~=6.1',\n    'flake8-pyi~=24.1.0',\n    'distro~=1.9.0',\n    'uvloop>=0.22.1; platform_system != \"Windows\" and python_version < \"3.15.0\"',\n    'gssapi; platform_system == \"Linux\"',\n    'k5test; platform_system == \"Linux\"',\n    'sspilib; platform_system == \"Windows\"',\n    'mypy~=1.8.0',\n    'pytest',\n]\ndocs = [\n    'Sphinx~=7.4',\n    'sphinx_rtd_theme>=1.2.2',\n]\n\n[build-system]\nrequires = [\n    \"setuptools>=77.0.3\",\n    \"Cython(>=3.2.1,<4.0.0)\"\n]\nbuild-backend = \"setuptools.build_meta\"\n\n[tool.setuptools]\nzip-safe = false\n\n[tool.setuptools.packages.find]\ninclude = [\"asyncpg\", \"asyncpg.*\"]\n\n[tool.setuptools.exclude-package-data]\n\"*\" = [\"*.c\", \"*.h\"]\n\n[tool.cibuildwheel]\nbuild-frontend = \"build\"\ntest-groups = \"test\"\nskip = \"cp38-*\"\n\n[tool.cibuildwheel.macos]\nbefore-all = \".github/workflows/install-postgres.sh\"\ntest-command = \"python {project}/tests/__init__.py\"\n\n[tool.cibuildwheel.windows]\ntest-command = \"python {project}\\\\tests\\\\__init__.py\"\n\n[tool.cibuildwheel.linux]\nbefore-all = \"\"\"\n    .github/workflows/install-postgres.sh \\\n    && .github/workflows/install-krb5.sh \\\n    \"\"\"\ntest-command = \"\"\"\\\n    PY=`which python` \\\n    && chmod -R go+rX \"$(dirname $(dirname $(dirname $PY)))\" \\\n    && su -l apgtest -c \"$PY {project}/tests/__init__.py\" \\\n    \"\"\"\n\n[tool.pytest.ini_options]\naddopts = \"--capture=no --assert=plain --strict-markers --tb=native --import-mode=importlib\"\ntestpaths = \"tests\"\nfilterwarnings = \"default\"\n\n[tool.coverage.run]\nbranch = true\nplugins = [\"Cython.Coverage\"]\nparallel = true\nsource = [\"asyncpg/\", \"tests/\"]\nomit = [\"*.pxd\"]\n\n[tool.coverage.report]\nexclude_lines = [\n    \"pragma: no cover\",\n    \"def __repr__\",\n    \"if debug\",\n    \"raise NotImplementedError\",\n    \"if __name__ == .__main__.\",\n]\nshow_missing = true\n\n[tool.mypy]\nexclude = [\n    \"^.eggs\",\n    \"^.github\",\n    \"^.vscode\",\n    \"^build\",\n    \"^dist\",\n    \"^docs\",\n    \"^tests\",\n]\nincremental = true\nstrict = true\nimplicit_reexport = true\n\n[[tool.mypy.overrides]]\nmodule = [\n    \"asyncpg._testbase\",\n    \"asyncpg._testbase.*\",\n    \"asyncpg.cluster\",\n    \"asyncpg.connect_utils\",\n    \"asyncpg.connection\",\n    \"asyncpg.connresource\",\n    \"asyncpg.cursor\",\n    \"asyncpg.exceptions\",\n    \"asyncpg.exceptions.*\",\n    \"asyncpg.pool\",\n    \"asyncpg.prepared_stmt\",\n    \"asyncpg.transaction\",\n    \"asyncpg.utils\",\n]\nignore_errors = true\n"
  },
  {
    "path": "setup.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport sys\n\nif sys.version_info < (3, 9):\n    raise RuntimeError('asyncpg requires Python 3.9 or greater')\n\nimport os\nimport os.path\nimport pathlib\nimport platform\nimport re\nimport subprocess\n\n# We use vanilla build_ext, to avoid importing Cython via\n# the setuptools version.\nimport setuptools\nfrom setuptools.command import build_py as setuptools_build_py\nfrom setuptools.command import sdist as setuptools_sdist\nfrom setuptools.command import build_ext as setuptools_build_ext\n\n\nCYTHON_DEPENDENCY = 'Cython(>=3.2.1,<4.0.0)'\n\nCFLAGS = ['-O2']\nLDFLAGS = []\n\nif platform.uname().system != 'Windows':\n    CFLAGS.extend(['-fsigned-char', '-Wall', '-Wsign-compare', '-Wconversion'])\n    # Link against libm (math library) for functions like log10()\n    LDFLAGS.extend(['-lm'])\n\n\n_ROOT = pathlib.Path(__file__).parent\n\n\nwith open(str(_ROOT / 'README.rst')) as f:\n    readme = f.read()\n\n\nwith open(str(_ROOT / 'asyncpg' / '_version.py')) as f:\n    for line in f:\n        if line.startswith('__version__: typing.Final ='):\n            _, _, version = line.partition('=')\n            VERSION = version.strip(\" \\n'\\\"\")\n            break\n    else:\n        raise RuntimeError(\n            'unable to read the version from asyncpg/_version.py')\n\n\nif (_ROOT / '.git').is_dir() and 'dev' in VERSION:\n    # This is a git checkout, use git to\n    # generate a precise version.\n    def git_commitish():\n        env = {}\n        v = os.environ.get('PATH')\n        if v is not None:\n            env['PATH'] = v\n\n        git = subprocess.run(['git', 'rev-parse', 'HEAD'], env=env,\n                             cwd=str(_ROOT), stdout=subprocess.PIPE)\n        if git.returncode == 0:\n            commitish = git.stdout.strip().decode('ascii')\n        else:\n            commitish = 'unknown'\n\n        return commitish\n\n    VERSION += '+' + git_commitish()[:7]\n\n\nclass VersionMixin:\n\n    def _fix_version(self, filename):\n        # Replace asyncpg.__version__ with the actual version\n        # of the distribution (possibly inferred from git).\n\n        with open(str(filename)) as f:\n            content = f.read()\n\n        version_re = r\"(.*__version__\\s*=\\s*)'[^']+'(.*)\"\n        repl = r\"\\1'{}'\\2\".format(self.distribution.metadata.version)\n        content = re.sub(version_re, repl, content)\n\n        with open(str(filename), 'w') as f:\n            f.write(content)\n\n\nclass sdist(setuptools_sdist.sdist, VersionMixin):\n\n    def make_release_tree(self, base_dir, files):\n        super().make_release_tree(base_dir, files)\n        self._fix_version(pathlib.Path(base_dir) / 'asyncpg' / '_version.py')\n\n\nclass build_py(setuptools_build_py.build_py, VersionMixin):\n\n    def build_module(self, module, module_file, package):\n        outfile, copied = super().build_module(module, module_file, package)\n\n        if module == '__init__' and package == 'asyncpg':\n            self._fix_version(outfile)\n\n        return outfile, copied\n\n\nclass build_ext(setuptools_build_ext.build_ext):\n\n    user_options = setuptools_build_ext.build_ext.user_options + [\n        ('cython-always', None,\n            'run cythonize() even if .c files are present'),\n        ('cython-annotate', None,\n            'Produce a colorized HTML version of the Cython source.'),\n        ('cython-directives=', None,\n            'Cython compiler directives'),\n    ]\n\n    def initialize_options(self):\n        # initialize_options() may be called multiple times on the\n        # same command object, so make sure not to override previously\n        # set options.\n        if getattr(self, '_initialized', False):\n            return\n\n        super(build_ext, self).initialize_options()\n\n        defines = [\n            \"CYTHON_USE_MODULE_STATE\",\n            \"CYTHON_PEP489_MULTI_PHASE_INIT\",\n            \"CYTHON_USE_TYPE_SPECS\",\n        ]\n\n        if os.environ.get('ASYNCPG_DEBUG'):\n            self.cython_always = True\n            self.cython_annotate = True\n            self.cython_directives = \"linetrace=True\"\n            self.debug = True\n\n            defines += [\"PG_DEBUG\", \"CYTHON_TRACE\", \"CYTHON_TRACE_NOGIL\"]\n        else:\n            self.cython_always = False\n            self.cython_annotate = None\n            self.cython_directives = None\n\n        self.define = \",\".join(defines)\n\n    def finalize_options(self):\n        # finalize_options() may be called multiple times on the\n        # same command object, so make sure not to override previously\n        # set options.\n        if getattr(self, '_initialized', False):\n            return\n\n        if not self.cython_always:\n            self.cython_always = bool(os.environ.get(\n                \"ASYNCPG_BUILD_CYTHON_ALWAYS\"))\n\n        if self.cython_annotate is None:\n            self.cython_annotate = os.environ.get(\n                \"ASYNCPG_BUILD_CYTHON_ANNOTATE\")\n\n        if self.cython_directives is None:\n            self.cython_directives = os.environ.get(\n                \"ASYNCPG_BUILD_CYTHON_DIRECTIVES\")\n\n        need_cythonize = self.cython_always\n        cfiles = {}\n\n        for extension in self.distribution.ext_modules:\n            for i, sfile in enumerate(extension.sources):\n                if sfile.endswith('.pyx'):\n                    prefix, ext = os.path.splitext(sfile)\n                    cfile = prefix + '.c'\n\n                    if os.path.exists(cfile) and not self.cython_always:\n                        extension.sources[i] = cfile\n                    else:\n                        if os.path.exists(cfile):\n                            cfiles[cfile] = os.path.getmtime(cfile)\n                        else:\n                            cfiles[cfile] = 0\n                        need_cythonize = True\n\n        if need_cythonize:\n            import pkg_resources\n\n            # Double check Cython presence in case setup_requires\n            # didn't go into effect (most likely because someone\n            # imported Cython before setup_requires injected the\n            # correct egg into sys.path.\n            try:\n                import Cython\n            except ImportError:\n                raise RuntimeError(\n                    'please install {} to compile asyncpg from source'.format(\n                        CYTHON_DEPENDENCY))\n\n            cython_dep = pkg_resources.Requirement.parse(CYTHON_DEPENDENCY)\n            if Cython.__version__ not in cython_dep:\n                raise RuntimeError(\n                    'asyncpg requires {}, got Cython=={}'.format(\n                        CYTHON_DEPENDENCY, Cython.__version__\n                    ))\n\n            from Cython.Build import cythonize\n\n            directives = {\n                'language_level': '3',\n                'freethreading_compatible': 'True',\n                'subinterpreters_compatible': 'own_gil',\n            }\n\n            if self.cython_directives:\n                for directive in self.cython_directives.split(','):\n                    k, _, v = directive.partition('=')\n                    if v.lower() == 'false':\n                        v = False\n                    if v.lower() == 'true':\n                        v = True\n\n                    directives[k] = v\n\n            self.distribution.ext_modules[:] = cythonize(\n                self.distribution.ext_modules,\n                compiler_directives=directives,\n                annotate=self.cython_annotate)\n\n        super(build_ext, self).finalize_options()\n\n\nsetup_requires = []\n\nif (\n    not (_ROOT / 'asyncpg' / 'protocol' / 'protocol.c').exists()\n    or os.environ.get(\"ASYNCPG_BUILD_CYTHON_ALWAYS\")\n):\n    # No Cython output, require Cython to build.\n    setup_requires.append(CYTHON_DEPENDENCY)\n\n\n_ = setuptools.setup(\n    version=VERSION,\n    ext_modules=[\n        setuptools.extension.Extension(\n            \"asyncpg.pgproto.pgproto\",\n            [\"asyncpg/pgproto/pgproto.pyx\"],\n            extra_compile_args=CFLAGS,\n            extra_link_args=LDFLAGS),\n\n        setuptools.extension.Extension(\n            \"asyncpg.protocol.record\",\n            [\"asyncpg/protocol/record/recordobj.c\"],\n            include_dirs=['asyncpg/protocol/record/'],\n            extra_compile_args=CFLAGS,\n            extra_link_args=LDFLAGS),\n\n        setuptools.extension.Extension(\n            \"asyncpg.protocol.protocol\",\n            [\"asyncpg/protocol/protocol.pyx\"],\n            include_dirs=['asyncpg/pgproto/'],\n            extra_compile_args=CFLAGS,\n            extra_link_args=LDFLAGS),\n    ],\n    cmdclass={'build_ext': build_ext, 'build_py': build_py, 'sdist': sdist},\n    setup_requires=setup_requires,\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport pathlib\nimport sys\nimport unittest\n\n\ndef suite():\n    test_loader = unittest.TestLoader()\n    test_suite = test_loader.discover(str(pathlib.Path(__file__).parent),\n                                      pattern='test_*.py')\n    return test_suite\n\n\nif __name__ == '__main__':\n    runner = unittest.runner.TextTestRunner(verbosity=2)\n    result = runner.run(suite())\n    sys.exit(not result.wasSuccessful())\n"
  },
  {
    "path": "tests/certs/ca.cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIGJjCCBA6gAwIBAgIICJCUmtkcj2MwDQYJKoZIhvcNAQELBQAwgaExCzAJBgNV\nBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYDVQQHDAdUb3JvbnRvMRgwFgYD\nVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsMDWFzeW5jcGcgdGVzdHMxHTAb\nBgNVBAMMFGFzeW5jcGcgdGVzdCByb290IGNhMR0wGwYJKoZIhvcNAQkBFg5oZWxs\nb0BtYWdpYy5pbzAeFw0yNDEwMTYxNzIzNTZaFw00MzEyMTcxNzIzNTZaMIGhMQsw\nCQYDVQQGEwJDQTEQMA4GA1UECAwHT250YXJpbzEQMA4GA1UEBwwHVG9yb250bzEY\nMBYGA1UECgwPTWFnaWNTdGFjayBJbmMuMRYwFAYDVQQLDA1hc3luY3BnIHRlc3Rz\nMR0wGwYDVQQDDBRhc3luY3BnIHRlc3Qgcm9vdCBjYTEdMBsGCSqGSIb3DQEJARYO\naGVsbG9AbWFnaWMuaW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCP\n+oCl0qrReSlWj+yvfGz68UQqm6joL9VgeA0Tvc8S23Ia3S73wcTTdGhIQwMOaIuW\ny+m3J3js2wtpF0fmULYHr1ED7vQ+QOWarTyv/cGxSCyOYo4KVPHBfT6lYQTJk5NW\nOc2wr5ff/9nhdO61sGxZa2GVBjmbLOJ9IBKTvRcmNgLmPo60wMHtF4L5/PuwVPuu\n+zRoETfEh12avtY7Y2G+0i4ZRm4uBmw7hmByWzWCwqrV619BaFHaJUf2bEh5eCbz\n1nhF7WHVjBfnSJOgDxmZbKZZPmNzTVm8UxN22g9Ao6cZSxjbFAdpIhlQhAT6sjlW\nhvI6b58A3AJKi7zo+a7lnbPIeckduSkgbil3LZ4KxWgx6fPCBLqGH1XN6I8MQnX/\ne1ewiFXwuZMb+FgoKxaQBseuPVaA3ViYefysjvLjP7U9eRzv6qRimOmH5efaplbD\nzGhRUKA8GgmN/B+S3ofqDhpp3zz7gFxjkE1f4/XNACqXt79iGaH+EscV4znxlsZj\ngUQYAcExpAmKrJg5kmxagHcgu0pVKlyUvSba/kKQ/aYDgdddgPutH+UHs5pssc69\nYBpEXQTG9CMeRh6ZUgcrR0foJLM5g2k53xpG1oTHiJcCKARFZPRpDoZ6NjCIuFKY\n6+HMcpFRVDsDnUXmFah9bUhsSQbc6MHHX/iTbpMGNwIDAQABo2AwXjAPBgNVHRMB\nAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUhGQbAW97KXQs68Z3efEj\n55zsc4UwHwYDVR0jBBgwFoAUhGQbAW97KXQs68Z3efEj55zsc4UwDQYJKoZIhvcN\nAQELBQADggIBADsy7jhBmwGbOZPox0XvB2XzWjOPl3uI3Ys3uGaAXVbGVnP3nDtU\nwaGg7Fhf/ibQVAOkWLfm9FCJEO6bEojF4CjCa//iMqXgnPJaWeYceb8+CzuF5Ukg\nn/kfbj04dVvOnPa8KYkMOWQ6zsBgKuNaA5jOKWYwoHFgQNjKRiVikyOp6zF3aPu0\nwW7M7FOVHn0ZhMRBcJG8dGbQ8vaeu8z4i04tlvpQaFgtY66ECeUwhTIrvVuqtQOl\njR//w70TUTIH3JzzYmyCubOCjdqcNRYPRRiA/L+mdzrE7honSTQfo0iupT/5bJcu\nGRjLHL/aRvYrq8ogqQKIYW0EbVuFzHfb+kPV61Bf5APbA26GU/14XkA4KwzJnDMR\nd2wr0RivSceXtY2ZakYP6+2cqjuhk6Y0tl0FBuyQXqAbe1L7X2VctLJMi5UgksVB\nq5rdHSJ3fbHRoCUpj4/rSafqJNHlAf2MEE/q8l0D8JhYoN69RhvyFQJLFEU4c74b\nXHdFt6bfyxm4+ZzUdj/TXadPAUO1YfQCn9Tf7QOoR68acSvQxEDbChZlJYkdAE+C\nzxNcoHVc6XIpk7NIr09qTQ5viz736fV6EI6OIoUaqrz9u+NZ3sPPD2Gf+rOinVFQ\nR2Q5kxHYo8Kt1DK0fFcUe1cOZk3df7seQWw1OdJngp5S7gEWBiWg8zr7\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/certs/ca.crl.pem",
    "content": "-----BEGIN X509 CRL-----\nMIIDAjCB6wIBATANBgkqhkiG9w0BAQsFADCBoTELMAkGA1UEBhMCQ0ExEDAOBgNV\nBAgMB09udGFyaW8xEDAOBgNVBAcMB1Rvcm9udG8xGDAWBgNVBAoMD01hZ2ljU3Rh\nY2sgSW5jLjEWMBQGA1UECwwNYXN5bmNwZyB0ZXN0czEdMBsGA1UEAwwUYXN5bmNw\nZyB0ZXN0IHJvb3QgY2ExHTAbBgkqhkiG9w0BCQEWDmhlbGxvQG1hZ2ljLmlvFw0y\nMTA5MTQxNjA2MDFaFw0yMTA5MTUxNjA2MDFaMBUwEwICEAAXDTIxMDkxNDE2MDYw\nMVowDQYJKoZIhvcNAQELBQADggIBAL4yfNmvGS8SkIVbRzdAC9+XJPw/dBJOUJwr\nEgERICAz7OTqG1PkmMhPL00Dm9fe52+KnSwHgL749W0S/X5rTNMSwLyGiiJ5HYbH\nGFRKQ/cvXLi4jYpSI1Ac94kk0japf3SfwEw3+122oba8SiAVP0nY3bHpHvNfOaDV\nfhbFTwb5bFm6ThqlKLZxGCKP0fGeQ4homuwgRiLE/UOiue5ted1ph0PkKVui208k\nFnhNYXSllakTGT8ZZZZVid/4tSHqJEY9vbdMXNv1GX8mhjoU1Gv9dOuyFGgUc9Vx\ne7gzf/Wf36vKI29o8QGkkTslRZpMG59z3sG4Y0vJEoqXMB6eQLOr5iUCyj2CyDha\n66pwrdc1fRt3EvNXUWkdHfY3EHb7DxueedDEgtmfSNbEaZTXa5RaZRavNGNTaPDf\nUcrDU4w1N0wkYLQxPqd+VPcf1iKyfkAydpeOq9CChqRD0Tx58eTn6N/lLGFPPRfs\nx47BA4FmefBeXZzd5HiXCUouk3qHIHs2yCzFs+TEBkx5eV42cP++HxjirPydLf6Y\nG/o/TKRnc/2Lw+dCzvUV/p3geuw4+vq1BIFanwB9jp4tGaBrffIAyle8vPQLw6bp\n1o1O39pdxniz+c9r0Kw/ETxTqRLbasSib5FHq5G/G9a+QxPsLAzKgwLWhR4fXvbu\nYPbhYhRP\n-----END X509 CRL-----\n"
  },
  {
    "path": "tests/certs/ca.key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEAj/qApdKq0XkpVo/sr3xs+vFEKpuo6C/VYHgNE73PEttyGt0u\n98HE03RoSEMDDmiLlsvptyd47NsLaRdH5lC2B69RA+70PkDlmq08r/3BsUgsjmKO\nClTxwX0+pWEEyZOTVjnNsK+X3//Z4XTutbBsWWthlQY5myzifSASk70XJjYC5j6O\ntMDB7ReC+fz7sFT7rvs0aBE3xIddmr7WO2NhvtIuGUZuLgZsO4Zgcls1gsKq1etf\nQWhR2iVH9mxIeXgm89Z4Re1h1YwX50iToA8ZmWymWT5jc01ZvFMTdtoPQKOnGUsY\n2xQHaSIZUIQE+rI5VobyOm+fANwCSou86Pmu5Z2zyHnJHbkpIG4pdy2eCsVoMenz\nwgS6hh9VzeiPDEJ1/3tXsIhV8LmTG/hYKCsWkAbHrj1WgN1YmHn8rI7y4z+1PXkc\n7+qkYpjph+Xn2qZWw8xoUVCgPBoJjfwfkt6H6g4aad88+4BcY5BNX+P1zQAql7e/\nYhmh/hLHFeM58ZbGY4FEGAHBMaQJiqyYOZJsWoB3ILtKVSpclL0m2v5CkP2mA4HX\nXYD7rR/lB7OabLHOvWAaRF0ExvQjHkYemVIHK0dH6CSzOYNpOd8aRtaEx4iXAigE\nRWT0aQ6GejYwiLhSmOvhzHKRUVQ7A51F5hWofW1IbEkG3OjBx1/4k26TBjcCAwEA\nAQKCAgABseW8zf+TyrTZX4VeRX008Q0n4UA6R4HgClnBDz12T94Gge8RHJdYE+k8\nXImXLFTkWA8uyEispSF7wbnndLDH42D1RmVarEHnsb1ipv6WOy7HGFLqvThBWluX\n783yH4oe/Dw3JcIIcYcbl9hNjD+iR9jUu8eG057w8SU21wWEPiOHmVntt80woNO6\nZKeD2mRCGZPy260H474O2ctE1LUsXWYMhx857HpusvTEs90r5mXDcetjpjo8cq7n\nsDukLm1q9m3hCNvbezQ21UxjmHnpK/XDXDAohdMWG/ZBMmz2ilanvhITVieGLdAV\nehBi8SEqqxkD5hd9l5lxTjbRmUrdRZilnUKqup9WcOTQYeAZ2WAazyYuFqWAwSf+\ndU+SzMTG+7ts9y4RbnWL9H6hN2GWMeNdLRVqE4aECMv7kAIJZ2u6VyNXSEoVueBM\nCJ7CU075QgxNL1REDWRBaUaflBhdwQFnMXBULw2E01KZFmQvZLe06SI/xjkB7oGU\nHdqWRDx0YP8lrFG35ukA2t+EswJxcbZHsagEdrz0jjz0a87vjgHnff1XpowhZU6M\n4OgtQpoM4t4O7xg/sl80c0WwVvsOHVkGwUARCfZ4F2fXnocpYOCWQQbsA/SH/qJ8\nl+ChM4XkBNzKAUtpwkozqisKURJKTAJyeuAKD4fXRX/IwcPUYQKCAQEAyp1iiuTX\npXzDso+3WPxLr3kwYJSUxpxSP4EjZZvzJoVflFBttUOoLURPEMrK5tEqWHqRrJto\n73s3yQt4xWUtUql5eCB69nIVjseRhsbXjNzMIC41u65aflfIqQztHzF2gdFMZh3I\ngBp87CzKHSf83ToN3QZtQxIvuPdYdxDIjCMHc5hgRSLNKGhKXs1qWA76ASGNwQKW\n7nUflWfDG3yZ7sWtmz7T2djz2zsmmzppCRRVjHAxQWZ+TxW+KsBOpGzgNvteUese\nZK2ARc6lLSdgS74J5U6j07dOzQZ4eVC/OPHAIbPZxJAZ7/waP7YM+h+ohU+G8kXL\nKevnXjsC2oa/FwKCAQEAteoHugnwXvl9VyPceGQeffmQIq095CoD35UVlq60yR/9\nzgGN8mrXuEgGyydCYrK0/pUYb1pQhk5Xy1D6t5ou44uYlGuksWDqquRwgl7qMMVE\n0GAwm+3wUmz7u5XD3uEJaGWV+gbvg8Hbvl3V/MzjlI4caAZ3lcNaX/Jf3xG6Gyfi\nSo0iQzVMN6NR7m+I32YFB3jxu9PlzUTEj+9SCHuERFAozuzwjdLwiYjNMzv0zPWj\nv3ERO2mX6PE6yN1XkBsCGGG9qVz/ZzvKOz8Dl4TryY0a5eg4QUEZ3nUlnpq9/8M3\nxcN6M2yK8XLbTmVhSHX2J5nVI3s+BTbVHBoO0edl4QKCAQBcmMbTUThYkgdh0Jpr\nWYpBXHJGgUDo78IK8bq6kiXygdunjYZF4/C1F1XHB9bo28itfP6cUr4HTFm3UL3W\nAKJQ99DinH11qbe+c+hHHxKddr73Kgc2ib0jpny2/YhUzCcrtvpiZNQf73sN+H46\nCu9eL0zsqSZAE8ypjKjqaUot+UhLhOTiU8BM6jSq1Nf3/Ig3Ah2lishtnCtd/XjG\nVBCJdeAcZf8tvR/dHlBLestL8fYS46cvC2dIP1iUcyS9smBZ4FE/wOM4Aa7wuDr2\nwtsYYnZlTKZEeK7TtlRSpRtvK9Sx0l8AnRatfZqFaW7O1K8QlcLHcCwkMYKgpvlr\n407rAoIBAQCi5nqa1xGgCux53wwr5wQDLTssQlS8//7N9ZQKhlIwFOzT0EKLha+9\nPwqOW46wEXXQ0DS8anTXgEpQMCkDxxcb/sLYjfhCOxaJh91Ucahnmg+ARdLhn1Xo\nid124qsu5/fju6xs5E8RfsTHmQHpypQ1UHkRklD+FJzWdJXzjM1KShHzTqUS6CRj\nYmYZDVnVK2dvhJd76knL4jve5KFiJTGRdvLEMhtL9Uwe7RlMOvGBpKpI4fhbarh1\nCafpfYRO8FCVAtmzUysHB9yV51zRD1+R8kDXBndxv9lpgx/4AnwID4nfF6hTamyV\nwJOwhUpzd+bBGZlql483Xh3Cd3cz8nIhAoIBACs/XIDpXojtWopHXZReNwhqPC1D\nq3rjpPrZ8uqDu0Z/iTTO9OSvYaMBTVjXQ7w8T3X3ilMr45kpsHx0TQeh3Jbjy459\nS9z+6MtSIM0fbpYBEfa7sirDQM/ZlgZjm7vq/4lBVFGFIw7vxu4m/G0oHtihWRKh\nClGG1Ypm00srgWihhjtRn8hfnLqCi4t9xxW1q8Te01Gem8H0nfNKfs5V8O4cKIZa\nizrfne/1Fto1khYFTlP6XdVHPjvl2/qX2WUz4G+2eNWGQVghC70cuV8kiFYlEXVp\na6w2oSx8jo+5qRZrMlUQP5bE7dOBvZuoBmEi/FVfRYuFdxSZ3H2VAZKRgC4=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/certs/client.cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEAzCCAuugAwIBAgIUPfej8IQ/5bCrihqWImrq2vKPOq0wDQYJKoZIhvcNAQEL\nBQAwgaMxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYDVQQHDAdU\nb3JvbnRvMRgwFgYDVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsMDWFzeW5j\ncGcgdGVzdHMxHzAdBgNVBAMMFmFzeW5jcGcgdGVzdCBjbGllbnQgQ0ExHTAbBgkq\nhkiG9w0BCQEWDmhlbGxvQG1hZ2ljLmlvMB4XDTIxMDgwOTIxNTA1MloXDTMyMDEw\nNDIxNTA1MlowgZUxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYD\nVQQHDAdUb3JvbnRvMRgwFgYDVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsM\nDWFzeW5jcGcgdGVzdHMxETAPBgNVBAMMCHNzbF91c2VyMR0wGwYJKoZIhvcNAQkB\nFg5oZWxsb0BtYWdpYy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAJjiP9Ik/KRRLK9GMvoH8m1LO+Gyrr8Gz36LpmKJMR/PpwTL+1pOkYSGhOyT3Cw9\n/kWWLJRCvYqKgFtYtbr4S6ReGm3GdSVW+sfVRYDrRQZLPgQSPeq25g2v8UZ63Ota\nlPAyUPUZKpxyWz8PL77lV8psb9yv14yBH2kv9BbxKPksWOU8p8OCn1Z3WFFl0ItO\nnzMvCp5os+xFrt4SpoRGTx9x4QleY+zrEsYZtmnV4wC+JuJkNw4fuCdrX5k7dghs\nuZkcsAZof1nMdYsYiazeDfQKZtJqh5kO7mpwvCudKUWaLJJUwiQA87BwSlnCd/Hh\nTZDbC+zeFNjTS49/4Q72xVECAwEAAaM7MDkwHwYDVR0jBBgwFoAUi1jMmAisuOib\nmHIE2n0W2WnnaL0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAwDQYJKoZIhvcNAQEL\nBQADggEBACbnp5oOp639ko4jn8axF+so91k0vIcgwDg+NqgtSRsuAENGumHAa8ec\nYOks0TCTvNN5E6AfNSxRat5CyguIlJ/Vy3KbkkFNXcCIcI/duAJvNphg7JeqYlQM\nVIJhrO/5oNQMzzTw8XzTHnciGbrbiZ04hjwrruEkvmIAwgQPhIgq4H6umTZauTvk\nDEo7uLm7RuG9hnDyWCdJxLLljefNL/EAuDYpPzgTeEN6JAnOu0ULIbpxpJKiYEId\n8I0U2n0I2NTDOHmsAJiXf8BiHHmpK5SXFyY9s2ZuGkCzvmeZlR81tTXmHZ3v1X2z\n8NajoAZfJ+QD50DrbF5E00yovZbyIB4=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/certs/client.csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIC2zCCAcMCAQAwgZUxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAw\nDgYDVQQHDAdUb3JvbnRvMRgwFgYDVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNV\nBAsMDWFzeW5jcGcgdGVzdHMxETAPBgNVBAMMCHNzbF91c2VyMR0wGwYJKoZIhvcN\nAQkBFg5oZWxsb0BtYWdpYy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAJjiP9Ik/KRRLK9GMvoH8m1LO+Gyrr8Gz36LpmKJMR/PpwTL+1pOkYSGhOyT\n3Cw9/kWWLJRCvYqKgFtYtbr4S6ReGm3GdSVW+sfVRYDrRQZLPgQSPeq25g2v8UZ6\n3OtalPAyUPUZKpxyWz8PL77lV8psb9yv14yBH2kv9BbxKPksWOU8p8OCn1Z3WFFl\n0ItOnzMvCp5os+xFrt4SpoRGTx9x4QleY+zrEsYZtmnV4wC+JuJkNw4fuCdrX5k7\ndghsuZkcsAZof1nMdYsYiazeDfQKZtJqh5kO7mpwvCudKUWaLJJUwiQA87BwSlnC\nd/HhTZDbC+zeFNjTS49/4Q72xVECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCG\nirI2ph09V/4BMe6QMhjBFUatwmTa/05PYGjvT3LAhRzEb3/o/gca0XFSAFrE6zIY\nDsgMk1c8aLr9DQsn9cf22oMFImKdnIZ3WLE9MXjN+s1Bjkiqt7uxDpxPo/DdfUTQ\nRQC5i/Z2tn29y9K09lEjp35ZhPp3tOA0V4CH0FThAjRR+amwaBjxQ7TTSNfoMUd7\ni/DrylwnNg1iEQmYUwJYopqgxtwseiBUSDXzEvjFPY4AvZKmEQmE5QkybpWIfivt\n1kmKhvKKpn5Cb6c0D3XoYqyPN3TxqjH9L8R+tWUCwhYJeDZj5DumFr3Hw/sx8tOL\nEctyS6XfO3S2KbmDiyv8\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "tests/certs/client.key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAmOI/0iT8pFEsr0Yy+gfybUs74bKuvwbPfoumYokxH8+nBMv7\nWk6RhIaE7JPcLD3+RZYslEK9ioqAW1i1uvhLpF4abcZ1JVb6x9VFgOtFBks+BBI9\n6rbmDa/xRnrc61qU8DJQ9RkqnHJbPw8vvuVXymxv3K/XjIEfaS/0FvEo+SxY5Tyn\nw4KfVndYUWXQi06fMy8Knmiz7EWu3hKmhEZPH3HhCV5j7OsSxhm2adXjAL4m4mQ3\nDh+4J2tfmTt2CGy5mRywBmh/Wcx1ixiJrN4N9Apm0mqHmQ7uanC8K50pRZosklTC\nJADzsHBKWcJ38eFNkNsL7N4U2NNLj3/hDvbFUQIDAQABAoIBAAIMVeqM0E2rQLwA\nZsJuxNKuBVlauXiZsMHzQQFk8SGJ+KTZzr5A+zYZT0KUIIj/M57fCi3aTwvCG0Ie\nCCE/HlRPZm8+D2e2qJlwxAOcI0qYS3ZmgCna1W4tgz/8eWU1y3UEV41RDv8VkR9h\nJrSaAfkWRtFgEbUyLaeNGuoLxQ7Bggo9zi1/xDJz/aZ/y4L4y8l1xs2eNVmbRGnj\nmPr1daeYhsWgaNiT/Wm3CAxvykptHavyWSsrXzCp0bEw6fAXxBqkeDFGIMVC9q3t\nZRFtqMHi9i7SJtH1XauOC6QxLYgSEmNEie1JYbNx2Zf4h2KvSwDxpTqWhOjJ/m5j\n/NSkASECgYEAyHQAqG90yz5QaYnC9lgUhGIMokg9O3LcEbeK7IKIPtC9xINOrnj6\necCfhfc1aP3wQI+VKC3kiYerfTJvVsU5CEawBQSRiBY/TZZ7hTR7Rkm3s4xeM+o6\n2zADdVUwmTVYwu0gUKCeDKO4iD8Uhh8J54JrKUejuG50VWZQWGVgqo0CgYEAwz+2\nVdYcfuQykMA3jQBnXmMMK92/Toq6FPDgsa45guEFD6Zfdi9347/0Ipt+cTNg0sUZ\nYBLOnNPwLn+yInfFa88Myf0UxCAOoZKfpJg/J27soUJzpd/CGx+vaAHrxMP6t/qo\nJAGMBIyOoqquId7jvErlC/sGBk/duya7IdiT1tUCgYBuvM8EPhaKlVE9DJL9Hmmv\nPK94E2poZiq3SutffzkfYpgDcPrNnh3ZlxVJn+kMqITKVcfz226On7mYP32MtQWt\n0cc57m0rfgbYqRJx4y1bBiyK7ze3fGWpYxv1/OsNKJBxlygsAp9toiC2fAqtkYYa\nNE1ZD6+dmr9/0jb+rnq5nQKBgQCtZvwsp4ePOmOeItgzJdSoAxdgLgQlYRd6WaN0\nqeLx1Z6FE6FceTPk1SmhQq+9IYAwMFQk+w78QU3iPg6ahfyTjsMw8M9sj3vvCyU1\nLPGJt/34CehjvKHLLQy/NlWJ3vPgSYDi2Wzc7WgQF72m3ykqpOlfBoWHPY8TE4bG\nvG4wMQKBgFSq2GDAJ1ovBl7yWYW7w4SM8X96YPOff+OmI4G/8+U7u3dDM1dYeQxD\n7BHLuvr4AXg27LC97u8/eFIBXC1elbco/nAKE1YHj2xcIb/4TsgAqkcysGV08ngi\ndULh3q0GpTYyuELZV4bfWE8MjSiGAH+nuMdXYDGuY2QnBq8MdSOH\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/certs/client.key.protected.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,B222CD7D00828606A07DBC489D400921\n\nLRHsNGUsD5bG9+x/1UlzImN0rqEF10sFPBmxKeQpXQ/hy4iR+X/Gagoyagi23wOn\nEZf0sCLJx95ixG+4fXJDX0jgBtqeziVNS4FLWHIuf3+blja8nf4tkmmH9pF8jFQ0\ni1an3TP6KRyDKa17gioOdtSsS51BZmPkp3MByJQsrMhyB0txEUsGtUMaBTYmVN/5\nuYHf9MsmfcfQy30nt2t6St6W82QupHHMOx5xyhPJo8cqQncZC7Dwo4hyDV3h3vWn\nUjaRZiEMmQ3IgCwfJd1VmMECvrwXd/sTOXNhofWwDQIqmQ3GGWdrRnmgD863BQT3\nV8RVyPLkutOnrZ/kiMSAuiXGsSYK0TV8F9TaP/abLob4P8jbKYLcuR7ws3cu1xBl\nXWt9RALxGPUyHIy+BWLXJTYL8T+TVJpiKsAGCQB54j8VQBSArwFL4LnzdUu1txe2\nqa6ZEwt4q6SEwOTJpJWz3oJ1j+OTsRCN+4dlyo7sEZMeyTRp9nUzwulhd+fOdIhY\n2UllMG71opKfNxZzEW7lq6E/waf0MmxwjUJmgwVO218yag9oknHnoFwewF42DGY7\n072h23EJeKla7sI+MAB18z01z6C/yHWXLybOlXaGqk6zOm3OvTUFnUXtKzlBO2v3\nFQwrOE5U/VEyQkNWzHzh4j4LxYEL9/B08PxaveUwvNVGn9I3YknE6uMfcU7VuxDq\n+6bgM6r+ez+9QLFSjH/gQuPs2DKX0h3b9ppQNx+MANX0DEGbGabJiBp887f8pG6Q\ntW0i0+rfzYz3JwnwIuMZjYz6qUlP4bJMEmmDfod3fbnvg3MoCSMTUvi1Tq3Iiv4L\nGM5/YNkL0V3PhOI686aBfU7GLGXQFhdbQ9xrSoQRBmmNBqTCSf+iIEoTxlBac8GQ\nvSzDO+A+ovBP36K13Yn7gzuN/3PLZXH2TZ8t2b/OkEXOciH5KbycGHQA7gqxX1P4\nJ55gpqPAWe8e7wKheWj3BMfmbWuH4rpiEkrLpqbTSfTwIKqplk253chmJj5I82XI\nioFLS5vCi9JJsTrQ720O+VQPVB5xeA80WL8NxamWQb/KkvVnb4dTmaV30RCgLLZC\ntuMx8YSW71ALLT15qFB2zlMDKZO1jjunNE71BUFBPIkTKEOCyMAiF60fFeIWezxy\nkvBBOg7+MTcZNeW110FqRWNGr2A5KYFN15g+YVpfEoF26slHisSjVW5ndzGh0kaQ\nsIOjQitA9JYoLua7sHvsr6H5KdCGjNxv7O7y8wLGBVApRhU0wxZtbClqqEUvCLLP\nUiLDp9L34wDL7sGrfNgWA4UuN29XQzTxI5kbv/EPKhyt2oVHLqUiE+eGyvnuYm+X\nKqFi016nQaxTU5Kr8Pl0pSHbJMLFDWLSpsbbTB6YJpdEGxJoj3JB3VncOpwcuK+G\nxZ1tV2orPt1s/6m+/ihzRgoEkyLwcLRPN7ojgD/sqS679ZGf1IkDMgFCQe4g0UWm\nFw7v816MNCgypUM5hQaU+Jp8vSlEc29RbrdSHbcxrKj/xPCLWrAbvmI5tgonKmuJ\nJ1LW8AXyh/EUp/uUh++jqVGx+8pFfcmJw6V6JrJzQ7HMlakkry7N1eAGrIJGtYCW\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/certs/client_ca.cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEKTCCAxGgAwIBAgIUKmL8tfNS9LIB6GLB9RpZpTyk3uIwDQYJKoZIhvcNAQEL\nBQAwgaMxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYDVQQHDAdU\nb3JvbnRvMRgwFgYDVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsMDWFzeW5j\ncGcgdGVzdHMxHzAdBgNVBAMMFmFzeW5jcGcgdGVzdCBjbGllbnQgQ0ExHTAbBgkq\nhkiG9w0BCQEWDmhlbGxvQG1hZ2ljLmlvMB4XDTIxMDgwOTIxNDQxM1oXDTQxMDgw\nNDIxNDQxM1owgaMxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYD\nVQQHDAdUb3JvbnRvMRgwFgYDVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsM\nDWFzeW5jcGcgdGVzdHMxHzAdBgNVBAMMFmFzeW5jcGcgdGVzdCBjbGllbnQgQ0Ex\nHTAbBgkqhkiG9w0BCQEWDmhlbGxvQG1hZ2ljLmlvMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAptRYfxKiWExfZguQDva53bIqYa4lJwZA86Qu0peBUcsd\nE6zyHNgVv4XSMim1FH12KQ4KPKuQAcVqRMCRAHqB96kUfWQqF//fLajr0umdzcbx\n+UTgNux8TkScTl9KNAxhiR/oOGbKFcNSs4raaG8puwwEN66uMhoKk2pN2NwDVfHa\nbTekJ3jouTcTCnqCynx4qwI4WStJkuW4IPCmDRVXxOOauT7YalElYLWYtAOqGEvf\nnoDK2Imhc0h6B5XW8nI54rVCXWwhW1v3RLAJGP+LwSy++bf08xmpHXdKkAj5BmUO\nQwJRiJ33Xa17rmi385egx8KpqV04YEAPdV1Z4QM6PQIDAQABo1MwUTAdBgNVHQ4E\nFgQUi1jMmAisuOibmHIE2n0W2WnnaL0wHwYDVR0jBBgwFoAUi1jMmAisuOibmHIE\n2n0W2WnnaL0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAifNE\nZLZXxECp2Sl6jCViZxgFf2+OHDvRORgI6J0heckYyYF/JHvLaDphh6TkSJAdT6Y3\nhAb7jueTMI+6RIdRzIjTKCGdJqUetiSfAbnQyIp2qmVqdjeFoXTvQL7BdkIE+kOW\n0iomMqDB3czTl//LrgVQCYqKM0D/Ytecpg2mbshLfpPxdHyliCJcb4SqfdrDnKoV\nHUduBjOVot+6bkB5SEGCrrB4KMFTzbAu+zriKWWz+uycIyeVMLEyhDs59vptOK6e\ngWkraG43LZY3cHPiVeN3tA/dWdyJf9rgK21zQDSMB8OSH4yQjdQmkkvRQBjp3Fcy\nw2SZIP4o9l1Y7+hMMw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/certs/client_ca.cert.srl",
    "content": "3DF7A3F0843FE5B0AB8A1A96226AEADAF28F3AAD\n"
  },
  {
    "path": "tests/certs/client_ca.key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAptRYfxKiWExfZguQDva53bIqYa4lJwZA86Qu0peBUcsdE6zy\nHNgVv4XSMim1FH12KQ4KPKuQAcVqRMCRAHqB96kUfWQqF//fLajr0umdzcbx+UTg\nNux8TkScTl9KNAxhiR/oOGbKFcNSs4raaG8puwwEN66uMhoKk2pN2NwDVfHabTek\nJ3jouTcTCnqCynx4qwI4WStJkuW4IPCmDRVXxOOauT7YalElYLWYtAOqGEvfnoDK\n2Imhc0h6B5XW8nI54rVCXWwhW1v3RLAJGP+LwSy++bf08xmpHXdKkAj5BmUOQwJR\niJ33Xa17rmi385egx8KpqV04YEAPdV1Z4QM6PQIDAQABAoIBABQrKcO7CftoyEO6\n9CCK/W9q4arLddxg6itKVwrInC66QnqlduO7z+1GjWHZHvYqMMXH17778r30EuPa\n7+zB4sKBI2QBXwFlwqJvgIsQCS7edVRwWjbpoiGIM+lZpcvjD0uXmuhurNGyumXQ\nTJVBkyb0zfG5YX/XHB40RNMJzjFuiMPDLVQmmDE//FOuWqBG88MgJP9Ghk3J7wA2\nJfDPavb49EzOCSh74zJWP7/QyybzF3ABCMu4OFkaOdqso8FS659XI55QReBbUppu\nFRkOgao1BclJhbBdrdtLNjlETM82tfVgW56vaIrrU2z7HskihEyMdB4c+CYbBnPx\nQqIhkhUCgYEA0SLVExtNy5Gmi6/ZY9tcd3QIuxcN6Xiup+LgIhWK3+GIoVOPsOjN\n27dlVRINPKhrCfVbrLxUtDN5PzphwSA2Qddm4jg3d5FzX+FgKHQpoaU1WjtRPP+w\nK+t6W/NbZ8Rn4JyhZQ3Yqj264NA2l3QmuTfZSUQ5m4x7EUakfGU7G1sCgYEAzDaU\njHsovn0FedOUaaYl6pgzjFV8ByPeT9usN54PZyuzyc+WunjJkxCQqD88J9jyG8XB\n3V3tQj/CNbMczrS2ZaJ29aI4b/8NwBNR9e6t01bY3B90GJi8S4B4Hf8tYyIlVdeL\ntCC4FCZhvl4peaK3AWBj4NhjvdB32ThDXSGxLEcCgYEAiA5tKHz+44ziGMZSW1B+\nm4f1liGtf1Jv7fD/d60kJ/qF9M50ENej9Wkel3Wi/u9ik5v4BCyRvpouKyBEMGxQ\nYA1OdaW1ECikMqBg+nB4FR1x1D364ABIEIqlk+SCdsOkANBlf2S+rCJ0zYUnvuhl\nuOHIjo3AHJ4MAnU+1V7WUTkCgYBkMedioc7U34x/QJNR3sY9ux2Xnh2zdyLNdc+i\nnjeafDPDMcoXhcoJERiYpCYEuwnXHIlI7pvJZHUKWe4pcTsI1NSfIk+ki7SYaCJP\nkyLQTY0rO3d/1fiU5tyIgzomqIs++fm+kEsg/8/3UkXxOyelUkDPAfy2FgGnn1ZV\n7ID8YwKBgQCeZCapdGJ6Iu5oYB17TyE5pLwb+QzaofR5uO8H4pXGVQyilKVCG9Dp\nGMnlXD7bwXPVKa8Icow2OIbmgrZ2mzOo9BSY3BlkKbpJDy7UNtAhzsHHN5/AEk8z\nYycWQtMiXI+cRsYO0eyHhJeSS2hX+JTe++iZX65twV53agzCHWRIbg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/certs/gen.py",
    "content": "import datetime\nimport os\n\nfrom cryptography import x509\nfrom cryptography.hazmat import backends\nfrom cryptography.hazmat.primitives import hashes\nfrom cryptography.hazmat.primitives import serialization\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.x509 import oid\n\n\ndef _new_cert(issuer=None, is_issuer=False, serial_number=None, **subject):\n    backend = backends.default_backend()\n    private_key = rsa.generate_private_key(\n        public_exponent=65537, key_size=4096, backend=backend\n    )\n    public_key = private_key.public_key()\n    subject = x509.Name(\n        [\n            x509.NameAttribute(getattr(oid.NameOID, key.upper()), value)\n            for key, value in subject.items()\n        ]\n    )\n    builder = (\n        x509.CertificateBuilder()\n        .subject_name(subject)\n        .public_key(public_key)\n        .serial_number(serial_number or int.from_bytes(os.urandom(8), \"big\"))\n    )\n    if issuer:\n        issuer_cert, signing_key = issuer\n        builder = (\n            builder.issuer_name(issuer_cert.subject)\n            .not_valid_before(issuer_cert.not_valid_before)\n            .not_valid_after(issuer_cert.not_valid_after)\n        )\n        aki_ext = x509.AuthorityKeyIdentifier(\n            key_identifier=issuer_cert.extensions.get_extension_for_class(\n                x509.SubjectKeyIdentifier\n            ).value.digest,\n            authority_cert_issuer=[x509.DirectoryName(issuer_cert.subject)],\n            authority_cert_serial_number=issuer_cert.serial_number,\n        )\n    else:\n        signing_key = private_key\n        builder = (\n            builder.issuer_name(subject)\n            .not_valid_before(\n                datetime.datetime.today() - datetime.timedelta(days=1)\n            )\n            .not_valid_after(\n                datetime.datetime.today() + datetime.timedelta(weeks=1000)\n            )\n        )\n        aki_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(\n            public_key\n        )\n    if is_issuer:\n        builder = (\n            builder.add_extension(\n                x509.BasicConstraints(ca=True, path_length=None),\n                critical=True,\n            )\n            .add_extension(\n                x509.KeyUsage(\n                    digital_signature=False,\n                    content_commitment=False,\n                    key_encipherment=False,\n                    data_encipherment=False,\n                    key_agreement=False,\n                    key_cert_sign=True,\n                    crl_sign=True,\n                    encipher_only=False,\n                    decipher_only=False,\n                ),\n                critical=False,\n            )\n            .add_extension(\n                x509.SubjectKeyIdentifier.from_public_key(public_key),\n                critical=False,\n            )\n            .add_extension(\n                aki_ext,\n                critical=False,\n            )\n        )\n    else:\n        builder = (\n            builder.add_extension(\n                x509.KeyUsage(\n                    digital_signature=True,\n                    content_commitment=False,\n                    key_encipherment=True,\n                    data_encipherment=False,\n                    key_agreement=False,\n                    key_cert_sign=False,\n                    crl_sign=False,\n                    encipher_only=False,\n                    decipher_only=False,\n                ),\n                critical=False,\n            )\n            .add_extension(\n                x509.BasicConstraints(ca=False, path_length=None),\n                critical=True,\n            )\n            .add_extension(\n                x509.ExtendedKeyUsage([oid.ExtendedKeyUsageOID.SERVER_AUTH]),\n                critical=False,\n            )\n            .add_extension(\n                x509.SubjectAlternativeName([x509.DNSName(\"localhost\")]),\n                critical=False,\n            )\n            .add_extension(\n                x509.SubjectKeyIdentifier.from_public_key(public_key),\n                critical=False,\n            )\n            .add_extension(\n                aki_ext,\n                critical=False,\n            )\n        )\n    certificate = builder.sign(\n        private_key=signing_key,\n        algorithm=hashes.SHA256(),\n        backend=backend,\n    )\n    return certificate, private_key\n\n\ndef _write_cert(path, cert_key_pair, password=None):\n    certificate, private_key = cert_key_pair\n    if password:\n        encryption = serialization.BestAvailableEncryption(password)\n    else:\n        encryption = serialization.NoEncryption()\n    with open(path + \".key.pem\", \"wb\") as f:\n        f.write(\n            private_key.private_bytes(\n                encoding=serialization.Encoding.PEM,\n                format=serialization.PrivateFormat.TraditionalOpenSSL,\n                encryption_algorithm=encryption,\n            )\n        )\n    with open(path + \".cert.pem\", \"wb\") as f:\n        f.write(\n            certificate.public_bytes(\n                encoding=serialization.Encoding.PEM,\n            )\n        )\n\n\ndef new_ca(path, **subject):\n    cert_key_pair = _new_cert(is_issuer=True, **subject)\n    _write_cert(path, cert_key_pair)\n    return cert_key_pair\n\n\ndef new_cert(\n    path, ca_cert_key_pair, password=None, is_issuer=False, **subject\n):\n    cert_key_pair = _new_cert(\n        issuer=ca_cert_key_pair, is_issuer=is_issuer, **subject\n    )\n    _write_cert(path, cert_key_pair, password)\n    return cert_key_pair\n\n\ndef new_crl(path, issuer, cert):\n    issuer_cert, signing_key = issuer\n    revoked_cert = (\n        x509.RevokedCertificateBuilder()\n        .serial_number(cert[0].serial_number)\n        .revocation_date(datetime.datetime.today())\n        .build()\n    )\n    builder = (\n        x509.CertificateRevocationListBuilder()\n        .issuer_name(issuer_cert.subject)\n        .last_update(datetime.datetime.today())\n        .next_update(datetime.datetime.today() + datetime.timedelta(days=1))\n        .add_revoked_certificate(revoked_cert)\n    )\n    crl = builder.sign(private_key=signing_key, algorithm=hashes.SHA256())\n    with open(path + \".crl.pem\", \"wb\") as f:\n        f.write(crl.public_bytes(encoding=serialization.Encoding.PEM))\n\n\ndef main():\n    ca = new_ca(\n        \"ca\",\n        country_name=\"CA\",\n        state_or_province_name=\"Ontario\",\n        locality_name=\"Toronto\",\n        organization_name=\"MagicStack Inc.\",\n        organizational_unit_name=\"asyncpg tests\",\n        common_name=\"asyncpg test root ca\",\n        email_address=\"hello@magic.io\",\n    )\n    server = new_cert(\n        \"server\",\n        ca,\n        country_name=\"CA\",\n        state_or_province_name=\"Ontario\",\n        organization_name=\"MagicStack Inc.\",\n        organizational_unit_name=\"asyncpg tests\",\n        common_name=\"localhost\",\n        email_address=\"hello@magic.io\",\n        serial_number=4096,\n    )\n    new_crl('server', ca, server)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "tests/certs/server.cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIG5jCCBM6gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgaExCzAJBgNVBAYTAkNB\nMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYDVQQHDAdUb3JvbnRvMRgwFgYDVQQKDA9N\nYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsMDWFzeW5jcGcgdGVzdHMxHTAbBgNVBAMM\nFGFzeW5jcGcgdGVzdCByb290IGNhMR0wGwYJKoZIhvcNAQkBFg5oZWxsb0BtYWdp\nYy5pbzAeFw0yNDEwMTYxNzIzNTZaFw00MzEyMTcxNzIzNTZaMIGEMQswCQYDVQQG\nEwJDQTEQMA4GA1UECAwHT250YXJpbzEYMBYGA1UECgwPTWFnaWNTdGFjayBJbmMu\nMRYwFAYDVQQLDA1hc3luY3BnIHRlc3RzMRIwEAYDVQQDDAlsb2NhbGhvc3QxHTAb\nBgkqhkiG9w0BCQEWDmhlbGxvQG1hZ2ljLmlvMIICIjANBgkqhkiG9w0BAQEFAAOC\nAg8AMIICCgKCAgEA3F017q/obCM1SsHY5dFz72pFgVMhBIZ6kdIInbFv7RmEykZz\nubbJnrgwgYDO5FKGUNO+a80AbjIvBrtPtXs9Ip/QDg0jqgw/MOADCxCzYnAQ2Ew2\ny1PfspGtdPhLNTmrO8+AxU2XmjsYY0+ysgUQQttOs9hJ79pIsKGBEES8g9oJTiIf\ntKgCxCIuhiZC+AgjeIQZUB9ccifmOGrCJYrD6LBuNGoQNW2/ykqjuHE8219dv1hV\ndo8azcp/WmejjQguZyU3S/AofnyyNE24rWpXbbFs+9FFaUXd8g/fWCwrRmcXpOaE\nlvkmMZyuT9kuglHsvpzzGGNSUpvVoPfldk/4JY/kJrA2G5pgTX6mGRYGEN0jmlCa\nyg/ZFn36G0mA5ZBH4Qln+lKUSjJH8bhlFXvXlE3Mc34OCdOAp1TRfOT/qCRKo9A5\nKCjVOvG5MAKE8TZnTFLCSx5gK/EdQ2iV7Sm3aVc2P4eEJh+nvv1LDVLQEAak6U+u\nsZN5+Wnu7wDKSlh80vTTtoqls5Uo3gIxHYnqX5Fj6nwCzGjjXISNE4OKZLuk3can\nmciEES3plUrut+O6a2JWiDoCrwX4blYXhtL92Xaer/Mk1TSf2JsmL6pODoapsA0S\nCHtpcgoodxdKriy1qUGsiNlPNVWjASGyKXoEZdv49wyoZuysudl1aS1w42UCAwEA\nAaOCAUEwggE9MAsGA1UdDwQEAwIFoDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG\nCCsGAQUFBwMBMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAdBgNVHQ4EFgQUO/cXg1uX\n2oHZodbw6F3/HakLdaQwgdUGA1UdIwSBzTCByoAUhGQbAW97KXQs68Z3efEj55zs\nc4WhgaekgaQwgaExCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMRAwDgYD\nVQQHDAdUb3JvbnRvMRgwFgYDVQQKDA9NYWdpY1N0YWNrIEluYy4xFjAUBgNVBAsM\nDWFzeW5jcGcgdGVzdHMxHTAbBgNVBAMMFGFzeW5jcGcgdGVzdCByb290IGNhMR0w\nGwYJKoZIhvcNAQkBFg5oZWxsb0BtYWdpYy5pb4IICJCUmtkcj2MwDQYJKoZIhvcN\nAQELBQADggIBAD4Ti52nEttUNay+sqqbDLtnSyMRsJI8agPqiHz6bYifSf530rlh\nqlHYUY5tgfrd8yDZNIe9Ib7Q1WQjgR8c/T9SoFnLl/tff1CVOAYQ/ffCZGTdBOSc\nKfdKEEvObWxWsqv31ZAMWVzfPsF7rwbTbZ8YdH2CNjxbZxrSEn2IrjplsoP5WMsE\n6t7Q+J5wpi2yiEI9PoY2wH5WBB8ONWvZfj9r6OrczlTEZ+L6eiip5kMiw5R9EVt6\nju2aMWqbZTI49Mu/qvXRAkwYvX7mrhuW/4mPHOW/zSnN7hOyjntx1fdnpPD5BTT6\nCoJ7nhWgnntw2kk2V9UBCYpVeqidDRrs+nr1xSpduuM1ve3SDkIpd6EGEUqZJ12s\n5xpCUFK67atCZOXbJXqanm+3N9kbqYuwkWoqnPjOfMYW7oABmUy8elVGGwTuiTI0\nsXS3aQJ+Bm7oqSXrIxUTjOUUaYNhhaqZdXaO/29vI2+i975Pt1ZLLPUkp0hsUgTT\nkryN02TlNTxxQafTWad6YdzyrwvMpV7vxf7JQkOKRwLinqLCDVxjBt66O9mLIpQF\nWIfWQG+X4sgobB0NTtBWeGkrIgnhUtsT0ibVm4JAC1cbxdLOq2dfcURC8UFWJXok\nyFr/uaDZiKKbUFXbalZwnx6H6ucfl5No3hheexadyIbPNcHhFJ9zGXot\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "tests/certs/server.crl.pem",
    "content": "-----BEGIN X509 CRL-----\nMIIDAjCB6wIBATANBgkqhkiG9w0BAQsFADCBoTELMAkGA1UEBhMCQ0ExEDAOBgNV\nBAgMB09udGFyaW8xEDAOBgNVBAcMB1Rvcm9udG8xGDAWBgNVBAoMD01hZ2ljU3Rh\nY2sgSW5jLjEWMBQGA1UECwwNYXN5bmNwZyB0ZXN0czEdMBsGA1UEAwwUYXN5bmNw\nZyB0ZXN0IHJvb3QgY2ExHTAbBgkqhkiG9w0BCQEWDmhlbGxvQG1hZ2ljLmlvFw0y\nNDEwMTcxNzIzNTZaFw0yNDEwMTgxNzIzNTZaMBUwEwICEAAXDTI0MTAxNzE3MjM1\nNlowDQYJKoZIhvcNAQELBQADggIBAEVNX72KK6etoZQOXzPgd8ZJNrYcsOwjNZFL\nZxC47uX+yrxjv7Wrrk4feyakFi5bL9n8/JMggcpxC6yxMQH/sdOZJ0BzKw3GUAxj\nm53i1GGO1lGdKH5a7uDPZVW362JwCVE81ROCdb1SL/yYmIwhD4w2bqjOQuI63Xe1\nMDfVZBqcIwzzkA5PEjTSFQIsBcHU+rDrWggkz/XJh5alRav8Gnj7KTE8U1z5UeKV\nLUk8L8+ZLW6XlrTnyjOn3qT7sZw2C/R46GCyHWwT5tbLhJhm2u1EuX3Iids02vIP\nw9bYf7+Uu2lsse9TuFNXtW0UFLdvVezomHjNBCaMI/MIvG4wSWnAo5bTtlowzxSy\n7rpQQYBebcl5somUAhHqs4dsxbEwCXMPDdapiXkhxR9R4nDvkfsgwyqIRsWsIEq6\nPFjjRySNFUg5/vqhVQrg0hV7ygzXfd/kIlud3ZkKnli51TuFMWKD5sMN0r8ITLdG\nusoJQiF6G3ByLQBnsiQoHbipWkWTOKmfB/cfaPXdagPZH6rQmJeeNq0vBy6VqbFi\n7D+BqABs+yIT6uJEEqyPGJttkUZP+0ziaK+DZF4MgJtiERtz2GjKMeh3h/YSqA27\n8El6na7hPA3k1pANkaOaKuxZYzrPsl3P91ISGL6E0dgd6f9NZMOxbhfNKoDsBJnd\nHjb3RTY4\n-----END X509 CRL-----\n"
  },
  {
    "path": "tests/certs/server.key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEA3F017q/obCM1SsHY5dFz72pFgVMhBIZ6kdIInbFv7RmEykZz\nubbJnrgwgYDO5FKGUNO+a80AbjIvBrtPtXs9Ip/QDg0jqgw/MOADCxCzYnAQ2Ew2\ny1PfspGtdPhLNTmrO8+AxU2XmjsYY0+ysgUQQttOs9hJ79pIsKGBEES8g9oJTiIf\ntKgCxCIuhiZC+AgjeIQZUB9ccifmOGrCJYrD6LBuNGoQNW2/ykqjuHE8219dv1hV\ndo8azcp/WmejjQguZyU3S/AofnyyNE24rWpXbbFs+9FFaUXd8g/fWCwrRmcXpOaE\nlvkmMZyuT9kuglHsvpzzGGNSUpvVoPfldk/4JY/kJrA2G5pgTX6mGRYGEN0jmlCa\nyg/ZFn36G0mA5ZBH4Qln+lKUSjJH8bhlFXvXlE3Mc34OCdOAp1TRfOT/qCRKo9A5\nKCjVOvG5MAKE8TZnTFLCSx5gK/EdQ2iV7Sm3aVc2P4eEJh+nvv1LDVLQEAak6U+u\nsZN5+Wnu7wDKSlh80vTTtoqls5Uo3gIxHYnqX5Fj6nwCzGjjXISNE4OKZLuk3can\nmciEES3plUrut+O6a2JWiDoCrwX4blYXhtL92Xaer/Mk1TSf2JsmL6pODoapsA0S\nCHtpcgoodxdKriy1qUGsiNlPNVWjASGyKXoEZdv49wyoZuysudl1aS1w42UCAwEA\nAQKCAgAXD9TfxfPCXWzrsJ3NGhPSr9crpvzYRw/3cs5esn3O3Sd92SGuAz3WfoWV\nCAX0SdlaBs7xjo1yUDjbsNQGtNRmaz3lj+Ug8WcrlkYQl7mDnnbPgX+6h8HsI5LO\nSwM+mWpyN/p3Vkd8vJ0wx4Z2sFD4rjruV2m60FK11DEi+A6X6JmmCQGIcTeDjzrk\njzHdrfxdqyAlt80qT+1Sui7XVE5sa7Uc3HzAcAaXr81dNXyeThIMPxJdS1y4F258\nkkbA27pU0Rrtt5SFUvIoxyQsrJRkcSJsDYVWHxm7MNi5luXF2G7WXcmX2JCcCz8I\nMZJ3JlvAbGyEgOB8r2e2u5AoHEu7xjpjJ0/6smmig7LDe96uNpg6zDwS3xl6rAup\nqgwJ5TTwY8BydVOtDqe5Na8yqLtwMr0yA+k2Hz856mzCTJEOI9TaOq/jtq+n4AXW\nlkBai762oVKSKYCVJSK6eslTf2bAqjT3jakbgqJLKmMo5XvCnYUWWIve0RhQMNT4\n0tiLCxKurYa7xPqgW26c/fEHvdBDrU1JAablcAjsW9sJ+KIlilK02M9DqF0RnBBI\nwK7Ql76ugsYbp8WBXkpFjMMyciMhqH8xJiyi7MuiCwpBGQwxBHHaX7f9OqDWOClR\nmVGjrZuk9oiI3waUjGG50SzLBlMbeIzMdXgRuM7fByq6DG0VgQKCAQEA8d2YCODh\nApCM7GB/tmANfVQ0tnfxUT3ceEAOH7XkI+nz87Zv/1k6NOklCMi+nUwoGQfM5CxU\nNdWC0I7wI1ATdllPStUAJ4c8xtdEdlrLHBcGNvhYbbqMWRsNGITstnAx3tZ4X32H\nduhS5wfPE/X25YMN+8Dtm7jifEMqoCUV55iZxfYs+LXxQF03KVAJ5Ie5a1ac5UCz\nzzu9fbYSs70ByJsHWt4ZOsPkJVmkmuXzUPvr72otUYYSdju0PgbJqRoEyTbCh3HT\nzo0emKl8jj7oTSzVNjb6AaB6nsKco6wQLQSlaxBzo0j7TBRylVtG81CYjr5LFpp0\nUQrHjLZnSTvC5wKCAQEA6T3yH6bFc9FcJGOW1jYozQ5y+NWkXv3MVFIf3IqPT76p\nrMEI6krmGUKi+otOaV2Axy36kOcbntzENMg++LPCe0SczK14+pwUrI91cp/Ega6K\n+/4sKvh8WDZhzVYkWs76UiRj7Ef4MvtsaPAcFN/Ek+fItDHFRoSGdm+vx+j3ZDxx\ntdRudTs0kYyhmdlM0kZTbXsmz37x6+45uO16s+D2lvX2PXM9Lve9z/Ti6nn9QvIF\nkM9ZmAU6epmMPsGKM9WOK/sTcPUnd3Ife9tmi3BRAAygDk6hFx67kAsc124oLeZ3\n0CJGshA+50hBAL7wiybLrBMRzHrElzsicppVbn3p0wKCAQAldmRBI8vWYNtjFYNS\nlUghnHRZuvRG2CUY/xrw8HR415jwq9ZnH8PzRBV3adiUdqJTVjD3OqKEgCC1+x3Y\n6mNJVoYAmkNe3ASe6+LvzhpdrHdK9maEAHwSpSz/Gj+r9m7TDDcy2zerRErq+/uo\nJNXsMMNutjBXiWiTRLgKfBQLfkh7MClBELVgec+8d2hA3IDszkqY+8+eDqvIF/aH\nnoPzNYgLHBGeV48z9dGYKHvqlEq0F6cTVIfxhkfhv51msuAA5pl07z2WZadSkBX5\n1maW5ZXUwukwbVHw20X12AXdYzXYAoFWzkwWOaiR18SClX47xd/NjXjswJWuBuay\noi4LAoIBAQDirP0+nYmQAYwXIWJaVNBaWQyLoLXaS7XkzNuCLncQ/S9RYVkUui3d\nptFVxUUzSVf6O0kkwjYpskxNL79jXPBJdGke0gidJktBWTq/Z15G2ibguCicqlnO\nMSvjrzAtwLGuWwdxfpBMm+TEJ3ZjIwWc6Mo5tZUP74PuXqTrGBI2LDgmiom/DQcN\n3SrAplrukMJLyD/zsF/U9vTKMKHrZ1q/Y9Mn7XMszkB+dnSBhIUKJsQZ9CoSgCJR\nPCD8bIOv1IATZjOCt/7fKt5GNPf30/QkpCB5RxlvqsKGPwaMp9YMpcsTT/x82SUJ\nCUODQg3sbovKc838d+PPRf04e51DgMNZAoIBAQC2uiJjluIKRabFSeSfu4+I6cEY\nkXI0F65UAudFmyXVfaQbO9DR0Y4bWPDfXAUimRvxixEhSrSIBZ/itVxzhOvqZrl1\nXRCZsTOVoz7Z8lcd8opxPBnWDk1m2nyajwPXp8ZLo67FG0bWbayVBBRxyvirrZjG\nPatRKMTyVLTCD+WlQiP4b4kShKdWA4ZH6pHUIviAotWqXMTsEKfupg9avxEk8GtH\nGZnXAmpnBqmbU4+3rNOaCZLdekVCoEtW0NGZEYEV5UQnZoWY6AiUUxGGE/qionKH\nsdKN+8CowudMH02bo1a0akS+eh+D/SGc/MLofH7uPWtX7l8sTvQivzDIkZeu\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/test__environment.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport os\nimport unittest\n\nimport asyncpg\nimport asyncpg.serverversion\n\nfrom asyncpg import _testbase as tb\n\n\nclass TestEnvironment(tb.ConnectedTestCase):\n    @unittest.skipIf(not os.environ.get('PGVERSION'),\n                     \"environ[PGVERSION] is not set\")\n    async def test_environment_server_version(self):\n        pgver = os.environ.get('PGVERSION')\n        env_ver = asyncpg.serverversion.split_server_version_string(pgver)\n        srv_ver = self.con.get_server_version()\n\n        self.assertEqual(\n            env_ver[:2], srv_ver[:2],\n            'Expecting PostgreSQL version {pgver}, got {maj}.{min}.'.format(\n                pgver=pgver, maj=srv_ver.major, min=srv_ver.minor)\n        )\n\n    @unittest.skipIf(not os.environ.get('ASYNCPG_VERSION'),\n                     \"environ[ASYNCPG_VERSION] is not set\")\n    @unittest.skipIf(\"dev\" in asyncpg.__version__,\n                     \"development version with git commit data\")\n    async def test_environment_asyncpg_version(self):\n        apgver = os.environ.get('ASYNCPG_VERSION')\n        self.assertEqual(\n            asyncpg.__version__, apgver,\n            'Expecting asyncpg version {}, got {}.'.format(\n                apgver, asyncpg.__version__)\n        )\n"
  },
  {
    "path": "tests/test__sourcecode.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nimport os\nimport subprocess\nimport sys\nimport unittest\n\n\ndef find_root():\n    return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n\n\nclass TestCodeQuality(unittest.TestCase):\n\n    def test_flake8(self):\n        try:\n            import flake8  # NoQA\n        except ImportError:\n            raise unittest.SkipTest('flake8 module is missing')\n\n        root_path = find_root()\n        config_path = os.path.join(root_path, '.flake8')\n        if not os.path.exists(config_path):\n            raise RuntimeError('could not locate .flake8 file')\n\n        try:\n            subprocess.run(\n                [sys.executable, '-m', 'flake8', '--config', config_path],\n                check=True,\n                stdout=subprocess.PIPE,\n                stderr=subprocess.STDOUT,\n                cwd=root_path)\n        except subprocess.CalledProcessError as ex:\n            output = ex.output.decode()\n            raise AssertionError(\n                'flake8 validation failed:\\n{}'.format(output)) from None\n\n    def test_mypy(self):\n        try:\n            import mypy  # NoQA\n        except ImportError:\n            raise unittest.SkipTest('mypy module is missing')\n\n        root_path = find_root()\n        config_path = os.path.join(root_path, 'pyproject.toml')\n        if not os.path.exists(config_path):\n            raise RuntimeError('could not locate mypy.ini file')\n\n        try:\n            subprocess.run(\n                [\n                    sys.executable,\n                    '-m',\n                    'mypy',\n                    '--config-file',\n                    config_path,\n                    'asyncpg'\n                ],\n                check=True,\n                stdout=subprocess.PIPE,\n                stderr=subprocess.STDOUT,\n                cwd=root_path\n            )\n        except subprocess.CalledProcessError as ex:\n            output = ex.output.decode()\n            raise AssertionError(\n                'mypy validation failed:\\n{}'.format(output)) from None\n"
  },
  {
    "path": "tests/test_adversity.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\"\"\"Tests how asyncpg behaves in non-ideal conditions.\"\"\"\n\nimport asyncio\nimport os\nimport platform\nimport unittest\n\nfrom asyncpg import _testbase as tb\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'using remote cluster for testing')\n@unittest.skipIf(\n    platform.system() == 'Windows',\n    'not compatible with ProactorEventLoop which is default in Python 3.8+')\nclass TestConnectionLoss(tb.ProxiedClusterTestCase):\n    @tb.with_timeout(30.0)\n    async def test_connection_close_timeout(self):\n        con = await self.connect()\n        self.proxy.trigger_connectivity_loss()\n        with self.assertRaises(asyncio.TimeoutError):\n            await con.close(timeout=0.5)\n\n    @tb.with_timeout(30.0)\n    async def test_pool_acquire_timeout(self):\n        pool = await self.create_pool(\n            database='postgres', min_size=2, max_size=2)\n        try:\n            self.proxy.trigger_connectivity_loss()\n            for _ in range(2):\n                with self.assertRaises(asyncio.TimeoutError):\n                    async with pool.acquire(timeout=0.5):\n                        pass\n            self.proxy.restore_connectivity()\n            async with pool.acquire(timeout=0.5):\n                pass\n        finally:\n            self.proxy.restore_connectivity()\n            pool.terminate()\n\n    @tb.with_timeout(30.0)\n    async def test_pool_release_timeout(self):\n        pool = await self.create_pool(\n            database='postgres', min_size=2, max_size=2)\n        try:\n            with self.assertRaises(asyncio.TimeoutError):\n                async with pool.acquire(timeout=0.5):\n                    self.proxy.trigger_connectivity_loss()\n        finally:\n            self.proxy.restore_connectivity()\n            pool.terminate()\n\n    @tb.with_timeout(30.0)\n    async def test_pool_handles_abrupt_connection_loss(self):\n        pool_size = 3\n        query_runtime = 0.5\n        pool_timeout = cmd_timeout = 1.0\n        concurrency = 9\n        pool_concurrency = (concurrency - 1) // pool_size + 1\n\n        # Worst expected runtime + 20% to account for other latencies.\n        worst_runtime = (pool_timeout + cmd_timeout) * pool_concurrency * 1.2\n\n        async def worker(pool):\n            async with pool.acquire(timeout=pool_timeout) as con:\n                await con.fetch('SELECT pg_sleep($1)', query_runtime)\n\n        def kill_connectivity():\n            self.proxy.trigger_connectivity_loss()\n\n        new_pool = self.create_pool(\n            database='postgres', min_size=pool_size, max_size=pool_size,\n            timeout=cmd_timeout, command_timeout=cmd_timeout)\n\n        with self.assertRunUnder(worst_runtime):\n            pool = await new_pool\n            try:\n                workers = [worker(pool) for _ in range(concurrency)]\n                self.loop.call_later(1, kill_connectivity)\n                await asyncio.gather(\n                    *workers, return_exceptions=True)\n            finally:\n                pool.terminate()\n"
  },
  {
    "path": "tests/test_cache_invalidation.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\n\nERRNUM = 'unexpected number of attributes of composite type'\nERRTYP = 'unexpected data type of composite type'\n\n\nclass TestCacheInvalidation(tb.ConnectedTestCase):\n\n    def _get_cached_statements(self, connection=None):\n        if connection is None:\n            connection = self.con\n        return list(connection._stmt_cache.iter_statements())\n\n    def _check_statements_are_not_closed(self, statements):\n        self.assertGreater(len(statements), 0)\n        self.assertTrue(all(not s.closed for s in statements))\n\n    def _check_statements_are_closed(self, statements):\n        self.assertGreater(len(statements), 0)\n        self.assertTrue(all(s.closed for s in statements))\n\n    async def test_prepare_cache_invalidation_silent(self):\n        await self.con.execute('CREATE TABLE tab1(a int, b int)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, 2)')\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, 2))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            await self.con.execute(\n                'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text')\n\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, '2'))\n\n            self._check_statements_are_closed(statements)\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n\n    async def test_prepare_cache_invalidation_in_transaction(self):\n        await self.con.execute('CREATE TABLE tab1(a int, b int)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, 2)')\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, 2))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            await self.con.execute(\n                'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text')\n\n            with self.assertRaisesRegex(asyncpg.InvalidCachedStatementError,\n                                        'cached statement plan is invalid'):\n                async with self.con.transaction():\n                    result = await self.con.fetchrow('SELECT * FROM tab1')\n\n            self._check_statements_are_closed(statements)\n\n            # This is now OK,\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, '2'))\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n\n    async def test_prepare_cache_invalidation_in_pool(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=2, max_size=2)\n\n        await self.con.execute('CREATE TABLE tab1(a int, b int)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, 2)')\n\n            con1 = await pool.acquire()\n            con2 = await pool.acquire()\n\n            result = await con1.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, 2))\n\n            result = await con2.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, 2))\n\n            statements1 = self._get_cached_statements(con1)\n            self._check_statements_are_not_closed(statements1)\n\n            statements2 = self._get_cached_statements(con2)\n            self._check_statements_are_not_closed(statements2)\n\n            await self.con.execute(\n                'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text')\n\n            # con1 tries the same plan, will invalidate the cache\n            # for the entire pool.\n            result = await con1.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, '2'))\n\n            self._check_statements_are_closed(statements1)\n            self._check_statements_are_closed(statements2)\n\n            async with con2.transaction():\n                # This should work, as con1 should have invalidated\n                # the plan cache.\n                result = await con2.fetchrow('SELECT * FROM tab1')\n                self.assertEqual(result, (1, '2'))\n\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await pool.release(con2)\n            await pool.release(con1)\n            await pool.close()\n\n    async def test_type_cache_invalidation_in_transaction(self):\n        await self.con.execute('CREATE TYPE typ1 AS (x int, y int)')\n        await self.con.execute('CREATE TABLE tab1(a int, b typ1)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, (2, 3))')\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            async with self.con.transaction():\n                await self.con.execute('ALTER TYPE typ1 ADD ATTRIBUTE c text')\n                with self.assertRaisesRegex(\n                        asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                    await self.con.fetchrow('SELECT * FROM tab1')\n\n                self._check_statements_are_closed(statements)\n\n                # The second request must be correct (cache was dropped):\n                result = await self.con.fetchrow('SELECT * FROM tab1')\n                self.assertEqual(result, (1, (2, 3, None)))\n\n            # This is now OK, the cache is actual after the transaction.\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3, None)))\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await self.con.execute('DROP TYPE typ1')\n\n    async def test_type_cache_invalidation_in_cancelled_transaction(self):\n        await self.con.execute('CREATE TYPE typ1 AS (x int, y int)')\n        await self.con.execute('CREATE TABLE tab1(a int, b typ1)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, (2, 3))')\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            try:\n                async with self.con.transaction():\n                    await self.con.execute(\n                        'ALTER TYPE typ1 ADD ATTRIBUTE c text')\n                    with self.assertRaisesRegex(\n                            asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                        await self.con.fetchrow('SELECT * FROM tab1')\n\n                    self._check_statements_are_closed(statements)\n\n                    # The second request must be correct (cache was dropped):\n                    result = await self.con.fetchrow('SELECT * FROM tab1')\n                    self.assertEqual(result, (1, (2, 3, None)))\n                    raise UserWarning  # Just to generate ROLLBACK\n            except UserWarning:\n                pass\n\n            with self.assertRaisesRegex(\n                    asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                await self.con.fetchrow('SELECT * FROM tab1')\n            # This is now OK, the cache is filled after being dropped.\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await self.con.execute('DROP TYPE typ1')\n\n    async def test_prepared_type_cache_invalidation(self):\n        await self.con.execute('CREATE TYPE typ1 AS (x int, y int)')\n        await self.con.execute('CREATE TABLE tab1(a int, b typ1)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, (2, 3))')\n            prep = await self.con._prepare('SELECT * FROM tab1',\n                                           use_cache=True)\n            result = await prep.fetchrow()\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            try:\n                async with self.con.transaction():\n                    await self.con.execute(\n                        'ALTER TYPE typ1 ADD ATTRIBUTE c text')\n                    with self.assertRaisesRegex(\n                            asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                        await prep.fetchrow()\n\n                    self._check_statements_are_closed(statements)\n\n                    # PS has its local cache for types codecs, even after the\n                    # cache cleanup it is not possible to use it.\n                    # That's why it is marked as closed.\n                    with self.assertRaisesRegex(\n                            asyncpg.InterfaceError,\n                            'the prepared statement is closed'):\n                        await prep.fetchrow()\n\n                    prep = await self.con._prepare('SELECT * FROM tab1',\n                                                   use_cache=True)\n                    # The second PS must be correct (cache was dropped):\n                    result = await prep.fetchrow()\n                    self.assertEqual(result, (1, (2, 3, None)))\n                    raise UserWarning  # Just to generate ROLLBACK\n            except UserWarning:\n                pass\n\n            with self.assertRaisesRegex(\n                    asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                await prep.fetchrow()\n\n            # Reprepare it again after dropping cache.\n            prep = await self.con._prepare('SELECT * FROM tab1',\n                                           use_cache=True)\n            # This is now OK, the cache is filled after being dropped.\n            result = await prep.fetchrow()\n            self.assertEqual(result, (1, (2, 3)))\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await self.con.execute('DROP TYPE typ1')\n\n    async def test_type_cache_invalidation_on_drop_type_attr(self):\n        await self.con.execute('CREATE TYPE typ1 AS (x int, y int, c text)')\n        await self.con.execute('CREATE TABLE tab1(a int, b typ1)')\n\n        try:\n            await self.con.execute(\n                'INSERT INTO tab1 VALUES (1, (2, 3, $1))', 'x')\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3, 'x')))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            await self.con.execute('ALTER TYPE typ1 DROP ATTRIBUTE x')\n            with self.assertRaisesRegex(\n                    asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                await self.con.fetchrow('SELECT * FROM tab1')\n\n            self._check_statements_are_closed(statements)\n\n            # This is now OK, the cache is filled after being dropped.\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (3, 'x')))\n\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await self.con.execute('DROP TYPE typ1')\n\n    async def test_type_cache_invalidation_on_change_attr(self):\n        await self.con.execute('CREATE TYPE typ1 AS (x int, y int)')\n        await self.con.execute('CREATE TABLE tab1(a int, b typ1)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, (2, 3))')\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements = self._get_cached_statements()\n            self._check_statements_are_not_closed(statements)\n\n            # It is slightly artificial, but can take place in transactional\n            # schema changing. Nevertheless, if the code checks and raises it\n            # the most probable reason is a difference with the cache type.\n            await self.con.execute('ALTER TYPE typ1 DROP ATTRIBUTE y')\n            await self.con.execute('ALTER TYPE typ1 ADD ATTRIBUTE y bigint')\n            with self.assertRaisesRegex(\n                    asyncpg.OutdatedSchemaCacheError, ERRTYP):\n                await self.con.fetchrow('SELECT * FROM tab1')\n\n            self._check_statements_are_closed(statements)\n\n            # This is now OK, the cache is filled after being dropped.\n            result = await self.con.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, None)))\n\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await self.con.execute('DROP TYPE typ1')\n\n    async def test_type_cache_invalidation_in_pool(self):\n        await self.con.execute('CREATE DATABASE testdb')\n        pool = await self.create_pool(database='postgres',\n                                      min_size=2, max_size=2)\n\n        pool_chk = await self.create_pool(database='testdb',\n                                          min_size=2, max_size=2)\n\n        await self.con.execute('CREATE TYPE typ1 AS (x int, y int)')\n        await self.con.execute('CREATE TABLE tab1(a int, b typ1)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, (2, 3))')\n\n            con1 = await pool.acquire()\n            con2 = await pool.acquire()\n\n            result = await con1.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements1 = self._get_cached_statements(con1)\n            self._check_statements_are_not_closed(statements1)\n\n            result = await con2.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements2 = self._get_cached_statements(con2)\n            self._check_statements_are_not_closed(statements2)\n\n            # Create the same schema in the \"testdb\", fetch data which caches\n            # type info.\n            con_chk = await pool_chk.acquire()\n            await con_chk.execute('CREATE TYPE typ1 AS (x int, y int)')\n            await con_chk.execute('CREATE TABLE tab1(a int, b typ1)')\n            await con_chk.execute('INSERT INTO tab1 VALUES (1, (2, 3))')\n            result = await con_chk.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3)))\n\n            statements_chk = self._get_cached_statements(con_chk)\n            self._check_statements_are_not_closed(statements_chk)\n\n            # Change schema in the databases.\n            await self.con.execute('ALTER TYPE typ1 ADD ATTRIBUTE c text')\n            await con_chk.execute('ALTER TYPE typ1 ADD ATTRIBUTE c text')\n\n            # con1 tries to get cached type info, fails, but invalidates the\n            # cache for the entire pool.\n            with self.assertRaisesRegex(\n                    asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                await con1.fetchrow('SELECT * FROM tab1')\n\n            self._check_statements_are_closed(statements1)\n            self._check_statements_are_closed(statements2)\n\n            async with con2.transaction():\n                # This should work, as con1 should have invalidated all caches.\n                result = await con2.fetchrow('SELECT * FROM tab1')\n                self.assertEqual(result, (1, (2, 3, None)))\n\n            # After all the con1 uses actual info from renewed cache entry.\n            result = await con1.fetchrow('SELECT * FROM tab1')\n            self.assertEqual(result, (1, (2, 3, None)))\n\n            # Check the invalidation is database-specific, i.e. cache entries\n            # for pool_chk/con_chk was not dropped via pool/con1.\n\n            self._check_statements_are_not_closed(statements_chk)\n\n            with self.assertRaisesRegex(\n                    asyncpg.OutdatedSchemaCacheError, ERRNUM):\n                await con_chk.fetchrow('SELECT * FROM tab1')\n\n            self._check_statements_are_closed(statements_chk)\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n            await self.con.execute('DROP TYPE typ1')\n            await pool.release(con2)\n            await pool.release(con1)\n            await pool.close()\n            await pool_chk.release(con_chk)\n            await pool_chk.close()\n            await self.con.execute('DROP DATABASE testdb')\n"
  },
  {
    "path": "tests/test_cancellation.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport asyncpg\n\nfrom asyncpg import _testbase as tb\n\n\nclass TestCancellation(tb.ConnectedTestCase):\n\n    async def test_cancellation_01(self):\n        st1000 = await self.con.prepare('SELECT 1000')\n\n        async def test0():\n            val = await self.con.execute('SELECT 42')\n            self.assertEqual(val, 'SELECT 1')\n\n        async def test1():\n            val = await self.con.fetchval('SELECT 42')\n            self.assertEqual(val, 42)\n\n        async def test2():\n            val = await self.con.fetchrow('SELECT 42')\n            self.assertEqual(val, (42,))\n\n        async def test3():\n            val = await self.con.fetch('SELECT 42')\n            self.assertEqual(val, [(42,)])\n\n        async def test4():\n            val = await self.con.prepare('SELECT 42')\n            self.assertEqual(await val.fetchval(), 42)\n\n        async def test5():\n            self.assertEqual(await st1000.fetchval(), 1000)\n\n        async def test6():\n            self.assertEqual(await st1000.fetchrow(), (1000,))\n\n        async def test7():\n            self.assertEqual(await st1000.fetch(), [(1000,)])\n\n        async def test8():\n            cur = await st1000.cursor()\n            self.assertEqual(await cur.fetchrow(), (1000,))\n\n        for test in {test0, test1, test2, test3, test4, test5,\n                     test6, test7, test8}:\n\n            with self.subTest(testfunc=test), self.assertRunUnder(1):\n                st = await self.con.prepare('SELECT pg_sleep(20)')\n                task = self.loop.create_task(st.fetch())\n                await asyncio.sleep(0.05)\n                task.cancel()\n\n                with self.assertRaises(asyncio.CancelledError):\n                    await task\n\n                async with self.con.transaction():\n                    await test()\n\n    async def test_cancellation_02(self):\n        st = await self.con.prepare('SELECT 1')\n        task = self.loop.create_task(st.fetch())\n        await asyncio.sleep(0.05)\n        task.cancel()\n        self.assertEqual(await task, [(1,)])\n\n    async def test_cancellation_03(self):\n        with self.assertRaises(asyncpg.InFailedSQLTransactionError):\n            async with self.con.transaction():\n                task = self.loop.create_task(\n                    self.con.fetch('SELECT pg_sleep(20)'))\n                await asyncio.sleep(0.05)\n                task.cancel()\n\n                with self.assertRaises(asyncio.CancelledError):\n                    await task\n\n                await self.con.fetch('SELECT generate_series(0, 100)')\n\n        self.assertEqual(\n            await self.con.fetchval('SELECT 42'),\n            42)\n\n    async def test_cancellation_04(self):\n        await self.con.fetchval('SELECT pg_sleep(0)')\n        waiter = asyncio.Future()\n        self.con._cancel_current_command(waiter)\n        await waiter\n        self.assertEqual(await self.con.fetchval('SELECT 42'), 42)\n"
  },
  {
    "path": "tests/test_codecs.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport datetime\nimport decimal\nimport ipaddress\nimport math\nimport os\nimport random\nimport struct\nimport unittest\nimport uuid\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\nfrom asyncpg import cluster as pg_cluster\n\n\ndef _timezone(offset):\n    minutes = offset // 60\n    return datetime.timezone(datetime.timedelta(minutes=minutes))\n\n\ndef _system_timezone():\n    d = datetime.datetime.now(datetime.timezone.utc).astimezone()\n    return datetime.timezone(d.utcoffset())\n\n\ninfinity_datetime = datetime.datetime(\n    datetime.MAXYEAR, 12, 31, 23, 59, 59, 999999)\nnegative_infinity_datetime = datetime.datetime(\n    datetime.MINYEAR, 1, 1, 0, 0, 0, 0)\n\ninfinity_date = datetime.date(datetime.MAXYEAR, 12, 31)\nnegative_infinity_date = datetime.date(datetime.MINYEAR, 1, 1)\ncurrent_timezone = _system_timezone()\ncurrent_date = datetime.date.today()\ncurrent_datetime = datetime.datetime.now()\n\n\ntype_samples = [\n    ('bool', 'bool', (\n        True, False,\n    )),\n    ('smallint', 'int2', (\n        -2 ** 15, 2 ** 15 - 1,\n        -1, 0, 1,\n    )),\n    ('int', 'int4', (\n        -2 ** 31, 2 ** 31 - 1,\n        -1, 0, 1,\n    )),\n    ('bigint', 'int8', (\n        -2 ** 63, 2 ** 63 - 1,\n        -1, 0, 1,\n    )),\n    ('numeric', 'numeric', (\n        -(2 ** 64),\n        2 ** 64,\n        -(2 ** 128),\n        2 ** 128,\n        -1, 0, 1,\n        decimal.Decimal(\"0.00000000000000\"),\n        decimal.Decimal(\"1.00000000000000\"),\n        decimal.Decimal(\"-1.00000000000000\"),\n        decimal.Decimal(\"-2.00000000000000\"),\n        decimal.Decimal(\"1000000000000000.00000000000000\"),\n        decimal.Decimal(1234),\n        decimal.Decimal(-1234),\n        decimal.Decimal(\"1234000000.00088883231\"),\n        decimal.Decimal(str(1234.00088883231)),\n        decimal.Decimal(\"3123.23111\"),\n        decimal.Decimal(\"-3123000000.23111\"),\n        decimal.Decimal(\"3123.2311100000\"),\n        decimal.Decimal(\"-03123.0023111\"),\n        decimal.Decimal(\"3123.23111\"),\n        decimal.Decimal(\"3123.23111\"),\n        decimal.Decimal(\"10000.23111\"),\n        decimal.Decimal(\"100000.23111\"),\n        decimal.Decimal(\"1000000.23111\"),\n        decimal.Decimal(\"10000000.23111\"),\n        decimal.Decimal(\"100000000.23111\"),\n        decimal.Decimal(\"1000000000.23111\"),\n        decimal.Decimal(\"1000000000.3111\"),\n        decimal.Decimal(\"1000000000.111\"),\n        decimal.Decimal(\"1000000000.11\"),\n        decimal.Decimal(\"100000000.0\"),\n        decimal.Decimal(\"10000000.0\"),\n        decimal.Decimal(\"1000000.0\"),\n        decimal.Decimal(\"100000.0\"),\n        decimal.Decimal(\"10000.0\"),\n        decimal.Decimal(\"1000.0\"),\n        decimal.Decimal(\"100.0\"),\n        decimal.Decimal(\"100\"),\n        decimal.Decimal(\"100.1\"),\n        decimal.Decimal(\"100.12\"),\n        decimal.Decimal(\"100.123\"),\n        decimal.Decimal(\"100.1234\"),\n        decimal.Decimal(\"100.12345\"),\n        decimal.Decimal(\"100.123456\"),\n        decimal.Decimal(\"100.1234567\"),\n        decimal.Decimal(\"100.12345679\"),\n        decimal.Decimal(\"100.123456790\"),\n        decimal.Decimal(\"100.123456790000000000000000\"),\n        decimal.Decimal(\"1.0\"),\n        decimal.Decimal(\"0.0\"),\n        decimal.Decimal(\"-1.0\"),\n        decimal.Decimal(\"1.0E-1000\"),\n        decimal.Decimal(\"1E1000\"),\n        decimal.Decimal(\"0.000000000000000000000000001\"),\n        decimal.Decimal(\"0.000000000000010000000000001\"),\n        decimal.Decimal(\"0.00000000000000000000000001\"),\n        decimal.Decimal(\"0.00000000100000000000000001\"),\n        decimal.Decimal(\"0.0000000000000000000000001\"),\n        decimal.Decimal(\"0.000000000000000000000001\"),\n        decimal.Decimal(\"0.00000000000000000000001\"),\n        decimal.Decimal(\"0.0000000000000000000001\"),\n        decimal.Decimal(\"0.000000000000000000001\"),\n        decimal.Decimal(\"0.00000000000000000001\"),\n        decimal.Decimal(\"0.0000000000000000001\"),\n        decimal.Decimal(\"0.000000000000000001\"),\n        decimal.Decimal(\"0.00000000000000001\"),\n        decimal.Decimal(\"0.0000000000000001\"),\n        decimal.Decimal(\"0.000000000000001\"),\n        decimal.Decimal(\"0.00000000000001\"),\n        decimal.Decimal(\"0.0000000000001\"),\n        decimal.Decimal(\"0.000000000001\"),\n        decimal.Decimal(\"0.00000000001\"),\n        decimal.Decimal(\"0.0000000001\"),\n        decimal.Decimal(\"0.000000001\"),\n        decimal.Decimal(\"0.00000001\"),\n        decimal.Decimal(\"0.0000001\"),\n        decimal.Decimal(\"0.000001\"),\n        decimal.Decimal(\"0.00001\"),\n        decimal.Decimal(\"0.0001\"),\n        decimal.Decimal(\"0.001\"),\n        decimal.Decimal(\"0.01\"),\n        decimal.Decimal(\"0.1\"),\n        decimal.Decimal(\"0.10\"),\n        decimal.Decimal(\"0.100\"),\n        decimal.Decimal(\"0.1000\"),\n        decimal.Decimal(\"0.10000\"),\n        decimal.Decimal(\"0.100000\"),\n        decimal.Decimal(\"0.00001000\"),\n        decimal.Decimal(\"0.000010000\"),\n        decimal.Decimal(\"0.0000100000\"),\n        decimal.Decimal(\"0.00001000000\"),\n        decimal.Decimal(\"1\" + \"0\" * 117 + \".\" + \"0\" * 161)\n    )),\n    ('bytea', 'bytea', (\n        bytes(range(256)),\n        bytes(range(255, -1, -1)),\n        b'\\x00\\x00',\n        b'foo',\n        b'f' * 1024 * 1024,\n        dict(input=bytearray(b'\\x02\\x01'), output=b'\\x02\\x01'),\n    )),\n    ('text', 'text', (\n        '',\n        'A' * (1024 * 1024 + 11)\n    )),\n    ('\"char\"', 'char', (\n        b'a',\n        b'b',\n        b'\\x00'\n    )),\n    ('timestamp', 'timestamp', [\n        datetime.datetime(3000, 5, 20, 5, 30, 10),\n        datetime.datetime(2000, 1, 1, 5, 25, 10),\n        datetime.datetime(500, 1, 1, 5, 25, 10),\n        datetime.datetime(250, 1, 1, 5, 25, 10),\n        infinity_datetime,\n        negative_infinity_datetime,\n        {'textinput': 'infinity', 'output': infinity_datetime},\n        {'textinput': '-infinity', 'output': negative_infinity_datetime},\n        {'input': datetime.date(2000, 1, 1),\n         'output': datetime.datetime(2000, 1, 1)},\n        {'textinput': '1970-01-01 20:31:23.648',\n         'output': datetime.datetime(1970, 1, 1, 20, 31, 23, 648000)},\n        {'input': datetime.datetime(1970, 1, 1, 20, 31, 23, 648000),\n         'textoutput': '1970-01-01 20:31:23.648'},\n    ]),\n    ('date', 'date', [\n        datetime.date(3000, 5, 20),\n        datetime.date(2000, 1, 1),\n        datetime.date(500, 1, 1),\n        infinity_date,\n        negative_infinity_date,\n        {'textinput': 'infinity', 'output': infinity_date},\n        {'textinput': '-infinity', 'output': negative_infinity_date},\n    ]),\n    ('time', 'time', [\n        datetime.time(12, 15, 20),\n        datetime.time(0, 1, 1),\n        datetime.time(23, 59, 59),\n    ]),\n    ('timestamptz', 'timestamptz', [\n        # It's converted to UTC. When it comes back out, it will be in UTC\n        # again. The datetime comparison will take the tzinfo into account.\n        datetime.datetime(1990, 5, 12, 10, 10, 0, tzinfo=_timezone(4000)),\n        datetime.datetime(1982, 5, 18, 10, 10, 0, tzinfo=_timezone(6000)),\n        datetime.datetime(1950, 1, 1, 10, 10, 0, tzinfo=_timezone(7000)),\n        datetime.datetime(1800, 1, 1, 10, 10, 0, tzinfo=_timezone(2000)),\n        datetime.datetime(2400, 1, 1, 10, 10, 0, tzinfo=_timezone(2000)),\n        infinity_datetime,\n        negative_infinity_datetime,\n        {\n            'input': current_date,\n            'output': datetime.datetime(\n                year=current_date.year, month=current_date.month,\n                day=current_date.day, tzinfo=current_timezone),\n        },\n        {\n            'input': current_datetime,\n            'output': current_datetime.replace(tzinfo=current_timezone),\n        }\n    ]),\n    ('timetz', 'timetz', [\n        # timetz retains the offset\n        datetime.time(10, 10, 0, tzinfo=_timezone(4000)),\n        datetime.time(10, 10, 0, tzinfo=_timezone(6000)),\n        datetime.time(10, 10, 0, tzinfo=_timezone(7000)),\n        datetime.time(10, 10, 0, tzinfo=_timezone(2000)),\n        datetime.time(22, 30, 0, tzinfo=_timezone(0)),\n    ]),\n    ('interval', 'interval', [\n        datetime.timedelta(40, 10, 1234),\n        datetime.timedelta(0, 0, 4321),\n        datetime.timedelta(0, 0),\n        datetime.timedelta(-100, 0),\n        datetime.timedelta(-100, -400),\n        {\n            'textinput': '-2 years -11 months -10 days '\n                         '-2 hours -800 milliseconds',\n            'output': datetime.timedelta(\n                days=(-2 * 365) + (-11 * 30) - 10,\n                seconds=(-2 * 3600),\n                milliseconds=-800\n            ),\n        },\n        {\n            'query': 'SELECT justify_hours($1::interval)::text',\n            'input': datetime.timedelta(\n                days=(-2 * 365) + (-11 * 30) - 10,\n                seconds=(-2 * 3600),\n                milliseconds=-800\n            ),\n            'textoutput': '-1070 days -02:00:00.8',\n        },\n    ]),\n    ('uuid', 'uuid', [\n        uuid.UUID('38a4ff5a-3a56-11e6-a6c2-c8f73323c6d4'),\n        uuid.UUID('00000000-0000-0000-0000-000000000000'),\n        {'input': '00000000-0000-0000-0000-000000000000',\n         'output': uuid.UUID('00000000-0000-0000-0000-000000000000')}\n    ]),\n    ('uuid[]', 'uuid[]', [\n        [uuid.UUID('38a4ff5a-3a56-11e6-a6c2-c8f73323c6d4'),\n         uuid.UUID('00000000-0000-0000-0000-000000000000')],\n        []\n    ]),\n    ('json', 'json', [\n        '[1, 2, 3, 4]',\n        '{\"a\": [1, 2], \"b\": 0}'\n    ]),\n    ('jsonb', 'jsonb', [\n        '[1, 2, 3, 4]',\n        '{\"a\": [1, 2], \"b\": 0}'\n    ], (9, 4)),\n    ('jsonpath', 'jsonpath', [\n        '$.\"track\".\"segments\"[*].\"HR\"?(@ > 130)',\n    ], (12, 0)),\n    ('oid[]', 'oid[]', [\n        [1, 2, 3, 4],\n        []\n    ]),\n    ('smallint[]', 'int2[]', [\n        [1, 2, 3, 4],\n        [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],\n        []\n    ]),\n    ('bigint[]', 'int8[]', [\n        [2 ** 42, -2 ** 54, 0],\n        []\n    ]),\n    ('int[]', 'int4[]', [\n        [2 ** 22, -2 ** 24, 0],\n        []\n    ]),\n    ('time[]', 'time[]', [\n        [datetime.time(12, 15, 20), datetime.time(0, 1, 1)],\n        []\n    ]),\n    ('text[]', 'text[]', [\n        ['ABCDE', 'EDCBA'],\n        [],\n        ['A' * 1024 * 1024] * 10\n    ]),\n    ('float8', 'float8', [\n        1.1,\n        -1.1,\n        0,\n        2,\n        1e-4,\n        -1e-20,\n        122.2e-100,\n        2e5,\n        math.pi,\n        math.e,\n        math.inf,\n        -math.inf,\n        math.nan,\n        {'textinput': 'infinity', 'output': math.inf},\n        {'textinput': '-infinity', 'output': -math.inf},\n        {'textinput': 'NaN', 'output': math.nan},\n    ]),\n    ('float4', 'float4', [\n        1.1,\n        -1.1,\n        0,\n        2,\n        1e-4,\n        -1e-20,\n        2e5,\n        math.pi,\n        math.e,\n        math.inf,\n        -math.inf,\n        math.nan,\n        {'textinput': 'infinity', 'output': math.inf},\n        {'textinput': '-infinity', 'output': -math.inf},\n        {'textinput': 'NaN', 'output': math.nan},\n    ]),\n    ('cidr', 'cidr', [\n        ipaddress.IPv4Network('255.255.255.255/32'),\n        ipaddress.IPv4Network('127.0.0.0/8'),\n        ipaddress.IPv4Network('127.1.0.0/16'),\n        ipaddress.IPv4Network('127.1.0.0/18'),\n        ipaddress.IPv4Network('10.0.0.0/32'),\n        ipaddress.IPv4Network('0.0.0.0/0'),\n        ipaddress.IPv6Network('ffff' + ':ffff' * 7 + '/128'),\n        ipaddress.IPv6Network('::1/128'),\n        ipaddress.IPv6Network('::/0'),\n    ]),\n    ('inet', 'inet', [\n        ipaddress.IPv4Address('255.255.255.255'),\n        ipaddress.IPv4Address('127.0.0.1'),\n        ipaddress.IPv4Address('0.0.0.0'),\n        ipaddress.IPv6Address('ffff' + ':ffff' * 7),\n        ipaddress.IPv6Address('::1'),\n        ipaddress.IPv6Address('::'),\n        ipaddress.IPv4Interface('10.0.0.1/30'),\n        ipaddress.IPv4Interface('0.0.0.0/0'),\n        ipaddress.IPv4Interface('255.255.255.255/31'),\n        dict(\n            input='127.0.0.0/8',\n            output=ipaddress.IPv4Interface('127.0.0.0/8')),\n        dict(\n            input='127.0.0.1/32',\n            output=ipaddress.IPv4Address('127.0.0.1')),\n        # Postgres appends /32 when casting to text explicitly, but\n        # *not* in inet_out.\n        dict(\n            input='10.11.12.13',\n            textoutput='10.11.12.13/32'\n        ),\n        dict(\n            input=ipaddress.IPv4Address('10.11.12.13'),\n            textoutput='10.11.12.13/32'\n        ),\n        dict(\n            input=ipaddress.IPv4Interface('10.11.12.13'),\n            textoutput='10.11.12.13/32'\n        ),\n        dict(\n            textinput='10.11.12.13',\n            output=ipaddress.IPv4Address('10.11.12.13'),\n        ),\n        dict(\n            textinput='10.11.12.13/0',\n            output=ipaddress.IPv4Interface('10.11.12.13/0'),\n        ),\n    ]),\n    ('macaddr', 'macaddr', [\n        '00:00:00:00:00:00',\n        'ff:ff:ff:ff:ff:ff'\n    ]),\n    ('txid_snapshot', 'txid_snapshot', [\n        (100, 1000, (100, 200, 300, 400))\n    ]),\n    ('pg_snapshot', 'pg_snapshot', [\n        (100, 1000, (100, 200, 300, 400))\n    ], (13, 0)),\n    ('xid', 'xid', (\n        2 ** 32 - 1,\n        0,\n        1,\n    )),\n    ('xid8', 'xid8', (\n        2 ** 64 - 1,\n        0,\n        1,\n    ), (13, 0)),\n    ('varbit', 'varbit', [\n        asyncpg.BitString('0000 0001'),\n        asyncpg.BitString('00010001'),\n        asyncpg.BitString(''),\n        asyncpg.BitString(),\n        asyncpg.BitString.frombytes(b'\\x00', bitlength=3),\n        asyncpg.BitString('0000 0000 1'),\n        dict(input=b'\\x01', output=asyncpg.BitString('0000 0001')),\n        dict(input=bytearray(b'\\x02'), output=asyncpg.BitString('0000 0010')),\n    ]),\n    ('path', 'path', [\n        asyncpg.Path(asyncpg.Point(0.0, 0.0), asyncpg.Point(1.0, 1.0)),\n        asyncpg.Path(asyncpg.Point(0.0, 0.0), asyncpg.Point(1.0, 1.0),\n                     is_closed=True),\n        dict(input=((0.0, 0.0), (1.0, 1.0)),\n             output=asyncpg.Path(asyncpg.Point(0.0, 0.0),\n                                 asyncpg.Point(1.0, 1.0),\n                                 is_closed=True)),\n        dict(input=[(0.0, 0.0), (1.0, 1.0)],\n             output=asyncpg.Path(asyncpg.Point(0.0, 0.0),\n                                 asyncpg.Point(1.0, 1.0),\n                                 is_closed=False)),\n    ]),\n    ('point', 'point', [\n        asyncpg.Point(0.0, 0.0),\n        asyncpg.Point(1.0, 2.0),\n    ]),\n    ('box', 'box', [\n        asyncpg.Box((1.0, 2.0), (0.0, 0.0)),\n    ]),\n    ('line', 'line', [\n        asyncpg.Line(1, 2, 3),\n    ], (9, 4)),\n    ('lseg', 'lseg', [\n        asyncpg.LineSegment((1, 2), (2, 2)),\n    ]),\n    ('polygon', 'polygon', [\n        asyncpg.Polygon(asyncpg.Point(0.0, 0.0), asyncpg.Point(1.0, 0.0),\n                        asyncpg.Point(1.0, 1.0), asyncpg.Point(0.0, 1.0)),\n    ]),\n    ('circle', 'circle', [\n        asyncpg.Circle((0.0, 0.0), 100),\n    ]),\n    ('tid', 'tid', [\n        (100, 200),\n        (0, 0),\n        (2147483647, 0),\n        (4294967295, 0),\n        (0, 32767),\n        (0, 65535),\n        (4294967295, 65535),\n    ]),\n    ('oid', 'oid', [\n        0,\n        10,\n        4294967295\n    ])\n]\n\n\nclass TestCodecs(tb.ConnectedTestCase):\n\n    async def test_standard_codecs(self):\n        \"\"\"Test encoding/decoding of standard data types and arrays thereof.\"\"\"\n        for (typname, intname, sample_data, *metadata) in type_samples:\n            if metadata and self.server_version < metadata[0]:\n                continue\n\n            st = await self.con.prepare(\n                \"SELECT $1::\" + typname\n            )\n\n            text_in = await self.con.prepare(\n                \"SELECT $1::text::\" + typname\n            )\n\n            text_out = await self.con.prepare(\n                \"SELECT $1::\" + typname + \"::text\"\n            )\n\n            for sample in sample_data:\n                with self.subTest(sample=sample, typname=typname):\n                    stmt = st\n                    if isinstance(sample, dict):\n                        if 'textinput' in sample:\n                            inputval = sample['textinput']\n                            stmt = text_in\n                        else:\n                            inputval = sample['input']\n\n                        if 'textoutput' in sample:\n                            outputval = sample['textoutput']\n                            if stmt is text_in:\n                                raise ValueError(\n                                    'cannot test \"textin\" and'\n                                    ' \"textout\" simultaneously')\n                            stmt = text_out\n                        else:\n                            outputval = sample['output']\n\n                        if sample.get('query'):\n                            stmt = await self.con.prepare(sample['query'])\n                    else:\n                        inputval = outputval = sample\n\n                    result = await stmt.fetchval(inputval)\n                    err_msg = (\n                        \"unexpected result for {} when passing {!r}: \"\n                        \"received {!r}, expected {!r}\".format(\n                            typname, inputval, result, outputval))\n\n                    if typname.startswith('float'):\n                        if math.isnan(outputval):\n                            if not math.isnan(result):\n                                self.fail(err_msg)\n                        else:\n                            self.assertTrue(\n                                math.isclose(result, outputval, rel_tol=1e-6),\n                                err_msg)\n                    else:\n                        self.assertEqual(result, outputval, err_msg)\n\n                    if (typname == 'numeric' and\n                            isinstance(inputval, decimal.Decimal)):\n                        self.assertEqual(\n                            result.as_tuple(),\n                            outputval.as_tuple(),\n                            err_msg,\n                        )\n\n            with self.subTest(sample=None, typname=typname):\n                # Test that None is handled for all types.\n                rsample = await st.fetchval(None)\n                self.assertIsNone(rsample)\n\n            at = st.get_attributes()\n            self.assertEqual(at[0].type.name, intname)\n\n    async def test_all_builtin_types_handled(self):\n        from asyncpg.protocol.protocol import BUILTIN_TYPE_OID_MAP\n\n        for oid, typename in BUILTIN_TYPE_OID_MAP.items():\n            codec = self.con.get_settings().get_data_codec(oid)\n            self.assertIsNotNone(\n                codec,\n                'core type {} ({}) is unhandled'.format(typename, oid))\n\n    async def test_void(self):\n        res = await self.con.fetchval('select pg_sleep(0)')\n        self.assertIsNone(res)\n        await self.con.fetchval('select now($1::void)', '')\n\n    def test_bitstring(self):\n        bitlen = random.randint(0, 1000)\n        bs = ''.join(random.choice(('1', '0', ' ')) for _ in range(bitlen))\n        bits = asyncpg.BitString(bs)\n        sanitized_bs = bs.replace(' ', '')\n        self.assertEqual(sanitized_bs,\n                         bits.as_string().replace(' ', ''))\n\n        expected_bytelen = \\\n            len(sanitized_bs) // 8 + (1 if len(sanitized_bs) % 8 else 0)\n\n        self.assertEqual(len(bits.bytes), expected_bytelen)\n\n        little, big = bits.to_int('little'), bits.to_int('big')\n        self.assertEqual(bits.from_int(little, len(bits), 'little'), bits)\n        self.assertEqual(bits.from_int(big, len(bits), 'big'), bits)\n\n        naive_little = 0\n        for i, c in enumerate(sanitized_bs):\n            naive_little |= int(c) << i\n        naive_big = 0\n        for c in sanitized_bs:\n            naive_big = (naive_big << 1) | int(c)\n\n        self.assertEqual(little, naive_little)\n        self.assertEqual(big, naive_big)\n\n    async def test_interval(self):\n        res = await self.con.fetchval(\"SELECT '5 years'::interval\")\n        self.assertEqual(res, datetime.timedelta(days=1825))\n\n        res = await self.con.fetchval(\"SELECT '5 years 1 month'::interval\")\n        self.assertEqual(res, datetime.timedelta(days=1855))\n\n        res = await self.con.fetchval(\"SELECT '-5 years'::interval\")\n        self.assertEqual(res, datetime.timedelta(days=-1825))\n\n        res = await self.con.fetchval(\"SELECT '-5 years -1 month'::interval\")\n        self.assertEqual(res, datetime.timedelta(days=-1855))\n\n    async def test_numeric(self):\n        # Test that we handle dscale correctly.\n        cases = [\n            '0.001',\n            '0.001000',\n            '1',\n            '1.00000'\n        ]\n\n        for case in cases:\n            res = await self.con.fetchval(\n                \"SELECT $1::numeric\", case)\n\n            self.assertEqual(str(res), case)\n\n        try:\n            await self.con.execute(\n                '''\n                    CREATE TABLE tab (v numeric(3, 2));\n                    INSERT INTO tab VALUES (0), (1);\n                ''')\n            res = await self.con.fetchval(\"SELECT v FROM tab WHERE v = $1\", 0)\n            self.assertEqual(str(res), '0.00')\n            res = await self.con.fetchval(\"SELECT v FROM tab WHERE v = $1\", 1)\n            self.assertEqual(str(res), '1.00')\n        finally:\n            await self.con.execute('DROP TABLE tab')\n\n        res = await self.con.fetchval(\n            \"SELECT $1::numeric\", decimal.Decimal('NaN'))\n        self.assertTrue(res.is_nan())\n\n        res = await self.con.fetchval(\n            \"SELECT $1::numeric\", decimal.Decimal('sNaN'))\n        self.assertTrue(res.is_nan())\n\n        if self.server_version < (14, 0):\n            with self.assertRaisesRegex(\n                asyncpg.DataError,\n                'invalid sign in external \"numeric\" value'\n            ):\n                await self.con.fetchval(\n                    \"SELECT $1::numeric\", decimal.Decimal('-Inf'))\n\n            with self.assertRaisesRegex(\n                asyncpg.DataError,\n                'invalid sign in external \"numeric\" value'\n            ):\n                await self.con.fetchval(\n                    \"SELECT $1::numeric\", decimal.Decimal('+Inf'))\n\n            with self.assertRaisesRegex(asyncpg.DataError, 'invalid'):\n                await self.con.fetchval(\n                    \"SELECT $1::numeric\", 'invalid')\n        else:\n            res = await self.con.fetchval(\n                \"SELECT $1::numeric\", decimal.Decimal(\"-Inf\"))\n            self.assertTrue(res.is_infinite())\n\n            res = await self.con.fetchval(\n                \"SELECT $1::numeric\", decimal.Decimal(\"+Inf\"))\n            self.assertTrue(res.is_infinite())\n\n        with self.assertRaisesRegex(asyncpg.DataError, 'invalid'):\n            await self.con.fetchval(\n                \"SELECT $1::numeric\", 'invalid')\n\n    async def test_unhandled_type_fallback(self):\n        await self.con.execute('''\n            CREATE EXTENSION IF NOT EXISTS isn\n        ''')\n\n        try:\n            input_val = '1436-4522'\n\n            res = await self.con.fetchrow('''\n                SELECT $1::issn AS issn, 42 AS int\n            ''', input_val)\n\n            self.assertEqual(res['issn'], input_val)\n            self.assertEqual(res['int'], 42)\n\n        finally:\n            await self.con.execute('''\n                DROP EXTENSION isn\n            ''')\n\n    async def test_invalid_input(self):\n        # The latter message appears beginning in Python 3.10.\n        integer_required = (\n            r\"(an integer is required|\"\n            r\"\\('str' object cannot be interpreted as an integer\\))\")\n\n        cases = [\n            ('bytea', 'a bytes-like object is required', [\n                1,\n                'aaa'\n            ]),\n            ('bool', 'a boolean is required', [\n                1,\n            ]),\n            ('int2', integer_required, [\n                '2',\n                'aa',\n            ]),\n            ('smallint', 'value out of int16 range', [\n                2**256,  # check for the same exception for any big numbers\n                decimal.Decimal(\"2000000000000000000000000000000\"),\n                0xffff,\n                0xffffffff,\n                32768,\n                -32769\n            ]),\n            ('float4', 'value out of float32 range', [\n                4.1 * 10 ** 40,\n                -4.1 * 10 ** 40,\n            ]),\n            ('int4', integer_required, [\n                '2',\n                'aa',\n            ]),\n            ('int', 'value out of int32 range', [\n                2**256,  # check for the same exception for any big numbers\n                decimal.Decimal(\"2000000000000000000000000000000\"),\n                0xffffffff,\n                2**31,\n                -2**31 - 1,\n            ]),\n            ('int8', integer_required, [\n                '2',\n                'aa',\n            ]),\n            ('bigint', 'value out of int64 range', [\n                2**256,  # check for the same exception for any big numbers\n                decimal.Decimal(\"2000000000000000000000000000000\"),\n                0xffffffffffffffff,\n                2**63,\n                -2**63 - 1,\n            ]),\n            ('text', 'expected str, got bytes', [\n                b'foo'\n            ]),\n            ('text', 'expected str, got list', [\n                [1]\n            ]),\n            ('tid', 'list or tuple expected', [\n                b'foo'\n            ]),\n            ('tid', 'invalid number of elements in tid tuple', [\n                [],\n                (),\n                [1, 2, 3],\n                (4,),\n            ]),\n            ('tid', 'tuple id block value out of uint32 range', [\n                (-1, 0),\n                (2**256, 0),\n                (0xffffffff + 1, 0),\n                (2**32, 0),\n            ]),\n            ('tid', 'tuple id offset value out of uint16 range', [\n                (0, -1),\n                (0, 2**256),\n                (0, 0xffff + 1),\n                (0, 0xffffffff),\n                (0, 65536),\n            ]),\n            ('oid', 'value out of uint32 range', [\n                2 ** 32,\n                -1,\n            ]),\n            ('timestamp', r\"expected a datetime\\.date.*got 'str'\", [\n                'foo'\n            ]),\n            ('timestamptz', r\"expected a datetime\\.date.*got 'str'\", [\n                'foo'\n            ]),\n        ]\n\n        for typname, errmsg, data in cases:\n            stmt = await self.con.prepare(\"SELECT $1::\" + typname)\n\n            for sample in data:\n                with self.subTest(sample=sample, typname=typname):\n                    full_errmsg = (\n                        r'invalid input for query argument \\$1:.*' + errmsg)\n\n                    with self.assertRaisesRegex(\n                            asyncpg.DataError, full_errmsg):\n                        await stmt.fetchval(sample)\n\n    async def test_arrays(self):\n        \"\"\"Test encoding/decoding of arrays (particularly multidimensional).\"\"\"\n        cases = [\n            (\n                r\"SELECT '[1:3][-1:0]={{1,2},{4,5},{6,7}}'::int[]\",\n                [[1, 2], [4, 5], [6, 7]]\n            ),\n            (\n                r\"SELECT '{{{{{{1}}}}}}'::int[]\",\n                [[[[[[1]]]]]]\n            ),\n            (\n                r\"SELECT '{1, 2, NULL}'::int[]::anyarray\",\n                [1, 2, None]\n            ),\n            (\n                r\"SELECT '{}'::int[]\",\n                []\n            ),\n        ]\n\n        for sql, expected in cases:\n            with self.subTest(sql=sql):\n                res = await self.con.fetchval(sql)\n                self.assertEqual(res, expected)\n\n        with self.assertRaises(asyncpg.ProgramLimitExceededError):\n            await self.con.fetchval(\"SELECT '{{{{{{{1}}}}}}}'::int[]\")\n\n        cases = [\n            [None],\n            [1, 2, 3, 4, 5, 6],\n            [[1, 2], [4, 5], [6, 7]],\n            [[[1], [2]], [[4], [5]], [[None], [7]]],\n            [[[[[[1]]]]]],\n            [[[[[[None]]]]]]\n        ]\n\n        st = await self.con.prepare(\n            \"SELECT $1::int[]\"\n        )\n\n        for case in cases:\n            with self.subTest(case=case):\n                result = await st.fetchval(case)\n                err_msg = (\n                    \"failed to return array data as-is; \"\n                    \"gave {!r}, received {!r}\".format(\n                        case, result))\n\n                self.assertEqual(result, case, err_msg)\n\n        # A sized iterable is fine as array input.\n        class Iterable:\n            def __iter__(self):\n                return iter([1, 2, 3])\n\n            def __len__(self):\n                return 3\n\n        result = await self.con.fetchval(\"SELECT $1::int[]\", Iterable())\n        self.assertEqual(result, [1, 2, 3])\n\n        # A pure container is _not_ OK for array input.\n        class SomeContainer:\n            def __contains__(self, item):\n                return False\n\n        with self.assertRaisesRegex(asyncpg.DataError,\n                                    'sized iterable container expected'):\n            result = await self.con.fetchval(\"SELECT $1::int[]\",\n                                             SomeContainer())\n\n        with self.assertRaisesRegex(asyncpg.DataError, 'dimensions'):\n            await self.con.fetchval(\n                \"SELECT $1::int[]\",\n                [[[[[[[1]]]]]]])\n\n        with self.assertRaisesRegex(asyncpg.DataError, 'non-homogeneous'):\n            await self.con.fetchval(\n                \"SELECT $1::int[]\",\n                [1, [1]])\n\n        with self.assertRaisesRegex(asyncpg.DataError, 'non-homogeneous'):\n            await self.con.fetchval(\n                \"SELECT $1::int[]\",\n                [[1], 1, [2]])\n\n        with self.assertRaisesRegex(asyncpg.DataError,\n                                    'invalid array element'):\n            await self.con.fetchval(\n                \"SELECT $1::int[]\",\n                [1, 't', 2])\n\n        with self.assertRaisesRegex(asyncpg.DataError,\n                                    'invalid array element'):\n            await self.con.fetchval(\n                \"SELECT $1::int[]\",\n                [[1], ['t'], [2]])\n\n        with self.assertRaisesRegex(asyncpg.DataError,\n                                    'sized iterable container expected'):\n            await self.con.fetchval(\n                \"SELECT $1::int[]\",\n                1)\n\n    async def test_composites(self):\n        \"\"\"Test encoding/decoding of composite types.\"\"\"\n        await self.con.execute('''\n            CREATE TYPE test_composite AS (\n                a int,\n                b text,\n                c int[]\n            )\n        ''')\n\n        st = await self.con.prepare('''\n            SELECT ROW(NULL, 1234, '5678', ROW(42, '42'))\n        ''')\n\n        res = await st.fetchval()\n\n        self.assertEqual(res, (None, 1234, '5678', (42, '42')))\n\n        with self.assertRaisesRegex(\n            asyncpg.UnsupportedClientFeatureError,\n            'query argument \\\\$1: input of anonymous '\n            'composite types is not supported',\n        ):\n            await self.con.fetchval(\"SELECT (1, 'foo') = $1\", (1, 'foo'))\n\n        try:\n            st = await self.con.prepare('''\n                SELECT ROW(\n                    NULL,\n                    '5678',\n                    ARRAY[9, NULL, 11]::int[]\n                )::test_composite AS test\n            ''')\n\n            res = await st.fetch()\n            res = res[0]['test']\n\n            self.assertIsNone(res['a'])\n            self.assertEqual(res['b'], '5678')\n            self.assertEqual(res['c'], [9, None, 11])\n\n            self.assertIsNone(res[0])\n            self.assertEqual(res[1], '5678')\n            self.assertEqual(res[2], [9, None, 11])\n\n            at = st.get_attributes()\n            self.assertEqual(len(at), 1)\n            self.assertEqual(at[0].name, 'test')\n            self.assertEqual(at[0].type.name, 'test_composite')\n            self.assertEqual(at[0].type.kind, 'composite')\n\n            res = await self.con.fetchval('''\n                SELECT $1::test_composite\n            ''', res)\n\n            # composite input as a mapping\n            res = await self.con.fetchval('''\n                SELECT $1::test_composite\n            ''', {'b': 'foo', 'a': 1, 'c': [1, 2, 3]})\n\n            self.assertEqual(res, (1, 'foo', [1, 2, 3]))\n\n            # Test None padding\n            res = await self.con.fetchval('''\n                SELECT $1::test_composite\n            ''', {'a': 1})\n\n            self.assertEqual(res, (1, None, None))\n\n            with self.assertRaisesRegex(\n                    asyncpg.DataError,\n                    \"'bad' is not a valid element\"):\n                await self.con.fetchval(\n                    \"SELECT $1::test_composite\",\n                    {'bad': 'foo'})\n\n        finally:\n            await self.con.execute('DROP TYPE test_composite')\n\n    async def test_domains(self):\n        \"\"\"Test encoding/decoding of composite types.\"\"\"\n        await self.con.execute('''\n            CREATE DOMAIN my_dom AS int\n        ''')\n\n        await self.con.execute('''\n            CREATE DOMAIN my_dom2 AS my_dom\n        ''')\n\n        try:\n            st = await self.con.prepare('''\n                SELECT 3::my_dom2\n            ''')\n            res = await st.fetchval()\n\n            self.assertEqual(res, 3)\n\n            st = await self.con.prepare('''\n                SELECT NULL::my_dom2\n            ''')\n            res = await st.fetchval()\n\n            self.assertIsNone(res)\n\n            at = st.get_attributes()\n            self.assertEqual(len(at), 1)\n            self.assertEqual(at[0].name, 'my_dom2')\n            self.assertEqual(at[0].type.name, 'int4')\n            self.assertEqual(at[0].type.kind, 'scalar')\n\n        finally:\n            await self.con.execute('DROP DOMAIN my_dom2')\n            await self.con.execute('DROP DOMAIN my_dom')\n\n    async def test_range_types(self):\n        \"\"\"Test encoding/decoding of range types.\"\"\"\n\n        cases = [\n            ('int4range', [\n                [(1, 9), asyncpg.Range(1, 10)],\n                [asyncpg.Range(0, 9, lower_inc=False, upper_inc=True),\n                 asyncpg.Range(1, 10)],\n                [(), asyncpg.Range(empty=True)],\n                [asyncpg.Range(empty=True), asyncpg.Range(empty=True)],\n                [(None, 2), asyncpg.Range(None, 3)],\n                [asyncpg.Range(None, 2, upper_inc=True),\n                 asyncpg.Range(None, 3)],\n                [(2,), asyncpg.Range(2, None)],\n                [(2, None), asyncpg.Range(2, None)],\n                [asyncpg.Range(2, None), asyncpg.Range(2, None)],\n                [(None, None), asyncpg.Range(None, None)],\n                [asyncpg.Range(None, None), asyncpg.Range(None, None)]\n            ])\n        ]\n\n        for (typname, sample_data) in cases:\n            st = await self.con.prepare(\n                \"SELECT $1::\" + typname\n            )\n\n            for sample, expected in sample_data:\n                with self.subTest(sample=sample, typname=typname):\n                    result = await st.fetchval(sample)\n                    self.assertEqual(result, expected)\n\n        with self.assertRaisesRegex(\n                asyncpg.DataError, 'list, tuple or Range object expected'):\n            await self.con.fetch(\"SELECT $1::int4range\", 'aa')\n\n        with self.assertRaisesRegex(\n                asyncpg.DataError, 'expected 0, 1 or 2 elements'):\n            await self.con.fetch(\"SELECT $1::int4range\", (0, 2, 3))\n\n        cases = [(asyncpg.Range(0, 1), asyncpg.Range(0, 1), 1),\n                 (asyncpg.Range(0, 1), asyncpg.Range(0, 2), 2),\n                 (asyncpg.Range(empty=True), asyncpg.Range(0, 2), 2),\n                 (asyncpg.Range(empty=True), asyncpg.Range(empty=True), 1),\n                 (asyncpg.Range(0, 1, upper_inc=True), asyncpg.Range(0, 1), 2),\n                 ]\n        for obj_a, obj_b, count in cases:\n            dic = {obj_a: 1, obj_b: 2}\n            self.assertEqual(len(dic), count)\n\n    async def test_multirange_types(self):\n        \"\"\"Test encoding/decoding of multirange types.\"\"\"\n\n        if self.server_version < (14, 0):\n            self.skipTest(\"this server does not support multirange types\")\n\n        cases = [\n            ('int4multirange', [\n                [\n                    [],\n                    []\n                ],\n                [\n                    [()],\n                    []\n                ],\n                [\n                    [asyncpg.Range(empty=True)],\n                    []\n                ],\n                [\n                    [asyncpg.Range(0, 9, lower_inc=False, upper_inc=True)],\n                    [asyncpg.Range(1, 10)]\n                ],\n                [\n                    [(1, 9), (9, 11)],\n                    [asyncpg.Range(1, 12)]\n                ],\n                [\n                    [(1, 9), (20, 30)],\n                    [asyncpg.Range(1, 10), asyncpg.Range(20, 31)]\n                ],\n                [\n                    [(None, 2)],\n                    [asyncpg.Range(None, 3)],\n                ]\n            ])\n        ]\n\n        for (typname, sample_data) in cases:\n            st = await self.con.prepare(\n                \"SELECT $1::\" + typname\n            )\n\n            for sample, expected in sample_data:\n                with self.subTest(sample=sample, typname=typname):\n                    result = await st.fetchval(sample)\n                    self.assertEqual(result, expected)\n\n        with self.assertRaisesRegex(\n                asyncpg.DataError, 'expected a sequence'):\n            await self.con.fetch(\"SELECT $1::int4multirange\", 1)\n\n    async def test_extra_codec_alias(self):\n        \"\"\"Test encoding/decoding of a builtin non-pg_catalog codec.\"\"\"\n        await self.con.execute('''\n            CREATE DOMAIN my_dec_t AS decimal;\n            CREATE EXTENSION IF NOT EXISTS hstore;\n            CREATE TYPE rec_t AS ( i my_dec_t, h hstore );\n        ''')\n\n        try:\n            await self.con.set_builtin_type_codec(\n                'hstore', codec_name='pg_contrib.hstore')\n\n            cases = [\n                {'ham': 'spam', 'nada': None},\n                {}\n            ]\n\n            st = await self.con.prepare('''\n                SELECT $1::hstore AS result\n            ''')\n\n            for case in cases:\n                res = await st.fetchval(case)\n                self.assertEqual(res, case)\n\n            res = await self.con.fetchval('''\n                SELECT $1::hstore AS result\n            ''', (('foo', '2'), ('bar', '3')))\n\n            self.assertEqual(res, {'foo': '2', 'bar': '3'})\n\n            with self.assertRaisesRegex(asyncpg.DataError,\n                                        'null value not allowed'):\n                await self.con.fetchval('''\n                    SELECT $1::hstore AS result\n                ''', {None: '1'})\n\n            await self.con.set_builtin_type_codec(\n                'my_dec_t', codec_name='decimal')\n\n            res = await self.con.fetchval('''\n                SELECT $1::my_dec_t AS result\n            ''', 44)\n\n            self.assertEqual(res, 44)\n\n            # Both my_dec_t and hstore are decoded in binary\n            res = await self.con.fetchval('''\n                SELECT ($1::my_dec_t, 'a=>1'::hstore)::rec_t AS result\n            ''', 44)\n\n            self.assertEqual(res, (44, {'a': '1'}))\n\n            # Now, declare only the text format for my_dec_t\n            await self.con.reset_type_codec('my_dec_t')\n            await self.con.set_builtin_type_codec(\n                'my_dec_t', codec_name='decimal', format='text')\n\n            # This should fail, as there is no binary codec for\n            # my_dec_t and text decoding of composites is not\n            # implemented.\n            with self.assertRaises(asyncpg.UnsupportedClientFeatureError):\n                res = await self.con.fetchval('''\n                    SELECT ($1::my_dec_t, 'a=>1'::hstore)::rec_t AS result\n                ''', 44)\n\n        finally:\n            await self.con.execute('''\n                DROP TYPE rec_t;\n                DROP EXTENSION hstore;\n                DROP DOMAIN my_dec_t;\n            ''')\n\n    async def test_custom_codec_text(self):\n        \"\"\"Test encoding/decoding using a custom codec in text mode.\"\"\"\n        await self.con.execute('''\n            CREATE EXTENSION IF NOT EXISTS hstore\n        ''')\n\n        def hstore_decoder(data):\n            result = {}\n            items = data.split(',')\n            for item in items:\n                k, _, v = item.partition('=>')\n                result[k.strip('\"')] = v.strip('\"')\n\n            return result\n\n        def hstore_encoder(obj):\n            return ','.join('{}=>{}'.format(k, v) for k, v in obj.items())\n\n        try:\n            await self.con.set_type_codec('hstore', encoder=hstore_encoder,\n                                          decoder=hstore_decoder)\n\n            st = await self.con.prepare('''\n                SELECT $1::hstore AS result\n            ''')\n\n            res = await st.fetchrow({'ham': 'spam'})\n            res = res['result']\n\n            self.assertEqual(res, {'ham': 'spam'})\n\n            pt = st.get_parameters()\n            self.assertTrue(isinstance(pt, tuple))\n            self.assertEqual(len(pt), 1)\n            self.assertEqual(pt[0].name, 'hstore')\n            self.assertEqual(pt[0].kind, 'scalar')\n            self.assertEqual(pt[0].schema, 'public')\n\n            at = st.get_attributes()\n            self.assertTrue(isinstance(at, tuple))\n            self.assertEqual(len(at), 1)\n            self.assertEqual(at[0].name, 'result')\n            self.assertEqual(at[0].type, pt[0])\n\n            err = 'cannot use custom codec on type public._hstore'\n            with self.assertRaisesRegex(asyncpg.InterfaceError, err):\n                await self.con.set_type_codec('_hstore',\n                                              encoder=hstore_encoder,\n                                              decoder=hstore_decoder)\n        finally:\n            await self.con.execute('''\n                DROP EXTENSION hstore\n            ''')\n\n    async def test_custom_codec_binary(self):\n        \"\"\"Test encoding/decoding using a custom codec in binary mode.\"\"\"\n        await self.con.execute('''\n            CREATE EXTENSION IF NOT EXISTS hstore\n        ''')\n\n        longstruct = struct.Struct('!L')\n        ulong_unpack = lambda b: longstruct.unpack_from(b)[0]\n        ulong_pack = longstruct.pack\n\n        def hstore_decoder(data):\n            result = {}\n            n = ulong_unpack(data)\n            view = memoryview(data)\n            ptr = 4\n\n            for i in range(n):\n                klen = ulong_unpack(view[ptr:ptr + 4])\n                ptr += 4\n                k = bytes(view[ptr:ptr + klen]).decode()\n                ptr += klen\n                vlen = ulong_unpack(view[ptr:ptr + 4])\n                ptr += 4\n                if vlen == -1:\n                    v = None\n                else:\n                    v = bytes(view[ptr:ptr + vlen]).decode()\n                    ptr += vlen\n\n                result[k] = v\n\n            return result\n\n        def hstore_encoder(obj):\n            buffer = bytearray(ulong_pack(len(obj)))\n\n            for k, v in obj.items():\n                kenc = k.encode()\n                buffer += ulong_pack(len(kenc)) + kenc\n\n                if v is None:\n                    buffer += b'\\xFF\\xFF\\xFF\\xFF'  # -1\n                else:\n                    venc = v.encode()\n                    buffer += ulong_pack(len(venc)) + venc\n\n            return buffer\n\n        try:\n            await self.con.set_type_codec('hstore', encoder=hstore_encoder,\n                                          decoder=hstore_decoder,\n                                          format='binary')\n\n            st = await self.con.prepare('''\n                SELECT $1::hstore AS result\n            ''')\n\n            res = await st.fetchrow({'ham': 'spam'})\n            res = res['result']\n\n            self.assertEqual(res, {'ham': 'spam'})\n\n            pt = st.get_parameters()\n            self.assertTrue(isinstance(pt, tuple))\n            self.assertEqual(len(pt), 1)\n            self.assertEqual(pt[0].name, 'hstore')\n            self.assertEqual(pt[0].kind, 'scalar')\n            self.assertEqual(pt[0].schema, 'public')\n\n            at = st.get_attributes()\n            self.assertTrue(isinstance(at, tuple))\n            self.assertEqual(len(at), 1)\n            self.assertEqual(at[0].name, 'result')\n            self.assertEqual(at[0].type, pt[0])\n\n        finally:\n            await self.con.execute('''\n                DROP EXTENSION hstore\n            ''')\n\n    async def test_custom_codec_on_domain(self):\n        \"\"\"Test encoding/decoding using a custom codec on a domain.\"\"\"\n        await self.con.execute('''\n            CREATE DOMAIN custom_codec_t AS int\n        ''')\n\n        try:\n            with self.assertRaisesRegex(\n                asyncpg.UnsupportedClientFeatureError,\n                'custom codecs on domain types are not supported'\n            ):\n                await self.con.set_type_codec(\n                    'custom_codec_t',\n                    encoder=lambda v: str(v),\n                    decoder=lambda v: int(v))\n        finally:\n            await self.con.execute('DROP DOMAIN custom_codec_t')\n\n    async def test_custom_codec_on_stdsql_types(self):\n        types = [\n            'smallint',\n            'int',\n            'integer',\n            'bigint',\n            'decimal',\n            'real',\n            'double precision',\n            'timestamp with timezone',\n            'time with timezone',\n            'timestamp without timezone',\n            'time without timezone',\n            'char',\n            'character',\n            'character varying',\n            'bit varying',\n            'CHARACTER VARYING'\n        ]\n\n        for t in types:\n            with self.subTest(type=t):\n                try:\n                    await self.con.set_type_codec(\n                        t,\n                        schema='pg_catalog',\n                        encoder=str,\n                        decoder=str,\n                        format='text'\n                    )\n                finally:\n                    await self.con.reset_type_codec(t, schema='pg_catalog')\n\n    async def test_custom_codec_on_enum(self):\n        \"\"\"Test encoding/decoding using a custom codec on an enum.\"\"\"\n        await self.con.execute('''\n            CREATE TYPE custom_codec_t AS ENUM ('foo', 'bar', 'baz')\n        ''')\n\n        try:\n            await self.con.set_type_codec(\n                'custom_codec_t',\n                encoder=lambda v: str(v).lstrip('enum :'),\n                decoder=lambda v: 'enum: ' + str(v))\n\n            v = await self.con.fetchval('SELECT $1::custom_codec_t', 'foo')\n            self.assertEqual(v, 'enum: foo')\n        finally:\n            await self.con.execute('DROP TYPE custom_codec_t')\n\n    async def test_custom_codec_on_enum_array(self):\n        \"\"\"Test encoding/decoding using a custom codec on an enum array.\n\n        Bug: https://github.com/MagicStack/asyncpg/issues/590\n        \"\"\"\n        await self.con.execute('''\n            CREATE TYPE custom_codec_t AS ENUM ('foo', 'bar', 'baz')\n        ''')\n\n        try:\n            await self.con.set_type_codec(\n                'custom_codec_t',\n                encoder=lambda v: str(v).lstrip('enum :'),\n                decoder=lambda v: 'enum: ' + str(v))\n\n            v = await self.con.fetchval(\n                \"SELECT ARRAY['foo', 'bar']::custom_codec_t[]\")\n            self.assertEqual(v, ['enum: foo', 'enum: bar'])\n\n            v = await self.con.fetchval(\n                'SELECT ARRAY[$1]::custom_codec_t[]', 'foo')\n            self.assertEqual(v, ['enum: foo'])\n\n            v = await self.con.fetchval(\"SELECT 'foo'::custom_codec_t\")\n            self.assertEqual(v, 'enum: foo')\n        finally:\n            await self.con.execute('DROP TYPE custom_codec_t')\n\n    async def test_custom_codec_override_binary(self):\n        \"\"\"Test overriding core codecs.\"\"\"\n        import json\n\n        conn = await self.connect()\n        try:\n            def _encoder(value):\n                return json.dumps(value).encode('utf-8')\n\n            def _decoder(value):\n                return json.loads(value.decode('utf-8'))\n\n            await conn.set_type_codec(\n                'json', encoder=_encoder, decoder=_decoder,\n                schema='pg_catalog', format='binary'\n            )\n\n            data = {'foo': 'bar', 'spam': 1}\n            res = await conn.fetchval('SELECT $1::json', data)\n            self.assertEqual(data, res)\n\n        finally:\n            await conn.close()\n\n    async def test_custom_codec_override_text(self):\n        \"\"\"Test overriding core codecs.\"\"\"\n        import json\n\n        conn = await self.connect()\n        try:\n            def _encoder(value):\n                return json.dumps(value)\n\n            def _decoder(value):\n                return json.loads(value)\n\n            await conn.set_type_codec(\n                'json', encoder=_encoder, decoder=_decoder,\n                schema='pg_catalog', format='text'\n            )\n\n            data = {'foo': 'bar', 'spam': 1}\n            res = await conn.fetchval('SELECT $1::json', data)\n            self.assertEqual(data, res)\n\n            res = await conn.fetchval('SELECT $1::json[]', [data])\n            self.assertEqual([data], res)\n\n            await conn.execute('CREATE DOMAIN my_json AS json')\n\n            res = await conn.fetchval('SELECT $1::my_json', data)\n            self.assertEqual(data, res)\n\n            def _encoder(value):\n                return value\n\n            def _decoder(value):\n                return value\n\n            await conn.set_type_codec(\n                'uuid', encoder=_encoder, decoder=_decoder,\n                schema='pg_catalog', format='text'\n            )\n\n            data = '14058ad9-0118-4b7e-ac15-01bc13e2ccd1'\n            res = await conn.fetchval('SELECT $1::uuid', data)\n            self.assertEqual(res, data)\n        finally:\n            await conn.execute('DROP DOMAIN IF EXISTS my_json')\n            await conn.close()\n\n    async def test_custom_codec_override_tuple(self):\n        \"\"\"Test overriding core codecs.\"\"\"\n        cases = [\n            ('date', (3,), '2000-01-04'),\n            ('date', (2**31 - 1,), 'infinity'),\n            ('date', (-2**31,), '-infinity'),\n            ('time', (60 * 10**6,), '00:01:00'),\n            ('timetz', (60 * 10**6, 12600), '00:01:00-03:30'),\n            ('timestamp', (60 * 10**6,), '2000-01-01 00:01:00'),\n            ('timestamp', (2**63 - 1,), 'infinity'),\n            ('timestamp', (-2**63,), '-infinity'),\n            ('timestamptz', (60 * 10**6,), '1999-12-31 19:01:00',\n                \"tab.v AT TIME ZONE 'EST'\"),\n            ('timestamptz', (2**63 - 1,), 'infinity'),\n            ('timestamptz', (-2**63,), '-infinity'),\n            ('interval', (2, 3, 1), '2 mons 3 days 00:00:00.000001')\n        ]\n\n        conn = await self.connect()\n\n        def _encoder(value):\n            return tuple(value)\n\n        def _decoder(value):\n            return tuple(value)\n\n        try:\n            for (typename, data, expected_result, *extra) in cases:\n                with self.subTest(type=typename):\n                    await self.con.execute(\n                        'CREATE TABLE tab (v {})'.format(typename))\n\n                    try:\n                        await conn.set_type_codec(\n                            typename, encoder=_encoder, decoder=_decoder,\n                            schema='pg_catalog', format='tuple'\n                        )\n\n                        await conn.execute(\n                            'INSERT INTO tab VALUES ($1)', data)\n\n                        res = await conn.fetchval('SELECT tab.v FROM tab')\n                        self.assertEqual(res, data)\n\n                        await conn.reset_type_codec(\n                            typename, schema='pg_catalog')\n\n                        if extra:\n                            val = extra[0]\n                        else:\n                            val = 'tab.v'\n\n                        res = await conn.fetchval(\n                            'SELECT ({val})::text FROM tab'.format(val=val))\n                        self.assertEqual(res, expected_result)\n                    finally:\n                        await self.con.execute('DROP TABLE tab')\n        finally:\n            await conn.close()\n\n    async def test_custom_codec_composite_tuple(self):\n        await self.con.execute('''\n            CREATE TYPE mycomplex AS (r float, i float);\n        ''')\n\n        try:\n            await self.con.set_type_codec(\n                'mycomplex',\n                encoder=lambda x: (x.real, x.imag),\n                decoder=lambda t: complex(t[0], t[1]),\n                format='tuple',\n            )\n\n            num = complex('1+2j')\n\n            res = await self.con.fetchval(\n                'SELECT $1::mycomplex',\n                num,\n            )\n\n            self.assertEqual(num, res)\n\n        finally:\n            await self.con.execute('''\n                DROP TYPE mycomplex;\n            ''')\n\n    async def test_custom_codec_composite_non_tuple(self):\n        await self.con.execute('''\n            CREATE TYPE mycomplex AS (r float, i float);\n        ''')\n\n        try:\n            with self.assertRaisesRegex(\n                asyncpg.UnsupportedClientFeatureError,\n                \"only tuple-format codecs can be used on composite types\",\n            ):\n                await self.con.set_type_codec(\n                    'mycomplex',\n                    encoder=lambda x: (x.real, x.imag),\n                    decoder=lambda t: complex(t[0], t[1]),\n                )\n        finally:\n            await self.con.execute('''\n                DROP TYPE mycomplex;\n            ''')\n\n    async def test_timetz_encoding(self):\n        try:\n            async with self.con.transaction():\n                await self.con.execute(\"SET TIME ZONE 'America/Toronto'\")\n                # Check decoding:\n                row = await self.con.fetchrow(\n                    'SELECT extract(epoch from now())::float8 AS epoch, '\n                    'now()::date as date, now()::timetz as time')\n                result = datetime.datetime.combine(row['date'], row['time'])\n                expected = datetime.datetime.fromtimestamp(row['epoch'],\n                                                           tz=result.tzinfo)\n                self.assertEqual(result, expected)\n\n                # Check encoding:\n                res = await self.con.fetchval(\n                    'SELECT now() = ($1::date + $2::timetz)',\n                    row['date'], row['time'])\n                self.assertTrue(res)\n        finally:\n            await self.con.execute('RESET ALL')\n\n    async def test_composites_in_arrays(self):\n        await self.con.execute('''\n            CREATE TYPE t AS (a text, b int);\n            CREATE TABLE tab (d t[]);\n        ''')\n\n        try:\n            await self.con.execute(\n                'INSERT INTO tab (d) VALUES ($1)',\n                [('a', 1)])\n\n            r = await self.con.fetchval('''\n                SELECT d FROM tab\n            ''')\n\n            self.assertEqual(r, [('a', 1)])\n        finally:\n            await self.con.execute('''\n                DROP TABLE tab;\n                DROP TYPE t;\n            ''')\n\n    async def test_table_as_composite(self):\n        await self.con.execute('''\n            CREATE TABLE tab (a text, b int);\n            INSERT INTO tab VALUES ('1', 1);\n        ''')\n\n        try:\n            r = await self.con.fetchrow('''\n                SELECT tab FROM tab\n            ''')\n\n            self.assertEqual(r, (('1', 1),))\n\n        finally:\n            await self.con.execute('''\n                DROP TABLE tab;\n            ''')\n\n    async def test_relacl_array_type(self):\n        await self.con.execute(r'''\n            CREATE USER \"\"\"u1'\";\n            CREATE USER \"{u2\";\n            CREATE USER \",u3\";\n            CREATE USER \"u4}\";\n            CREATE USER \"u5\"\"\";\n            CREATE USER \"u6\\\"\"\";\n            CREATE USER \"u7\\\";\n            CREATE USER norm1;\n            CREATE USER norm2;\n            CREATE TABLE t0 (); GRANT SELECT ON t0 TO norm1;\n            CREATE TABLE t1 (); GRANT SELECT ON t1 TO \"\"\"u1'\";\n            CREATE TABLE t2 (); GRANT SELECT ON t2 TO \"{u2\";\n            CREATE TABLE t3 (); GRANT SELECT ON t3 TO \",u3\";\n            CREATE TABLE t4 (); GRANT SELECT ON t4 TO \"u4}\";\n            CREATE TABLE t5 (); GRANT SELECT ON t5 TO \"u5\"\"\";\n            CREATE TABLE t6 (); GRANT SELECT ON t6 TO \"u6\\\"\"\";\n            CREATE TABLE t7 (); GRANT SELECT ON t7 TO \"u7\\\";\n\n            CREATE TABLE a1 ();\n                GRANT SELECT ON a1 TO \"\"\"u1'\";\n                GRANT SELECT ON a1 TO \"{u2\";\n                GRANT SELECT ON a1 TO \",u3\";\n                GRANT SELECT ON a1 TO \"norm1\";\n                GRANT SELECT ON a1 TO \"u4}\";\n                GRANT SELECT ON a1 TO \"u5\"\"\";\n                GRANT SELECT ON a1 TO \"u6\\\"\"\";\n                GRANT SELECT ON a1 TO \"u7\\\";\n                GRANT SELECT ON a1 TO \"norm2\";\n\n            CREATE TABLE a2 ();\n                GRANT SELECT ON a2 TO \"\"\"u1'\" WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \"{u2\"   WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \",u3\"   WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \"norm1\" WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \"u4}\"   WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \"u5\"\"\"  WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \"u6\\\"\"\" WITH GRANT OPTION;\n                GRANT SELECT ON a2 TO \"u7\\\"   WITH GRANT OPTION;\n\n            SET SESSION AUTHORIZATION \"\"\"u1'\"; GRANT SELECT ON a2 TO \"norm2\";\n            SET SESSION AUTHORIZATION \"{u2\";   GRANT SELECT ON a2 TO \"norm2\";\n            SET SESSION AUTHORIZATION \",u3\";   GRANT SELECT ON a2 TO \"norm2\";\n            SET SESSION AUTHORIZATION \"u4}\";   GRANT SELECT ON a2 TO \"norm2\";\n            SET SESSION AUTHORIZATION \"u5\"\"\";  GRANT SELECT ON a2 TO \"norm2\";\n            SET SESSION AUTHORIZATION \"u6\\\"\"\"; GRANT SELECT ON a2 TO \"norm2\";\n            SET SESSION AUTHORIZATION \"u7\\\";   GRANT SELECT ON a2 TO \"norm2\";\n            RESET SESSION AUTHORIZATION;\n        ''')\n\n        try:\n            rows = await self.con.fetch('''\n                SELECT\n                    relacl,\n                    relacl::text[] AS chk,\n                    relacl::text[]::text AS text_\n                FROM\n                    pg_catalog.pg_class\n                WHERE\n                    relacl IS NOT NULL\n            ''')\n\n            for row in rows:\n                self.assertEqual(row['relacl'], row['chk'],)\n\n        finally:\n            await self.con.execute(r'''\n                DROP TABLE t0;\n                DROP TABLE t1;\n                DROP TABLE t2;\n                DROP TABLE t3;\n                DROP TABLE t4;\n                DROP TABLE t5;\n                DROP TABLE t6;\n                DROP TABLE t7;\n                DROP TABLE a1;\n                DROP TABLE a2;\n                DROP USER \"\"\"u1'\";\n                DROP USER \"{u2\";\n                DROP USER \",u3\";\n                DROP USER \"u4}\";\n                DROP USER \"u5\"\"\";\n                DROP USER \"u6\\\"\"\";\n                DROP USER \"u7\\\";\n                DROP USER norm1;\n                DROP USER norm2;\n            ''')\n\n    async def test_enum(self):\n        await self.con.execute('''\n            CREATE TYPE enum_t AS ENUM ('abc', 'def', 'ghi');\n            CREATE TABLE tab (\n                a text,\n                b enum_t\n            );\n            INSERT INTO tab (a, b) VALUES ('foo', 'abc');\n            INSERT INTO tab (a, b) VALUES ('bar', 'def');\n        ''')\n\n        try:\n            for i in range(10):\n                r = await self.con.fetch('''\n                    SELECT a, b FROM tab ORDER BY b\n                ''')\n\n                self.assertEqual(r, [('foo', 'abc'), ('bar', 'def')])\n\n        finally:\n            await self.con.execute('''\n                DROP TABLE tab;\n                DROP TYPE enum_t;\n            ''')\n\n    async def test_unknown_type_text_fallback(self):\n        await self.con.execute(r'CREATE EXTENSION citext')\n        await self.con.execute(r'''\n            CREATE DOMAIN citext_dom AS citext\n        ''')\n        await self.con.execute(r'''\n            CREATE TYPE citext_range AS RANGE (SUBTYPE = citext)\n        ''')\n        await self.con.execute(r'''\n            CREATE TYPE citext_comp AS (t citext)\n        ''')\n\n        try:\n            # Check that plain fallback works.\n            result = await self.con.fetchval('''\n                SELECT $1::citext\n            ''', 'citext')\n\n            self.assertEqual(result, 'citext')\n\n            # Check that domain fallback works.\n            result = await self.con.fetchval('''\n                SELECT $1::citext_dom\n            ''', 'citext')\n\n            self.assertEqual(result, 'citext')\n\n            # Check that array fallback works.\n            cases = [\n                ['a', 'b'],\n                [None, 'b'],\n                [],\n                ['  a', '  b'],\n                ['\"a', r'\\\"\"'],\n                [['\"a', r'\\\"\"'], [',', '\",']],\n            ]\n\n            for case in cases:\n                result = await self.con.fetchval('''\n                    SELECT\n                        $1::citext[]\n                ''', case)\n\n                self.assertEqual(result, case)\n\n            # Text encoding of ranges and composite types\n            # is not supported yet.\n            with self.assertRaisesRegex(\n                    asyncpg.UnsupportedClientFeatureError,\n                    'text encoding of range types is not supported'):\n\n                await self.con.fetchval('''\n                    SELECT\n                        $1::citext_range\n                ''', ['a', 'z'])\n\n            with self.assertRaisesRegex(\n                    asyncpg.UnsupportedClientFeatureError,\n                    'text encoding of composite types is not supported'):\n\n                await self.con.fetchval('''\n                    SELECT\n                        $1::citext_comp\n                ''', ('a',))\n\n            # Check that setting a custom codec clears the codec\n            # cache properly and that subsequent queries work\n            # as expected.\n            await self.con.set_type_codec(\n                'citext', encoder=lambda d: d, decoder=lambda d: 'CI: ' + d)\n\n            result = await self.con.fetchval('''\n                SELECT\n                    $1::citext[]\n            ''', ['a', 'b'])\n\n            self.assertEqual(result, ['CI: a', 'CI: b'])\n\n        finally:\n            await self.con.execute(r'DROP TYPE citext_comp')\n            await self.con.execute(r'DROP TYPE citext_range')\n            await self.con.execute(r'DROP TYPE citext_dom')\n            await self.con.execute(r'DROP EXTENSION citext')\n\n    async def test_enum_in_array(self):\n        await self.con.execute('''\n            CREATE TYPE enum_t AS ENUM ('abc', 'def', 'ghi');\n        ''')\n\n        try:\n            result = await self.con.fetchrow('''SELECT $1::enum_t[];''',\n                                             ['abc'])\n            self.assertEqual(result, (['abc'],))\n\n            result = await self.con.fetchrow('''SELECT ARRAY[$1::enum_t];''',\n                                             'abc')\n\n            self.assertEqual(result, (['abc'],))\n\n        finally:\n            await self.con.execute('''\n                DROP TYPE enum_t;\n            ''')\n\n    async def test_enum_and_range(self):\n        await self.con.execute('''\n            CREATE TYPE enum_t AS ENUM ('abc', 'def', 'ghi');\n            CREATE TABLE testtab (\n                a int4range,\n                b enum_t\n            );\n\n            INSERT INTO testtab VALUES (\n                '[10, 20)', 'abc'\n            );\n        ''')\n\n        try:\n            result = await self.con.fetchrow('''\n                SELECT testtab.a FROM testtab WHERE testtab.b = $1\n            ''', 'abc')\n\n            self.assertEqual(result, (asyncpg.Range(10, 20),))\n        finally:\n            await self.con.execute('''\n                DROP TABLE testtab;\n                DROP TYPE enum_t;\n            ''')\n\n    async def test_enum_in_composite(self):\n        await self.con.execute('''\n            CREATE TYPE enum_t AS ENUM ('abc', 'def', 'ghi');\n            CREATE TYPE composite_w_enum AS (a int, b enum_t);\n        ''')\n\n        try:\n            result = await self.con.fetchval('''\n                SELECT ROW(1, 'def'::enum_t)::composite_w_enum\n            ''')\n            self.assertEqual(set(result.items()), {('a', 1), ('b', 'def')})\n\n        finally:\n            await self.con.execute('''\n                DROP TYPE composite_w_enum;\n                DROP TYPE enum_t;\n            ''')\n\n    async def test_enum_function_return(self):\n        await self.con.execute('''\n            CREATE TYPE enum_t AS ENUM ('abc', 'def', 'ghi');\n            CREATE FUNCTION return_enum() RETURNS enum_t\n            LANGUAGE plpgsql AS $$\n            BEGIN\n                RETURN 'abc'::enum_t;\n            END;\n            $$;\n        ''')\n\n        try:\n            result = await self.con.fetchval('''SELECT return_enum()''')\n            self.assertEqual(result, 'abc')\n\n        finally:\n            await self.con.execute('''\n                DROP FUNCTION return_enum();\n                DROP TYPE enum_t;\n            ''')\n\n    async def test_no_result(self):\n        st = await self.con.prepare('rollback')\n        self.assertTupleEqual(st.get_attributes(), ())\n\n    async def test_array_with_custom_json_text_codec(self):\n        import json\n\n        await self.con.execute('CREATE TABLE tab (id serial, val json[]);')\n        insert_sql = 'INSERT INTO tab (val) VALUES (cast($1 AS json[]));'\n        query_sql = 'SELECT val FROM tab ORDER BY id DESC;'\n        try:\n            for custom_codec in [False, True]:\n                if custom_codec:\n                    await self.con.set_type_codec(\n                        'json',\n                        encoder=lambda v: v,\n                        decoder=json.loads,\n                        schema=\"pg_catalog\",\n                    )\n\n                for val in ['\"null\"', '22', 'null', '[2]', '{\"a\": null}']:\n                    await self.con.execute(insert_sql, [val])\n                    result = await self.con.fetchval(query_sql)\n                    if custom_codec:\n                        self.assertEqual(result, [json.loads(val)])\n                    else:\n                        self.assertEqual(result, [val])\n\n                await self.con.execute(insert_sql, [None])\n                result = await self.con.fetchval(query_sql)\n                self.assertEqual(result, [None])\n\n                await self.con.execute(insert_sql, None)\n                result = await self.con.fetchval(query_sql)\n                self.assertEqual(result, None)\n\n        finally:\n            await self.con.execute('''\n                DROP TABLE tab;\n            ''')\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'using remote cluster for testing')\nclass TestCodecsLargeOIDs(tb.ConnectedTestCase):\n    LARGE_OID = 2147483648\n\n    @classmethod\n    def setup_cluster(cls):\n        cls.cluster = cls.new_cluster(pg_cluster.TempCluster)\n        cls.cluster.reset_wal(oid=cls.LARGE_OID)\n        cls.start_cluster(cls.cluster)\n\n    async def test_custom_codec_large_oid(self):\n        await self.con.execute('CREATE DOMAIN test_domain_t AS int')\n        try:\n            oid = await self.con.fetchval('''\n                SELECT oid FROM pg_type WHERE typname = 'test_domain_t'\n            ''')\n\n            expected_oid = self.LARGE_OID\n            if self.server_version >= (11, 0):\n                # PostgreSQL 11 automatically creates a domain array type\n                # _before_ the domain type, so the expected OID is\n                # off by one.\n                expected_oid += 1\n\n            self.assertEqual(oid, expected_oid)\n\n            # Test that introspection handles large OIDs\n            v = await self.con.fetchval('SELECT $1::test_domain_t', 10)\n            self.assertEqual(v, 10)\n\n        finally:\n            await self.con.execute('DROP DOMAIN test_domain_t')\n"
  },
  {
    "path": "tests/test_connect.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport contextlib\nimport gc\nimport ipaddress\nimport os\nimport pathlib\nimport platform\nimport shutil\nimport socket\nimport ssl\nimport stat\nimport tempfile\nimport textwrap\nimport unittest\nimport unittest.mock\nimport urllib.parse\nimport warnings\nimport weakref\n\nimport distro\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\nfrom asyncpg import connection as pg_connection\nfrom asyncpg import connect_utils\nfrom asyncpg import cluster as pg_cluster\nfrom asyncpg import exceptions\nfrom asyncpg.connect_utils import SSLMode\nfrom asyncpg.serverversion import split_server_version_string\n\n_system = platform.uname().system\n\n\nCERTS = os.path.join(os.path.dirname(__file__), 'certs')\nSSL_CA_CERT_FILE = os.path.join(CERTS, 'ca.cert.pem')\nSSL_CA_CRL_FILE = os.path.join(CERTS, 'ca.crl.pem')\nSSL_CERT_FILE = os.path.join(CERTS, 'server.cert.pem')\nSSL_KEY_FILE = os.path.join(CERTS, 'server.key.pem')\nCLIENT_CA_CERT_FILE = os.path.join(CERTS, 'client_ca.cert.pem')\nCLIENT_SSL_CERT_FILE = os.path.join(CERTS, 'client.cert.pem')\nCLIENT_SSL_KEY_FILE = os.path.join(CERTS, 'client.key.pem')\nCLIENT_SSL_PROTECTED_KEY_FILE = os.path.join(CERTS, 'client.key.protected.pem')\n\nif _system == 'Windows':\n    DEFAULT_GSSLIB = 'sspi'\n    OTHER_GSSLIB = 'gssapi'\nelse:\n    DEFAULT_GSSLIB = 'gssapi'\n    OTHER_GSSLIB = 'sspi'\n\n\n@contextlib.contextmanager\ndef mock_dot_postgresql(*, ca=True, crl=False, client=False, protected=False):\n    with tempfile.TemporaryDirectory() as temp_dir:\n        home = pathlib.Path(temp_dir)\n        pg_home = home / '.postgresql'\n        pg_home.mkdir()\n        if ca:\n            shutil.copyfile(SSL_CA_CERT_FILE, pg_home / 'root.crt')\n        if crl:\n            shutil.copyfile(SSL_CA_CRL_FILE, pg_home / 'root.crl')\n        if client:\n            shutil.copyfile(CLIENT_SSL_CERT_FILE, pg_home / 'postgresql.crt')\n            if protected:\n                shutil.copyfile(\n                    CLIENT_SSL_PROTECTED_KEY_FILE, pg_home / 'postgresql.key'\n                )\n            else:\n                shutil.copyfile(\n                    CLIENT_SSL_KEY_FILE, pg_home / 'postgresql.key'\n                )\n        with unittest.mock.patch(\n            'pathlib.Path.home', unittest.mock.Mock(return_value=home)\n        ):\n            yield\n\n\n@contextlib.contextmanager\ndef mock_no_home_dir():\n    with unittest.mock.patch(\n        'pathlib.Path.home', unittest.mock.Mock(side_effect=RuntimeError)\n    ):\n        yield\n\n\n@contextlib.contextmanager\ndef mock_dev_null_home_dir():\n    with unittest.mock.patch(\n        'pathlib.Path.home',\n        unittest.mock.Mock(return_value=pathlib.Path('/dev/null')),\n    ):\n        yield\n\n\nclass TestSettings(tb.ConnectedTestCase):\n\n    async def test_get_settings_01(self):\n        self.assertEqual(\n            self.con.get_settings().client_encoding,\n            'UTF8')\n\n    async def test_server_version_01(self):\n        version = self.con.get_server_version()\n        version_num = await self.con.fetchval(\"SELECT current_setting($1)\",\n                                              'server_version_num', column=0)\n        ver_maj = int(version_num[:-4])\n        ver_min = int(version_num[-4:-2])\n        ver_fix = int(version_num[-2:])\n\n        self.assertEqual(version[:3], (ver_maj, ver_min, ver_fix))\n\n    def test_server_version_02(self):\n        versions = [\n            (\"9.2\", (9, 2, 0, 'final', 0),),\n            (\"Postgres-XL 9.2.1\", (9, 2, 1, 'final', 0),),\n            (\"9.4beta1\", (9, 4, 0, 'beta', 1),),\n            (\"10devel\", (10, 0, 0, 'devel', 0),),\n            (\"10beta2\", (10, 0, 0, 'beta', 2),),\n            # For PostgreSQL versions >=10 we always\n            # set version.minor to 0.\n            (\"10.1\", (10, 0, 1, 'final', 0),),\n            (\"11.1.2\", (11, 0, 1, 'final', 0),),\n            (\"PostgreSQL 10.1 (Debian 10.1-3)\", (10, 0, 1, 'final', 0),),\n            (\"PostgreSQL 11.2-YB-2.7.1.1-b0 on x86_64-pc-linux-gnu, \"\n             \"compiled by gcc (Homebrew gcc 5.5.0_4) 5.5.0, 64-bit\",\n             (11, 0, 2, \"final\", 0),),\n        ]\n        for version, expected in versions:\n            result = split_server_version_string(version)\n            self.assertEqual(expected, result)\n\n\nCORRECT_PASSWORD = 'correct\\u1680password'\n\n\nclass BaseTestAuthentication(tb.ConnectedTestCase):\n    USERS = []\n\n    def setUp(self):\n        super().setUp()\n\n        if not self.cluster.is_managed():\n            self.skipTest('unmanaged cluster')\n\n        self.cluster.reset_hba()\n\n        create_script = []\n        for username, method, password in self.USERS:\n            if method == 'scram-sha-256' and self.server_version.major < 10:\n                continue\n\n            # if this is a SCRAM password, we need to set the encryption method\n            # to \"scram-sha-256\" in order to properly hash the password\n            if method == 'scram-sha-256':\n                create_script.append(\n                    \"SET password_encryption = 'scram-sha-256';\"\n                )\n\n            create_script.append(\n                'CREATE ROLE \"{}\" WITH LOGIN{};'.format(\n                    username,\n                    f' PASSWORD E{(password or \"\")!r}'\n                )\n            )\n\n            # to be courteous to the MD5 test, revert back to MD5 after the\n            # scram-sha-256 password is set\n            if method == 'scram-sha-256':\n                create_script.append(\n                    \"SET password_encryption = 'md5';\"\n                )\n\n            if _system != 'Windows' and method != 'gss':\n                self.cluster.add_hba_entry(\n                    type='local',\n                    database='postgres', user=username,\n                    auth_method=method)\n\n            self.cluster.add_hba_entry(\n                type='host', address=ipaddress.ip_network('127.0.0.0/24'),\n                database='postgres', user=username,\n                auth_method=method)\n\n            self.cluster.add_hba_entry(\n                type='host', address=ipaddress.ip_network('::1/128'),\n                database='postgres', user=username,\n                auth_method=method)\n\n        # Put hba changes into effect\n        self.cluster.reload()\n\n        create_script = '\\n'.join(create_script)\n        self.loop.run_until_complete(self.con.execute(create_script))\n\n    def tearDown(self):\n        # Reset cluster's pg_hba.conf since we've meddled with it\n        self.cluster.trust_local_connections()\n\n        drop_script = []\n        for username, method, _ in self.USERS:\n            if method == 'scram-sha-256' and self.server_version.major < 10:\n                continue\n\n            drop_script.append('DROP ROLE \"{}\";'.format(username))\n\n        drop_script = '\\n'.join(drop_script)\n        self.loop.run_until_complete(self.con.execute(drop_script))\n\n        super().tearDown()\n\n\nclass TestAuthentication(BaseTestAuthentication):\n    USERS = [\n        ('trust_user', 'trust', None),\n        ('reject_user', 'reject', None),\n        ('scram_sha_256_user', 'scram-sha-256', CORRECT_PASSWORD),\n        ('md5_user', 'md5', CORRECT_PASSWORD),\n        ('password_user', 'password', CORRECT_PASSWORD),\n    ]\n\n    async def _try_connect(self, **kwargs):\n        # On Windows the server sometimes just closes\n        # the connection sooner than we receive the\n        # actual error.\n        if _system == 'Windows':\n            for tried in range(3):\n                try:\n                    return await self.connect(**kwargs)\n                except asyncpg.ConnectionDoesNotExistError:\n                    pass\n\n        return await self.connect(**kwargs)\n\n    async def test_auth_bad_user(self):\n        with self.assertRaises(\n                asyncpg.InvalidAuthorizationSpecificationError):\n            await self._try_connect(user='__nonexistent__')\n\n    async def test_auth_trust(self):\n        conn = await self.connect(user='trust_user')\n        await conn.close()\n\n    async def test_auth_reject(self):\n        with self.assertRaisesRegex(\n                asyncpg.InvalidAuthorizationSpecificationError,\n                'pg_hba.conf rejects connection'):\n            await self._try_connect(user='reject_user')\n\n    async def test_auth_password_cleartext(self):\n        conn = await self.connect(\n            user='password_user',\n            password=CORRECT_PASSWORD)\n        await conn.close()\n\n        with self.assertRaisesRegex(\n                asyncpg.InvalidPasswordError,\n                'password authentication failed for user \"password_user\"'):\n            await self._try_connect(\n                user='password_user',\n                password='wrongpassword')\n\n    async def test_auth_password_cleartext_callable(self):\n        def get_correctpassword():\n            return CORRECT_PASSWORD\n\n        def get_wrongpassword():\n            return 'wrongpassword'\n\n        conn = await self.connect(\n            user='password_user',\n            password=get_correctpassword)\n        await conn.close()\n\n        with self.assertRaisesRegex(\n                asyncpg.InvalidPasswordError,\n                'password authentication failed for user \"password_user\"'):\n            await self._try_connect(\n                user='password_user',\n                password=get_wrongpassword)\n\n    async def test_auth_password_cleartext_callable_coroutine(self):\n        async def get_correctpassword():\n            return CORRECT_PASSWORD\n\n        async def get_wrongpassword():\n            return 'wrongpassword'\n\n        conn = await self.connect(\n            user='password_user',\n            password=get_correctpassword)\n        await conn.close()\n\n        with self.assertRaisesRegex(\n                asyncpg.InvalidPasswordError,\n                'password authentication failed for user \"password_user\"'):\n            await self._try_connect(\n                user='password_user',\n                password=get_wrongpassword)\n\n    async def test_auth_password_cleartext_callable_awaitable(self):\n        async def get_correctpassword():\n            return CORRECT_PASSWORD\n\n        async def get_wrongpassword():\n            return 'wrongpassword'\n\n        conn = await self.connect(\n            user='password_user',\n            password=lambda: get_correctpassword())\n        await conn.close()\n\n        with self.assertRaisesRegex(\n                asyncpg.InvalidPasswordError,\n                'password authentication failed for user \"password_user\"'):\n            await self._try_connect(\n                user='password_user',\n                password=lambda: get_wrongpassword())\n\n    async def test_auth_password_md5(self):\n        conn = await self.connect(\n            user='md5_user', password=CORRECT_PASSWORD)\n        await conn.close()\n\n        with self.assertRaisesRegex(\n                asyncpg.InvalidPasswordError,\n                'password authentication failed for user \"md5_user\"'):\n            await self._try_connect(\n                user='md5_user', password='wrongpassword')\n\n    async def test_auth_password_scram_sha_256(self):\n        # scram is only supported in PostgreSQL 10 and above\n        if self.server_version.major < 10:\n            return\n\n        conn = await self.connect(\n            user='scram_sha_256_user', password=CORRECT_PASSWORD)\n        await conn.close()\n\n        with self.assertRaisesRegex(\n                asyncpg.InvalidPasswordError,\n                'password authentication failed for user \"scram_sha_256_user\"'\n        ):\n            await self._try_connect(\n                user='scram_sha_256_user', password='wrongpassword')\n\n        # various SASL prep tests\n        # first ensure that password are being hashed for SCRAM-SHA-256\n        await self.con.execute(\"SET password_encryption = 'scram-sha-256';\")\n        alter_password = \"ALTER ROLE scram_sha_256_user PASSWORD E{!r};\"\n        passwords = [\n            'nonascii\\u1680space',  # C.1.2\n            'common\\u1806nothing',  # B.1\n            'ab\\ufb01c',            # normalization\n            'ab\\u007fc',            # C.2.1\n            'ab\\u206ac',            # C.2.2, C.6\n            'ab\\ue000c',            # C.3, C.5\n            'ab\\ufdd0c',            # C.4\n            'ab\\u2ff0c',            # C.7\n            'ab\\u2000c',            # C.8\n            'ab\\ue0001',            # C.9\n        ]\n\n        # ensure the passwords that go through SASLprep work\n        for password in passwords:\n            # update the password\n            await self.con.execute(alter_password.format(password))\n            # test to see that passwords are properly SASL prepped\n            conn = await self.connect(\n                user='scram_sha_256_user', password=password)\n            await conn.close()\n\n        alter_password = \\\n            f\"ALTER ROLE scram_sha_256_user PASSWORD E{CORRECT_PASSWORD!r};\"\n        await self.con.execute(alter_password)\n        await self.con.execute(\"SET password_encryption = 'md5';\")\n\n    @unittest.mock.patch('hashlib.md5', side_effect=ValueError(\"no md5\"))\n    async def test_auth_md5_unsupported(self, _):\n        with self.assertRaisesRegex(\n            exceptions.InternalClientError,\n            \".*no md5.*\",\n        ):\n            await self.connect(user='md5_user', password=CORRECT_PASSWORD)\n\n\n@unittest.skipIf(\n    distro.id() == \"alpine\",\n    \"Alpine Linux ships PostgreSQL without GSS auth support\",\n)\nclass TestGssAuthentication(BaseTestAuthentication):\n    @classmethod\n    def setUpClass(cls):\n        try:\n            from k5test.realm import K5Realm\n        except ModuleNotFoundError:\n            raise unittest.SkipTest('k5test not installed')\n\n        cls.realm = K5Realm()\n        cls.addClassCleanup(cls.realm.stop)\n        # Setup environment before starting the cluster.\n        patch = unittest.mock.patch.dict(os.environ, cls.realm.env)\n        patch.start()\n        cls.addClassCleanup(patch.stop)\n        # Add credentials.\n        cls.realm.addprinc('postgres/localhost')\n        cls.realm.extract_keytab('postgres/localhost', cls.realm.keytab)\n\n        cls.USERS = [\n            (cls.realm.user_princ, 'gss', None),\n            (f'wrong-{cls.realm.user_princ}', 'gss', None),\n        ]\n        super().setUpClass()\n\n        cls.cluster.override_connection_spec(host='localhost')\n\n    @classmethod\n    def get_server_settings(cls):\n        settings = super().get_server_settings()\n        settings['krb_server_keyfile'] = f'FILE:{cls.realm.keytab}'\n        return settings\n\n    @classmethod\n    def setup_cluster(cls):\n        cls.cluster = cls.new_cluster(pg_cluster.TempCluster)\n        cls.start_cluster(\n            cls.cluster, server_settings=cls.get_server_settings())\n\n    async def test_auth_gssapi_ok(self):\n        conn = await self.connect(user=self.realm.user_princ)\n        await conn.close()\n\n    async def test_auth_gssapi_bad_srvname(self):\n        # Service name mismatch.\n        with self.assertRaisesRegex(\n            exceptions.InternalClientError,\n            'Server .* not found'\n        ):\n            await self.connect(user=self.realm.user_princ, krbsrvname='wrong')\n\n    async def test_auth_gssapi_bad_user(self):\n        # Credentials mismatch.\n        with self.assertRaisesRegex(\n            exceptions.InvalidAuthorizationSpecificationError,\n            'GSSAPI authentication failed for user'\n        ):\n            await self.connect(user=f'wrong-{self.realm.user_princ}')\n\n\n@unittest.skipIf(_system != 'Windows', 'SSPI is only available on Windows')\nclass TestSspiAuthentication(BaseTestAuthentication):\n    @classmethod\n    def setUpClass(cls):\n        cls.username = f'{os.getlogin()}@{socket.gethostname()}'\n        cls.USERS = [\n            (cls.username, 'sspi', None),\n            (f'wrong-{cls.username}', 'sspi', None),\n        ]\n        super().setUpClass()\n\n    async def test_auth_sspi(self):\n        conn = await self.connect(user=self.username)\n        await conn.close()\n\n        # Credentials mismatch.\n        with self.assertRaisesRegex(\n            exceptions.InvalidAuthorizationSpecificationError,\n            'SSPI authentication failed for user'\n        ):\n            await self.connect(user=f'wrong-{self.username}')\n\n\nclass TestConnectParams(tb.TestCase):\n\n    TESTS = [\n        {\n            'name': 'all_env_default_ssl',\n            'env': {\n                'PGUSER': 'user',\n                'PGDATABASE': 'testdb',\n                'PGPASSWORD': 'passw',\n                'PGHOST': 'host',\n                'PGPORT': '123'\n            },\n            'result': ([('host', 123)], {\n                'user': 'user',\n                'password': 'passw',\n                'database': 'testdb',\n                'ssl': True,\n                'sslmode': SSLMode.prefer,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'params_override_env',\n            'env': {\n                'PGUSER': 'user',\n                'PGDATABASE': 'testdb',\n                'PGPASSWORD': 'passw',\n                'PGHOST': 'host',\n                'PGPORT': '123'\n            },\n\n            'host': 'host2',\n            'port': '456',\n            'user': 'user2',\n            'password': 'passw2',\n            'database': 'db2',\n\n            'result': ([('host2', 456)], {\n                'user': 'user2',\n                'password': 'passw2',\n                'database': 'db2',\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'params_override_env_and_dsn',\n            'env': {\n                'PGUSER': 'user',\n                'PGDATABASE': 'testdb',\n                'PGPASSWORD': 'passw',\n                'PGHOST': 'host',\n                'PGPORT': '123',\n                'PGSSLMODE': 'allow'\n            },\n\n            'dsn': 'postgres://user3:123123@localhost/abcdef',\n\n            'host': 'host2',\n            'port': '456',\n            'user': 'user2',\n            'password': 'passw2',\n            'database': 'db2',\n            'ssl': False,\n\n            'result': ([('host2', 456)], {\n                'user': 'user2',\n                'password': 'passw2',\n                'database': 'db2',\n                'sslmode': SSLMode.disable,\n                'ssl': False,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'dsn_overrides_env_partially',\n            'env': {\n                'PGUSER': 'user',\n                'PGDATABASE': 'testdb',\n                'PGPASSWORD': 'passw',\n                'PGHOST': 'host',\n                'PGPORT': '123',\n                'PGSSLMODE': 'allow'\n            },\n\n            'dsn': 'postgres://user3:123123@localhost:5555/abcdef',\n\n            'result': ([('localhost', 5555)], {\n                'user': 'user3',\n                'password': '123123',\n                'database': 'abcdef',\n                'ssl': True,\n                'sslmode': SSLMode.allow,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'params_override_env_and_dsn_ssl_prefer',\n            'env': {\n                'PGUSER': 'user',\n                'PGDATABASE': 'testdb',\n                'PGPASSWORD': 'passw',\n                'PGHOST': 'host',\n                'PGPORT': '123',\n                'PGSSLMODE': 'prefer'\n            },\n\n            'dsn': 'postgres://user3:123123@localhost/abcdef',\n\n            'host': 'host2',\n            'port': '456',\n            'user': 'user2',\n            'password': 'passw2',\n            'database': 'db2',\n            'ssl': False,\n\n            'result': ([('host2', 456)], {\n                'user': 'user2',\n                'password': 'passw2',\n                'database': 'db2',\n                'sslmode': SSLMode.disable,\n                'ssl': False,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'params_ssl_negotiation_dsn',\n            'env': {\n                'PGSSLNEGOTIATION': 'postgres'\n            },\n\n            'dsn': 'postgres://u:p@localhost/d?sslnegotiation=direct',\n\n            'result': ([('localhost', 5432)], {\n                'user': 'u',\n                'password': 'p',\n                'database': 'd',\n                'ssl_negotiation': 'direct',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'params_ssl_negotiation_env',\n            'env': {\n                'PGSSLNEGOTIATION': 'direct'\n            },\n\n            'dsn': 'postgres://u:p@localhost/d',\n\n            'result': ([('localhost', 5432)], {\n                'user': 'u',\n                'password': 'p',\n                'database': 'd',\n                'ssl_negotiation': 'direct',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'params_ssl_negotiation_params',\n            'env': {\n                'PGSSLNEGOTIATION': 'direct'\n            },\n\n            'dsn': 'postgres://u:p@localhost/d',\n            'direct_tls': False,\n\n            'result': ([('localhost', 5432)], {\n                'user': 'u',\n                'password': 'p',\n                'database': 'd',\n                'ssl_negotiation': 'postgres',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'dsn_overrides_env_partially_ssl_prefer',\n            'env': {\n                'PGUSER': 'user',\n                'PGDATABASE': 'testdb',\n                'PGPASSWORD': 'passw',\n                'PGHOST': 'host',\n                'PGPORT': '123',\n                'PGSSLMODE': 'prefer'\n            },\n\n            'dsn': 'postgres://user3:123123@localhost:5555/abcdef',\n\n            'result': ([('localhost', 5555)], {\n                'user': 'user3',\n                'password': '123123',\n                'database': 'abcdef',\n                'ssl': True,\n                'sslmode': SSLMode.prefer,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'dsn_only',\n            'dsn': 'postgres://user3:123123@localhost:5555/abcdef',\n            'result': ([('localhost', 5555)], {\n                'user': 'user3',\n                'password': '123123',\n                'database': 'abcdef',\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'dsn_only_multi_host',\n            'dsn': 'postgresql://user@host1,host2/db',\n            'result': ([('host1', 5432), ('host2', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'dsn_only_multi_host_and_port',\n            'dsn': 'postgresql://user@host1:1111,host2:2222/db',\n            'result': ([('host1', 1111), ('host2', 2222)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'target_session_attrs',\n            'dsn': 'postgresql://user@host1:1111,host2:2222/db'\n                   '?target_session_attrs=read-only',\n            'result': ([('host1', 1111), ('host2', 2222)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'read-only',\n            })\n        },\n\n        {\n            'name': 'target_session_attrs_2',\n            'dsn': 'postgresql://user@host1:1111,host2:2222/db'\n                   '?target_session_attrs=read-only',\n            'target_session_attrs': 'read-write',\n            'result': ([('host1', 1111), ('host2', 2222)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'read-write',\n            })\n        },\n\n        {\n            'name': 'target_session_attrs_3',\n            'dsn': 'postgresql://user@host1:1111,host2:2222/db',\n            'env': {\n                'PGTARGETSESSIONATTRS': 'read-only',\n            },\n            'result': ([('host1', 1111), ('host2', 2222)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'read-only',\n            })\n        },\n\n        {\n            'name': 'krbsrvname',\n            'dsn': 'postgresql://user@host/db?krbsrvname=srv_qs',\n            'env': {\n                'PGKRBSRVNAME': 'srv_env',\n            },\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'krbsrvname': 'srv_qs',\n            })\n        },\n\n        {\n            'name': 'krbsrvname_2',\n            'dsn': 'postgresql://user@host/db?krbsrvname=srv_qs',\n            'krbsrvname': 'srv_kws',\n            'env': {\n                'PGKRBSRVNAME': 'srv_env',\n            },\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'krbsrvname': 'srv_kws',\n            })\n        },\n\n        {\n            'name': 'krbsrvname_3',\n            'dsn': 'postgresql://user@host/db',\n            'env': {\n                'PGKRBSRVNAME': 'srv_env',\n            },\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'krbsrvname': 'srv_env',\n            })\n        },\n\n        {\n            'name': 'gsslib',\n            'dsn': f'postgresql://user@host/db?gsslib={OTHER_GSSLIB}',\n            'env': {\n                'PGGSSLIB': 'ignored',\n            },\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'gsslib': OTHER_GSSLIB,\n            })\n        },\n\n        {\n            'name': 'gsslib_2',\n            'dsn': 'postgresql://user@host/db?gsslib=ignored',\n            'gsslib': OTHER_GSSLIB,\n            'env': {\n                'PGGSSLIB': 'ignored',\n            },\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'gsslib': OTHER_GSSLIB,\n            })\n        },\n\n        {\n            'name': 'gsslib_3',\n            'dsn': 'postgresql://user@host/db',\n            'env': {\n                'PGGSSLIB': OTHER_GSSLIB,\n            },\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'gsslib': OTHER_GSSLIB,\n            })\n        },\n\n        {\n            'name': 'gsslib_4',\n            'dsn': 'postgresql://user@host/db',\n            'result': ([('host', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n                'gsslib': DEFAULT_GSSLIB,\n            })\n        },\n\n        {\n            'name': 'gsslib_5',\n            'dsn': 'postgresql://user@host/db?gsslib=invalid',\n            'error': (\n                exceptions.ClientConfigurationError,\n                \"gsslib parameter must be either 'gssapi' or 'sspi'\"\n            ),\n        },\n\n        # broken by https://github.com/python/cpython/pull/129418\n        # {\n        #     'name': 'dsn_ipv6_multi_host',\n        #     'dsn': 'postgresql://user@[2001:db8::1234%25eth0],[::1]/db',\n        #     'result': ([('2001:db8::1234%eth0', 5432), ('::1', 5432)], {\n        #         'database': 'db',\n        #         'user': 'user',\n        #         'target_session_attrs': 'any',\n        #     })\n        # },\n\n        # {\n        #     'name': 'dsn_ipv6_multi_host_port',\n        #     'dsn': 'postgresql://user@[2001:db8::1234]:1111,[::1]:2222/db',\n        #     'result': ([('2001:db8::1234', 1111), ('::1', 2222)], {\n        #         'database': 'db',\n        #         'user': 'user',\n        #         'target_session_attrs': 'any',\n        #     })\n        # },\n\n        {\n            'name': 'dsn_ipv6_multi_host_query_part',\n            'dsn': 'postgresql:///db?user=user&host=[2001:db8::1234],[::1]',\n            'result': ([('2001:db8::1234', 5432), ('::1', 5432)], {\n                'database': 'db',\n                'user': 'user',\n                'target_session_attrs': 'any',\n            })\n        },\n\n\n        {\n            'name': 'dsn_combines_env_multi_host',\n            'env': {\n                'PGHOST': 'host1:1111,host2:2222',\n                'PGUSER': 'foo',\n            },\n            'dsn': 'postgresql:///db',\n            'result': ([('host1', 1111), ('host2', 2222)], {\n                'database': 'db',\n                'user': 'foo',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'dsn_multi_host_combines_env',\n            'env': {\n                'PGUSER': 'foo',\n            },\n            'dsn': 'postgresql:///db?host=host1:1111,host2:2222',\n            'result': ([('host1', 1111), ('host2', 2222)], {\n                'database': 'db',\n                'user': 'foo',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'params_multi_host_dsn_env_mix',\n            'env': {\n                'PGUSER': 'foo',\n            },\n            'dsn': 'postgresql:///db',\n            'host': ['host1', 'host2'],\n            'result': ([('host1', 5432), ('host2', 5432)], {\n                'database': 'db',\n                'user': 'foo',\n                'target_session_attrs': 'any',\n            })\n        },\n        {\n            'name': 'params_multi_host_dsn_env_mix_tuple',\n            'env': {\n                'PGUSER': 'foo',\n            },\n            'dsn': 'postgresql:///db',\n            'host': ('host1', 'host2'),\n            'result': ([('host1', 5432), ('host2', 5432)], {\n                'database': 'db',\n                'user': 'foo',\n                'target_session_attrs': 'any',\n            })\n        },\n\n        {\n            'name': 'params_combine_dsn_settings_override_and_ssl',\n            'dsn': 'postgresql://user3:123123@localhost:5555/'\n                   'abcdef?param=sss&param=123&host=testhost&user=testuser'\n                   '&port=2222&database=testdb&sslmode=require',\n            'host': '127.0.0.1',\n            'port': '888',\n            'user': 'me',\n            'password': 'ask',\n            'database': 'db',\n            'result': ([('127.0.0.1', 888)], {\n                'server_settings': {'param': '123'},\n                'user': 'me',\n                'password': 'ask',\n                'database': 'db',\n                'ssl': True,\n                'sslmode': SSLMode.require,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'params_settings_and_ssl_override_dsn',\n            'dsn': 'postgresql://user3:123123@localhost:5555/'\n                   'abcdef?param=sss&param=123&host=testhost&user=testuser'\n                   '&port=2222&database=testdb&sslmode=disable',\n            'host': '127.0.0.1',\n            'port': '888',\n            'user': 'me',\n            'password': 'ask',\n            'database': 'db',\n            'server_settings': {'aa': 'bb'},\n            'ssl': True,\n            'result': ([('127.0.0.1', 888)], {\n                'server_settings': {'aa': 'bb', 'param': '123'},\n                'user': 'me',\n                'password': 'ask',\n                'database': 'db',\n                'sslmode': SSLMode.verify_full,\n                'ssl': True,\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'dsn_only_unix',\n            'dsn': 'postgresql:///dbname?host=/unix_sock/test&user=spam',\n            'result': ([os.path.join('/unix_sock/test', '.s.PGSQL.5432')], {\n                'user': 'spam',\n                'database': 'dbname',\n                'target_session_attrs': 'any'})\n        },\n\n        {\n            'name': 'dsn_only_quoted',\n            'dsn': 'postgresql://us%40r:p%40ss@h%40st1,h%40st2:543%33/d%62',\n            'result': (\n                [('h@st1', 5432), ('h@st2', 5433)],\n                {\n                    'user': 'us@r',\n                    'password': 'p@ss',\n                    'database': 'db',\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n\n        {\n            'name': 'dsn_only_unquoted_host',\n            'dsn': 'postgresql://user:p@ss@host/db',\n            'result': (\n                [('ss@host', 5432)],\n                {\n                    'user': 'user',\n                    'password': 'p',\n                    'database': 'db',\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n\n        {\n            'name': 'dsn_only_quoted_params',\n            'dsn': 'postgresql:///d%62?user=us%40r&host=h%40st&port=543%33',\n            'result': (\n                [('h@st', 5433)],\n                {\n                    'user': 'us@r',\n                    'database': 'db',\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n\n        {\n            'name': 'dsn_only_illegal_protocol',\n            'dsn': 'pq:///dbname?host=/unix_sock/test&user=spam',\n            'error': (ValueError, 'invalid DSN')\n        },\n        {\n            'name': 'dsn_params_ports_mismatch_dsn_multi_hosts',\n            'dsn': 'postgresql://host1,host2,host3/db',\n            'port': [111, 222],\n            'error': (\n                exceptions.InterfaceError,\n                'could not match 2 port numbers to 3 hosts'\n            )\n        },\n        {\n            'name': 'dsn_only_quoted_unix_host_port_in_params',\n            'dsn': 'postgres://user@?port=56226&host=%2Ftmp',\n            'result': (\n                [os.path.join('/tmp', '.s.PGSQL.56226')],\n                {\n                    'user': 'user',\n                    'database': 'user',\n                    'sslmode': SSLMode.disable,\n                    'ssl': None,\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n        {\n            'name': 'dsn_only_cloudsql',\n            'dsn': 'postgres:///db?host=/cloudsql/'\n                   'project:region:instance-name&user=spam',\n            'result': (\n                [os.path.join(\n                    '/cloudsql/project:region:instance-name',\n                    '.s.PGSQL.5432'\n                )], {\n                    'user': 'spam',\n                    'database': 'db',\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n        {\n            'name': 'dsn_only_cloudsql_unix_and_tcp',\n            'dsn': 'postgres:///db?host=127.0.0.1:5432,/cloudsql/'\n                   'project:region:instance-name,localhost:5433&user=spam',\n            'result': (\n                [\n                    ('127.0.0.1', 5432),\n                    os.path.join(\n                        '/cloudsql/project:region:instance-name',\n                        '.s.PGSQL.5432'\n                    ),\n                    ('localhost', 5433)\n                ], {\n                    'user': 'spam',\n                    'database': 'db',\n                    'ssl': True,\n                    'sslmode': SSLMode.prefer,\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n        {\n            'name': 'multi_host_single_port',\n            'dsn': 'postgres:///postgres?host=127.0.0.1,127.0.0.2&port=5432'\n                   '&user=postgres',\n            'result': (\n                [\n                    ('127.0.0.1', 5432),\n                    ('127.0.0.2', 5432)\n                ], {\n                    'user': 'postgres',\n                    'database': 'postgres',\n                    'target_session_attrs': 'any',\n                }\n            )\n        },\n    ]\n\n    @contextlib.contextmanager\n    def environ(self, **kwargs):\n        old_vals = {}\n        for key in kwargs:\n            if key in os.environ:\n                old_vals[key] = os.environ[key]\n\n        for key, val in kwargs.items():\n            if val is None:\n                if key in os.environ:\n                    del os.environ[key]\n            else:\n                os.environ[key] = val\n\n        try:\n            yield\n        finally:\n            for key in kwargs:\n                if key in os.environ:\n                    del os.environ[key]\n            for key, val in old_vals.items():\n                os.environ[key] = val\n\n    def run_testcase(self, testcase):\n        env = testcase.get('env', {})\n        test_env = {'PGHOST': None, 'PGPORT': None,\n                    'PGUSER': None, 'PGPASSWORD': None,\n                    'PGDATABASE': None, 'PGSSLMODE': None,\n                    'PGSERVICE': None, }\n        test_env.update(env)\n\n        dsn = testcase.get('dsn')\n        user = testcase.get('user')\n        port = testcase.get('port')\n        host = testcase.get('host')\n        password = testcase.get('password')\n        passfile = testcase.get('passfile')\n        database = testcase.get('database')\n        sslmode = testcase.get('ssl')\n        direct_tls = testcase.get('direct_tls')\n        server_settings = testcase.get('server_settings')\n        target_session_attrs = testcase.get('target_session_attrs')\n        krbsrvname = testcase.get('krbsrvname')\n        gsslib = testcase.get('gsslib')\n        service = testcase.get('service')\n        servicefile = testcase.get('servicefile')\n\n        expected = testcase.get('result')\n        expected_error = testcase.get('error')\n        if expected is None and expected_error is None:\n            raise RuntimeError(\n                'invalid test case: either \"result\" or \"error\" key '\n                'has to be specified')\n        if expected is not None and expected_error is not None:\n            raise RuntimeError(\n                'invalid test case: either \"result\" or \"error\" key '\n                'has to be specified, got both')\n\n        with contextlib.ExitStack() as es:\n            es.enter_context(self.subTest(dsn=dsn, env=env))\n            es.enter_context(self.environ(**test_env))\n\n            if expected_error:\n                es.enter_context(self.assertRaisesRegex(*expected_error))\n\n            addrs, params = connect_utils._parse_connect_dsn_and_args(\n                dsn=dsn, host=host, port=port, user=user, password=password,\n                passfile=passfile, database=database, ssl=sslmode,\n                direct_tls=direct_tls,\n                server_settings=server_settings,\n                target_session_attrs=target_session_attrs,\n                krbsrvname=krbsrvname, gsslib=gsslib,\n                service=service, servicefile=servicefile)\n\n            params = {\n                k: v for k, v in params._asdict().items()\n                if v is not None or (expected is not None and k in expected[1])\n            }\n\n            if isinstance(params.get('ssl'), ssl.SSLContext):\n                params['ssl'] = True\n\n            result = (addrs, params)\n\n        if expected is not None:\n            if 'ssl' not in expected[1]:\n                # Avoid the hassle of specifying the default SSL mode\n                # unless explicitly tested for.\n                params.pop('ssl', None)\n                params.pop('sslmode', None)\n            if 'direct_tls' not in expected[1]:\n                # Avoid the hassle of specifying direct_tls\n                # unless explicitly tested for\n                params.pop('direct_tls', False)\n            if 'ssl_negotiation' not in expected[1]:\n                # Avoid the hassle of specifying sslnegotiation\n                # unless explicitly tested for\n                params.pop('ssl_negotiation', False)\n            if 'gsslib' not in expected[1]:\n                # Avoid the hassle of specifying gsslib\n                # unless explicitly tested for\n                params.pop('gsslib', None)\n\n            self.assertEqual(expected, result, 'Testcase: {}'.format(testcase))\n\n    def test_test_connect_params_environ(self):\n        self.assertNotIn('AAAAAAAAAA123', os.environ)\n        self.assertNotIn('AAAAAAAAAA456', os.environ)\n        self.assertNotIn('AAAAAAAAAA789', os.environ)\n\n        try:\n\n            os.environ['AAAAAAAAAA456'] = '123'\n            os.environ['AAAAAAAAAA789'] = '123'\n\n            with self.environ(AAAAAAAAAA123='1',\n                              AAAAAAAAAA456='2',\n                              AAAAAAAAAA789=None):\n\n                self.assertEqual(os.environ['AAAAAAAAAA123'], '1')\n                self.assertEqual(os.environ['AAAAAAAAAA456'], '2')\n                self.assertNotIn('AAAAAAAAAA789', os.environ)\n\n            self.assertNotIn('AAAAAAAAAA123', os.environ)\n            self.assertEqual(os.environ['AAAAAAAAAA456'], '123')\n            self.assertEqual(os.environ['AAAAAAAAAA789'], '123')\n\n        finally:\n            for key in {'AAAAAAAAAA123', 'AAAAAAAAAA456', 'AAAAAAAAAA789'}:\n                if key in os.environ:\n                    del os.environ[key]\n\n    def test_test_connect_params_run_testcase(self):\n        with self.environ(PGPORT='777'):\n            self.run_testcase({\n                'env': {\n                    'PGUSER': '__test__'\n                },\n                'host': 'abc',\n                'result': (\n                    [('abc', 5432)],\n                    {'user': '__test__',\n                     'database': '__test__',\n                     'target_session_attrs': 'any'}\n                )\n            })\n\n    def test_connect_params(self):\n        for testcase in self.TESTS:\n            self.run_testcase(testcase)\n\n    def test_connect_connection_service_file(self):\n        connection_service_file = tempfile.NamedTemporaryFile(\n            'w+t', delete=False)\n        connection_service_file.write(textwrap.dedent('''\n[test_service_dbname]\nport=5433\nhost=somehost\ndbname=test_dbname\nuser=admin\npassword=test_password\ntarget_session_attrs=primary\nkrbsrvname=fakekrbsrvname\ngsslib=sspi\n\n[test_service_database]\nport=5433\nhost=somehost\ndatabase=test_dbname\nuser=admin\npassword=test_password\ntarget_session_attrs=primary\nkrbsrvname=fakekrbsrvname\ngsslib=sspi\n        '''))\n        connection_service_file.close()\n        os.chmod(connection_service_file.name, stat.S_IWUSR | stat.S_IRUSR)\n        try:\n            # Test connection service file with dbname\n            self.run_testcase({\n                'dsn': 'postgresql://?service=test_service_dbname',\n                'env': {\n                    'PGSERVICEFILE': connection_service_file.name\n                },\n                'result': (\n                    [('somehost', 5433)],\n                    {\n                        'user': 'admin',\n                        'password': 'test_password',\n                        'database': 'test_dbname',\n                        'target_session_attrs': 'primary',\n                        'krbsrvname': 'fakekrbsrvname',\n                        'gsslib': 'sspi',\n                    }\n                )\n            })\n            # Test connection service file with database\n            self.run_testcase({\n                'dsn': 'postgresql://?service=test_service_database',\n                'env': {\n                    'PGSERVICEFILE': connection_service_file.name\n                },\n                'result': (\n                    [('somehost', 5433)],\n                    {\n                        'user': 'admin',\n                        'password': 'test_password',\n                        'database': 'test_dbname',\n                        'target_session_attrs': 'primary',\n                        'krbsrvname': 'fakekrbsrvname',\n                        'gsslib': 'sspi',\n                    }\n                )\n            })\n            # Test that envvars are overridden by service file\n            self.run_testcase({\n                'dsn': 'postgresql://?service=test_service_dbname',\n                'env': {\n                    'PGUSER': 'user',\n                    'PGSERVICEFILE': connection_service_file.name\n                },\n                'result': (\n                    [('somehost', 5433)],\n                    {\n                        'user': 'admin',\n                        'password': 'test_password',\n                        'database': 'test_dbname',\n                        'target_session_attrs': 'primary',\n                        'krbsrvname': 'fakekrbsrvname',\n                        'gsslib': 'sspi',\n                    }\n                )\n            })\n            # Test that dsn params overwrite service file\n            self.run_testcase({\n                'dsn': 'postgresql://?service={}&dbname={}'.format(\n                    \"test_service_dbname\", \"test_dbname_dsn\"\n                ),\n                'env': {\n                    'PGSERVICEFILE': connection_service_file.name\n                },\n                'result': (\n                    [('somehost', 5433)],\n                    {\n                        'user': 'admin',\n                        'password': 'test_password',\n                        'database': 'test_dbname_dsn',\n                        'target_session_attrs': 'primary',\n                        'krbsrvname': 'fakekrbsrvname',\n                        'gsslib': 'sspi',\n                    }\n                )\n            })\n        finally:\n            os.unlink(connection_service_file.name)\n\n    def test_connect_pgpass_regular(self):\n        passfile = tempfile.NamedTemporaryFile('w+t', delete=False)\n        passfile.write(textwrap.dedent(R'''\n            abc:*:*:user:password from pgpass for user@abc\n            localhost:*:*:*:password from pgpass for localhost\n            cde:5433:*:*:password from pgpass for cde:5433\n\n            *:*:*:testuser:password from pgpass for testuser\n            *:*:testdb:*:password from pgpass for testdb\n            # comment\n            *:*:test\\:db:test\\\\:password from pgpass with escapes\n        '''))\n        passfile.close()\n        os.chmod(passfile.name, stat.S_IWUSR | stat.S_IRUSR)\n\n        try:\n            # passfile path in env\n            self.run_testcase({\n                'env': {\n                    'PGPASSFILE': passfile.name\n                },\n                'host': 'abc',\n                'user': 'user',\n                'database': 'db',\n                'result': (\n                    [('abc', 5432)],\n                    {\n                        'password': 'password from pgpass for user@abc',\n                        'user': 'user',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            # passfile path as explicit arg\n            self.run_testcase({\n                'host': 'abc',\n                'user': 'user',\n                'database': 'db',\n                'passfile': passfile.name,\n                'result': (\n                    [('abc', 5432)],\n                    {\n                        'password': 'password from pgpass for user@abc',\n                        'user': 'user',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            # passfile path in dsn\n            self.run_testcase({\n                'dsn': 'postgres://user@abc/db?passfile={}'.format(\n                    passfile.name),\n                'result': (\n                    [('abc', 5432)],\n                    {\n                        'password': 'password from pgpass for user@abc',\n                        'user': 'user',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            self.run_testcase({\n                'host': 'localhost',\n                'user': 'user',\n                'database': 'db',\n                'passfile': passfile.name,\n                'result': (\n                    [('localhost', 5432)],\n                    {\n                        'password': 'password from pgpass for localhost',\n                        'user': 'user',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            if _system != 'Windows':\n                # unix socket gets normalized as localhost\n                self.run_testcase({\n                    'host': '/tmp',\n                    'user': 'user',\n                    'database': 'db',\n                    'passfile': passfile.name,\n                    'result': (\n                        ['/tmp/.s.PGSQL.5432'],\n                        {\n                            'password': 'password from pgpass for localhost',\n                            'user': 'user',\n                            'database': 'db',\n                            'target_session_attrs': 'any',\n                        }\n                    )\n                })\n\n            # port matching (also tests that `:` can be part of password)\n            self.run_testcase({\n                'host': 'cde',\n                'port': 5433,\n                'user': 'user',\n                'database': 'db',\n                'passfile': passfile.name,\n                'result': (\n                    [('cde', 5433)],\n                    {\n                        'password': 'password from pgpass for cde:5433',\n                        'user': 'user',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            # user matching\n            self.run_testcase({\n                'host': 'def',\n                'user': 'testuser',\n                'database': 'db',\n                'passfile': passfile.name,\n                'result': (\n                    [('def', 5432)],\n                    {\n                        'password': 'password from pgpass for testuser',\n                        'user': 'testuser',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            # database matching\n            self.run_testcase({\n                'host': 'efg',\n                'user': 'user',\n                'database': 'testdb',\n                'passfile': passfile.name,\n                'result': (\n                    [('efg', 5432)],\n                    {\n                        'password': 'password from pgpass for testdb',\n                        'user': 'user',\n                        'database': 'testdb',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n            # test escaping\n            self.run_testcase({\n                'host': 'fgh',\n                'user': R'test\\\\',\n                'database': R'test\\:db',\n                'passfile': passfile.name,\n                'result': (\n                    [('fgh', 5432)],\n                    {\n                        'password': 'password from pgpass with escapes',\n                        'user': R'test\\\\',\n                        'database': R'test\\:db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n        finally:\n            os.unlink(passfile.name)\n\n    @unittest.skipIf(_system == 'Windows', 'no mode checking on Windows')\n    def test_connect_pgpass_badness_mode(self):\n        # Verify that .pgpass permissions are checked\n        with tempfile.NamedTemporaryFile('w+t') as passfile:\n            os.chmod(passfile.name,\n                     stat.S_IWUSR | stat.S_IRUSR | stat.S_IWGRP | stat.S_IRGRP)\n\n            with self.assertWarnsRegex(\n                    UserWarning,\n                    'password file .* has group or world access'):\n                self.run_testcase({\n                    'host': 'abc',\n                    'user': 'user',\n                    'database': 'db',\n                    'passfile': passfile.name,\n                    'result': (\n                        [('abc', 5432)],\n                        {\n                            'user': 'user',\n                            'database': 'db',\n                            'target_session_attrs': 'any',\n                        }\n                    )\n                })\n\n    def test_connect_pgpass_badness_non_file(self):\n        # Verify warnings when .pgpass is not a file\n        with tempfile.TemporaryDirectory() as passfile:\n            with self.assertWarnsRegex(\n                    UserWarning,\n                    'password file .* is not a plain file'):\n                self.run_testcase({\n                    'host': 'abc',\n                    'user': 'user',\n                    'database': 'db',\n                    'passfile': passfile,\n                    'result': (\n                        [('abc', 5432)],\n                        {\n                            'user': 'user',\n                            'database': 'db',\n                            'target_session_attrs': 'any',\n                        }\n                    )\n                })\n\n    def test_connect_pgpass_nonexistent(self):\n        # nonexistent passfile is OK\n        self.run_testcase({\n            'host': 'abc',\n            'user': 'user',\n            'database': 'db',\n            'passfile': 'totally nonexistent',\n            'result': (\n                [('abc', 5432)],\n                {\n                    'user': 'user',\n                    'database': 'db',\n                    'target_session_attrs': 'any',\n                }\n            )\n        })\n\n    @unittest.skipIf(_system == 'Windows', 'no mode checking on Windows')\n    def test_connect_pgpass_inaccessible_file(self):\n        with tempfile.NamedTemporaryFile('w+t') as passfile:\n            os.chmod(passfile.name, stat.S_IWUSR)\n\n            # nonexistent passfile is OK\n            self.run_testcase({\n                'host': 'abc',\n                'user': 'user',\n                'database': 'db',\n                'passfile': passfile.name,\n                'result': (\n                    [('abc', 5432)],\n                    {\n                        'user': 'user',\n                        'database': 'db',\n                        'target_session_attrs': 'any',\n                    }\n                )\n            })\n\n    @unittest.skipIf(_system == 'Windows', 'no mode checking on Windows')\n    def test_connect_pgpass_inaccessible_directory(self):\n        with tempfile.TemporaryDirectory() as passdir:\n            with tempfile.NamedTemporaryFile('w+t', dir=passdir) as passfile:\n                os.chmod(passdir, stat.S_IWUSR)\n\n                try:\n                    # nonexistent passfile is OK\n                    self.run_testcase({\n                        'host': 'abc',\n                        'user': 'user',\n                        'database': 'db',\n                        'passfile': passfile.name,\n                        'result': (\n                            [('abc', 5432)],\n                            {\n                                'user': 'user',\n                                'database': 'db',\n                                'target_session_attrs': 'any',\n                            }\n                        )\n                    })\n                finally:\n                    os.chmod(passdir, stat.S_IRWXU)\n\n    async def test_connect_args_validation(self):\n        for val in {-1, 'a', True, False, 0}:\n            with self.assertRaisesRegex(ValueError, 'greater than 0'):\n                await asyncpg.connect(command_timeout=val)\n\n        for arg in {'max_cacheable_statement_size',\n                    'max_cached_statement_lifetime',\n                    'statement_cache_size'}:\n            for val in {None, -1, True, False}:\n                with self.assertRaisesRegex(ValueError, 'greater or equal'):\n                    await asyncpg.connect(**{arg: val})\n\n\nclass TestConnection(tb.ConnectedTestCase):\n\n    async def test_connection_isinstance(self):\n        self.assertTrue(isinstance(self.con, pg_connection.Connection))\n        self.assertTrue(isinstance(self.con, object))\n        self.assertFalse(isinstance(self.con, list))\n\n    async def test_connection_use_after_close(self):\n        def check():\n            return self.assertRaisesRegex(asyncpg.InterfaceError,\n                                          'connection is closed')\n\n        await self.con.close()\n\n        with check():\n            await self.con.add_listener('aaa', lambda: None)\n\n        with check():\n            self.con.transaction()\n\n        with check():\n            await self.con.executemany('SELECT 1', [])\n\n        with check():\n            await self.con.set_type_codec('aaa', encoder=None, decoder=None)\n\n        with check():\n            await self.con.set_builtin_type_codec('aaa', codec_name='aaa')\n\n        for meth in ('execute', 'fetch', 'fetchval', 'fetchrow',\n                     'prepare', 'cursor'):\n\n            with check():\n                await getattr(self.con, meth)('SELECT 1')\n\n        with check():\n            await self.con.reset()\n\n    @unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\n    async def test_connection_ssl_to_no_ssl_server(self):\n        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n        ssl_context.load_verify_locations(SSL_CA_CERT_FILE)\n\n        with self.assertRaisesRegex(ConnectionError, 'rejected SSL'):\n            await self.connect(\n                host='localhost',\n                user='ssl_user',\n                ssl=ssl_context)\n\n    @unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\n    async def test_connection_sslmode_no_ssl_server(self):\n        async def verify_works(sslmode):\n            con = None\n            try:\n                con = await self.connect(\n                    dsn='postgresql://foo/?sslmode=' + sslmode,\n                    user='postgres',\n                    database='postgres',\n                    host='localhost')\n                self.assertEqual(await con.fetchval('SELECT 42'), 42)\n                self.assertFalse(con._protocol.is_ssl)\n            finally:\n                if con:\n                    await con.close()\n\n        async def verify_fails(sslmode):\n            con = None\n            try:\n                with self.assertRaises(ConnectionError):\n                    con = await self.connect(\n                        dsn='postgresql://foo/?sslmode=' + sslmode,\n                        user='postgres',\n                        database='postgres',\n                        host='localhost')\n                    await con.fetchval('SELECT 42')\n            finally:\n                if con:\n                    await con.close()\n\n        await verify_works('disable')\n        await verify_works('allow')\n        await verify_works('prefer')\n        await verify_fails('require')\n        with mock_dot_postgresql():\n            await verify_fails('require')\n            await verify_fails('verify-ca')\n            await verify_fails('verify-full')\n\n    async def test_connection_implicit_host(self):\n        conn_spec = self.get_connection_spec()\n        con = await asyncpg.connect(\n            port=conn_spec.get('port'),\n            database=conn_spec.get('database'),\n            user=conn_spec.get('user'))\n        await con.close()\n\n    @unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\n    async def test_connection_no_home_dir(self):\n        with mock_no_home_dir():\n            con = await self.connect(\n                dsn='postgresql://foo/',\n                user='postgres',\n                database='postgres',\n                host='localhost')\n            await con.fetchval('SELECT 42')\n            await con.close()\n\n        with mock_dev_null_home_dir():\n            con = await self.connect(\n                dsn='postgresql://foo/',\n                user='postgres',\n                database='postgres',\n                host='localhost')\n            await con.fetchval('SELECT 42')\n            await con.close()\n\n        with self.assertRaisesRegex(\n            exceptions.ClientConfigurationError,\n            r'root certificate file \"~/\\.postgresql/root\\.crt\" does not exist'\n        ):\n            with mock_no_home_dir():\n                await self.connect(\n                    host='localhost',\n                    user='ssl_user',\n                    ssl='verify-full')\n\n        with self.assertRaisesRegex(\n            exceptions.ClientConfigurationError,\n            r'root certificate file \".*\" does not exist'\n        ):\n            with mock_dev_null_home_dir():\n                await self.connect(\n                    host='localhost',\n                    user='ssl_user',\n                    ssl='verify-full')\n\n\nclass BaseTestSSLConnection(tb.ConnectedTestCase):\n    @classmethod\n    def get_server_settings(cls):\n        conf = super().get_server_settings()\n        conf.update({\n            'ssl': 'on',\n            'ssl_cert_file': SSL_CERT_FILE,\n            'ssl_key_file': SSL_KEY_FILE,\n            'ssl_ca_file': CLIENT_CA_CERT_FILE,\n        })\n        if cls.cluster.get_pg_version() >= (12, 0):\n            conf['ssl_min_protocol_version'] = 'TLSv1.2'\n            conf['ssl_max_protocol_version'] = 'TLSv1.2'\n\n        return conf\n\n    @classmethod\n    def setup_cluster(cls):\n        cls.cluster = cls.new_cluster(pg_cluster.TempCluster)\n        cls.start_cluster(\n            cls.cluster, server_settings=cls.get_server_settings())\n\n    def setUp(self):\n        super().setUp()\n\n        self.cluster.reset_hba()\n\n        create_script = []\n        create_script.append('CREATE ROLE ssl_user WITH LOGIN;')\n        create_script.append('GRANT ALL ON SCHEMA public TO ssl_user;')\n\n        self._add_hba_entry()\n\n        # Put hba changes into effect\n        self.cluster.reload()\n\n        create_script = '\\n'.join(create_script)\n        self.loop.run_until_complete(self.con.execute(create_script))\n\n    def tearDown(self):\n        # Reset cluster's pg_hba.conf since we've meddled with it\n        self.cluster.trust_local_connections()\n\n        drop_script = []\n        drop_script.append('REVOKE ALL ON SCHEMA public FROM ssl_user;')\n        drop_script.append('DROP ROLE ssl_user;')\n        drop_script = '\\n'.join(drop_script)\n        self.loop.run_until_complete(self.con.execute(drop_script))\n\n        super().tearDown()\n\n    def _add_hba_entry(self):\n        raise NotImplementedError()\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\nclass TestSSLConnection(BaseTestSSLConnection):\n    def _add_hba_entry(self):\n        self.cluster.add_hba_entry(\n            type='hostssl', address=ipaddress.ip_network('127.0.0.0/24'),\n            database='postgres', user='ssl_user',\n            auth_method='trust')\n\n        self.cluster.add_hba_entry(\n            type='hostssl', address=ipaddress.ip_network('::1/128'),\n            database='postgres', user='ssl_user',\n            auth_method='trust')\n\n    async def test_ssl_connection_custom_context(self):\n        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n        ssl_context.load_verify_locations(SSL_CA_CERT_FILE)\n\n        con = await self.connect(\n            host='localhost',\n            user='ssl_user',\n            ssl=ssl_context)\n\n        try:\n            self.assertEqual(await con.fetchval('SELECT 42'), 42)\n\n            with self.assertRaises(asyncio.TimeoutError):\n                await con.execute('SELECT pg_sleep(5)', timeout=0.5)\n\n            self.assertEqual(await con.fetchval('SELECT 43'), 43)\n        finally:\n            await con.close()\n\n    async def test_ssl_connection_sslmode(self):\n        async def verify_works(sslmode, *, host='localhost'):\n            con = None\n            try:\n                con = await self.connect(\n                    dsn='postgresql://foo/postgres?sslmode=' + sslmode,\n                    host=host,\n                    user='ssl_user')\n                self.assertEqual(await con.fetchval('SELECT 42'), 42)\n                self.assertTrue(con._protocol.is_ssl)\n            finally:\n                if con:\n                    await con.close()\n\n        async def verify_fails(sslmode, *, host='localhost', exn_type):\n            # XXX: uvloop artifact\n            old_handler = self.loop.get_exception_handler()\n            con = None\n            try:\n                self.loop.set_exception_handler(lambda *args: None)\n                with self.assertRaises(exn_type):\n                    con = await self.connect(\n                        dsn='postgresql://foo/?sslmode=' + sslmode,\n                        host=host,\n                        user='ssl_user')\n                    await con.fetchval('SELECT 42')\n            finally:\n                if con:\n                    await con.close()\n                self.loop.set_exception_handler(old_handler)\n\n        invalid_auth_err = asyncpg.InvalidAuthorizationSpecificationError\n        await verify_fails('disable', exn_type=invalid_auth_err)\n        await verify_works('allow')\n        await verify_works('prefer')\n        await verify_works('require')\n        await verify_fails('verify-ca', exn_type=ValueError)\n        await verify_fails('verify-full', exn_type=ValueError)\n\n        with mock_dot_postgresql():\n            await verify_works('require')\n            await verify_works('verify-ca')\n            await verify_works('verify-ca', host='127.0.0.1')\n            await verify_works('verify-full')\n            await verify_fails('verify-full', host='127.0.0.1',\n                               exn_type=ssl.CertificateError)\n\n        with mock_dot_postgresql(crl=True):\n            await verify_fails('disable', exn_type=invalid_auth_err)\n            await verify_works('allow')\n            await verify_works('prefer')\n            await verify_fails('require',\n                               exn_type=ssl.SSLError)\n            await verify_fails('verify-ca',\n                               exn_type=ssl.SSLError)\n            await verify_fails('verify-ca', host='127.0.0.1',\n                               exn_type=ssl.SSLError)\n            await verify_fails('verify-full',\n                               exn_type=ssl.SSLError)\n\n    async def test_ssl_connection_default_context(self):\n        # XXX: uvloop artifact\n        old_handler = self.loop.get_exception_handler()\n        try:\n            self.loop.set_exception_handler(lambda *args: None)\n            with self.assertRaisesRegex(ssl.SSLError, 'verify failed'):\n                await self.connect(\n                    host='localhost',\n                    user='ssl_user',\n                    ssl=True)\n        finally:\n            self.loop.set_exception_handler(old_handler)\n\n    async def test_ssl_connection_pool(self):\n        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n        ssl_context.load_verify_locations(SSL_CA_CERT_FILE)\n\n        pool = await self.create_pool(\n            host='localhost',\n            user='ssl_user',\n            database='postgres',\n            min_size=5,\n            max_size=10,\n            ssl=ssl_context)\n\n        async def worker():\n            async with pool.acquire() as con:\n                self.assertEqual(await con.fetchval('SELECT 42'), 42)\n\n                with self.assertRaises(asyncio.TimeoutError):\n                    await con.execute('SELECT pg_sleep(5)', timeout=0.5)\n\n                self.assertEqual(await con.fetchval('SELECT 43'), 43)\n\n        tasks = [worker() for _ in range(100)]\n        await asyncio.gather(*tasks)\n        await pool.close()\n\n    async def test_executemany_uvloop_ssl_issue_700(self):\n        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)\n        ssl_context.load_verify_locations(SSL_CA_CERT_FILE)\n\n        con = await self.connect(\n            host='localhost',\n            user='ssl_user',\n            ssl=ssl_context)\n\n        try:\n            await con.execute('CREATE TABLE test_many (v int)')\n            await con.executemany(\n                'INSERT INTO test_many VALUES ($1)',\n                [(x + 1,) for x in range(100)]\n            )\n            self.assertEqual(\n                await con.fetchval('SELECT sum(v) FROM test_many'), 5050\n            )\n        finally:\n            try:\n                await con.execute('DROP TABLE IF EXISTS test_many')\n            finally:\n                await con.close()\n\n    async def test_tls_version(self):\n        if self.cluster.get_pg_version() < (12, 0):\n            self.skipTest(\"PostgreSQL < 12 cannot set ssl protocol version\")\n\n        # XXX: uvloop artifact\n        old_handler = self.loop.get_exception_handler()\n\n        with warnings.catch_warnings():\n            warnings.filterwarnings(\n                \"ignore\",\n                message=\"ssl.TLSVersion.TLSv1_1 is deprecated\",\n                category=DeprecationWarning\n            )\n            try:\n                self.loop.set_exception_handler(lambda *args: None)\n                with self.assertRaisesRegex(\n                    ssl.SSLError,\n                    '(protocol version)|(handshake failure)',\n                ):\n                    await self.connect(\n                        dsn='postgresql://ssl_user@localhost/postgres'\n                            '?sslmode=require&ssl_min_protocol_version=TLSv1.3'\n                    )\n                with self.assertRaises((ssl.SSLError, ConnectionResetError)):\n                    await self.connect(\n                        dsn='postgresql://ssl_user@localhost/postgres'\n                            '?sslmode=require'\n                            '&ssl_min_protocol_version=TLSv1.1'\n                            '&ssl_max_protocol_version=TLSv1.1'\n                    )\n                if not ssl.OPENSSL_VERSION.startswith('LibreSSL'):\n                    with self.assertRaisesRegex(ssl.SSLError, 'no protocols'):\n                        await self.connect(\n                            dsn='postgresql://ssl_user@localhost/postgres'\n                                '?sslmode=require'\n                                '&ssl_min_protocol_version=TLSv1.2'\n                                '&ssl_max_protocol_version=TLSv1.1'\n                        )\n                con = await self.connect(\n                    dsn='postgresql://ssl_user@localhost/postgres'\n                        '?sslmode=require'\n                        '&ssl_min_protocol_version=TLSv1.2'\n                        '&ssl_max_protocol_version=TLSv1.2'\n                )\n                try:\n                    self.assertEqual(await con.fetchval('SELECT 42'), 42)\n                finally:\n                    await con.close()\n            finally:\n                self.loop.set_exception_handler(old_handler)\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\nclass TestClientSSLConnection(BaseTestSSLConnection):\n    def _add_hba_entry(self):\n        self.cluster.add_hba_entry(\n            type='hostssl', address=ipaddress.ip_network('127.0.0.0/24'),\n            database='postgres', user='ssl_user',\n            auth_method='cert')\n\n        self.cluster.add_hba_entry(\n            type='hostssl', address=ipaddress.ip_network('::1/128'),\n            database='postgres', user='ssl_user',\n            auth_method='cert')\n\n    async def test_ssl_connection_client_auth_fails_with_wrong_setup(self):\n        ssl_context = ssl.create_default_context(\n            ssl.Purpose.SERVER_AUTH,\n            cafile=SSL_CA_CERT_FILE,\n        )\n\n        with self.assertRaisesRegex(\n            exceptions.InvalidAuthorizationSpecificationError,\n            \"requires a valid client certificate\",\n        ):\n            await self.connect(\n                host='localhost',\n                user='ssl_user',\n                ssl=ssl_context,\n            )\n\n    async def _test_works(self, **conn_args):\n        con = await self.connect(**conn_args)\n\n        try:\n            self.assertEqual(await con.fetchval('SELECT 42'), 42)\n        finally:\n            await con.close()\n\n    async def test_ssl_connection_client_auth_custom_context(self):\n        for key_file in (CLIENT_SSL_KEY_FILE, CLIENT_SSL_PROTECTED_KEY_FILE):\n            ssl_context = ssl.create_default_context(\n                ssl.Purpose.SERVER_AUTH,\n                cafile=SSL_CA_CERT_FILE,\n            )\n            ssl_context.load_cert_chain(\n                CLIENT_SSL_CERT_FILE,\n                keyfile=key_file,\n                password='secRet',\n            )\n            await self._test_works(\n                host='localhost',\n                user='ssl_user',\n                ssl=ssl_context,\n            )\n\n    async def test_ssl_connection_client_auth_dsn(self):\n        params = {\n            'sslrootcert': SSL_CA_CERT_FILE,\n            'sslcert': CLIENT_SSL_CERT_FILE,\n            'sslkey': CLIENT_SSL_KEY_FILE,\n            'sslmode': 'verify-full',\n        }\n        params_str = urllib.parse.urlencode(params)\n        dsn = 'postgres://ssl_user@localhost/postgres?' + params_str\n        await self._test_works(dsn=dsn)\n\n        params['sslkey'] = CLIENT_SSL_PROTECTED_KEY_FILE\n        params['sslpassword'] = 'secRet'\n        params_str = urllib.parse.urlencode(params)\n        dsn = 'postgres://ssl_user@localhost/postgres?' + params_str\n        await self._test_works(dsn=dsn)\n\n    async def test_ssl_connection_client_auth_env(self):\n        env = {\n            'PGSSLROOTCERT': SSL_CA_CERT_FILE,\n            'PGSSLCERT': CLIENT_SSL_CERT_FILE,\n            'PGSSLKEY': CLIENT_SSL_KEY_FILE,\n        }\n        dsn = 'postgres://ssl_user@localhost/postgres?sslmode=verify-full'\n        with unittest.mock.patch.dict('os.environ', env):\n            await self._test_works(dsn=dsn)\n\n        env['PGSSLKEY'] = CLIENT_SSL_PROTECTED_KEY_FILE\n        with unittest.mock.patch.dict('os.environ', env):\n            await self._test_works(dsn=dsn + '&sslpassword=secRet')\n\n    async def test_ssl_connection_client_auth_dot_postgresql(self):\n        dsn = 'postgres://ssl_user@localhost/postgres?sslmode=verify-full'\n        with mock_dot_postgresql(client=True):\n            await self._test_works(dsn=dsn)\n        with mock_dot_postgresql(client=True, protected=True):\n            await self._test_works(dsn=dsn + '&sslpassword=secRet')\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\nclass TestNoSSLConnection(BaseTestSSLConnection):\n    def _add_hba_entry(self):\n        self.cluster.add_hba_entry(\n            type='hostnossl', address=ipaddress.ip_network('127.0.0.0/24'),\n            database='postgres', user='ssl_user',\n            auth_method='trust')\n\n        self.cluster.add_hba_entry(\n            type='hostnossl', address=ipaddress.ip_network('::1/128'),\n            database='postgres', user='ssl_user',\n            auth_method='trust')\n\n    async def test_nossl_connection_sslmode(self):\n        async def verify_works(sslmode, *, host='localhost'):\n            con = None\n            try:\n                con = await self.connect(\n                    dsn='postgresql://foo/postgres?sslmode=' + sslmode,\n                    host=host,\n                    user='ssl_user')\n                self.assertEqual(await con.fetchval('SELECT 42'), 42)\n                self.assertFalse(con._protocol.is_ssl)\n            finally:\n                if con:\n                    await con.close()\n\n        async def verify_fails(sslmode, *, host='localhost'):\n            # XXX: uvloop artifact\n            old_handler = self.loop.get_exception_handler()\n            con = None\n            try:\n                self.loop.set_exception_handler(lambda *args: None)\n                with self.assertRaises(\n                        asyncpg.InvalidAuthorizationSpecificationError\n                ):\n                    con = await self.connect(\n                        dsn='postgresql://foo/?sslmode=' + sslmode,\n                        host=host,\n                        user='ssl_user')\n                    await con.fetchval('SELECT 42')\n            finally:\n                if con:\n                    await con.close()\n                self.loop.set_exception_handler(old_handler)\n\n        await verify_works('disable')\n        await verify_works('allow')\n        await verify_works('prefer')\n        await verify_fails('require')\n        with mock_dot_postgresql():\n            await verify_fails('require')\n            await verify_fails('verify-ca')\n            await verify_fails('verify-full')\n\n    async def test_nossl_connection_prefer_cancel(self):\n        con = await self.connect(\n            dsn='postgresql://foo/postgres?sslmode=prefer',\n            host='localhost',\n            user='ssl_user')\n        try:\n            self.assertFalse(con._protocol.is_ssl)\n            with self.assertRaises(asyncio.TimeoutError):\n                await con.execute('SELECT pg_sleep(5)', timeout=0.5)\n            val = await con.fetchval('SELECT 123')\n            self.assertEqual(val, 123)\n        finally:\n            await con.close()\n\n    async def test_nossl_connection_pool(self):\n        pool = await self.create_pool(\n            host='localhost',\n            user='ssl_user',\n            database='postgres',\n            min_size=5,\n            max_size=10,\n            ssl='prefer')\n\n        async def worker():\n            async with pool.acquire() as con:\n                self.assertFalse(con._protocol.is_ssl)\n                self.assertEqual(await con.fetchval('SELECT 42'), 42)\n\n                with self.assertRaises(asyncio.TimeoutError):\n                    await con.execute('SELECT pg_sleep(5)', timeout=0.5)\n\n                self.assertEqual(await con.fetchval('SELECT 43'), 43)\n\n        tasks = [worker() for _ in range(100)]\n        await asyncio.gather(*tasks)\n        await pool.close()\n\n\nclass TestConnectionGC(tb.ClusterTestCase):\n\n    async def _run_no_explicit_close_test(self):\n        gc_was_enabled = gc.isenabled()\n        gc.disable()\n        try:\n            con = await self.connect()\n            await con.fetchval(\"select 123\")\n            proto = con._protocol\n            conref = weakref.ref(con)\n            del con\n\n            self.assertIsNone(conref())\n            self.assertTrue(proto.is_closed())\n\n            # tick event loop; asyncio.selector_events._SelectorSocketTransport\n            # needs a chance to close itself and remove its reference to proto\n            await asyncio.sleep(0)\n            protoref = weakref.ref(proto)\n            del proto\n            self.assertIsNone(protoref())\n        finally:\n            if gc_was_enabled:\n                gc.enable()\n\n    async def test_no_explicit_close_no_debug(self):\n        olddebug = self.loop.get_debug()\n        self.loop.set_debug(False)\n        try:\n            with self.assertWarnsRegex(\n                    ResourceWarning,\n                    r'unclosed connection.*run in asyncio debug'):\n                await self._run_no_explicit_close_test()\n        finally:\n            self.loop.set_debug(olddebug)\n\n    async def test_no_explicit_close_with_debug(self):\n        olddebug = self.loop.get_debug()\n        self.loop.set_debug(True)\n        try:\n            with self.assertWarnsRegex(ResourceWarning,\n                                       r'unclosed connection') as rw:\n                await self._run_no_explicit_close_test()\n\n            msg = \" \".join(rw.warning.args)\n            self.assertIn(' created at:\\n', msg)\n            self.assertIn('in test_no_explicit_close_with_debug', msg)\n        finally:\n            self.loop.set_debug(olddebug)\n\n\nclass TestConnectionAttributes(tb.HotStandbyTestCase):\n\n    async def _run_connection_test(\n        self, connect, target_attribute, expected_port\n    ):\n        conn = await connect(target_session_attrs=target_attribute)\n        self.assertTrue(_get_connected_host(conn).endswith(expected_port))\n        await conn.close()\n\n    async def test_target_server_attribute_port(self):\n        master_port = self.master_cluster.get_connection_spec()['port']\n        standby_port = self.standby_cluster.get_connection_spec()['port']\n        tests = [\n            (self.connect_primary, 'primary', master_port),\n            (self.connect_standby, 'standby', standby_port),\n        ]\n\n        for connect, target_attr, expected_port in tests:\n            await self._run_connection_test(\n                connect, target_attr, expected_port\n            )\n        if self.master_cluster.get_pg_version()[0] < 14:\n            self.skipTest(\"PostgreSQL<14 does not support these features\")\n        tests = [\n            (self.connect_primary, 'read-write', master_port),\n            (self.connect_standby, 'read-only', standby_port),\n        ]\n\n        for connect, target_attr, expected_port in tests:\n            await self._run_connection_test(\n                connect, target_attr, expected_port\n            )\n\n    async def test_target_attribute_not_matched(self):\n        tests = [\n            (self.connect_standby, 'primary'),\n            (self.connect_primary, 'standby'),\n        ]\n\n        for connect, target_attr in tests:\n            with self.assertRaises(exceptions.TargetServerAttributeNotMatched):\n                await connect(target_session_attrs=target_attr)\n\n        if self.master_cluster.get_pg_version()[0] < 14:\n            self.skipTest(\"PostgreSQL<14 does not support these features\")\n        tests = [\n            (self.connect_standby, 'read-write'),\n            (self.connect_primary, 'read-only'),\n        ]\n\n        for connect, target_attr in tests:\n            with self.assertRaises(exceptions.TargetServerAttributeNotMatched):\n                await connect(target_session_attrs=target_attr)\n\n    async def test_prefer_standby_when_standby_is_up(self):\n        con = await self.connect(target_session_attrs='prefer-standby')\n        standby_port = self.standby_cluster.get_connection_spec()['port']\n        connected_host = _get_connected_host(con)\n        self.assertTrue(connected_host.endswith(standby_port))\n        await con.close()\n\n    async def test_prefer_standby_picks_master_when_standby_is_down(self):\n        primary_spec = self.get_cluster_connection_spec(self.master_cluster)\n        connection_spec = {\n            'host': [\n                primary_spec['host'],\n                'unlocalhost',\n            ],\n            'port': [primary_spec['port'], 15345],\n            'database': primary_spec['database'],\n            'user': primary_spec['user'],\n            'target_session_attrs': 'prefer-standby'\n        }\n\n        con = await self.connect(**connection_spec)\n        master_port = self.master_cluster.get_connection_spec()['port']\n        connected_host = _get_connected_host(con)\n        self.assertTrue(connected_host.endswith(master_port))\n        await con.close()\n\n\ndef _get_connected_host(con):\n    peername = con._transport.get_extra_info('peername')\n    if isinstance(peername, tuple):\n        peername = \"\".join((str(s) for s in peername if s))\n    return peername\n"
  },
  {
    "path": "tests/test_copy.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport datetime\nimport io\nimport os\nimport tempfile\nimport unittest\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\n\n\nclass TestCopyFrom(tb.ConnectedTestCase):\n\n    async def test_copy_from_table_basics(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, \"b~\" text, i int);\n            INSERT INTO copytab (a, \"b~\", i) (\n                SELECT 'a' || i::text, 'b' || i::text, i\n                FROM generate_series(1, 5) AS i\n            );\n            INSERT INTO copytab (a, \"b~\", i) VALUES('*', NULL, NULL);\n        ''')\n\n        try:\n            f = io.BytesIO()\n\n            # Basic functionality.\n            res = await self.con.copy_from_table('copytab', output=f)\n\n            self.assertEqual(res, 'COPY 6')\n\n            output = f.getvalue().decode().split('\\n')\n            self.assertEqual(\n                output,\n                [\n                    'a1\\tb1\\t1',\n                    'a2\\tb2\\t2',\n                    'a3\\tb3\\t3',\n                    'a4\\tb4\\t4',\n                    'a5\\tb5\\t5',\n                    '*\\t\\\\N\\t\\\\N',\n                    ''\n                ]\n            )\n\n            # Test parameters.\n            await self.con.execute('SET search_path=none')\n\n            f.seek(0)\n            f.truncate()\n\n            res = await self.con.copy_from_table(\n                'copytab', output=f, columns=('a', 'b~'),\n                schema_name='public', format='csv',\n                delimiter='|', null='n-u-l-l', header=True,\n                quote='*', escape='!', force_quote=('a',))\n\n            output = f.getvalue().decode().split('\\n')\n\n            self.assertEqual(\n                output,\n                [\n                    'a|b~',\n                    '*a1*|b1',\n                    '*a2*|b2',\n                    '*a3*|b3',\n                    '*a4*|b4',\n                    '*a5*|b5',\n                    '*!**|n-u-l-l',\n                    ''\n                ]\n            )\n\n            await self.con.execute('SET search_path=public')\n        finally:\n            await self.con.execute('DROP TABLE public.copytab')\n\n    async def test_copy_from_table_large_rows(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b text);\n            INSERT INTO copytab (a, b) (\n                SELECT\n                    repeat('a' || i::text, 500000),\n                    repeat('b' || i::text, 500000)\n                FROM\n                    generate_series(1, 5) AS i\n            );\n        ''')\n\n        try:\n            f = io.BytesIO()\n\n            # Basic functionality.\n            res = await self.con.copy_from_table('copytab', output=f)\n\n            self.assertEqual(res, 'COPY 5')\n\n            output = f.getvalue().decode().split('\\n')\n            self.assertEqual(\n                output,\n                [\n                    'a1' * 500000 + '\\t' + 'b1' * 500000,\n                    'a2' * 500000 + '\\t' + 'b2' * 500000,\n                    'a3' * 500000 + '\\t' + 'b3' * 500000,\n                    'a4' * 500000 + '\\t' + 'b4' * 500000,\n                    'a5' * 500000 + '\\t' + 'b5' * 500000,\n                    ''\n                ]\n            )\n        finally:\n            await self.con.execute('DROP TABLE public.copytab')\n\n    async def test_copy_from_query_basics(self):\n        f = io.BytesIO()\n\n        res = await self.con.copy_from_query('''\n            SELECT\n                repeat('a' || i::text, 500000),\n                repeat('b' || i::text, 500000)\n            FROM\n                generate_series(1, 5) AS i\n        ''', output=f)\n\n        self.assertEqual(res, 'COPY 5')\n\n        output = f.getvalue().decode().split('\\n')\n        self.assertEqual(\n            output,\n            [\n                'a1' * 500000 + '\\t' + 'b1' * 500000,\n                'a2' * 500000 + '\\t' + 'b2' * 500000,\n                'a3' * 500000 + '\\t' + 'b3' * 500000,\n                'a4' * 500000 + '\\t' + 'b4' * 500000,\n                'a5' * 500000 + '\\t' + 'b5' * 500000,\n                ''\n            ]\n        )\n\n    async def test_copy_from_query_with_args(self):\n        f = io.BytesIO()\n\n        res = await self.con.copy_from_query('''\n            SELECT\n                i,\n                i * 10,\n                $2::text\n            FROM\n                generate_series(1, 5) AS i\n            WHERE\n                i = $1\n        ''', 3, None, output=f)\n\n        self.assertEqual(res, 'COPY 1')\n\n        output = f.getvalue().decode().split('\\n')\n        self.assertEqual(\n            output,\n            [\n                '3\\t30\\t\\\\N',\n                ''\n            ]\n        )\n\n    async def test_copy_from_query_to_path(self):\n        with tempfile.NamedTemporaryFile() as f:\n            f.close()\n            await self.con.copy_from_query('''\n                SELECT\n                    i, i * 10\n                FROM\n                    generate_series(1, 5) AS i\n                WHERE\n                    i = $1\n            ''', 3, output=f.name)\n\n            with open(f.name, 'rb') as fr:\n                output = fr.read().decode().split('\\n')\n                self.assertEqual(\n                    output,\n                    [\n                        '3\\t30',\n                        ''\n                    ]\n                )\n\n    async def test_copy_from_query_to_path_like(self):\n        with tempfile.NamedTemporaryFile() as f:\n            f.close()\n\n            class Path:\n                def __init__(self, path):\n                    self.path = path\n\n                def __fspath__(self):\n                    return self.path\n\n            await self.con.copy_from_query('''\n                SELECT\n                    i, i * 10\n                FROM\n                    generate_series(1, 5) AS i\n                WHERE\n                    i = $1\n            ''', 3, output=Path(f.name))\n\n            with open(f.name, 'rb') as fr:\n                output = fr.read().decode().split('\\n')\n                self.assertEqual(\n                    output,\n                    [\n                        '3\\t30',\n                        ''\n                    ]\n                )\n\n    async def test_copy_from_query_to_bad_output(self):\n        with self.assertRaisesRegex(TypeError, 'output is expected to be'):\n            await self.con.copy_from_query('''\n                SELECT\n                    i, i * 10\n                FROM\n                    generate_series(1, 5) AS i\n                WHERE\n                    i = $1\n            ''', 3, output=1)\n\n    async def test_copy_from_query_to_sink(self):\n        with tempfile.NamedTemporaryFile() as f:\n            async def writer(data):\n                # Sleeping here to simulate slow output sink to test\n                # backpressure.\n                await asyncio.sleep(0.05)\n                f.write(data)\n\n            await self.con.copy_from_query('''\n                SELECT\n                    repeat('a', 500)\n                FROM\n                    generate_series(1, 5000) AS i\n            ''', output=writer)\n\n            f.seek(0)\n\n            output = f.read().decode().split('\\n')\n            self.assertEqual(\n                output,\n                [\n                    'a' * 500\n                ] * 5000 + ['']\n            )\n\n        self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n    async def test_copy_from_query_cancellation_explicit(self):\n        async def writer(data):\n            # Sleeping here to simulate slow output sink to test\n            # backpressure.\n            await asyncio.sleep(0.5)\n\n        coro = self.con.copy_from_query('''\n            SELECT\n                repeat('a', 500)\n            FROM\n                generate_series(1, 5000) AS i\n        ''', output=writer)\n\n        task = self.loop.create_task(coro)\n        await asyncio.sleep(0.7)\n        task.cancel()\n\n        with self.assertRaises(asyncio.CancelledError):\n            await task\n\n        self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n    async def test_copy_from_query_cancellation_on_sink_error(self):\n        async def writer(data):\n            await asyncio.sleep(0.05)\n            raise RuntimeError('failure')\n\n        coro = self.con.copy_from_query('''\n            SELECT\n                repeat('a', 500)\n            FROM\n                generate_series(1, 5000) AS i\n        ''', output=writer)\n\n        task = self.loop.create_task(coro)\n\n        with self.assertRaises(RuntimeError):\n            await task\n\n        self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n    async def test_copy_from_query_cancellation_while_waiting_for_data(self):\n        async def writer(data):\n            pass\n\n        coro = self.con.copy_from_query('''\n            SELECT\n                pg_sleep(60)\n            FROM\n                generate_series(1, 5000) AS i\n        ''', output=writer)\n\n        task = self.loop.create_task(coro)\n        await asyncio.sleep(0.7)\n        task.cancel()\n\n        with self.assertRaises(asyncio.CancelledError):\n            await task\n\n        self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n    async def test_copy_from_query_timeout_1(self):\n        async def writer(data):\n            await asyncio.sleep(0.05)\n\n        coro = self.con.copy_from_query('''\n            SELECT\n                repeat('a', 500)\n            FROM\n                generate_series(1, 5000) AS i\n        ''', output=writer, timeout=0.10)\n\n        task = self.loop.create_task(coro)\n\n        with self.assertRaises(asyncio.TimeoutError):\n            await task\n\n        self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n    async def test_copy_from_query_timeout_2(self):\n        async def writer(data):\n            try:\n                await asyncio.sleep(10)\n            except asyncio.TimeoutError:\n                raise\n            else:\n                self.fail('TimeoutError not raised')\n\n        coro = self.con.copy_from_query('''\n            SELECT\n                repeat('a', 500)\n            FROM\n                generate_series(1, 5000) AS i\n        ''', output=writer, timeout=0.10)\n\n        task = self.loop.create_task(coro)\n\n        with self.assertRaises(asyncio.TimeoutError):\n            await task\n\n        self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n\nclass TestCopyTo(tb.ConnectedTestCase):\n\n    async def test_copy_to_table_basics(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, \"b~\" text, i int);\n        ''')\n\n        try:\n            f = io.BytesIO()\n            f.write(\n                '\\n'.join([\n                    'a1\\tb1\\t1',\n                    'a2\\tb2\\t2',\n                    'a3\\tb3\\t3',\n                    'a4\\tb4\\t4',\n                    'a5\\tb5\\t5',\n                    '*\\t\\\\N\\t\\\\N',\n                    ''\n                ]).encode('utf-8')\n            )\n            f.seek(0)\n\n            res = await self.con.copy_to_table('copytab', source=f)\n            self.assertEqual(res, 'COPY 6')\n\n            output = await self.con.fetch(\"\"\"\n                SELECT * FROM copytab ORDER BY a\n            \"\"\")\n            self.assertEqual(\n                output,\n                [\n                    ('*', None, None),\n                    ('a1', 'b1', 1),\n                    ('a2', 'b2', 2),\n                    ('a3', 'b3', 3),\n                    ('a4', 'b4', 4),\n                    ('a5', 'b5', 5),\n                ]\n            )\n\n            # Test parameters.\n            await self.con.execute('TRUNCATE copytab')\n            await self.con.execute('SET search_path=none')\n\n            f.seek(0)\n            f.truncate()\n\n            f.write(\n                '\\n'.join([\n                    'a|b~',\n                    '*a1*|b1',\n                    '*a2*|b2',\n                    '*a3*|b3',\n                    '*a4*|b4',\n                    '*a5*|b5',\n                    '*!**|*n-u-l-l*',\n                    'n-u-l-l|bb',\n                ]).encode('utf-8')\n            )\n            f.seek(0)\n\n            if self.con.get_server_version() < (9, 4):\n                force_null = None\n                forced_null_expected = 'n-u-l-l'\n            else:\n                force_null = ('b~',)\n                forced_null_expected = None\n\n            res = await self.con.copy_to_table(\n                'copytab', source=f, columns=('a', 'b~'),\n                schema_name='public', format='csv',\n                delimiter='|', null='n-u-l-l', header=True,\n                quote='*', escape='!', force_not_null=('a',),\n                force_null=force_null)\n\n            self.assertEqual(res, 'COPY 7')\n\n            await self.con.execute('SET search_path=public')\n\n            output = await self.con.fetch(\"\"\"\n                SELECT * FROM copytab ORDER BY a\n            \"\"\")\n            self.assertEqual(\n                output,\n                [\n                    ('*', forced_null_expected, None),\n                    ('a1', 'b1', None),\n                    ('a2', 'b2', None),\n                    ('a3', 'b3', None),\n                    ('a4', 'b4', None),\n                    ('a5', 'b5', None),\n                    ('n-u-l-l', 'bb', None),\n                ]\n            )\n\n        finally:\n            await self.con.execute('DROP TABLE public.copytab')\n\n    async def test_copy_to_table_large_rows(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b text);\n        ''')\n\n        try:\n            class _Source:\n                def __init__(self):\n                    self.rowcount = 0\n\n                def __aiter__(self):\n                    return self\n\n                async def __anext__(self):\n                    if self.rowcount >= 100:\n                        raise StopAsyncIteration\n                    else:\n                        self.rowcount += 1\n                        return b'a1' * 500000 + b'\\t' + b'b1' * 500000 + b'\\n'\n\n            res = await self.con.copy_to_table('copytab', source=_Source())\n\n            self.assertEqual(res, 'COPY 100')\n\n        finally:\n            await self.con.execute('DROP TABLE copytab')\n\n    async def test_copy_to_table_from_bytes_like(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b text);\n        ''')\n\n        try:\n            data = memoryview((b'a1' * 500 + b'\\t' + b'b1' * 500 + b'\\n') * 2)\n            res = await self.con.copy_to_table('copytab', source=data)\n            self.assertEqual(res, 'COPY 2')\n        finally:\n            await self.con.execute('DROP TABLE copytab')\n\n    async def test_copy_to_table_fail_in_source_1(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b text);\n        ''')\n\n        try:\n            class _Source:\n                def __init__(self):\n                    self.rowcount = 0\n\n                def __aiter__(self):\n                    return self\n\n                async def __anext__(self):\n                    raise RuntimeError('failure in source')\n\n            with self.assertRaisesRegex(RuntimeError, 'failure in source'):\n                await self.con.copy_to_table('copytab', source=_Source())\n\n            # Check that the protocol has recovered.\n            self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n        finally:\n            await self.con.execute('DROP TABLE copytab')\n\n    async def test_copy_to_table_fail_in_source_2(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b text);\n        ''')\n\n        try:\n            class _Source:\n                def __init__(self):\n                    self.rowcount = 0\n\n                def __aiter__(self):\n                    return self\n\n                async def __anext__(self):\n                    if self.rowcount == 0:\n                        self.rowcount += 1\n                        return b'a\\tb\\n'\n                    else:\n                        raise RuntimeError('failure in source')\n\n            with self.assertRaisesRegex(RuntimeError, 'failure in source'):\n                await self.con.copy_to_table('copytab', source=_Source())\n\n            # Check that the protocol has recovered.\n            self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n        finally:\n            await self.con.execute('DROP TABLE copytab')\n\n    async def test_copy_to_table_timeout(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b text);\n        ''')\n\n        try:\n            class _Source:\n                def __init__(self, loop):\n                    self.rowcount = 0\n                    self.loop = loop\n\n                def __aiter__(self):\n                    return self\n\n                async def __anext__(self):\n                    self.rowcount += 1\n                    await asyncio.sleep(60)\n                    return b'a1' * 50 + b'\\t' + b'b1' * 50 + b'\\n'\n\n            with self.assertRaises(asyncio.TimeoutError):\n                await self.con.copy_to_table(\n                    'copytab', source=_Source(self.loop), timeout=0.10)\n\n            # Check that the protocol has recovered.\n            self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n        finally:\n            await self.con.execute('DROP TABLE copytab')\n\n    async def test_copy_to_table_from_file_path(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, \"b~\" text, i int);\n        ''')\n\n        f = tempfile.NamedTemporaryFile(delete=False)\n        try:\n            f.write(\n                '\\n'.join([\n                    'a1\\tb1\\t1',\n                    'a2\\tb2\\t2',\n                    'a3\\tb3\\t3',\n                    'a4\\tb4\\t4',\n                    'a5\\tb5\\t5',\n                    '*\\t\\\\N\\t\\\\N',\n                    ''\n                ]).encode('utf-8')\n            )\n            f.close()\n\n            res = await self.con.copy_to_table('copytab', source=f.name)\n            self.assertEqual(res, 'COPY 6')\n\n            output = await self.con.fetch(\"\"\"\n                SELECT * FROM copytab ORDER BY a\n            \"\"\")\n            self.assertEqual(\n                output,\n                [\n                    ('*', None, None),\n                    ('a1', 'b1', 1),\n                    ('a2', 'b2', 2),\n                    ('a3', 'b3', 3),\n                    ('a4', 'b4', 4),\n                    ('a5', 'b5', 5),\n                ]\n            )\n\n        finally:\n            await self.con.execute('DROP TABLE public.copytab')\n            os.unlink(f.name)\n\n    async def test_copy_records_to_table_1(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a text, b int, c timestamptz);\n        ''')\n\n        try:\n            date = datetime.datetime.now(tz=datetime.timezone.utc)\n            delta = datetime.timedelta(days=1)\n\n            records = [\n                ('a-{}'.format(i), i, date + delta)\n                for i in range(100)\n            ]\n\n            records.append(('a-100', None, None))\n\n            res = await self.con.copy_records_to_table(\n                'copytab', records=records)\n\n            self.assertEqual(res, 'COPY 101')\n\n        finally:\n            await self.con.execute('DROP TABLE copytab')\n\n    async def test_copy_records_to_table_where(self):\n        if not self.con._server_caps.sql_copy_from_where:\n            raise unittest.SkipTest(\n                'COPY WHERE not supported on server')\n\n        await self.con.execute('''\n            CREATE TABLE copytab_where(a text, b int, c timestamptz);\n        ''')\n\n        try:\n            date = datetime.datetime.now(tz=datetime.timezone.utc)\n            delta = datetime.timedelta(days=1)\n\n            records = [\n                ('a-{}'.format(i), i, date + delta)\n                for i in range(100)\n            ]\n\n            records.append(('a-100', None, None))\n            records.append(('b-999', None, None))\n\n            res = await self.con.copy_records_to_table(\n                'copytab_where', records=records, where='a <> \\'b-999\\'')\n\n            self.assertEqual(res, 'COPY 101')\n\n        finally:\n            await self.con.execute('DROP TABLE copytab_where')\n\n    async def test_copy_records_to_table_async(self):\n        await self.con.execute('''\n            CREATE TABLE copytab_async(a text, b int, c timestamptz);\n        ''')\n\n        try:\n            date = datetime.datetime.now(tz=datetime.timezone.utc)\n            delta = datetime.timedelta(days=1)\n\n            async def record_generator():\n                for i in range(100):\n                    yield ('a-{}'.format(i), i, date + delta)\n\n                yield ('a-100', None, None)\n\n            res = await self.con.copy_records_to_table(\n                'copytab_async', records=record_generator(),\n            )\n\n            self.assertEqual(res, 'COPY 101')\n\n        finally:\n            await self.con.execute('DROP TABLE copytab_async')\n\n    async def test_copy_records_to_table_no_binary_codec(self):\n        await self.con.execute('''\n            CREATE TABLE copytab(a uuid);\n        ''')\n\n        try:\n            def _encoder(value):\n                return value\n\n            def _decoder(value):\n                return value\n\n            await self.con.set_type_codec(\n                'uuid', encoder=_encoder, decoder=_decoder,\n                schema='pg_catalog', format='text'\n            )\n\n            records = [('2975ab9a-f79c-4ab4-9be5-7bc134d952f0',)]\n\n            with self.assertRaisesRegex(\n                    asyncpg.InternalClientError, 'no binary format encoder'):\n                await self.con.copy_records_to_table(\n                    'copytab', records=records)\n\n        finally:\n            await self.con.reset_type_codec(\n                'uuid', schema='pg_catalog'\n            )\n            await self.con.execute('DROP TABLE copytab')\n"
  },
  {
    "path": "tests/test_cursor.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncpg\nimport inspect\n\nfrom asyncpg import _testbase as tb\n\n\nclass TestIterableCursor(tb.ConnectedTestCase):\n\n    async def test_cursor_iterable_01(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n        expected = await st.fetch()\n\n        for prefetch in range(1, 25):\n            with self.subTest(prefetch=prefetch):\n                async with self.con.transaction():\n                    result = []\n                    async for rec in st.cursor(prefetch=prefetch):\n                        result.append(rec)\n\n                self.assertEqual(\n                    result, expected,\n                    'result != expected for prefetch={}'.format(prefetch))\n\n    async def test_cursor_iterable_02(self):\n        # Test that it's not possible to create a cursor without hold\n        # outside of a transaction\n        s = await self.con.prepare(\n            'DECLARE t BINARY CURSOR WITHOUT HOLD FOR SELECT 1')\n        with self.assertRaises(asyncpg.NoActiveSQLTransactionError):\n            await s.fetch()\n\n        # Now test that statement.cursor() does not let you\n        # iterate over it outside of a transaction\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n\n        it = st.cursor(prefetch=5).__aiter__()\n        if inspect.isawaitable(it):\n            it = await it\n\n        with self.assertRaisesRegex(asyncpg.NoActiveSQLTransactionError,\n                                    'cursor cannot be created.*transaction'):\n            await it.__anext__()\n\n    async def test_cursor_iterable_03(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n\n        it = st.cursor().__aiter__()\n        if inspect.isawaitable(it):\n            it = await it\n\n        st._state.mark_closed()\n\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'statement is closed'):\n            async for _ in it:  # NOQA\n                pass\n\n    async def test_cursor_iterable_04(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n        st._state.mark_closed()\n\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'statement is closed'):\n            async for _ in st.cursor():  # NOQA\n                pass\n\n    async def test_cursor_iterable_05(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n        for prefetch in range(-1, 1):\n            with self.subTest(prefetch=prefetch):\n                with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                            'must be greater than zero'):\n                    async for _ in st.cursor(prefetch=prefetch):  # NOQA\n                        pass\n\n    async def test_cursor_iterable_06(self):\n        recs = []\n\n        async with self.con.transaction():\n            await self.con.execute('''\n                CREATE TABLE cursor_iterable_06 (id int);\n                INSERT INTO cursor_iterable_06 VALUES (0), (1);\n            ''')\n            try:\n                cur = self.con.cursor('SELECT * FROM cursor_iterable_06')\n                async for rec in cur:\n                    recs.append(rec)\n            finally:\n                # Check that after iteration has exhausted the cursor,\n                # its associated portal is closed properly, unlocking\n                # the table.\n                await self.con.execute('DROP TABLE cursor_iterable_06')\n\n        self.assertEqual(recs, [(i,) for i in range(2)])\n\n\nclass TestCursor(tb.ConnectedTestCase):\n\n    async def test_cursor_01(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n        with self.assertRaisesRegex(asyncpg.NoActiveSQLTransactionError,\n                                    'cursor cannot be created.*transaction'):\n            await st.cursor()\n\n    async def test_cursor_02(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n        async with self.con.transaction():\n            cur = await st.cursor()\n\n            for i in range(-1, 1):\n                with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                            'greater than zero'):\n                    await cur.fetch(i)\n\n            res = await cur.fetch(2)\n            self.assertEqual(res, [(0,), (1,)])\n\n            rec = await cur.fetchrow()\n            self.assertEqual(rec, (2,))\n\n            r = repr(cur)\n            self.assertTrue(r.startswith('<asyncpg.Cursor '))\n            self.assertNotIn(' exhausted ', r)\n            self.assertIn('\"SELECT generate', r)\n\n            moved = await cur.forward(5)\n            self.assertEqual(moved, 5)\n\n            rec = await cur.fetchrow()\n            self.assertEqual(rec, (8,))\n\n            res = await cur.fetch(100)\n            self.assertEqual(res, [(i,) for i in range(9, 21)])\n\n            self.assertIsNone(await cur.fetchrow())\n            self.assertEqual(await cur.fetch(5), [])\n\n            r = repr(cur)\n            self.assertTrue(r.startswith('<asyncpg.Cursor '))\n            self.assertIn(' exhausted ', r)\n            self.assertIn('\"SELECT generate', r)\n\n    async def test_cursor_03(self):\n        st = await self.con.prepare('SELECT generate_series(0, 20)')\n        async with self.con.transaction():\n            with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                        'prefetch argument can only'):\n                await st.cursor(prefetch=10)\n\n    async def test_cursor_04(self):\n        async with self.con.transaction():\n            st = await self.con.cursor('SELECT generate_series(0, 100)')\n            await st.forward(42)\n            self.assertEqual(await st.fetchrow(), (42,))\n"
  },
  {
    "path": "tests/test_exceptions.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\n\n\nclass TestExceptions(tb.ConnectedTestCase):\n\n    def test_exceptions_exported(self):\n        for err in ('PostgresError', 'SubstringError', 'InterfaceError'):\n            self.assertTrue(hasattr(asyncpg, err))\n            self.assertIn(err, asyncpg.__all__)\n\n        for err in ('PostgresMessage',):\n            self.assertFalse(hasattr(asyncpg, err))\n            self.assertNotIn(err, asyncpg.__all__)\n\n        self.assertIsNone(asyncpg.PostgresError.schema_name)\n\n    async def test_exceptions_unpacking(self):\n        try:\n            await self.con.execute('SELECT * FROM _nonexistent_')\n        except asyncpg.UndefinedTableError as e:\n            self.assertEqual(e.sqlstate, '42P01')\n            self.assertEqual(e.position, '15')\n            self.assertEqual(e.query, 'SELECT * FROM _nonexistent_')\n            self.assertIsNotNone(e.severity)\n        else:\n            self.fail('UndefinedTableError not raised')\n\n    async def test_exceptions_str(self):\n        try:\n            await self.con.execute('''\n                 CREATE FUNCTION foo() RETURNS bool AS $$ $$ LANGUAGE SQL;\n            ''')\n        except asyncpg.InvalidFunctionDefinitionError as e:\n            if self.server_version < (17, 0):\n                detail = (\n                    \"Function's final statement must be SELECT or \"\n                    \"INSERT/UPDATE/DELETE RETURNING.\"\n                )\n            else:\n                detail = (\n                    \"Function's final statement must be SELECT or \"\n                    \"INSERT/UPDATE/DELETE/MERGE RETURNING.\"\n                )\n\n            self.assertEqual(e.detail, detail)\n            self.assertIn('DETAIL:  Function', str(e))\n        else:\n            self.fail('InvalidFunctionDefinitionError not raised')\n"
  },
  {
    "path": "tests/test_execute.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport asyncpg\n\nfrom asyncpg import _testbase as tb\nfrom asyncpg import exceptions\n\n\nclass TestExecuteScript(tb.ConnectedTestCase):\n\n    async def test_execute_script_1(self):\n        self.assertEqual(self.con._protocol.queries_count, 0)\n        status = await self.con.execute('''\n            SELECT 1;\n\n            SELECT true FROM pg_type WHERE false = true;\n\n            SELECT generate_series(0, 9);\n        ''')\n        self.assertEqual(self.con._protocol.queries_count, 1)\n        self.assertEqual(status, 'SELECT 10')\n\n    async def test_execute_script_2(self):\n        status = await self.con.execute('''\n            CREATE TABLE mytab (a int);\n        ''')\n        self.assertEqual(status, 'CREATE TABLE')\n\n        try:\n            status = await self.con.execute('''\n                INSERT INTO mytab (a) VALUES ($1), ($2)\n            ''', 10, 20)\n            self.assertEqual(status, 'INSERT 0 2')\n        finally:\n            await self.con.execute('DROP TABLE mytab')\n\n    async def test_execute_script_3(self):\n        with self.assertRaisesRegex(asyncpg.PostgresSyntaxError,\n                                    'cannot insert multiple commands'):\n\n            await self.con.execute('''\n                CREATE TABLE mytab (a int);\n                INSERT INTO mytab (a) VALUES ($1), ($2);\n            ''', 10, 20)\n\n    async def test_execute_script_check_transactionality(self):\n        with self.assertRaises(asyncpg.PostgresError):\n            await self.con.execute('''\n                CREATE TABLE mytab (a int);\n                SELECT * FROM mytab WHERE 1 / 0 = 1;\n            ''')\n\n        with self.assertRaisesRegex(asyncpg.PostgresError,\n                                    '\"mytab\" does not exist'):\n\n            await self.con.prepare('''\n                SELECT * FROM mytab\n            ''')\n\n    async def test_execute_exceptions_1(self):\n        with self.assertRaisesRegex(asyncpg.PostgresError,\n                                    'relation \"__dne__\" does not exist'):\n\n            await self.con.execute('select * from __dne__')\n\n    async def test_execute_script_interrupted_close(self):\n        fut = self.loop.create_task(\n            self.con.execute('''SELECT pg_sleep(10)'''))\n\n        await asyncio.sleep(0.2)\n\n        self.assertFalse(self.con.is_closed())\n        await self.con.close()\n        self.assertTrue(self.con.is_closed())\n\n        with self.assertRaises(asyncpg.QueryCanceledError):\n            await fut\n\n    async def test_execute_script_interrupted_terminate(self):\n        fut = self.loop.create_task(\n            self.con.execute('''SELECT pg_sleep(10)'''))\n\n        await asyncio.sleep(0.2)\n\n        self.assertFalse(self.con.is_closed())\n        self.con.terminate()\n        self.assertTrue(self.con.is_closed())\n\n        with self.assertRaisesRegex(asyncpg.ConnectionDoesNotExistError,\n                                    'closed in the middle'):\n            await fut\n\n        self.con.terminate()\n\n\nclass TestExecuteMany(tb.ConnectedTestCase):\n    def setUp(self):\n        super().setUp()\n        self.loop.run_until_complete(self.con.execute(\n            'CREATE TABLE exmany (a text, b int PRIMARY KEY)'))\n\n    def tearDown(self):\n        self.loop.run_until_complete(self.con.execute('DROP TABLE exmany'))\n        super().tearDown()\n\n    async def test_executemany_basic(self):\n        result = await self.con.executemany('''\n            INSERT INTO exmany VALUES($1, $2)\n        ''', [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n\n        self.assertIsNone(result)\n\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n\n        # Empty set\n        await self.con.executemany('''\n            INSERT INTO exmany VALUES($1, $2)\n        ''', ())\n\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n\n    async def test_executemany_returning(self):\n        result = await self.con.fetchmany('''\n            INSERT INTO exmany VALUES($1, $2) RETURNING a, b\n        ''', [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n\n        # Empty set\n        await self.con.fetchmany('''\n            INSERT INTO exmany VALUES($1, $2) RETURNING a, b\n        ''', ())\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n\n        # Without \"RETURNING\"\n        result = await self.con.fetchmany('''\n            INSERT INTO exmany VALUES($1, $2)\n        ''', [('e', 5), ('f', 6)])\n        self.assertEqual(result, [])\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)\n        ])\n\n    async def test_executemany_bad_input(self):\n        with self.assertRaisesRegex(\n            exceptions.DataError,\n            r\"invalid input in executemany\\(\\) argument sequence element #1: \"\n            r\"expected a sequence\",\n        ):\n            await self.con.executemany('''\n                INSERT INTO exmany (b) VALUES($1)\n            ''', [(0,), {1: 0}])\n\n        with self.assertRaisesRegex(\n            exceptions.DataError,\n            r\"invalid input for query argument \\$1 in element #1 of \"\n            r\"executemany\\(\\) sequence: 'bad'\",\n        ):\n            await self.con.executemany('''\n                INSERT INTO exmany (b) VALUES($1)\n            ''', [(0,), (\"bad\",)])\n\n    async def test_executemany_error_in_input_gen(self):\n        bad_data = ([1 / 0] for v in range(10))\n\n        with self.assertRaises(ZeroDivisionError):\n            async with self.con.transaction():\n                await self.con.executemany('''\n                    INSERT INTO exmany (b)VALUES($1)\n                ''', bad_data)\n\n        good_data = ([v] for v in range(10))\n        async with self.con.transaction():\n            await self.con.executemany('''\n                INSERT INTO exmany (b)VALUES($1)\n            ''', good_data)\n\n    async def test_executemany_server_failure(self):\n        with self.assertRaises(exceptions.UniqueViolationError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES($1, $2)\n            ''', [\n                ('a', 1), ('b', 2), ('c', 2), ('d', 4)\n            ])\n        result = await self.con.fetch('SELECT * FROM exmany')\n        self.assertEqual(result, [])\n\n    async def test_executemany_server_failure_after_writes(self):\n        with self.assertRaises(exceptions.UniqueViolationError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES($1, $2)\n            ''', [('a' * 32768, x) for x in range(10)] + [\n                ('b', 12), ('c', 12), ('d', 14)\n            ])\n        result = await self.con.fetch('SELECT b FROM exmany')\n        self.assertEqual(result, [])\n\n    async def test_executemany_server_failure_during_writes(self):\n        # failure at the beginning, server error detected in the middle\n        pos = 0\n\n        def gen():\n            nonlocal pos\n            while pos < 128:\n                pos += 1\n                if pos < 3:\n                    yield ('a', 0)\n                else:\n                    yield 'a' * 32768, pos\n\n        with self.assertRaises(exceptions.UniqueViolationError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES($1, $2)\n            ''', gen())\n        result = await self.con.fetch('SELECT b FROM exmany')\n        self.assertEqual(result, [])\n        self.assertLess(pos, 128, 'should stop early')\n\n    async def test_executemany_client_failure_after_writes(self):\n        with self.assertRaises(ZeroDivisionError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES($1, $2)\n            ''', (('a' * 32768, y + y / y) for y in range(10, -1, -1)))\n        result = await self.con.fetch('SELECT b FROM exmany')\n        self.assertEqual(result, [])\n\n    async def test_executemany_timeout(self):\n        with self.assertRaises(asyncio.TimeoutError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES(pg_sleep(0.1) || $1, $2)\n            ''', [('a' * 32768, x) for x in range(128)], timeout=0.5)\n        result = await self.con.fetch('SELECT * FROM exmany')\n        self.assertEqual(result, [])\n\n    async def test_executemany_timeout_flow_control(self):\n        event = asyncio.Event()\n\n        async def locker():\n            test_func = getattr(self, self._testMethodName).__func__\n            opts = getattr(test_func, '__connect_options__', {})\n            conn = await self.connect(**opts)\n            try:\n                tx = conn.transaction()\n                await tx.start()\n                await conn.execute(\"UPDATE exmany SET a = '1' WHERE b = 10\")\n                event.set()\n                await asyncio.sleep(1)\n                await tx.rollback()\n            finally:\n                event.set()\n                await conn.close()\n\n        await self.con.executemany('''\n            INSERT INTO exmany VALUES(NULL, $1)\n        ''', [(x,) for x in range(128)])\n        fut = asyncio.ensure_future(locker())\n        await event.wait()\n        with self.assertRaises(asyncio.TimeoutError):\n            await self.con.executemany('''\n                UPDATE exmany SET a = $1 WHERE b = $2\n            ''', [('a' * 32768, x) for x in range(128)], timeout=0.5)\n        await fut\n        result = await self.con.fetch(\n            'SELECT * FROM exmany WHERE a IS NOT NULL')\n        self.assertEqual(result, [])\n\n    async def test_executemany_client_failure_in_transaction(self):\n        tx = self.con.transaction()\n        await tx.start()\n        with self.assertRaises(ZeroDivisionError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES($1, $2)\n            ''', (('a' * 32768, y + y / y) for y in range(10, -1, -1)))\n        result = await self.con.fetch('SELECT b FROM exmany')\n        # only 2 batches executed (2 x 4)\n        self.assertEqual(\n            [x[0] for x in result], [y + 1 for y in range(10, 2, -1)])\n        await tx.rollback()\n        result = await self.con.fetch('SELECT b FROM exmany')\n        self.assertEqual(result, [])\n\n    async def test_executemany_client_server_failure_conflict(self):\n        self.con._transport.set_write_buffer_limits(65536 * 64, 16384 * 64)\n        with self.assertRaises(exceptions.UniqueViolationError):\n            await self.con.executemany('''\n                INSERT INTO exmany VALUES($1, 0)\n            ''', (('a' * 32768,) for y in range(4, -1, -1) if y / y))\n        result = await self.con.fetch('SELECT b FROM exmany')\n        self.assertEqual(result, [])\n\n    async def test_executemany_prepare(self):\n        stmt = await self.con.prepare('''\n            INSERT INTO exmany VALUES($1, $2)\n        ''')\n        result = await stmt.executemany([\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n        self.assertIsNone(result)\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n        # Empty set\n        await stmt.executemany(())\n        result = await self.con.fetch('''\n            SELECT * FROM exmany\n        ''')\n        self.assertEqual(result, [\n            ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n        ])\n"
  },
  {
    "path": "tests/test_introspection.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport json\n\nfrom asyncpg import _testbase as tb\nfrom asyncpg import connection as apg_con\n\n\nMAX_RUNTIME = 0.25\n\n\nclass SlowIntrospectionConnection(apg_con.Connection):\n    \"\"\"Connection class to test introspection races.\"\"\"\n    introspect_count = 0\n\n    async def _introspect_types(self, *args, **kwargs):\n        self.introspect_count += 1\n        await asyncio.sleep(0.4)\n        return await super()._introspect_types(*args, **kwargs)\n\n\nclass TestIntrospection(tb.ConnectedTestCase):\n    @classmethod\n    def setUpClass(cls):\n        super().setUpClass()\n        cls.adminconn = cls.loop.run_until_complete(cls.connect())\n        cls.loop.run_until_complete(\n            cls.adminconn.execute('CREATE DATABASE asyncpg_intro_test'))\n\n    @classmethod\n    def tearDownClass(cls):\n        cls.loop.run_until_complete(\n            cls.adminconn.execute('DROP DATABASE asyncpg_intro_test'))\n\n        cls.loop.run_until_complete(cls.adminconn.close())\n        cls.adminconn = None\n\n        super().tearDownClass()\n\n    @classmethod\n    def get_server_settings(cls):\n        settings = super().get_server_settings()\n        settings.pop('jit', None)\n        return settings\n\n    def setUp(self):\n        super().setUp()\n        self.loop.run_until_complete(self._add_custom_codec(self.con))\n\n    async def _add_custom_codec(self, conn):\n        # mess up with the codec - builtin introspection shouldn't be affected\n        await conn.set_type_codec(\n            \"oid\",\n            schema=\"pg_catalog\",\n            encoder=lambda value: None,\n            decoder=lambda value: None,\n            format=\"text\",\n        )\n\n    @tb.with_connection_options(database='asyncpg_intro_test')\n    async def test_introspection_on_large_db(self):\n        await self.con.execute(\n            'CREATE TABLE base ({})'.format(\n                ','.join('c{:02} varchar'.format(n) for n in range(50))\n            )\n        )\n        for n in range(1000):\n            await self.con.execute(\n                'CREATE TABLE child_{:04} () inherits (base)'.format(n)\n            )\n\n        with self.assertRunUnder(MAX_RUNTIME):\n            await self.con.fetchval('SELECT $1::int[]', [1, 2])\n\n    @tb.with_connection_options(statement_cache_size=0)\n    async def test_introspection_no_stmt_cache_01(self):\n        old_uid = apg_con._uid\n\n        self.assertEqual(self.con._stmt_cache.get_max_size(), 0)\n        await self.con.fetchval('SELECT $1::int[]', [1, 2])\n\n        await self.con.execute('''\n            CREATE EXTENSION IF NOT EXISTS hstore\n        ''')\n\n        try:\n            await self.con.set_builtin_type_codec(\n                'hstore', codec_name='pg_contrib.hstore')\n        finally:\n            await self.con.execute('''\n                DROP EXTENSION hstore\n            ''')\n\n        self.assertEqual(apg_con._uid, old_uid)\n\n    @tb.with_connection_options(max_cacheable_statement_size=1)\n    async def test_introspection_no_stmt_cache_02(self):\n        # max_cacheable_statement_size will disable caching both for\n        # the user query and for the introspection query.\n        old_uid = apg_con._uid\n\n        await self.con.fetchval('SELECT $1::int[]', [1, 2])\n\n        await self.con.execute('''\n            CREATE EXTENSION IF NOT EXISTS hstore\n        ''')\n\n        try:\n            await self.con.set_builtin_type_codec(\n                'hstore', codec_name='pg_contrib.hstore')\n        finally:\n            await self.con.execute('''\n                DROP EXTENSION hstore\n            ''')\n\n        self.assertEqual(apg_con._uid, old_uid)\n\n    @tb.with_connection_options(max_cacheable_statement_size=10000)\n    async def test_introspection_no_stmt_cache_03(self):\n        # max_cacheable_statement_size will disable caching for\n        # the user query but not for the introspection query.\n        old_uid = apg_con._uid\n\n        await self.con.fetchval(\n            \"SELECT $1::int[], '{foo}'\".format(foo='a' * 10000), [1, 2])\n\n        self.assertGreater(apg_con._uid, old_uid)\n\n    async def test_introspection_sticks_for_ps(self):\n        # Test that the introspected codec pipeline for a prepared\n        # statement is not affected by a subsequent codec cache bust.\n\n        ps = await self.con._prepare('SELECT $1::json[]', use_cache=True)\n\n        try:\n            # Setting a custom codec blows the codec cache for derived types.\n            await self.con.set_type_codec(\n                'json', encoder=lambda v: v, decoder=json.loads,\n                schema='pg_catalog', format='text'\n            )\n\n            # The originally prepared statement should still be OK and\n            # use the previously selected codec.\n            self.assertEqual(await ps.fetchval(['{\"foo\": 1}']), ['{\"foo\": 1}'])\n\n            # The new query uses the custom codec.\n            v = await self.con.fetchval('SELECT $1::json[]', ['{\"foo\": 1}'])\n            self.assertEqual(v, [{'foo': 1}])\n\n        finally:\n            await self.con.reset_type_codec(\n                'json', schema='pg_catalog')\n\n    async def test_introspection_retries_after_cache_bust(self):\n        # Test that codec cache bust racing with the introspection\n        # query would cause introspection to retry.\n        slow_intro_conn = await self.connect(\n            connection_class=SlowIntrospectionConnection)\n        await self._add_custom_codec(slow_intro_conn)\n        try:\n            await self.con.execute('''\n                CREATE DOMAIN intro_1_t AS int;\n                CREATE DOMAIN intro_2_t AS int;\n            ''')\n\n            await slow_intro_conn.fetchval('''\n                SELECT $1::intro_1_t\n            ''', 10)\n            # slow_intro_conn cache is now populated with intro_1_t\n\n            async def wait_and_drop():\n                await asyncio.sleep(0.1)\n                await slow_intro_conn.reload_schema_state()\n\n            # Now, in parallel, run another query that\n            # references both intro_1_t and intro_2_t.\n            await asyncio.gather(\n                slow_intro_conn.fetchval('''\n                    SELECT $1::intro_1_t, $2::intro_2_t\n                ''', 10, 20),\n                wait_and_drop()\n            )\n\n            # Initial query + two tries for the second query.\n            self.assertEqual(slow_intro_conn.introspect_count, 3)\n\n        finally:\n            await self.con.execute('''\n                DROP DOMAIN intro_1_t;\n                DROP DOMAIN intro_2_t;\n            ''')\n            await slow_intro_conn.close()\n\n    @tb.with_connection_options(database='asyncpg_intro_test')\n    async def test_introspection_loads_basetypes_of_domains(self):\n        # Test that basetypes of domains are loaded to the\n        # client encode/decode cache\n        await self.con.execute('''\n            DROP TABLE IF EXISTS test;\n            DROP DOMAIN IF EXISTS num_array;\n            CREATE DOMAIN num_array numeric[];\n            CREATE TABLE test (\n                num num_array\n            );\n        ''')\n\n        try:\n            # if domain basetypes are not loaded, this insert will fail\n            await self.con.execute(\n                'INSERT INTO test (num) VALUES ($1)', ([1, 2],))\n        finally:\n            await self.con.execute('''\n                DROP TABLE IF EXISTS test;\n                DROP DOMAIN IF EXISTS num_array;\n            ''')\n"
  },
  {
    "path": "tests/test_listeners.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport os\nimport platform\nimport unittest\n\nfrom asyncpg import _testbase as tb\nfrom asyncpg import exceptions\n\n\nclass TestListeners(tb.ClusterTestCase):\n\n    async def test_listen_01(self):\n        async with self.create_pool(database='postgres') as pool:\n            async with pool.acquire() as con:\n\n                q1 = asyncio.Queue()\n                q2 = asyncio.Queue()\n                q3 = asyncio.Queue()\n\n                def listener1(*args):\n                    q1.put_nowait(args)\n\n                def listener2(*args):\n                    q2.put_nowait(args)\n\n                async def async_listener3(*args):\n                    q3.put_nowait(args)\n\n                await con.add_listener('test', listener1)\n                await con.add_listener('test', listener2)\n                await con.add_listener('test', async_listener3)\n\n                await con.execute(\"NOTIFY test, 'aaaa'\")\n\n                self.assertEqual(\n                    await q1.get(),\n                    (con, con.get_server_pid(), 'test', 'aaaa'))\n                self.assertEqual(\n                    await q2.get(),\n                    (con, con.get_server_pid(), 'test', 'aaaa'))\n                self.assertEqual(\n                    await q3.get(),\n                    (con, con.get_server_pid(), 'test', 'aaaa'))\n\n                await con.remove_listener('test', listener2)\n                await con.remove_listener('test', async_listener3)\n\n                await con.execute(\"NOTIFY test, 'aaaa'\")\n\n                self.assertEqual(\n                    await q1.get(),\n                    (con, con.get_server_pid(), 'test', 'aaaa'))\n                with self.assertRaises(asyncio.TimeoutError):\n                    await asyncio.wait_for(q2.get(), timeout=0.05)\n\n                await con.reset()\n                await con.remove_listener('test', listener1)\n                await con.execute(\"NOTIFY test, 'aaaa'\")\n\n                with self.assertRaises(asyncio.TimeoutError):\n                    await asyncio.wait_for(q1.get(), timeout=0.05)\n                with self.assertRaises(asyncio.TimeoutError):\n                    await asyncio.wait_for(q2.get(), timeout=0.05)\n\n    async def test_listen_02(self):\n        async with self.create_pool(database='postgres') as pool:\n            async with pool.acquire() as con1, pool.acquire() as con2:\n\n                q1 = asyncio.Queue()\n\n                def listener1(*args):\n                    q1.put_nowait(args)\n\n                await con1.add_listener('ipc', listener1)\n                await con2.execute(\"NOTIFY ipc, 'hello'\")\n\n                self.assertEqual(\n                    await q1.get(),\n                    (con1, con2.get_server_pid(), 'ipc', 'hello'))\n\n                await con1.remove_listener('ipc', listener1)\n\n    async def test_listen_notletters(self):\n        async with self.create_pool(database='postgres') as pool:\n            async with pool.acquire() as con1, pool.acquire() as con2:\n\n                q1 = asyncio.Queue()\n\n                def listener1(*args):\n                    q1.put_nowait(args)\n\n                await con1.add_listener('12+\"34', listener1)\n                await con2.execute(\"\"\"NOTIFY \"12+\"\"34\", 'hello'\"\"\")\n\n                self.assertEqual(\n                    await q1.get(),\n                    (con1, con2.get_server_pid(), '12+\"34', 'hello'))\n\n                await con1.remove_listener('12+\"34', listener1)\n\n    async def test_dangling_listener_warns(self):\n        async with self.create_pool(database='postgres') as pool:\n            with self.assertWarnsRegex(\n                    exceptions.InterfaceWarning,\n                    '.*Connection.*is being released to the pool but '\n                    'has 1 active notification listener'):\n                async with pool.acquire() as con:\n                    def listener1(*args):\n                        pass\n\n                    await con.add_listener('ipc', listener1)\n\n\nclass TestLogListeners(tb.ConnectedTestCase):\n\n    @tb.with_connection_options(server_settings={\n        'client_min_messages': 'notice'\n    })\n    async def test_log_listener_01(self):\n        q1 = asyncio.Queue()\n        q2 = asyncio.Queue()\n\n        def notice_callb(con, message):\n            # Message fields depend on PG version, hide some values.\n            dct = message.as_dict()\n            del dct['server_source_line']\n            q1.put_nowait((con, type(message), dct))\n\n        async def async_notice_callb(con, message):\n            # Message fields depend on PG version, hide some values.\n            dct = message.as_dict()\n            del dct['server_source_line']\n            q2.put_nowait((con, type(message), dct))\n\n        async def raise_notice():\n            await self.con.execute(\n                \"\"\"DO $$\n                    BEGIN RAISE NOTICE 'catch me!'; END;\n                $$ LANGUAGE plpgsql\"\"\"\n            )\n\n        async def raise_warning():\n            await self.con.execute(\n                \"\"\"DO $$\n                    BEGIN RAISE WARNING 'catch me!'; END;\n                $$ LANGUAGE plpgsql\"\"\"\n            )\n\n        con = self.con\n        con.add_log_listener(notice_callb)\n        con.add_log_listener(async_notice_callb)\n\n        expected_msg = {\n            'context': 'PL/pgSQL function inline_code_block line 2 at RAISE',\n            'message': 'catch me!',\n            'server_source_function': 'exec_stmt_raise',\n        }\n\n        expected_msg_notice = {\n            **expected_msg,\n            'severity': 'NOTICE',\n            'severity_en': 'NOTICE',\n            'sqlstate': '00000',\n        }\n\n        expected_msg_warn = {\n            **expected_msg,\n            'severity': 'WARNING',\n            'severity_en': 'WARNING',\n            'sqlstate': '01000',\n        }\n\n        if con.get_server_version() < (9, 6):\n            del expected_msg_notice['context']\n            del expected_msg_notice['severity_en']\n            del expected_msg_warn['context']\n            del expected_msg_warn['severity_en']\n\n        await raise_notice()\n        await raise_warning()\n\n        msg = await q1.get()\n        msg[2].pop('server_source_filename', None)\n        self.assertEqual(\n            msg,\n            (con, exceptions.PostgresLogMessage, expected_msg_notice))\n\n        msg = await q1.get()\n        msg[2].pop('server_source_filename', None)\n        self.assertEqual(\n            msg,\n            (con, exceptions.PostgresWarning, expected_msg_warn))\n\n        msg = await q2.get()\n        msg[2].pop('server_source_filename', None)\n        self.assertEqual(\n            msg,\n            (con, exceptions.PostgresLogMessage, expected_msg_notice))\n\n        msg = await q2.get()\n        msg[2].pop('server_source_filename', None)\n        self.assertEqual(\n            msg,\n            (con, exceptions.PostgresWarning, expected_msg_warn))\n\n        con.remove_log_listener(notice_callb)\n        con.remove_log_listener(async_notice_callb)\n\n        await raise_notice()\n        self.assertTrue(q1.empty())\n\n        con.add_log_listener(notice_callb)\n        await raise_notice()\n        await q1.get()\n        self.assertTrue(q1.empty())\n        await con.reset()\n        await raise_notice()\n        self.assertTrue(q1.empty())\n\n    @tb.with_connection_options(server_settings={\n        'client_min_messages': 'notice'\n    })\n    async def test_log_listener_02(self):\n        q1 = asyncio.Queue()\n\n        cur_id = None\n\n        def notice_callb(con, message):\n            q1.put_nowait((con, cur_id, message.message))\n\n        con = self.con\n        await con.execute(\n            \"CREATE FUNCTION _test(i INT) RETURNS int LANGUAGE plpgsql AS $$\"\n            \" BEGIN\"\n            \" RAISE NOTICE '1_%', i;\"\n            \" PERFORM pg_sleep(0.1);\"\n            \" RAISE NOTICE '2_%', i;\"\n            \" RETURN i;\"\n            \" END\"\n            \"$$\"\n        )\n\n        try:\n            con.add_log_listener(notice_callb)\n            for cur_id in range(10):\n                await con.execute(\"SELECT _test($1)\", cur_id)\n\n            for cur_id in range(10):\n                self.assertEqual(\n                    q1.get_nowait(),\n                    (con, cur_id, '1_%s' % cur_id))\n                self.assertEqual(\n                    q1.get_nowait(),\n                    (con, cur_id, '2_%s' % cur_id))\n\n            con.remove_log_listener(notice_callb)\n            self.assertTrue(q1.empty())\n        finally:\n            await con.execute('DROP FUNCTION _test(i INT)')\n\n    @tb.with_connection_options(server_settings={\n        'client_min_messages': 'notice'\n    })\n    async def test_log_listener_03(self):\n        q1 = asyncio.Queue()\n\n        async def raise_message(level, code):\n            await self.con.execute(\"\"\"\n                DO $$ BEGIN\n                    RAISE {} 'catch me!' USING ERRCODE = '{}';\n                END; $$ LANGUAGE plpgsql;\n            \"\"\".format(level, code))\n\n        def notice_callb(con, message):\n            # Message fields depend on PG version, hide some values.\n            q1.put_nowait(message)\n\n        self.con.add_log_listener(notice_callb)\n\n        await raise_message('WARNING', '99999')\n        msg = await q1.get()\n        self.assertIsInstance(msg, exceptions.PostgresWarning)\n        self.assertEqual(msg.sqlstate, '99999')\n\n        await raise_message('WARNING', '01004')\n        msg = await q1.get()\n        self.assertIsInstance(msg, exceptions.StringDataRightTruncation)\n        self.assertEqual(msg.sqlstate, '01004')\n\n        with self.assertRaises(exceptions.InvalidCharacterValueForCastError):\n            await raise_message('', '22018')\n        self.assertTrue(q1.empty())\n\n    async def test_dangling_log_listener_warns(self):\n        async with self.create_pool(database='postgres') as pool:\n            with self.assertWarnsRegex(\n                    exceptions.InterfaceWarning,\n                    '.*Connection.*is being released to the pool but '\n                    'has 1 active log listener'):\n                async with pool.acquire() as con:\n                    def listener1(*args):\n                        pass\n\n                    con.add_log_listener(listener1)\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'using remote cluster for testing')\n@unittest.skipIf(\n    platform.system() == 'Windows',\n    'not compatible with ProactorEventLoop which is default in Python 3.8+')\nclass TestConnectionTerminationListener(tb.ProxiedClusterTestCase):\n\n    async def test_connection_termination_callback_called_on_remote(self):\n\n        called = False\n        async_called = False\n\n        def close_cb(con):\n            nonlocal called\n            called = True\n\n        async def async_close_cb(con):\n            nonlocal async_called\n            async_called = True\n\n        con = await self.connect()\n        con.add_termination_listener(close_cb)\n        con.add_termination_listener(async_close_cb)\n        self.proxy.close_all_connections()\n        try:\n            await con.fetchval('SELECT 1')\n        except Exception:\n            pass\n        self.assertTrue(called)\n        self.assertTrue(async_called)\n\n    async def test_connection_termination_callback_called_on_local(self):\n\n        called = False\n\n        def close_cb(con):\n            nonlocal called\n            called = True\n\n        con = await self.connect()\n        con.add_termination_listener(close_cb)\n        await con.close()\n        await asyncio.sleep(0)\n        self.assertTrue(called)\n"
  },
  {
    "path": "tests/test_logging.py",
    "content": "import asyncio\n\nfrom asyncpg import _testbase as tb\nfrom asyncpg import exceptions\n\n\nclass LogCollector:\n    def __init__(self):\n        self.records = []\n\n    def __call__(self, record):\n        self.records.append(record)\n\n\nclass TestQueryLogging(tb.ConnectedTestCase):\n\n    async def test_logging_context(self):\n        queries = asyncio.Queue()\n\n        def query_saver(record):\n            queries.put_nowait(record)\n\n        log = LogCollector()\n\n        with self.con.query_logger(query_saver):\n            self.assertEqual(len(self.con._query_loggers), 1)\n            await self.con.execute(\"SELECT 1\")\n            with self.con.query_logger(log):\n                self.assertEqual(len(self.con._query_loggers), 2)\n                await self.con.execute(\"SELECT 2\")\n\n        r1 = await queries.get()\n        r2 = await queries.get()\n        self.assertEqual(r1.query, \"SELECT 1\")\n        self.assertEqual(r2.query, \"SELECT 2\")\n        self.assertEqual(len(log.records), 1)\n        self.assertEqual(log.records[0].query, \"SELECT 2\")\n        self.assertEqual(len(self.con._query_loggers), 0)\n\n    async def test_error_logging(self):\n        log = LogCollector()\n        with self.con.query_logger(log):\n            with self.assertRaises(exceptions.UndefinedColumnError):\n                await self.con.execute(\"SELECT x\")\n\n        await asyncio.sleep(0)  # wait for logging\n        self.assertEqual(len(log.records), 1)\n        self.assertEqual(\n            type(log.records[0].exception),\n            exceptions.UndefinedColumnError\n        )\n"
  },
  {
    "path": "tests/test_pool.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport inspect\nimport os\nimport pathlib\nimport platform\nimport random\nimport textwrap\nimport time\nimport unittest\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\nfrom asyncpg import connection as pg_connection\nfrom asyncpg import pool as pg_pool\nfrom asyncpg import cluster as pg_cluster\n\n_system = platform.uname().system\n\n\nPOOL_NOMINAL_TIMEOUT = 0.5\n\n\nclass SlowResetConnection(pg_connection.Connection):\n    \"\"\"Connection class to simulate races with Connection.reset().\"\"\"\n    async def reset(self, *, timeout=None):\n        await asyncio.sleep(0.2)\n        return await super().reset(timeout=timeout)\n\n\nclass SlowCancelConnection(pg_connection.Connection):\n    \"\"\"Connection class to simulate races with Connection._cancel().\"\"\"\n    async def _cancel(self, waiter):\n        await asyncio.sleep(0.2)\n        return await super()._cancel(waiter)\n\n\nclass TestPool(tb.ConnectedTestCase):\n\n    async def test_pool_01(self):\n        for n in {1, 5, 10, 20, 100}:\n            with self.subTest(tasksnum=n):\n                pool = await self.create_pool(database='postgres',\n                                              min_size=5, max_size=10)\n\n                async def worker():\n                    con = await pool.acquire()\n                    self.assertEqual(await con.fetchval('SELECT 1'), 1)\n                    await pool.release(con)\n\n                tasks = [worker() for _ in range(n)]\n                await asyncio.gather(*tasks)\n                await pool.close()\n\n    async def test_pool_02(self):\n        for n in {1, 3, 5, 10, 20, 100}:\n            with self.subTest(tasksnum=n):\n                async with self.create_pool(database='postgres',\n                                            min_size=5, max_size=5) as pool:\n\n                    async def worker():\n                        con = await pool.acquire(timeout=5)\n                        self.assertEqual(await con.fetchval('SELECT 1'), 1)\n                        await pool.release(con)\n\n                    tasks = [worker() for _ in range(n)]\n                    await asyncio.gather(*tasks)\n\n    async def test_pool_03(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        con = await pool.acquire(timeout=1)\n        with self.assertRaises(asyncio.TimeoutError):\n            await pool.acquire(timeout=0.03)\n\n        pool.terminate()\n        del con\n\n    async def test_pool_04(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        con = await pool.acquire(timeout=POOL_NOMINAL_TIMEOUT)\n\n        # Manual termination of pool connections releases the\n        # pool item immediately.\n        con.terminate()\n        self.assertIsNone(pool._holders[0]._con)\n        self.assertIsNone(pool._holders[0]._in_use)\n\n        con = await pool.acquire(timeout=POOL_NOMINAL_TIMEOUT)\n        self.assertEqual(await con.fetchval('SELECT 1'), 1)\n\n        await con.close()\n        self.assertIsNone(pool._holders[0]._con)\n        self.assertIsNone(pool._holders[0]._in_use)\n        # Calling release should not hurt.\n        await pool.release(con)\n\n        pool.terminate()\n\n    async def test_pool_05(self):\n        for n in {1, 3, 5, 10, 20, 100}:\n            with self.subTest(tasksnum=n):\n                pool = await self.create_pool(database='postgres',\n                                              min_size=5, max_size=10)\n\n                async def worker():\n                    async with pool.acquire() as con:\n                        self.assertEqual(await con.fetchval('SELECT 1'), 1)\n\n                tasks = [worker() for _ in range(n)]\n                await asyncio.gather(*tasks)\n                await pool.close()\n\n    async def test_pool_06(self):\n        fut = asyncio.Future()\n\n        async def setup(con):\n            fut.set_result(con)\n\n        async with self.create_pool(database='postgres',\n                                    min_size=5, max_size=5,\n                                    setup=setup) as pool:\n            async with pool.acquire() as con:\n                pass\n\n        self.assertIs(con, await fut)\n\n    async def test_pool_07(self):\n        cons = set()\n        connect_called = 0\n        init_called = 0\n        setup_called = 0\n        reset_called = 0\n\n        async def connect(*args, **kwargs):\n            nonlocal connect_called\n            connect_called += 1\n            return await pg_connection.connect(*args, **kwargs)\n\n        async def setup(con):\n            nonlocal setup_called\n            if con._con not in cons:  # `con` is `PoolConnectionProxy`.\n                raise RuntimeError('init was not called before setup')\n            setup_called += 1\n\n        async def init(con):\n            nonlocal init_called\n            if con in cons:\n                raise RuntimeError('init was called more than once')\n            cons.add(con)\n            init_called += 1\n\n        async def reset(con):\n            nonlocal reset_called\n            reset_called += 1\n\n        async def user(pool):\n            async with pool.acquire() as con:\n                if con._con not in cons:  # `con` is `PoolConnectionProxy`.\n                    raise RuntimeError('init was not called')\n\n        async with self.create_pool(database='postgres',\n                                    min_size=2,\n                                    max_size=5,\n                                    connect=connect,\n                                    init=init,\n                                    setup=setup,\n                                    reset=reset) as pool:\n            users = asyncio.gather(*[user(pool) for _ in range(10)])\n            await users\n\n        self.assertEqual(len(cons), 5)\n        self.assertEqual(connect_called, 5)\n        self.assertEqual(init_called, 5)\n        self.assertEqual(setup_called, 10)\n        self.assertEqual(reset_called, 10)\n\n        async def bad_connect(*args, **kwargs):\n            return 1\n\n        with self.assertRaisesRegex(\n            asyncpg.InterfaceError,\n            \"expected pool connect callback to return an instance of \"\n            \"'asyncpg\\\\.connection\\\\.Connection', got 'int'\"\n        ):\n            await self.create_pool(database='postgres', connect=bad_connect)\n\n    async def test_pool_08(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        con = await pool.acquire(timeout=POOL_NOMINAL_TIMEOUT)\n        with self.assertRaisesRegex(asyncpg.InterfaceError, 'is not a member'):\n            await pool.release(con._con)\n\n    async def test_pool_09(self):\n        pool1 = await self.create_pool(database='postgres',\n                                       min_size=1, max_size=1)\n\n        pool2 = await self.create_pool(database='postgres',\n                                       min_size=1, max_size=1)\n\n        try:\n            con = await pool1.acquire(timeout=POOL_NOMINAL_TIMEOUT)\n            with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                        'is not a member'):\n                await pool2.release(con)\n        finally:\n            await pool1.release(con)\n\n        await pool1.close()\n        await pool2.close()\n\n    async def test_pool_10(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        con = await pool.acquire()\n        await pool.release(con)\n        await pool.release(con)\n\n        await pool.close()\n\n    async def test_pool_11(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        async with pool.acquire() as con:\n            self.assertIn(repr(con._con), repr(con))  # Test __repr__.\n\n            ps = await con.prepare('SELECT 1')\n            txn = con.transaction()\n            async with con.transaction():\n                cur = await con.cursor('SELECT 1')\n                ps_cur = await ps.cursor()\n\n        self.assertIn('[released]', repr(con))\n\n        with self.assertRaisesRegex(\n                asyncpg.InterfaceError,\n                r'cannot call Connection\\.execute.*released back to the pool'):\n\n            con.execute('select 1')\n\n        for meth in ('fetchval', 'fetchrow', 'fetch', 'explain',\n                     'get_query', 'get_statusmsg', 'get_parameters',\n                     'get_attributes'):\n            with self.assertRaisesRegex(\n                    asyncpg.InterfaceError,\n                    r'cannot call PreparedStatement\\.{meth}.*released '\n                    r'back to the pool'.format(meth=meth)):\n\n                getattr(ps, meth)()\n\n        for c in (cur, ps_cur):\n            for meth in ('fetch', 'fetchrow'):\n                with self.assertRaisesRegex(\n                        asyncpg.InterfaceError,\n                        r'cannot call Cursor\\.{meth}.*released '\n                        r'back to the pool'.format(meth=meth)):\n\n                    getattr(c, meth)()\n\n            with self.assertRaisesRegex(\n                    asyncpg.InterfaceError,\n                    r'cannot call Cursor\\.forward.*released '\n                    r'back to the pool'):\n\n                c.forward(1)\n\n        for meth in ('start', 'commit', 'rollback'):\n            with self.assertRaisesRegex(\n                    asyncpg.InterfaceError,\n                    r'cannot call Transaction\\.{meth}.*released '\n                    r'back to the pool'.format(meth=meth)):\n\n                getattr(txn, meth)()\n\n        await pool.close()\n\n    async def test_pool_12(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        async with pool.acquire() as con:\n            self.assertTrue(isinstance(con, pg_connection.Connection))\n            self.assertFalse(isinstance(con, list))\n\n        await pool.close()\n\n    async def test_pool_13(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        async with pool.acquire() as con:\n            self.assertIn('Execute an SQL command', con.execute.__doc__)\n            self.assertEqual(con.execute.__name__, 'execute')\n\n            self.assertIn(\n                str(inspect.signature(con.execute))[1:],\n                str(inspect.signature(pg_connection.Connection.execute)))\n\n        await pool.close()\n\n    def test_pool_init_run_until_complete(self):\n        pool_init = self.create_pool(database='postgres')\n        pool = self.loop.run_until_complete(pool_init)\n        self.assertIsInstance(pool, asyncpg.pool.Pool)\n\n    async def test_pool_exception_in_setup_and_init(self):\n        class Error(Exception):\n            pass\n\n        async def setup(con):\n            nonlocal setup_calls, last_con\n            last_con = con\n            setup_calls += 1\n            if setup_calls > 1:\n                cons.append(con)\n            else:\n                cons.append('error')\n                raise Error\n\n        with self.subTest(method='setup'):\n            setup_calls = 0\n            last_con = None\n            cons = []\n            async with self.create_pool(database='postgres',\n                                        min_size=1, max_size=1,\n                                        setup=setup) as pool:\n                with self.assertRaises(Error):\n                    await pool.acquire()\n                self.assertTrue(last_con.is_closed())\n\n                async with pool.acquire() as con:\n                    self.assertEqual(cons, ['error', con])\n\n        with self.subTest(method='init'):\n            setup_calls = 0\n            last_con = None\n            cons = []\n            async with self.create_pool(database='postgres',\n                                        min_size=0, max_size=1,\n                                        init=setup) as pool:\n                with self.assertRaises(Error):\n                    await pool.acquire()\n                self.assertTrue(last_con.is_closed())\n\n                async with pool.acquire() as con:\n                    self.assertEqual(await con.fetchval('select 1::int'), 1)\n                    self.assertEqual(cons, ['error', con._con])\n\n    async def test_pool_auth(self):\n        if not self.cluster.is_managed():\n            self.skipTest('unmanaged cluster')\n\n        self.cluster.reset_hba()\n\n        if _system != 'Windows':\n            self.cluster.add_hba_entry(\n                type='local',\n                database='postgres', user='pooluser',\n                auth_method='md5')\n\n        self.cluster.add_hba_entry(\n            type='host', address='127.0.0.1/32',\n            database='postgres', user='pooluser',\n            auth_method='md5')\n\n        self.cluster.add_hba_entry(\n            type='host', address='::1/128',\n            database='postgres', user='pooluser',\n            auth_method='md5')\n\n        self.cluster.reload()\n\n        try:\n            await self.con.execute('''\n                CREATE ROLE pooluser WITH LOGIN PASSWORD 'poolpassword'\n            ''')\n\n            pool = await self.create_pool(database='postgres',\n                                          user='pooluser',\n                                          password='poolpassword',\n                                          min_size=5, max_size=10)\n\n            async def worker():\n                con = await pool.acquire()\n                self.assertEqual(await con.fetchval('SELECT 1'), 1)\n                await pool.release(con)\n\n            tasks = [worker() for _ in range(5)]\n            await asyncio.gather(*tasks)\n            await pool.close()\n\n        finally:\n            await self.con.execute('DROP ROLE pooluser')\n\n            # Reset cluster's pg_hba.conf since we've meddled with it\n            self.cluster.trust_local_connections()\n            self.cluster.reload()\n\n    async def test_pool_handles_task_cancel_in_acquire_with_timeout(self):\n        # See https://github.com/MagicStack/asyncpg/issues/547\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        async def worker():\n            async with pool.acquire(timeout=100):\n                pass\n\n        # Schedule task\n        task = self.loop.create_task(worker())\n        # Yield to task, but cancel almost immediately\n        await asyncio.sleep(0.00000000001)\n        # Cancel the worker.\n        task.cancel()\n        # Wait to make sure the cleanup has completed.\n        await asyncio.sleep(0.4)\n        # Check that the connection has been returned to the pool.\n        self.assertEqual(pool._queue.qsize(), 1)\n\n    async def test_pool_handles_task_cancel_in_release(self):\n        # Use SlowResetConnectionPool to simulate\n        # the Task.cancel() and __aexit__ race.\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1,\n                                      connection_class=SlowResetConnection)\n\n        async def worker():\n            async with pool.acquire():\n                pass\n\n        task = self.loop.create_task(worker())\n        # Let the worker() run.\n        await asyncio.sleep(0.1)\n        # Cancel the worker.\n        task.cancel()\n        # Wait to make sure the cleanup has completed.\n        await asyncio.sleep(0.4)\n        # Check that the connection has been returned to the pool.\n        self.assertEqual(pool._queue.qsize(), 1)\n\n    async def test_pool_handles_query_cancel_in_release(self):\n        # Use SlowResetConnectionPool to simulate\n        # the Task.cancel() and __aexit__ race.\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1,\n                                      connection_class=SlowCancelConnection)\n\n        async def worker():\n            async with pool.acquire() as con:\n                await con.execute('SELECT pg_sleep(10)')\n\n        task = self.loop.create_task(worker())\n        # Let the worker() run.\n        await asyncio.sleep(0.1)\n        # Cancel the worker.\n        task.cancel()\n        # Wait to make sure the cleanup has completed.\n        await asyncio.sleep(0.5)\n        # Check that the connection has been returned to the pool.\n        self.assertEqual(pool._queue.qsize(), 1)\n\n    async def test_pool_no_acquire_deadlock(self):\n        async with self.create_pool(database='postgres',\n                                    min_size=1, max_size=1,\n                                    max_queries=1) as pool:\n\n            async def sleep_and_release():\n                async with pool.acquire() as con:\n                    await con.execute('SELECT pg_sleep(1)')\n\n            asyncio.ensure_future(sleep_and_release())\n            await asyncio.sleep(0.5)\n\n            async with pool.acquire() as con:\n                await con.fetchval('SELECT 1')\n\n    async def test_pool_config_persistence(self):\n        N = 100\n        cons = set()\n\n        class MyConnection(asyncpg.Connection):\n            async def foo(self):\n                return 42\n\n            async def fetchval(self, query):\n                res = await super().fetchval(query)\n                return res + 1\n\n        async def test(pool):\n            async with pool.acquire() as con:\n                self.assertEqual(await con.fetchval('SELECT 1'), 2)\n                self.assertEqual(await con.foo(), 42)\n                self.assertTrue(isinstance(con, MyConnection))\n                self.assertEqual(con._con._config.statement_cache_size, 3)\n                cons.add(con)\n\n        async with self.create_pool(\n                database='postgres', min_size=10, max_size=10,\n                max_queries=1, connection_class=MyConnection,\n                statement_cache_size=3) as pool:\n\n            await asyncio.gather(*[test(pool) for _ in range(N)])\n\n        self.assertEqual(len(cons), N)\n\n    async def test_pool_release_in_xact(self):\n        \"\"\"Test that Connection.reset() closes any open transaction.\"\"\"\n        async with self.create_pool(database='postgres',\n                                    min_size=1, max_size=1) as pool:\n            async def get_xact_id(con):\n                return await con.fetchval('select txid_current()')\n\n            with self.assertLoopErrorHandlerCalled('an active transaction'):\n                async with pool.acquire() as con:\n                    real_con = con._con  # unwrap PoolConnectionProxy\n\n                    id1 = await get_xact_id(con)\n\n                    tr = con.transaction()\n                    self.assertIsNone(con._con._top_xact)\n                    await tr.start()\n                    self.assertIs(real_con._top_xact, tr)\n\n                    id2 = await get_xact_id(con)\n                    self.assertNotEqual(id1, id2)\n\n            self.assertIsNone(real_con._top_xact)\n\n            async with pool.acquire() as con:\n                self.assertIs(con._con, real_con)\n                self.assertIsNone(con._con._top_xact)\n                id3 = await get_xact_id(con)\n                self.assertNotEqual(id2, id3)\n\n    async def test_pool_connection_methods(self):\n        async def test_fetch(pool):\n            i = random.randint(0, 20)\n            await asyncio.sleep(random.random() / 100)\n            r = await pool.fetch('SELECT {}::int'.format(i))\n            self.assertEqual(r, [(i,)])\n            return 1\n\n        async def test_fetchrow(pool):\n            i = random.randint(0, 20)\n            await asyncio.sleep(random.random() / 100)\n            r = await pool.fetchrow('SELECT {}::int'.format(i))\n            self.assertEqual(r, (i,))\n            return 1\n\n        async def test_fetchval(pool):\n            i = random.randint(0, 20)\n            await asyncio.sleep(random.random() / 100)\n            r = await pool.fetchval('SELECT {}::int'.format(i))\n            self.assertEqual(r, i)\n            return 1\n\n        async def test_execute(pool):\n            await asyncio.sleep(random.random() / 100)\n            r = await pool.execute('SELECT generate_series(0, 10)')\n            self.assertEqual(r, 'SELECT {}'.format(11))\n            return 1\n\n        async def test_execute_with_arg(pool):\n            i = random.randint(0, 20)\n            await asyncio.sleep(random.random() / 100)\n            r = await pool.execute('SELECT generate_series(0, $1)', i)\n            self.assertEqual(r, 'SELECT {}'.format(i + 1))\n            return 1\n\n        async def run(N, meth):\n            async with self.create_pool(database='postgres',\n                                        min_size=5, max_size=10) as pool:\n\n                coros = [meth(pool) for _ in range(N)]\n                res = await asyncio.gather(*coros)\n                self.assertEqual(res, [1] * N)\n\n        methods = [test_fetch, test_fetchrow, test_fetchval,\n                   test_execute, test_execute_with_arg]\n\n        with tb.silence_asyncio_long_exec_warning():\n            for method in methods:\n                with self.subTest(method=method.__name__):\n                    await run(200, method)\n\n    async def test_pool_connection_execute_many(self):\n        async def worker(pool):\n            await asyncio.sleep(random.random() / 100)\n            await pool.executemany('''\n                INSERT INTO exmany VALUES($1, $2)\n            ''', [\n                ('a', 1), ('b', 2), ('c', 3), ('d', 4)\n            ])\n            return 1\n\n        N = 200\n\n        async with self.create_pool(database='postgres',\n                                    min_size=5, max_size=10) as pool:\n\n            await pool.execute('CREATE TABLE exmany (a text, b int)')\n            try:\n\n                coros = [worker(pool) for _ in range(N)]\n                res = await asyncio.gather(*coros)\n                self.assertEqual(res, [1] * N)\n\n                n_rows = await pool.fetchval('SELECT count(*) FROM exmany')\n                self.assertEqual(n_rows, N * 4)\n\n            finally:\n                await pool.execute('DROP TABLE exmany')\n\n    async def test_pool_max_inactive_time_01(self):\n        async with self.create_pool(\n                database='postgres', min_size=1, max_size=1,\n                max_inactive_connection_lifetime=0.1) as pool:\n\n            # Test that it's OK if a query takes longer time to execute\n            # than `max_inactive_connection_lifetime`.\n\n            con = pool._holders[0]._con\n\n            for _ in range(3):\n                await pool.execute('SELECT pg_sleep(0.5)')\n                self.assertIs(pool._holders[0]._con, con)\n\n                self.assertEqual(\n                    await pool.execute('SELECT 1::int'),\n                    'SELECT 1')\n                self.assertIs(pool._holders[0]._con, con)\n\n    async def test_pool_max_inactive_time_02(self):\n        async with self.create_pool(\n                database='postgres', min_size=1, max_size=1,\n                max_inactive_connection_lifetime=0.5) as pool:\n\n            # Test that we have a new connection after pool not\n            # being used longer than `max_inactive_connection_lifetime`.\n\n            con = pool._holders[0]._con\n\n            self.assertEqual(\n                await pool.execute('SELECT 1::int'),\n                'SELECT 1')\n            self.assertIs(pool._holders[0]._con, con)\n\n            await asyncio.sleep(1)\n            self.assertIs(pool._holders[0]._con, None)\n\n            self.assertEqual(\n                await pool.execute('SELECT 1::int'),\n                'SELECT 1')\n            self.assertIsNot(pool._holders[0]._con, con)\n\n    async def test_pool_max_inactive_time_03(self):\n        async with self.create_pool(\n                database='postgres', min_size=1, max_size=1,\n                max_inactive_connection_lifetime=1) as pool:\n\n            # Test that we start counting inactive time *after*\n            # the connection is being released back to the pool.\n\n            con = pool._holders[0]._con\n\n            await pool.execute('SELECT pg_sleep(0.5)')\n            await asyncio.sleep(0.6)\n\n            self.assertIs(pool._holders[0]._con, con)\n\n            self.assertEqual(\n                await pool.execute('SELECT 1::int'),\n                'SELECT 1')\n            self.assertIs(pool._holders[0]._con, con)\n\n    async def test_pool_max_inactive_time_04(self):\n        # Chaos test for max_inactive_connection_lifetime.\n        DURATION = 2.0\n        START = time.monotonic()\n        N = 0\n\n        async def worker(pool):\n            nonlocal N\n            await asyncio.sleep(random.random() / 10 + 0.1)\n            async with pool.acquire() as con:\n                if random.random() > 0.5:\n                    await con.execute('SELECT pg_sleep({:.2f})'.format(\n                        random.random() / 10))\n                self.assertEqual(\n                    await con.fetchval('SELECT 42::int'),\n                    42)\n\n            if time.monotonic() - START < DURATION:\n                await worker(pool)\n\n            N += 1\n\n        async with self.create_pool(\n                database='postgres', min_size=10, max_size=30,\n                max_inactive_connection_lifetime=0.1) as pool:\n\n            workers = [worker(pool) for _ in range(50)]\n            await asyncio.gather(*workers)\n\n        self.assertGreaterEqual(N, 50)\n\n    async def test_pool_max_inactive_time_05(self):\n        # Test that idle never-acquired connections abide by\n        # the max inactive lifetime.\n        async with self.create_pool(\n                database='postgres', min_size=2, max_size=2,\n                max_inactive_connection_lifetime=0.2) as pool:\n\n            self.assertIsNotNone(pool._holders[0]._con)\n            self.assertIsNotNone(pool._holders[1]._con)\n\n            await pool.execute('SELECT pg_sleep(0.3)')\n            await asyncio.sleep(0.3)\n\n            self.assertIs(pool._holders[0]._con, None)\n            # The connection in the second holder was never used,\n            # but should be closed nonetheless.\n            self.assertIs(pool._holders[1]._con, None)\n\n    async def test_pool_handles_inactive_connection_errors(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        con = await pool.acquire(timeout=POOL_NOMINAL_TIMEOUT)\n\n        true_con = con._con\n\n        await pool.release(con)\n\n        # we simulate network error by terminating the connection\n        true_con.terminate()\n\n        # now pool should reopen terminated connection\n        async with pool.acquire(timeout=POOL_NOMINAL_TIMEOUT) as con:\n            self.assertEqual(await con.fetchval('SELECT 1'), 1)\n            await con.close()\n\n        await pool.close()\n\n    async def test_pool_size_and_capacity(self):\n        async with self.create_pool(\n            database='postgres',\n            min_size=2,\n            max_size=3,\n        ) as pool:\n            self.assertEqual(pool.get_min_size(), 2)\n            self.assertEqual(pool.get_max_size(), 3)\n            self.assertEqual(pool.get_size(), 2)\n            self.assertEqual(pool.get_idle_size(), 2)\n\n            async with pool.acquire():\n                self.assertEqual(pool.get_idle_size(), 1)\n\n                async with pool.acquire():\n                    self.assertEqual(pool.get_idle_size(), 0)\n\n                    async with pool.acquire():\n                        self.assertEqual(pool.get_size(), 3)\n                        self.assertEqual(pool.get_idle_size(), 0)\n\n    async def test_pool_closing(self):\n        async with self.create_pool() as pool:\n            self.assertFalse(pool.is_closing())\n            await pool.close()\n            self.assertTrue(pool.is_closing())\n\n        async with self.create_pool() as pool:\n            self.assertFalse(pool.is_closing())\n            pool.terminate()\n            self.assertTrue(pool.is_closing())\n\n    async def test_pool_handles_transaction_exit_in_asyncgen_1(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        locals_ = {}\n        exec(textwrap.dedent('''\\\n            async def iterate(con):\n                async with con.transaction():\n                    for record in await con.fetch(\"SELECT 1\"):\n                        yield record\n        '''), globals(), locals_)\n        iterate = locals_['iterate']\n\n        class MyException(Exception):\n            pass\n\n        with self.assertRaises(MyException):\n            async with pool.acquire() as con:\n                async for _ in iterate(con):  # noqa\n                    raise MyException()\n\n    async def test_pool_handles_transaction_exit_in_asyncgen_2(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        locals_ = {}\n        exec(textwrap.dedent('''\\\n            async def iterate(con):\n                async with con.transaction():\n                    for record in await con.fetch(\"SELECT 1\"):\n                        yield record\n        '''), globals(), locals_)\n        iterate = locals_['iterate']\n\n        class MyException(Exception):\n            pass\n\n        with self.assertRaises(MyException):\n            async with pool.acquire() as con:\n                iterator = iterate(con)\n                async for _ in iterator:  # noqa\n                    raise MyException()\n\n            del iterator\n\n    async def test_pool_handles_asyncgen_finalization(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        locals_ = {}\n        exec(textwrap.dedent('''\\\n            async def iterate(con):\n                for record in await con.fetch(\"SELECT 1\"):\n                    yield record\n        '''), globals(), locals_)\n        iterate = locals_['iterate']\n\n        class MyException(Exception):\n            pass\n\n        with self.assertRaises(MyException):\n            async with pool.acquire() as con:\n                async with con.transaction():\n                    async for _ in iterate(con):  # noqa\n                        raise MyException()\n\n    async def test_pool_close_waits_for_release(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        flag = self.loop.create_future()\n        conn_released = False\n\n        async def worker():\n            nonlocal conn_released\n\n            async with pool.acquire() as connection:\n                async with connection.transaction():\n                    flag.set_result(True)\n                    await asyncio.sleep(0.1)\n\n            conn_released = True\n\n        self.loop.create_task(worker())\n\n        await flag\n        await pool.close()\n        self.assertTrue(conn_released)\n\n    async def test_pool_close_timeout(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        flag = self.loop.create_future()\n\n        async def worker():\n            async with pool.acquire():\n                flag.set_result(True)\n                await asyncio.sleep(0.5)\n\n        task = self.loop.create_task(worker())\n\n        with self.assertRaises(asyncio.TimeoutError):\n            await flag\n            await asyncio.wait_for(pool.close(), timeout=0.1)\n\n        await task\n\n    async def test_pool_expire_connections(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        con = await pool.acquire()\n        try:\n            await pool.expire_connections()\n        finally:\n            await pool.release(con)\n\n        self.assertIsNone(pool._holders[0]._con)\n        await pool.close()\n\n    async def test_pool_set_connection_args(self):\n        pool = await self.create_pool(database='postgres',\n                                      min_size=1, max_size=1)\n\n        # Test that connection is expired on release.\n        con = await pool.acquire()\n        connspec = self.get_connection_spec()\n        try:\n            connspec['server_settings']['application_name'] = \\\n                'set_conn_args_test'\n        except KeyError:\n            connspec['server_settings'] = {\n                'application_name': 'set_conn_args_test'\n            }\n\n        pool.set_connect_args(**connspec)\n        await pool.expire_connections()\n        await pool.release(con)\n\n        con = await pool.acquire()\n        self.assertEqual(con.get_settings().application_name,\n                         'set_conn_args_test')\n        await pool.release(con)\n\n        # Test that connection is expired before acquire.\n        connspec = self.get_connection_spec()\n        try:\n            connspec['server_settings']['application_name'] = \\\n                'set_conn_args_test'\n        except KeyError:\n            connspec['server_settings'] = {\n                'application_name': 'set_conn_args_test_2'\n            }\n\n        pool.set_connect_args(**connspec)\n        await pool.expire_connections()\n\n        con = await pool.acquire()\n        self.assertEqual(con.get_settings().application_name,\n                         'set_conn_args_test_2')\n        await pool.release(con)\n        await pool.close()\n\n    async def test_pool_init_race(self):\n        pool = self.create_pool(database='postgres', min_size=1, max_size=1)\n\n        t1 = asyncio.ensure_future(pool)\n        t2 = asyncio.ensure_future(pool)\n\n        await t1\n        with self.assertRaisesRegex(\n                asyncpg.InterfaceError,\n                r'pool is being initialized in another task'):\n            await t2\n\n        await pool.close()\n\n    async def test_pool_init_and_use_race(self):\n        pool = self.create_pool(database='postgres', min_size=1, max_size=1)\n\n        pool_task = asyncio.ensure_future(pool)\n        await asyncio.sleep(0)\n\n        with self.assertRaisesRegex(\n                asyncpg.InterfaceError,\n                r'being initialized, but not yet ready'):\n\n            await pool.fetchval('SELECT 1')\n\n        await pool_task\n        await pool.close()\n\n    async def test_pool_remote_close(self):\n        pool = await self.create_pool(min_size=1, max_size=1)\n        backend_pid_fut = self.loop.create_future()\n\n        async def worker():\n            async with pool.acquire() as conn:\n                pool_backend_pid = await conn.fetchval(\n                    'SELECT pg_backend_pid()')\n                backend_pid_fut.set_result(pool_backend_pid)\n                await asyncio.sleep(0.2)\n\n        task = self.loop.create_task(worker())\n        try:\n            conn = await self.connect()\n            backend_pid = await backend_pid_fut\n            await conn.execute('SELECT pg_terminate_backend($1)', backend_pid)\n        finally:\n            await conn.close()\n\n        await task\n\n        # Check that connection_lost has released the pool holder.\n        conn = await pool.acquire(timeout=0.1)\n        await pool.release(conn)\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'unmanaged cluster')\nclass TestPoolReconnectWithTargetSessionAttrs(tb.ClusterTestCase):\n\n    @classmethod\n    def setup_cluster(cls):\n        cls.cluster = cls.new_cluster(pg_cluster.TempCluster)\n        cls.start_cluster(cls.cluster)\n\n    async def simulate_cluster_recovery_mode(self):\n        port = self.cluster.get_connection_spec()['port']\n        await self.loop.run_in_executor(\n            None,\n            lambda: self.cluster.stop()\n        )\n\n        # Simulate recovery mode\n        (pathlib.Path(self.cluster._data_dir) / 'standby.signal').touch()\n\n        await self.loop.run_in_executor(\n            None,\n            lambda: self.cluster.start(\n                port=port,\n                server_settings=self.get_server_settings(),\n            )\n        )\n\n    async def test_full_reconnect_on_node_change_role(self):\n        if self.cluster.get_pg_version() < (12, 0):\n            self.skipTest(\"PostgreSQL < 12 cannot support standby.signal\")\n            return\n\n        pool = await self.create_pool(\n            min_size=1,\n            max_size=1,\n            target_session_attrs='primary'\n        )\n\n        # Force a new connection to be created\n        await pool.fetchval('SELECT 1')\n\n        await self.simulate_cluster_recovery_mode()\n\n        # current pool connection info cache is expired,\n        # but we don't know it yet\n        with self.assertRaises(asyncpg.TargetServerAttributeNotMatched) as cm:\n            await pool.execute('SELECT 1')\n\n        self.assertEqual(\n            cm.exception.args[0],\n            \"None of the hosts match the target attribute requirement \"\n            \"<SessionAttribute.primary: 'primary'>\"\n        )\n\n        # force reconnect\n        with self.assertRaises(asyncpg.TargetServerAttributeNotMatched) as cm:\n            await pool.execute('SELECT 1')\n\n        self.assertEqual(\n            cm.exception.args[0],\n            \"None of the hosts match the target attribute requirement \"\n            \"<SessionAttribute.primary: 'primary'>\"\n        )\n\n\n@unittest.skipIf(os.environ.get('PGHOST'), 'using remote cluster for testing')\nclass TestHotStandby(tb.HotStandbyTestCase):\n    def create_pool(self, **kwargs):\n        conn_spec = self.standby_cluster.get_connection_spec()\n        conn_spec.update(kwargs)\n        return pg_pool.create_pool(loop=self.loop, **conn_spec)\n\n    async def test_standby_pool_01(self):\n        for n in {1, 3, 5, 10, 20, 100}:\n            with self.subTest(tasksnum=n):\n                pool = await self.create_pool(\n                    database='postgres', user='postgres',\n                    min_size=5, max_size=10)\n\n                async def worker():\n                    con = await pool.acquire()\n                    self.assertEqual(await con.fetchval('SELECT 1'), 1)\n                    await pool.release(con)\n\n                tasks = [worker() for _ in range(n)]\n                await asyncio.gather(*tasks)\n                await pool.close()\n\n    async def test_standby_cursors(self):\n        con = await self.standby_cluster.connect(\n            database='postgres', user='postgres', loop=self.loop)\n\n        try:\n            async with con.transaction():\n                cursor = await con.cursor('SELECT 1')\n                self.assertEqual(await cursor.fetchrow(), (1,))\n        finally:\n            await con.close()\n"
  },
  {
    "path": "tests/test_prepare.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport asyncpg\nimport gc\nimport unittest\n\nfrom asyncpg import _testbase as tb\nfrom asyncpg import exceptions\n\n\nclass TestPrepare(tb.ConnectedTestCase):\n\n    async def test_prepare_01(self):\n        self.assertEqual(self.con._protocol.queries_count, 0)\n        st = await self.con.prepare('SELECT 1 = $1 AS test')\n        self.assertEqual(self.con._protocol.queries_count, 0)\n        self.assertEqual(st.get_query(), 'SELECT 1 = $1 AS test')\n\n        rec = await st.fetchrow(1)\n        self.assertEqual(self.con._protocol.queries_count, 1)\n        self.assertTrue(rec['test'])\n        self.assertEqual(len(rec), 1)\n\n        self.assertEqual(False, await st.fetchval(10))\n        self.assertEqual(self.con._protocol.queries_count, 2)\n\n    async def test_prepare_02(self):\n        with self.assertRaisesRegex(Exception, 'column \"a\" does not exist'):\n            await self.con.prepare('SELECT a')\n\n    async def test_prepare_03(self):\n        cases = [\n            ('text', (\"'NULL'\", 'NULL'), [\n                'aaa',\n                None\n            ]),\n\n            ('decimal', ('0', 0), [\n                123,\n                123.5,\n                None\n            ])\n        ]\n\n        for type, (none_name, none_val), vals in cases:\n            st = await self.con.prepare('''\n                    SELECT CASE WHEN $1::{type} IS NULL THEN {default}\n                    ELSE $1::{type} END'''.format(\n                type=type, default=none_name))\n\n            for val in vals:\n                with self.subTest(type=type, value=val):\n                    res = await st.fetchval(val)\n                    if val is None:\n                        self.assertEqual(res, none_val)\n                    else:\n                        self.assertEqual(res, val)\n\n    async def test_prepare_04(self):\n        s = await self.con.prepare('SELECT $1::smallint')\n        self.assertEqual(await s.fetchval(10), 10)\n\n        s = await self.con.prepare('SELECT $1::smallint * 2')\n        self.assertEqual(await s.fetchval(10), 20)\n\n        s = await self.con.prepare('SELECT generate_series(5,10)')\n        self.assertEqual(await s.fetchval(), 5)\n        # Since the \"execute\" message was sent with a limit=1,\n        # we will receive a PortalSuspended message, instead of\n        # CommandComplete.  Which means there will be no status\n        # message set.\n        self.assertIsNone(s.get_statusmsg())\n        # Repeat the same test for 'fetchrow()'.\n        self.assertEqual(await s.fetchrow(), (5,))\n        self.assertIsNone(s.get_statusmsg())\n\n    async def test_prepare_05_unknownoid(self):\n        s = await self.con.prepare(\"SELECT 'test'\")\n        self.assertEqual(await s.fetchval(), 'test')\n\n    async def test_prepare_06_interrupted_close(self):\n        stmt = await self.con.prepare('''SELECT pg_sleep(10)''')\n        fut = self.loop.create_task(stmt.fetch())\n\n        await asyncio.sleep(0.2)\n\n        self.assertFalse(self.con.is_closed())\n        await self.con.close()\n        self.assertTrue(self.con.is_closed())\n\n        with self.assertRaises(asyncpg.QueryCanceledError):\n            await fut\n\n        # Test that it's OK to call close again\n        await self.con.close()\n\n    async def test_prepare_07_interrupted_terminate(self):\n        stmt = await self.con.prepare('''SELECT pg_sleep(10)''')\n        fut = self.loop.create_task(stmt.fetchval())\n\n        await asyncio.sleep(0.2)\n\n        self.assertFalse(self.con.is_closed())\n        self.con.terminate()\n        self.assertTrue(self.con.is_closed())\n\n        with self.assertRaisesRegex(asyncpg.ConnectionDoesNotExistError,\n                                    'closed in the middle'):\n            await fut\n\n        # Test that it's OK to call terminate again\n        self.con.terminate()\n\n    async def test_prepare_08_big_result(self):\n        stmt = await self.con.prepare('select generate_series(0,10000)')\n        result = await stmt.fetch()\n\n        self.assertEqual(len(result), 10001)\n        self.assertEqual(\n            [r[0] for r in result],\n            list(range(10001)))\n\n    async def test_prepare_09_raise_error(self):\n        # Stress test ReadBuffer.read_cstr()\n        msg = '0' * 1024 * 100\n        query = \"\"\"\n        DO language plpgsql $$\n        BEGIN\n        RAISE EXCEPTION '{}';\n        END\n        $$;\"\"\".format(msg)\n\n        stmt = await self.con.prepare(query)\n        with self.assertRaisesRegex(asyncpg.RaiseError, msg):\n            with tb.silence_asyncio_long_exec_warning():\n                await stmt.fetchval()\n\n    async def test_prepare_10_stmt_lru(self):\n        cache = self.con._stmt_cache\n\n        query = 'select {}'\n        cache_max = cache.get_max_size()\n        iter_max = cache_max * 2 + 11\n\n        # First, we have no cached statements.\n        self.assertEqual(len(cache), 0)\n\n        stmts = []\n        for i in range(iter_max):\n            s = await self.con._prepare(query.format(i), use_cache=True)\n            self.assertEqual(await s.fetchval(), i)\n            stmts.append(s)\n\n        # At this point our cache should be full.\n        self.assertEqual(len(cache), cache_max)\n        self.assertTrue(all(not s.closed for s in cache.iter_statements()))\n\n        # Since there are references to the statements (`stmts` list),\n        # no statements are scheduled to be closed.\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        # Removing refs to statements and preparing a new statement\n        # will cause connection to cleanup any stale statements.\n        stmts.clear()\n        gc.collect()\n\n        # Now we have a bunch of statements that have no refs to them\n        # scheduled to be closed.\n        self.assertEqual(len(self.con._stmts_to_close), iter_max - cache_max)\n        self.assertTrue(all(s.closed for s in self.con._stmts_to_close))\n        self.assertTrue(all(not s.closed for s in cache.iter_statements()))\n\n        zero = await self.con.prepare(query.format(0))\n        # Hence, all stale statements should be closed now.\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        # The number of cached statements will stay the same though.\n        self.assertEqual(len(cache), cache_max)\n        self.assertTrue(all(not s.closed for s in cache.iter_statements()))\n\n        # After closing all statements will be closed.\n        await self.con.close()\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n        self.assertEqual(len(cache), 0)\n\n        # An attempt to perform an operation on a closed statement\n        # will trigger an error.\n        with self.assertRaisesRegex(asyncpg.InterfaceError, 'is closed'):\n            await zero.fetchval()\n\n    async def test_prepare_11_stmt_gc(self):\n        # Test that prepared statements should stay in the cache after\n        # they are GCed.\n\n        cache = self.con._stmt_cache\n\n        # First, we have no cached statements.\n        self.assertEqual(len(cache), 0)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        # The prepared statement that we'll create will be GCed\n        # right await.  However, its state should be still in\n        # in the statements LRU cache.\n        await self.con._prepare('select 1', use_cache=True)\n        gc.collect()\n\n        self.assertEqual(len(cache), 1)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n    async def test_prepare_12_stmt_gc(self):\n        # Test that prepared statements are closed when there is no space\n        # for them in the LRU cache and there are no references to them.\n\n        cache = self.con._stmt_cache\n        cache_max = cache.get_max_size()\n\n        # First, we have no cached statements.\n        self.assertEqual(len(cache), 0)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        stmt = await self.con._prepare('select 100000000', use_cache=True)\n        self.assertEqual(len(cache), 1)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        for i in range(cache_max):\n            await self.con._prepare('select {}'.format(i), use_cache=True)\n\n        self.assertEqual(len(cache), cache_max)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        del stmt\n        gc.collect()\n\n        self.assertEqual(len(cache), cache_max)\n        self.assertEqual(len(self.con._stmts_to_close), 1)\n\n    async def test_prepare_13_connect(self):\n        v = await self.con.fetchval(\n            'SELECT $1::smallint AS foo', 10, column='foo')\n        self.assertEqual(v, 10)\n\n        r = await self.con.fetchrow('SELECT $1::smallint * 2 AS test', 10)\n        self.assertEqual(r['test'], 20)\n\n        rows = await self.con.fetch('SELECT generate_series(0,$1::int)', 3)\n        self.assertEqual([r[0] for r in rows], [0, 1, 2, 3])\n\n    async def test_prepare_14_explain(self):\n        # Test simple EXPLAIN.\n        stmt = await self.con.prepare('SELECT typname FROM pg_type')\n        plan = await stmt.explain()\n        self.assertEqual(plan[0]['Plan']['Relation Name'], 'pg_type')\n\n        # Test \"EXPLAIN ANALYZE\".\n        stmt = await self.con.prepare(\n            'SELECT typname, typlen FROM pg_type WHERE typlen > $1')\n        plan = await stmt.explain(2, analyze=True)\n        self.assertEqual(plan[0]['Plan']['Relation Name'], 'pg_type')\n        self.assertIn('Actual Total Time', plan[0]['Plan'])\n\n        # Test that 'EXPLAIN ANALYZE' is executed in a transaction\n        # that gets rollbacked.\n        tr = self.con.transaction()\n        await tr.start()\n        try:\n            await self.con.execute('CREATE TABLE mytab (a int)')\n            stmt = await self.con.prepare(\n                'INSERT INTO mytab (a) VALUES (1), (2)')\n            plan = await stmt.explain(analyze=True)\n            self.assertEqual(plan[0]['Plan']['Operation'], 'Insert')\n\n            # Check that no data was inserted\n            res = await self.con.fetch('SELECT * FROM mytab')\n            self.assertEqual(res, [])\n        finally:\n            await tr.rollback()\n\n    async def test_prepare_15_stmt_gc_cache_disabled(self):\n        # Test that even if the statements cache is off, we're still\n        # cleaning up GCed statements.\n\n        cache = self.con._stmt_cache\n\n        self.assertEqual(len(cache), 0)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        # Disable cache\n        cache.set_max_size(0)\n\n        stmt = await self.con._prepare('select 100000000', use_cache=True)\n        self.assertEqual(len(cache), 0)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        del stmt\n        gc.collect()\n\n        # After GC, _stmts_to_close should contain stmt's state\n        self.assertEqual(len(cache), 0)\n        self.assertEqual(len(self.con._stmts_to_close), 1)\n\n        # Next \"prepare\" call will trigger a cleanup\n        stmt = await self.con._prepare('select 1', use_cache=True)\n        self.assertEqual(len(cache), 0)\n        self.assertEqual(len(self.con._stmts_to_close), 0)\n\n        del stmt\n\n    async def test_prepare_16_command_result(self):\n        async def status(query):\n            stmt = await self.con.prepare(query)\n            await stmt.fetch()\n            return stmt.get_statusmsg()\n\n        try:\n            self.assertEqual(\n                await status('CREATE TABLE mytab (a int)'),\n                'CREATE TABLE')\n\n            self.assertEqual(\n                await status('INSERT INTO mytab (a) VALUES (1), (2)'),\n                'INSERT 0 2')\n\n            self.assertEqual(\n                await status('SELECT a FROM mytab'),\n                'SELECT 2')\n\n            self.assertEqual(\n                await status('UPDATE mytab SET a = 3 WHERE a = 1'),\n                'UPDATE 1')\n        finally:\n            self.assertEqual(\n                await status('DROP TABLE mytab'),\n                'DROP TABLE')\n\n    async def test_prepare_17_stmt_closed_lru(self):\n        st = await self.con.prepare('SELECT 1')\n        st._state.mark_closed()\n        with self.assertRaisesRegex(asyncpg.InterfaceError, 'is closed'):\n            await st.fetch()\n\n        st = await self.con.prepare('SELECT 1')\n        self.assertEqual(await st.fetchval(), 1)\n\n    async def test_prepare_18_empty_result(self):\n        # test EmptyQueryResponse protocol message\n        st = await self.con.prepare('')\n        self.assertEqual(await st.fetch(), [])\n        self.assertIsNone(await st.fetchval())\n        self.assertIsNone(await st.fetchrow())\n\n        self.assertEqual(await self.con.fetch(''), [])\n        self.assertIsNone(await self.con.fetchval(''))\n        self.assertIsNone(await self.con.fetchrow(''))\n\n    async def test_prepare_19_concurrent_calls(self):\n        st = self.loop.create_task(self.con.fetchval(\n            'SELECT ROW(pg_sleep(0.1), 1)'))\n\n        # Wait for some time to make sure the first query is fully\n        # prepared (!) and is now awaiting the results (!!).\n        await asyncio.sleep(0.01)\n\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'another operation'):\n            await self.con.execute('SELECT 2')\n\n        self.assertEqual(await st, (None, 1))\n\n    async def test_prepare_20_concurrent_calls(self):\n        expected = ((None, 1),)\n\n        for methname, val in [('fetch', [expected]),\n                              ('fetchval', expected[0]),\n                              ('fetchrow', expected)]:\n\n            with self.subTest(meth=methname):\n\n                meth = getattr(self.con, methname)\n\n                vf = self.loop.create_task(\n                    meth('SELECT ROW(pg_sleep(0.1), 1)'))\n\n                await asyncio.sleep(0.01)\n\n                with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                            'another operation'):\n                    await meth('SELECT 2')\n\n                self.assertEqual(await vf, val)\n\n    async def test_prepare_21_errors(self):\n        stmt = await self.con.prepare('SELECT 10 / $1::int')\n\n        with self.assertRaises(asyncpg.DivisionByZeroError):\n            await stmt.fetchval(0)\n\n        self.assertEqual(await stmt.fetchval(5), 2)\n\n    async def test_prepare_22_empty(self):\n        # Support for empty target list was added in PostgreSQL 9.4\n        if self.server_version < (9, 4):\n            raise unittest.SkipTest(\n                'PostgreSQL servers < 9.4 do not support empty target list.')\n\n        result = await self.con.fetchrow('SELECT')\n        self.assertEqual(result, ())\n        self.assertEqual(repr(result), '<Record>')\n\n    async def test_prepare_statement_invalid(self):\n        await self.con.execute('CREATE TABLE tab1(a int, b int)')\n\n        try:\n            await self.con.execute('INSERT INTO tab1 VALUES (1, 2)')\n\n            stmt = await self.con.prepare('SELECT * FROM tab1')\n\n            await self.con.execute(\n                'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text')\n\n            with self.assertRaisesRegex(asyncpg.InvalidCachedStatementError,\n                                        'cached statement plan is invalid'):\n                await stmt.fetchrow()\n\n        finally:\n            await self.con.execute('DROP TABLE tab1')\n\n    @tb.with_connection_options(statement_cache_size=0)\n    async def test_prepare_23_no_stmt_cache_seq(self):\n        self.assertEqual(self.con._stmt_cache.get_max_size(), 0)\n\n        async def check_simple():\n            # Run a simple query a few times.\n            self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n            self.assertEqual(await self.con.fetchval('SELECT 2'), 2)\n            self.assertEqual(await self.con.fetchval('SELECT 1'), 1)\n\n        await check_simple()\n\n        # Run a query that timeouts.\n        with self.assertRaises(asyncio.TimeoutError):\n            await self.con.fetchrow('select pg_sleep(10)', timeout=0.02)\n\n        # Check that we can run new queries after a timeout.\n        await check_simple()\n\n        # Try a cursor/timeout combination. Cursors should always use\n        # named prepared statements.\n        async with self.con.transaction():\n            with self.assertRaises(asyncio.TimeoutError):\n                async for _ in self.con.cursor(   # NOQA\n                        'select pg_sleep(10)', timeout=0.1):\n                    pass\n\n        # Check that we can run queries after a failed cursor\n        # operation.\n        await check_simple()\n\n    @tb.with_connection_options(max_cached_statement_lifetime=142)\n    async def test_prepare_24_max_lifetime(self):\n        cache = self.con._stmt_cache\n\n        self.assertEqual(cache.get_max_lifetime(), 142)\n        cache.set_max_lifetime(1)\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        state = s._state\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        self.assertIs(s._state, state)\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        self.assertIs(s._state, state)\n\n        await asyncio.sleep(1)\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        self.assertIsNot(s._state, state)\n\n    @tb.with_connection_options(max_cached_statement_lifetime=0.5)\n    async def test_prepare_25_max_lifetime_reset(self):\n        cache = self.con._stmt_cache\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        state = s._state\n\n        # Disable max_lifetime\n        cache.set_max_lifetime(0)\n\n        await asyncio.sleep(1)\n\n        # The statement should still be cached (as we disabled the timeout).\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        self.assertIs(s._state, state)\n\n    @tb.with_connection_options(max_cached_statement_lifetime=0.5)\n    async def test_prepare_26_max_lifetime_max_size(self):\n        cache = self.con._stmt_cache\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        state = s._state\n\n        # Disable max_lifetime\n        cache.set_max_size(0)\n\n        s = await self.con._prepare('SELECT 1', use_cache=True)\n        self.assertIsNot(s._state, state)\n\n        # Check that nothing crashes after the initial timeout\n        await asyncio.sleep(1)\n\n    @tb.with_connection_options(max_cacheable_statement_size=50)\n    async def test_prepare_27_max_cacheable_statement_size(self):\n        cache = self.con._stmt_cache\n\n        await self.con._prepare('SELECT 1', use_cache=True)\n        self.assertEqual(len(cache), 1)\n\n        # Test that long and explicitly created prepared statements\n        # are not cached.\n        await self.con._prepare(\"SELECT \\'\" + \"a\" * 50 + \"\\'\", use_cache=True)\n        self.assertEqual(len(cache), 1)\n\n        # Test that implicitly created long prepared statements\n        # are not cached.\n        await self.con.fetchval(\"SELECT \\'\" + \"a\" * 50 + \"\\'\")\n        self.assertEqual(len(cache), 1)\n\n        # Test that short prepared statements can still be cached.\n        await self.con._prepare('SELECT 2', use_cache=True)\n        self.assertEqual(len(cache), 2)\n\n    async def test_prepare_28_max_args(self):\n        N = 32768\n        args = ','.join('${}'.format(i) for i in range(1, N + 1))\n        query = 'SELECT ARRAY[{}]'.format(args)\n\n        with self.assertRaisesRegex(\n                exceptions.InterfaceError,\n                'the number of query arguments cannot exceed 32767'):\n            await self.con.fetchval(query, *range(1, N + 1))\n\n    async def test_prepare_29_duplicates(self):\n        # In addition to test_record.py, let's have a full functional\n        # test for records with duplicate keys.\n        r = await self.con.fetchrow('SELECT 1 as a, 2 as b, 3 as a')\n        self.assertEqual(list(r.items()), [('a', 1), ('b', 2), ('a', 3)])\n        self.assertEqual(list(r.keys()), ['a', 'b', 'a'])\n        self.assertEqual(list(r.values()), [1, 2, 3])\n        self.assertEqual(r['a'], 3)\n        self.assertEqual(r['b'], 2)\n        self.assertEqual(r[0], 1)\n        self.assertEqual(r[1], 2)\n        self.assertEqual(r[2], 3)\n\n    async def test_prepare_30_invalid_arg_count(self):\n        with self.assertRaisesRegex(\n                exceptions.InterfaceError,\n                'the server expects 1 argument for this query, 0 were passed'):\n            await self.con.fetchval('SELECT $1::int')\n\n        with self.assertRaisesRegex(\n                exceptions.InterfaceError,\n                'the server expects 0 arguments for this query, 1 was passed'):\n            await self.con.fetchval('SELECT 1', 1)\n\n    async def test_prepare_31_pgbouncer_note(self):\n        try:\n            await self.con.execute(\"\"\"\n                DO $$ BEGIN\n                    RAISE EXCEPTION\n                        'duplicate statement' USING ERRCODE = '42P05';\n                END; $$ LANGUAGE plpgsql;\n            \"\"\")\n        except asyncpg.DuplicatePreparedStatementError as e:\n            self.assertTrue('pgbouncer' in e.hint)\n        else:\n            self.fail('DuplicatePreparedStatementError not raised')\n\n        try:\n            await self.con.execute(\"\"\"\n                DO $$ BEGIN\n                    RAISE EXCEPTION\n                        'invalid statement' USING ERRCODE = '26000';\n                END; $$ LANGUAGE plpgsql;\n            \"\"\")\n        except asyncpg.InvalidSQLStatementNameError as e:\n            self.assertTrue('pgbouncer' in e.hint)\n        else:\n            self.fail('InvalidSQLStatementNameError not raised')\n\n    async def test_prepare_does_not_use_cache(self):\n        cache = self.con._stmt_cache\n\n        # prepare with disabled cache\n        await self.con.prepare('select 1')\n        self.assertEqual(len(cache), 0)\n\n    async def test_prepare_explicitly_named(self):\n        ps = await self.con.prepare('select 1', name='foobar')\n        self.assertEqual(ps.get_name(), 'foobar')\n        self.assertEqual(await self.con.fetchval('EXECUTE foobar'), 1)\n\n        with self.assertRaisesRegex(\n            exceptions.DuplicatePreparedStatementError,\n            'prepared statement \"foobar\" already exists',\n        ):\n            await self.con.prepare('select 1', name='foobar')\n\n    async def test_prepare_fetchmany(self):\n        tr = self.con.transaction()\n        await tr.start()\n        try:\n            await self.con.execute('CREATE TABLE fetchmany (a int, b text)')\n\n            stmt = await self.con.prepare(\n                'INSERT INTO fetchmany (a, b) VALUES ($1, $2) RETURNING a, b'\n            )\n            result = await stmt.fetchmany([(1, 'a'), (2, 'b'), (3, 'c')])\n            self.assertEqual(result, [(1, 'a'), (2, 'b'), (3, 'c')])\n        finally:\n            await tr.rollback()\n"
  },
  {
    "path": "tests/test_record.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport contextlib\nimport collections\nimport gc\nimport pickle\nimport sys\n\nimport asyncpg\nfrom asyncpg import _testbase as tb\nfrom asyncpg.protocol.protocol import _create_record as Record\n\n\nR_A = collections.OrderedDict([('a', 0)])\nR_AB = collections.OrderedDict([('a', 0), ('b', 1)])\nR_AC = collections.OrderedDict([('a', 0), ('c', 1)])\nR_ABC = collections.OrderedDict([('a', 0), ('b', 1), ('c', 2)])\n\n\nclass CustomRecord(asyncpg.Record):\n    pass\n\n\nclass AnotherCustomRecord(asyncpg.Record):\n    pass\n\n\nclass TestRecord(tb.ConnectedTestCase):\n\n    @contextlib.contextmanager\n    def checkref(self, *objs):\n        cnt = [sys.getrefcount(objs[i]) for i in range(len(objs))]\n        yield\n        for _ in range(3):\n            gc.collect()\n        for i in range(len(objs)):\n            before = cnt[i]\n            after = sys.getrefcount(objs[i])\n            if before != after:\n                self.fail('refcounts differ for {!r}: {:+}'.format(\n                    objs[i], after - before))\n\n    def test_record_gc(self):\n        elem = object()\n        mapping = {}\n        with self.checkref(mapping, elem):\n            r = Record(mapping, (elem,))\n            del r\n\n        key = 'spam'\n        val = int('101010')\n        mapping = {key: val}\n        with self.checkref(key, val):\n            r = Record(mapping, (0,))\n            with self.assertRaises(RuntimeError):\n                r[key]\n            del r\n\n        key = 'spam'\n        val = 'ham'\n        mapping = {key: val}\n        with self.checkref(key, val):\n            r = Record(mapping, (0,))\n            with self.assertRaises(RuntimeError):\n                r[key]\n            del r\n\n    def test_record_freelist_ok(self):\n        for _ in range(10000):\n            Record(R_A, (42,))\n            Record(R_AB, (42, 42,))\n\n    def test_record_len_getindex(self):\n        r = Record(R_A, (42,))\n        self.assertEqual(len(r), 1)\n        self.assertEqual(r[0], 42)\n        self.assertEqual(r['a'], 42)\n\n        r = Record(R_AB, (42, 43))\n        self.assertEqual(len(r), 2)\n        self.assertEqual(r[0], 42)\n        self.assertEqual(r[1], 43)\n        self.assertEqual(r['a'], 42)\n        self.assertEqual(r['b'], 43)\n\n        with self.assertRaisesRegex(IndexError,\n                                    'record index out of range'):\n            r[1000]\n\n        with self.assertRaisesRegex(KeyError, 'spam'):\n            r['spam']\n\n        with self.assertRaisesRegex(KeyError, 'spam'):\n            Record(None, (1,))['spam']\n\n        with self.assertRaisesRegex(RuntimeError, 'invalid record descriptor'):\n            Record({'spam': 123}, (1,))['spam']\n\n    def test_record_slice(self):\n        r = Record(R_ABC, (1, 2, 3))\n        self.assertEqual(r[:], (1, 2, 3))\n        self.assertEqual(r[:1], (1,))\n        self.assertEqual(r[::-1], (3, 2, 1))\n        self.assertEqual(r[::-2], (3, 1))\n        self.assertEqual(r[1:2], (2,))\n        self.assertEqual(r[2:2], ())\n\n    def test_record_immutable(self):\n        r = Record(R_A, (42,))\n        with self.assertRaisesRegex(TypeError, 'does not support item'):\n            r[0] = 1\n\n    def test_record_repr(self):\n        self.assertEqual(\n            repr(Record(R_A, (42,))),\n            '<Record a=42>')\n\n        self.assertEqual(\n            repr(Record(R_AB, (42, -1))),\n            '<Record a=42 b=-1>')\n\n        # test invalid records just in case\n        with self.assertRaisesRegex(RuntimeError, 'invalid .* mapping'):\n            repr(Record(R_A, (42, 43)))\n        self.assertEqual(repr(Record(R_AB, (42,))), '<Record a=42>')\n\n        class Key:\n            def __str__(self):\n                1 / 0\n\n            def __repr__(self):\n                1 / 0\n\n        with self.assertRaises(ZeroDivisionError):\n            repr(Record({Key(): 0}, (42,)))\n        with self.assertRaises(ZeroDivisionError):\n            repr(Record(R_A, (Key(),)))\n\n    def test_record_iter(self):\n        r = Record(R_AB, (42, 43))\n        with self.checkref(r):\n            self.assertEqual(iter(r).__length_hint__(), 2)\n            self.assertEqual(tuple(r), (42, 43))\n\n    def test_record_values(self):\n        r = Record(R_AB, (42, 43))\n        vv = r.values()\n        self.assertEqual(tuple(vv), (42, 43))\n        self.assertTrue(\n            repr(vv).startswith('<asyncpg.protocol.record.RecordIterator '))\n\n    def test_record_keys(self):\n        r = Record(R_AB, (42, 43))\n        vv = r.keys()\n        self.assertEqual(tuple(vv), ('a', 'b'))\n        self.assertEqual(list(Record(None, (42, 43)).keys()), [])\n\n    def test_record_items(self):\n        r = Record(R_AB, (42, 43))\n\n        self.assertEqual(dict(r), {'a': 42, 'b': 43})\n        self.assertEqual(\n            list(collections.OrderedDict(r).items()),\n            [('a', 42), ('b', 43)])\n\n        with self.checkref(r):\n            rk = r.items()\n            self.assertEqual(rk.__length_hint__(), 2)\n            self.assertEqual(next(rk), ('a', 42))\n            self.assertEqual(rk.__length_hint__(), 1)\n            self.assertEqual(next(rk), ('b', 43))\n            self.assertEqual(rk.__length_hint__(), 0)\n\n            with self.assertRaises(StopIteration):\n                next(rk)\n            with self.assertRaises(StopIteration):\n                next(rk)\n\n            self.assertEqual(rk.__length_hint__(), 0)\n\n        self.assertEqual(list(r.items()), [('a', 42), ('b', 43)])\n\n        # Check invalid records just in case\n        r = Record(R_A, (42, 43))\n        self.assertEqual(list(r.items()), [('a', 42)])\n        r = Record(R_AB, (42,))\n        self.assertEqual(list(r.items()), [('a', 42)])\n\n        # Try to iterate over exhausted items() iterator\n        r = Record(R_A, (42, 43))\n        it = r.items()\n        list(it)\n        list(it)\n\n    def test_record_hash(self):\n        AB = collections.namedtuple('AB', ('a', 'b'))\n        r1 = Record(R_AB, (42, 43))\n        r2 = Record(R_AB, (42, 43))\n        r3 = Record(R_AB, (42, 45))\n        r4 = (42, 43)\n        r5 = AB(42, 43)\n\n        self.assertEqual(hash(r1), hash(r2))\n        self.assertNotEqual(hash(r1), hash(r3))\n        self.assertEqual(hash(r1), hash(r4))\n        self.assertEqual(hash(r1), hash(r5))\n\n        d = {}\n        d[r1] = 123\n        self.assertEqual(d[r1], 123)\n        self.assertIn(r2, d)\n        self.assertEqual(d[r2], 123)\n        self.assertNotIn(r3, d)\n        self.assertIn(r4, d)\n\n    def test_record_contains(self):\n        r = Record(R_AB, (42, 43))\n        self.assertIn('a', r)\n        self.assertIn('b', r)\n        self.assertNotIn('z', r)\n\n        r = Record(None, (42, 43))\n        self.assertNotIn('a', r)\n\n        with self.assertRaises(TypeError):\n            type(r).__contains__(None, 'a')\n\n    def test_record_cmp(self):\n        AB = collections.namedtuple('AB', ('a', 'b'))\n\n        r1 = Record(R_AB, (42, 43))\n        r2 = Record(R_AB, (42, 43))\n        r3 = Record(R_AB.copy(), (42, 43))\n\n        r4 = Record(R_AB.copy(), (42, 45))\n        r5 = Record(R_ABC, (42, 46, 57))\n        r6 = Record(R_AC, (42, 43))\n\n        r7 = (42, 43)\n        r8 = [42, 43]\n\n        r9 = AB(42, 43)\n        r10 = AB(42, 44)\n\n        self.assertEqual(r1, r2)\n        self.assertEqual(r1, r3)\n        self.assertEqual(r1, r6)\n        self.assertEqual(r1, r7)\n        self.assertEqual(r1, r9)\n\n        self.assertNotEqual(r1, r4)\n        self.assertNotEqual(r1, r10)\n        self.assertNotEqual(r1, (42,))\n        self.assertNotEqual(r1, r5)\n        self.assertNotEqual(r4, r5)\n        self.assertNotEqual(r4, r6)\n        self.assertNotEqual(r6, r5)\n        self.assertNotEqual(r1, r8)\n        self.assertNotEqual(r8, r6)\n\n        self.assertLess(r1, r4)\n        self.assertGreater(r4, r1)\n\n        self.assertLess(r1, r5)\n        self.assertLess(r7, r5)\n        self.assertLess(r1, r10)\n        self.assertGreater(r5, r6)\n        self.assertGreater(r5, r7)\n        self.assertGreater(r5, r4)\n\n        with self.assertRaisesRegex(\n                TypeError, \"unorderable|'<' not supported\"):\n            r1 < r8\n\n        self.assertEqual(\n            sorted([r1, r2, r3, r4, r5, r6, r7]),\n            [r1, r2, r3, r6, r7, r4, r5])\n\n    def test_record_get(self):\n        r = Record(R_AB, (42, 43))\n        with self.checkref(r):\n            self.assertEqual(r.get('a'), 42)\n            self.assertEqual(r.get('nonexistent'), None)\n            self.assertEqual(r.get('nonexistent', 'default'), 'default')\n\n    def test_record_not_pickleable(self):\n        r = Record(R_A, (42,))\n        with self.assertRaises(Exception):\n            pickle.dumps(r)\n\n    def test_record_empty(self):\n        r = Record(None, ())\n        self.assertEqual(r, ())\n        self.assertLess(r, (1,))\n        self.assertEqual(len(r), 0)\n        self.assertFalse(r)\n        self.assertNotIn('a', r)\n        self.assertEqual(repr(r), '<Record>')\n        self.assertEqual(str(r), '<Record>')\n        with self.assertRaisesRegex(KeyError, 'aaa'):\n            r['aaa']\n        self.assertEqual(dict(r.items()), {})\n        self.assertEqual(list(r.keys()), [])\n        self.assertEqual(list(r.values()), [])\n\n    async def test_record_duplicate_colnames(self):\n        \"\"\"Test that Record handles duplicate column names.\"\"\"\n\n        records_descs = [\n            [('a', 1)],\n            [('a', 1), ('a', 2)],\n            [('a', 1), ('b', 2), ('a', 3)],\n            [('a', 1), ('b', 2), ('a', 3), ('c', 4), ('b', 5)],\n        ]\n\n        for desc in records_descs:\n            items = collections.OrderedDict(desc)\n\n            query = 'SELECT ' + ', '.join(\n                ['{} as {}'.format(p[1], p[0]) for p in desc])\n\n            with self.subTest(query=query):\n                r = await self.con.fetchrow(query)\n                for idx, (field, val) in enumerate(desc):\n                    self.assertEqual(r[idx], val)\n                    self.assertEqual(r[field], items[field])\n\n                expected_repr = '<Record {}>'.format(\n                    ' '.join('{}={}'.format(p[0], p[1]) for p in desc))\n                self.assertEqual(repr(r), expected_repr)\n\n                self.assertEqual(list(r.items()), desc)\n                self.assertEqual(list(r.values()), [p[1] for p in desc])\n                self.assertEqual(list(r.keys()), [p[0] for p in desc])\n\n    async def test_record_isinstance(self):\n        \"\"\"Test that Record works with isinstance.\"\"\"\n        r = await self.con.fetchrow('SELECT 1 as a, 2 as b')\n        self.assertTrue(isinstance(r, asyncpg.Record))\n\n    async def test_record_no_new(self):\n        \"\"\"Instances of Record cannot be directly created.\"\"\"\n        with self.assertRaisesRegex(\n            TypeError,\n            \"cannot create 'asyncpg.protocol.record.Record' instances\",\n        ):\n            asyncpg.Record()\n\n    @tb.with_connection_options(record_class=CustomRecord)\n    async def test_record_subclass_01(self):\n        r = await self.con.fetchrow(\"SELECT 1 as a, '2' as b\")\n        self.assertIsInstance(r, CustomRecord)\n\n        r = await self.con.fetch(\"SELECT 1 as a, '2' as b\")\n        self.assertIsInstance(r[0], CustomRecord)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor(\"SELECT 1 as a, '2' as b\")\n            r = await cur.fetchrow()\n            self.assertIsInstance(r, CustomRecord)\n\n            cur = await self.con.cursor(\"SELECT 1 as a, '2' as b\")\n            r = await cur.fetch(1)\n            self.assertIsInstance(r[0], CustomRecord)\n\n        async with self.con.transaction():\n            cur = self.con.cursor(\"SELECT 1 as a, '2' as b\")\n            async for r in cur:\n                self.assertIsInstance(r, CustomRecord)\n\n        ps = await self.con.prepare(\"SELECT 1 as a, '2' as b\")\n        r = await ps.fetchrow()\n        self.assertIsInstance(r, CustomRecord)\n\n    async def test_record_subclass_02(self):\n        r = await self.con.fetchrow(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=CustomRecord,\n        )\n        self.assertIsInstance(r, CustomRecord)\n\n        r = await self.con.fetch(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=CustomRecord,\n        )\n        self.assertIsInstance(r[0], CustomRecord)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=CustomRecord,\n            )\n            r = await cur.fetchrow()\n            self.assertIsInstance(r, CustomRecord)\n\n            cur = await self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=CustomRecord,\n            )\n            r = await cur.fetch(1)\n            self.assertIsInstance(r[0], CustomRecord)\n\n        async with self.con.transaction():\n            cur = self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=CustomRecord,\n            )\n            async for r in cur:\n                self.assertIsInstance(r, CustomRecord)\n\n        ps = await self.con.prepare(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=CustomRecord,\n        )\n        r = await ps.fetchrow()\n        self.assertIsInstance(r, CustomRecord)\n\n        r = await ps.fetch()\n        self.assertIsInstance(r[0], CustomRecord)\n\n    @tb.with_connection_options(record_class=AnotherCustomRecord)\n    async def test_record_subclass_03(self):\n        r = await self.con.fetchrow(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=CustomRecord,\n        )\n        self.assertIsInstance(r, CustomRecord)\n\n        r = await self.con.fetch(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=CustomRecord,\n        )\n        self.assertIsInstance(r[0], CustomRecord)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=CustomRecord,\n            )\n            r = await cur.fetchrow()\n            self.assertIsInstance(r, CustomRecord)\n\n            cur = await self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=CustomRecord,\n            )\n            r = await cur.fetch(1)\n            self.assertIsInstance(r[0], CustomRecord)\n\n        async with self.con.transaction():\n            cur = self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=CustomRecord,\n            )\n            async for r in cur:\n                self.assertIsInstance(r, CustomRecord)\n\n        ps = await self.con.prepare(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=CustomRecord,\n        )\n        r = await ps.fetchrow()\n        self.assertIsInstance(r, CustomRecord)\n\n        r = await ps.fetch()\n        self.assertIsInstance(r[0], CustomRecord)\n\n    @tb.with_connection_options(record_class=CustomRecord)\n    async def test_record_subclass_04(self):\n        r = await self.con.fetchrow(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=asyncpg.Record,\n        )\n        self.assertIs(type(r), asyncpg.Record)\n\n        r = await self.con.fetch(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=asyncpg.Record,\n        )\n        self.assertIs(type(r[0]), asyncpg.Record)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=asyncpg.Record,\n            )\n            r = await cur.fetchrow()\n            self.assertIs(type(r), asyncpg.Record)\n\n            cur = await self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=asyncpg.Record,\n            )\n            r = await cur.fetch(1)\n            self.assertIs(type(r[0]), asyncpg.Record)\n\n        async with self.con.transaction():\n            cur = self.con.cursor(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=asyncpg.Record,\n            )\n            async for r in cur:\n                self.assertIs(type(r), asyncpg.Record)\n\n        ps = await self.con.prepare(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=asyncpg.Record,\n        )\n        r = await ps.fetchrow()\n        self.assertIs(type(r), asyncpg.Record)\n\n        r = await ps.fetch()\n        self.assertIs(type(r[0]), asyncpg.Record)\n\n    async def test_record_subclass_05(self):\n        class MyRecord(asyncpg.Record):\n            pass\n\n        r = await self.con.fetchrow(\n            \"SELECT 1 as a, '2' as b\",\n            record_class=MyRecord,\n        )\n        self.assertIsInstance(r, MyRecord)\n        self.assertEqual(repr(r), \"<MyRecord a=1 b='2'>\")\n\n        self.assertEqual(list(r.items()), [('a', 1), ('b', '2')])\n        self.assertEqual(list(r.keys()), ['a', 'b'])\n        self.assertEqual(list(r.values()), [1, '2'])\n        self.assertIn('b', r)\n        self.assertEqual(next(iter(r)), 1)\n\n    async def test_record_subclass_06(self):\n        class MyRecord(asyncpg.Record):\n            def __init__(self):\n                raise AssertionError('this is not supposed to be called')\n\n        class MyRecord2(asyncpg.Record):\n            def __new__(cls):\n                raise AssertionError('this is not supposed to be called')\n\n        class MyRecordBad:\n            pass\n\n        with self.assertRaisesRegex(\n            asyncpg.InterfaceError,\n            'record_class must not redefine __new__ or __init__',\n        ):\n            await self.con.fetchrow(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=MyRecord,\n            )\n\n        with self.assertRaisesRegex(\n            asyncpg.InterfaceError,\n            'record_class must not redefine __new__ or __init__',\n        ):\n            await self.con.fetchrow(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=MyRecord2,\n            )\n\n        with self.assertRaisesRegex(\n            asyncpg.InterfaceError,\n            'record_class is expected to be a subclass of asyncpg.Record',\n        ):\n            await self.con.fetchrow(\n                \"SELECT 1 as a, '2' as b\",\n                record_class=MyRecordBad,\n            )\n\n        with self.assertRaisesRegex(\n            asyncpg.InterfaceError,\n            'record_class is expected to be a subclass of asyncpg.Record',\n        ):\n            await self.connect(record_class=MyRecordBad)\n"
  },
  {
    "path": "tests/test_subinterpreters.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport textwrap\nimport threading\nimport unittest\n\ntry:\n    from concurrent import interpreters\nexcept ImportError:\n    pass\nelse:\n    class TestSubinterpreters(unittest.TestCase):\n        def test_record_module_loads_in_subinterpreter(self) -> None:\n            def run_in_subinterpreter() -> None:\n                interp = interpreters.create()\n\n                try:\n                    code = textwrap.dedent(\"\"\"\\\n                    import asyncpg.protocol.record as record\n                    assert record.Record is not None\n                    \"\"\")\n                    interp.exec(code)\n                finally:\n                    interp.close()\n\n            thread = threading.Thread(target=run_in_subinterpreter)\n            thread.start()\n            thread.join()\n\n        def test_record_module_state_isolation(self) -> None:\n            import asyncpg.protocol.record\n\n            main_record_id = id(asyncpg.protocol.record.Record)\n\n            def run_in_subinterpreter() -> None:\n                interp = interpreters.create()\n\n                try:\n                    code = textwrap.dedent(f\"\"\"\\\n                    import asyncpg.protocol.record as record\n\n                    sub_record_id = id(record.Record)\n                    main_id = {main_record_id}\n\n                    assert sub_record_id != main_id, (\n                        f\"Record type objects are the same: \"\n                        f\"{{sub_record_id}} == {{main_id}}. \"\n                        f\"This indicates shared global state.\"\n                    )\n                    \"\"\")\n                    interp.exec(code)\n                finally:\n                    interp.close()\n\n            thread = threading.Thread(target=run_in_subinterpreter)\n            thread.start()\n            thread.join()\n\n\nif __name__ == \"__main__\":\n    _ = unittest.main()\n"
  },
  {
    "path": "tests/test_test.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\nimport types\nimport unittest\n\n\nfrom asyncpg import _testbase as tb\n\n\nclass BaseSimpleTestCase:\n\n    async def test_tests_zero_error(self):\n        await asyncio.sleep(0.01)\n        1 / 0\n\n\nclass TestTests(unittest.TestCase):\n\n    def test_tests_fail_1(self):\n        SimpleTestCase = types.new_class('SimpleTestCase',\n                                         (BaseSimpleTestCase, tb.TestCase))\n\n        suite = unittest.TestSuite()\n        suite.addTest(SimpleTestCase('test_tests_zero_error'))\n\n        result = unittest.TestResult()\n        suite.run(result)\n\n        self.assertIn('ZeroDivisionError', result.errors[0][1])\n\n\nclass TestHelpers(tb.TestCase):\n\n    async def test_tests_assertLoopErrorHandlerCalled_01(self):\n        with self.assertRaisesRegex(AssertionError, r'no message.*was logged'):\n            with self.assertLoopErrorHandlerCalled('aa'):\n                self.loop.call_exception_handler({'message': 'bb a bb'})\n\n        with self.assertLoopErrorHandlerCalled('aa'):\n            self.loop.call_exception_handler({'message': 'bbaabb'})\n"
  },
  {
    "path": "tests/test_timeout.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncio\n\nimport asyncpg\nfrom asyncpg import connection as pg_connection\nfrom asyncpg import _testbase as tb\n\n\nMAX_RUNTIME = 0.5\n\n\nclass TestTimeout(tb.ConnectedTestCase):\n\n    async def test_timeout_01(self):\n        for methname in {'fetch', 'fetchrow', 'fetchval', 'execute'}:\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                meth = getattr(self.con, methname)\n                await meth('select pg_sleep(10)', timeout=0.02)\n            self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n    async def test_timeout_02(self):\n        st = await self.con.prepare('select pg_sleep(10)')\n\n        for methname in {'fetch', 'fetchrow', 'fetchval'}:\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                meth = getattr(st, methname)\n                await meth(timeout=0.02)\n            self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n    async def test_timeout_03(self):\n        task = self.loop.create_task(\n            self.con.fetch('select pg_sleep(10)', timeout=0.2))\n        await asyncio.sleep(0.05)\n        task.cancel()\n        with self.assertRaises(asyncio.CancelledError), \\\n                self.assertRunUnder(MAX_RUNTIME):\n            await task\n        self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n    async def test_timeout_04(self):\n        st = await self.con.prepare('select pg_sleep(10)', timeout=0.1)\n        with self.assertRaises(asyncio.TimeoutError), \\\n                self.assertRunUnder(MAX_RUNTIME):\n            async with self.con.transaction():\n                async for _ in st.cursor(timeout=0.1):  # NOQA\n                    pass\n        self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n        st = await self.con.prepare('select pg_sleep(10)', timeout=0.1)\n        async with self.con.transaction():\n            cur = await st.cursor()\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                await cur.fetch(1, timeout=0.1)\n        self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n    async def test_timeout_05(self):\n        # Stress-test timeouts - try to trigger a race condition\n        # between a cancellation request to Postgres and next\n        # query (SELECT 1)\n        for _ in range(500):\n            with self.assertRaises(asyncio.TimeoutError):\n                await self.con.fetch('SELECT pg_sleep(1)', timeout=1e-10)\n            self.assertEqual(await self.con.fetch('SELECT 1'), [(1,)])\n\n    async def test_timeout_06(self):\n        async with self.con.transaction():\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                async for _ in self.con.cursor(   # NOQA\n                        'select pg_sleep(10)', timeout=0.1):\n                    pass\n        self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n        async with self.con.transaction():\n            cur = await self.con.cursor('select pg_sleep(10)')\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                await cur.fetch(1, timeout=0.1)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor('select pg_sleep(10)')\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                await cur.forward(1, timeout=1e-10)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor('select pg_sleep(10)')\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                await cur.fetchrow(timeout=0.1)\n\n        async with self.con.transaction():\n            cur = await self.con.cursor('select pg_sleep(10)')\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                await cur.fetchrow(timeout=0.1)\n\n            with self.assertRaises(asyncpg.InFailedSQLTransactionError):\n                await cur.fetch(1)\n\n        self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n    async def test_invalid_timeout(self):\n        for command_timeout in ('a', False, -1):\n            with self.subTest(command_timeout=command_timeout):\n                with self.assertRaisesRegex(ValueError,\n                                            'invalid command_timeout'):\n                    await self.connect(command_timeout=command_timeout)\n\n        # Note: negative timeouts are OK for method calls.\n        for methname in {'fetch', 'fetchrow', 'fetchval', 'execute'}:\n            for timeout in ('a', False):\n                with self.subTest(timeout=timeout):\n                    with self.assertRaisesRegex(ValueError, 'invalid timeout'):\n                        await self.con.execute('SELECT 1', timeout=timeout)\n\n\nclass TestConnectionCommandTimeout(tb.ConnectedTestCase):\n\n    @tb.with_connection_options(command_timeout=0.2)\n    async def test_command_timeout_01(self):\n        for methname in {'fetch', 'fetchrow', 'fetchval', 'execute'}:\n            with self.assertRaises(asyncio.TimeoutError), \\\n                    self.assertRunUnder(MAX_RUNTIME):\n                meth = getattr(self.con, methname)\n                await meth('select pg_sleep(10)')\n            self.assertEqual(await self.con.fetch('select 1'), [(1,)])\n\n\nclass SlowPrepareConnection(pg_connection.Connection):\n    \"\"\"Connection class to test timeouts.\"\"\"\n    async def _get_statement(self, query, timeout, **kwargs):\n        await asyncio.sleep(0.3)\n        return await super()._get_statement(query, timeout, **kwargs)\n\n\nclass TestTimeoutCoversPrepare(tb.ConnectedTestCase):\n\n    @tb.with_connection_options(connection_class=SlowPrepareConnection,\n                                command_timeout=0.3)\n    async def test_timeout_covers_prepare_01(self):\n        for methname in {'fetch', 'fetchrow', 'fetchval', 'execute'}:\n            with self.assertRaises(asyncio.TimeoutError):\n                meth = getattr(self.con, methname)\n                await meth('select pg_sleep($1)', 0.2)\n"
  },
  {
    "path": "tests/test_transaction.py",
    "content": "# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport asyncpg\n\nfrom asyncpg import _testbase as tb\n\n\nclass TestTransaction(tb.ConnectedTestCase):\n\n    async def test_transaction_regular(self):\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n        tr = self.con.transaction()\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        with self.assertRaises(ZeroDivisionError):\n            async with tr as with_tr:\n                self.assertIs(self.con._top_xact, tr)\n                self.assertTrue(self.con.is_in_transaction())\n\n                # We don't return the transaction object from __aenter__,\n                # to make it harder for people to use '.rollback()' and\n                # '.commit()' from within an 'async with' block.\n                self.assertIsNone(with_tr)\n\n                await self.con.execute('''\n                    CREATE TABLE mytab (a int);\n                ''')\n\n                1 / 0\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        with self.assertRaisesRegex(asyncpg.PostgresError,\n                                    '\"mytab\" does not exist'):\n            await self.con.prepare('''\n                SELECT * FROM mytab\n            ''')\n\n    async def test_transaction_nested(self):\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        tr = self.con.transaction()\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        with self.assertRaises(ZeroDivisionError):\n            async with tr:\n                self.assertIs(self.con._top_xact, tr)\n                self.assertTrue(self.con.is_in_transaction())\n\n                await self.con.execute('''\n                    CREATE TABLE mytab (a int);\n                ''')\n\n                async with self.con.transaction():\n                    self.assertIs(self.con._top_xact, tr)\n                    self.assertTrue(self.con.is_in_transaction())\n\n                    await self.con.execute('''\n                        INSERT INTO mytab (a) VALUES (1), (2);\n                    ''')\n\n                self.assertIs(self.con._top_xact, tr)\n                self.assertTrue(self.con.is_in_transaction())\n\n                with self.assertRaises(ZeroDivisionError):\n                    in_tr = self.con.transaction()\n                    async with in_tr:\n\n                        self.assertIs(self.con._top_xact, tr)\n                        self.assertTrue(self.con.is_in_transaction())\n\n                        await self.con.execute('''\n                            INSERT INTO mytab (a) VALUES (3), (4);\n                        ''')\n\n                        1 / 0\n\n                st = await self.con.prepare('SELECT * FROM mytab;')\n\n                recs = []\n                async for rec in st.cursor():\n                    recs.append(rec)\n\n                self.assertEqual(len(recs), 2)\n                self.assertEqual(recs[0][0], 1)\n                self.assertEqual(recs[1][0], 2)\n\n                self.assertIs(self.con._top_xact, tr)\n                self.assertTrue(self.con.is_in_transaction())\n\n                1 / 0\n\n        self.assertIs(self.con._top_xact, None)\n        self.assertFalse(self.con.is_in_transaction())\n\n        with self.assertRaisesRegex(asyncpg.PostgresError,\n                                    '\"mytab\" does not exist'):\n            await self.con.prepare('''\n                SELECT * FROM mytab\n            ''')\n\n    async def test_transaction_interface_errors(self):\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        tr = self.con.transaction(readonly=True, isolation='serializable')\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'cannot start; .* already started'):\n            async with tr:\n                await tr.start()\n\n        self.assertTrue(repr(tr).startswith(\n            '<asyncpg.Transaction state:rolledback serializable readonly'))\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'cannot start; .* already rolled back'):\n            async with tr:\n                pass\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        tr = self.con.transaction()\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'cannot manually commit.*async with'):\n            async with tr:\n                await tr.commit()\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        tr = self.con.transaction()\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'cannot manually rollback.*async with'):\n            async with tr:\n                await tr.rollback()\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        tr = self.con.transaction()\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'cannot enter context:.*async with'):\n            async with tr:\n                async with tr:\n                    pass\n\n    async def test_transaction_within_manual_transaction(self):\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n        await self.con.execute('BEGIN')\n\n        tr = self.con.transaction()\n        self.assertIsNone(self.con._top_xact)\n        self.assertTrue(self.con.is_in_transaction())\n\n        with self.assertRaisesRegex(asyncpg.InterfaceError,\n                                    'cannot use Connection.transaction'):\n            await tr.start()\n\n        with self.assertLoopErrorHandlerCalled(\n                'Resetting connection with an active transaction'):\n            await self.con.reset()\n\n        self.assertIsNone(self.con._top_xact)\n        self.assertFalse(self.con.is_in_transaction())\n\n    async def test_isolation_level(self):\n        await self.con.reset()\n        default_isolation = await self.con.fetchval(\n            'SHOW default_transaction_isolation'\n        )\n        isolation_levels = {\n            None: default_isolation,\n            'read_committed': 'read committed',\n            'read_uncommitted': 'read uncommitted',\n            'repeatable_read': 'repeatable read',\n            'serializable': 'serializable',\n        }\n        set_sql = 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL '\n        get_sql = 'SHOW TRANSACTION ISOLATION LEVEL'\n        for tx_level in isolation_levels:\n            for conn_level in isolation_levels:\n                with self.subTest(conn=conn_level, tx=tx_level):\n                    if conn_level:\n                        await self.con.execute(\n                            set_sql + isolation_levels[conn_level]\n                        )\n                    level = await self.con.fetchval(get_sql)\n                    self.assertEqual(level, isolation_levels[conn_level])\n                    async with self.con.transaction(isolation=tx_level):\n                        level = await self.con.fetchval(get_sql)\n                        self.assertEqual(\n                            level,\n                            isolation_levels[tx_level or conn_level],\n                        )\n                    await self.con.reset()\n\n    async def test_nested_isolation_level(self):\n        set_sql = 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL '\n        isolation_levels = {\n            'read_committed': 'read committed',\n            'read_uncommitted': 'read uncommitted',\n            'repeatable_read': 'repeatable read',\n            'serializable': 'serializable',\n        }\n        for inner in [None] + list(isolation_levels):\n            for outer, outer_sql_level in isolation_levels.items():\n                for implicit in [False, True]:\n                    with self.subTest(\n                        implicit=implicit, outer=outer, inner=inner,\n                    ):\n                        if implicit:\n                            await self.con.execute(set_sql + outer_sql_level)\n                            outer_level = None\n                        else:\n                            outer_level = outer\n\n                        async with self.con.transaction(isolation=outer_level):\n                            if inner and outer != inner:\n                                with self.assertRaisesRegex(\n                                    asyncpg.InterfaceError,\n                                    'current {!r} != outer {!r}'.format(\n                                        inner, outer\n                                    )\n                                ):\n                                    async with self.con.transaction(\n                                            isolation=inner,\n                                    ):\n                                        pass\n                            else:\n                                async with self.con.transaction(\n                                        isolation=inner,\n                                ):\n                                    pass\n"
  },
  {
    "path": "tests/test_types.py",
    "content": "# Copyright (C) 2016-present the ayncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\nfrom itertools import product\n\nfrom asyncpg.types import Range\nfrom asyncpg import _testbase as tb\n\n\nclass TestTypes(tb.TestCase):\n\n    def test_range_issubset(self):\n        subs = [\n            Range(empty=True),\n            Range(lower=1, upper=5, lower_inc=True, upper_inc=False),\n            Range(lower=1, upper=5, lower_inc=True, upper_inc=True),\n            Range(lower=1, upper=5, lower_inc=False, upper_inc=True),\n            Range(lower=1, upper=5, lower_inc=False, upper_inc=False),\n            Range(lower=-5, upper=10),\n            Range(lower=2, upper=3),\n            Range(lower=1, upper=None),\n            Range(lower=None, upper=None)\n        ]\n\n        sups = [\n            Range(empty=True),\n            Range(lower=1, upper=5, lower_inc=True, upper_inc=False),\n            Range(lower=1, upper=5, lower_inc=True, upper_inc=True),\n            Range(lower=1, upper=5, lower_inc=False, upper_inc=True),\n            Range(lower=1, upper=5, lower_inc=False, upper_inc=False),\n            Range(lower=None, upper=None)\n        ]\n\n        # Each row is 1 subs with all sups\n        results = [\n            True, True, True, True, True, True,\n            False, True, True, False, False, True,\n            False, False, True, False, False, True,\n            False, False, True, True, False, True,\n            False, True, True, True, True, True,\n            False, False, False, False, False, True,\n            False, True, True, True, True, True,\n            False, False, False, False, False, True,\n            False, False, False, False, False, True\n        ]\n\n        for (sub, sup), res in zip(product(subs, sups), results):\n            self.assertIs(\n                sub.issubset(sup), res, \"Sub:{}, Sup:{}\".format(sub, sup)\n            )\n            self.assertIs(\n                sup.issuperset(sub), res, \"Sub:{}, Sup:{}\".format(sub, sup)\n            )\n"
  },
  {
    "path": "tests/test_utils.py",
    "content": "# Copyright (C) 2016-present the ayncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport datetime\n\nfrom asyncpg import utils\nfrom asyncpg import _testbase as tb\n\n\nclass TestUtils(tb.ConnectedTestCase):\n\n    async def test_mogrify_simple(self):\n        cases = [\n            ('timestamp',\n                datetime.datetime(2016, 10, 10),\n                \"SELECT '2016-10-10 00:00:00'::timestamp\"),\n            ('int[]',\n                [[1, 2], [3, 4]],\n                \"SELECT '{{1,2},{3,4}}'::int[]\"),\n        ]\n\n        for typename, data, expected in cases:\n            with self.subTest(value=data, type=typename):\n                mogrified = await utils._mogrify(\n                    self.con, 'SELECT $1::{}'.format(typename), [data])\n                self.assertEqual(mogrified, expected)\n\n    async def test_mogrify_multiple(self):\n        mogrified = await utils._mogrify(\n            self.con, 'SELECT $1::int, $2::int[]',\n            [1, [2, 3, 4, 5]])\n        expected = \"SELECT '1'::int, '{2,3,4,5}'::int[]\"\n        self.assertEqual(mogrified, expected)\n"
  },
  {
    "path": "tools/generate_exceptions.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport argparse\nimport builtins\nimport re\nimport string\nimport textwrap\n\nfrom asyncpg.exceptions import _base as apg_exc\n\n\n_namemap = {\n    '08001': 'ClientCannotConnectError',\n    '08004': 'ConnectionRejectionError',\n    '08006': 'ConnectionFailureError',\n    '38002': 'ModifyingExternalRoutineSQLDataNotPermittedError',\n    '38003': 'ProhibitedExternalRoutineSQLStatementAttemptedError',\n    '38004': 'ReadingExternalRoutineSQLDataNotPermittedError',\n    '39004': 'NullValueInExternalRoutineNotAllowedError',\n    '42000': 'SyntaxOrAccessError',\n    'XX000': 'InternalServerError',\n}\n\n\n_subclassmap = {\n    # Special subclass of FeatureNotSupportedError\n    # raised by Postgres in RevalidateCachedQuery.\n    '0A000': ['InvalidCachedStatementError']\n}\n\n\ndef _get_error_name(sqlstatename, msgtype, sqlstate):\n    if sqlstate in _namemap:\n        return _namemap[sqlstate]\n\n    parts = string.capwords(sqlstatename.replace('_', ' ')).split(' ')\n    if parts[-1] in {'Exception', 'Failure'}:\n        parts[-1] = 'Error'\n\n    if parts[-1] != 'Error' and msgtype != 'W':\n        parts.append('Error')\n\n    for i, part in enumerate(parts):\n        if part == 'Fdw':\n            parts[i] = 'FDW'\n        elif part == 'Io':\n            parts[i] = 'IO'\n        elif part == 'Plpgsql':\n            parts[i] = 'PLPGSQL'\n        elif part == 'Sql':\n            parts[i] = 'SQL'\n\n    errname = ''.join(parts)\n\n    if hasattr(builtins, errname):\n        errname = 'Postgres' + errname\n\n    return errname\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='generate _exceptions.py from postgres/errcodes.txt')\n    parser.add_argument('errcodesfile', type=str,\n                        help='path to errcodes.txt in PostgreSQL source')\n\n    args = parser.parse_args()\n\n    with open(args.errcodesfile, 'r') as errcodes_f:\n        errcodes = errcodes_f.read()\n\n    section_re = re.compile(r'^Section: .*')\n\n    tpl = \"\"\"\\\nclass {clsname}({base}):\n    {docstring}{sqlstate}\"\"\"\n\n    new_section = True\n    section_class = None\n\n    buf = '# GENERATED FROM postgresql/src/backend/utils/errcodes.txt\\n' + \\\n          '# DO NOT MODIFY, use tools/generate_exceptions.py to update\\n\\n' + \\\n          'from ._base import *  # NOQA\\nfrom . import _base\\n\\n\\n'\n\n    classes = []\n    clsnames = set()\n\n    def _add_class(clsname, base, sqlstate, docstring):\n        if sqlstate:\n            sqlstate = \"sqlstate = '{}'\".format(sqlstate)\n        else:\n            sqlstate = ''\n\n        txt = tpl.format(clsname=clsname, base=base, sqlstate=sqlstate,\n                         docstring=docstring)\n\n        if not sqlstate and not docstring:\n            txt += 'pass'\n\n        if len(txt.splitlines()[0]) > 79:\n            txt = txt.replace('(', '(\\n        ', 1)\n\n        classes.append(txt)\n        clsnames.add(clsname)\n\n    for line in errcodes.splitlines():\n        if not line.strip() or line.startswith('#'):\n            continue\n\n        if section_re.match(line):\n            new_section = True\n            continue\n\n        parts = re.split(r'\\s+', line)\n\n        if len(parts) < 4:\n            continue\n\n        sqlstate = parts[0]\n        msgtype = parts[1]\n        name = parts[3]\n\n        clsname = _get_error_name(name, msgtype, sqlstate)\n\n        if clsname in {'SuccessfulCompletionError'}:\n            continue\n\n        if clsname in clsnames:\n            raise ValueError(\n                'duplicate exception class name: {}'.format(clsname))\n\n        if new_section:\n            section_class = clsname\n            if clsname == 'PostgresWarning':\n                base = '_base.PostgresLogMessage, Warning'\n            else:\n                if msgtype == 'W':\n                    base = 'PostgresWarning'\n                else:\n                    base = '_base.PostgresError'\n\n            new_section = False\n        else:\n            base = section_class\n\n        existing = apg_exc.PostgresMessageMeta.get_message_class_for_sqlstate(\n            sqlstate)\n\n        if (existing and existing is not apg_exc.UnknownPostgresError and\n                existing.__doc__):\n            docstring = '\"\"\"{}\"\"\"\\n\\n    '.format(existing.__doc__)\n        else:\n            docstring = ''\n\n        _add_class(clsname=clsname, base=base, sqlstate=sqlstate,\n                   docstring=docstring)\n\n        subclasses = _subclassmap.get(sqlstate, [])\n        for subclass in subclasses:\n            existing = getattr(apg_exc, subclass, None)\n            if existing and existing.__doc__:\n                docstring = '\"\"\"{}\"\"\"\\n\\n    '.format(existing.__doc__)\n            else:\n                docstring = ''\n\n            _add_class(clsname=subclass, base=clsname, sqlstate=None,\n                       docstring=docstring)\n\n    buf += '\\n\\n\\n'.join(classes)\n\n    _all = textwrap.wrap(', '.join('{!r}'.format(c) for c in sorted(clsnames)))\n    buf += '\\n\\n\\n__all__ = (\\n    {}\\n)'.format(\n        '\\n    '.join(_all))\n\n    buf += '\\n\\n__all__ += _base.__all__'\n\n    print(buf)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "tools/generate_type_map.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2016-present the asyncpg authors and contributors\n# <see AUTHORS file>\n#\n# This module is part of asyncpg and is released under\n# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0\n\n\nimport argparse\nimport asyncio\n\nimport asyncpg\n\n\n# Array types with builtin codecs, necessary for codec\n# bootstrap to work\n#\n_BUILTIN_ARRAYS = ('_text', '_oid')\n\n_INVALIDOID = 0\n\n# postgresql/src/include/access/transam.h: FirstBootstrapObjectId\n_MAXBUILTINOID = 10000 - 1\n\n# A list of alternative names for builtin types.\n_TYPE_ALIASES = {\n    'smallint': 'int2',\n    'int': 'int4',\n    'integer': 'int4',\n    'bigint': 'int8',\n    'decimal': 'numeric',\n    'real': 'float4',\n    'double precision': 'float8',\n    'timestamp with timezone': 'timestamptz',\n    'timestamp without timezone': 'timestamp',\n    'time with timezone': 'timetz',\n    'time without timezone': 'time',\n    'char': 'bpchar',\n    'character': 'bpchar',\n    'character varying': 'varchar',\n    'bit varying': 'varbit'\n}\n\n\nasync def runner(args):\n    conn = await asyncpg.connect(host=args.pghost, port=args.pgport,\n                                 user=args.pguser)\n\n    buf = (\n        '# Copyright (C) 2016-present the asyncpg authors and contributors\\n'\n        '# <see AUTHORS file>\\n'\n        '#\\n'\n        '# This module is part of asyncpg and is released under\\n'\n        '# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0'\n        '\\n\\n\\n'\n        '# GENERATED FROM pg_catalog.pg_type\\n' +\n        '# DO NOT MODIFY, use tools/generate_type_map.py to update\\n\\n' +\n        'DEF INVALIDOID = {}\\n'.format(_INVALIDOID) +\n        'DEF MAXBUILTINOID = {}\\n'.format(_MAXBUILTINOID)\n    )\n\n    pg_types = await conn.fetch('''\n        SELECT\n            oid,\n            typname\n        FROM\n            pg_catalog.pg_type\n        WHERE\n            typtype IN ('b', 'p')\n            AND (typelem = 0 OR typname = any($1) OR typlen > 0)\n            AND oid <= $2\n        ORDER BY\n            oid\n    ''', _BUILTIN_ARRAYS, _MAXBUILTINOID)\n\n    defs = []\n    typemap = {}\n    array_types = []\n\n    for pg_type in pg_types:\n        typeoid = pg_type['oid']\n        typename = pg_type['typname']\n\n        defname = '{}OID'.format(typename.upper())\n        defs.append('DEF {name} = {oid}'.format(name=defname, oid=typeoid))\n\n        if typename in _BUILTIN_ARRAYS:\n            array_types.append(defname)\n            typename = typename[1:] + '[]'\n\n        typemap[defname] = typename\n\n    buf += 'DEF MAXSUPPORTEDOID = {}\\n\\n'.format(pg_types[-1]['oid'])\n\n    buf += '\\n'.join(defs)\n\n    buf += '\\n\\ncdef ARRAY_TYPES = ({},)'.format(', '.join(array_types))\n\n    f_typemap = ('{}: {!r}'.format(dn, n) for dn, n in sorted(typemap.items()))\n    buf += '\\n\\nBUILTIN_TYPE_OID_MAP = {{\\n    {}\\n}}'.format(\n        ',\\n    '.join(f_typemap))\n    buf += ('\\n\\nBUILTIN_TYPE_NAME_MAP = ' +\n            '{v: k for k, v in BUILTIN_TYPE_OID_MAP.items()}')\n\n    for k, v in _TYPE_ALIASES.items():\n        buf += ('\\n\\nBUILTIN_TYPE_NAME_MAP[{!r}] = \\\\\\n    '\n                'BUILTIN_TYPE_NAME_MAP[{!r}]'.format(k, v))\n\n    print(buf)\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='generate protocol/pgtypes.pxi from pg_catalog.pg_types')\n    parser.add_argument(\n        '--pghost', type=str, default='127.0.0.1',\n        help='PostgreSQL server host')\n    parser.add_argument(\n        '--pgport', type=int, default=5432,\n        help='PostgreSQL server port')\n    parser.add_argument(\n        '--pguser', type=str, default='postgres',\n        help='PostgreSQL server user')\n\n    args = parser.parse_args()\n    asyncio.run(runner(args))\n\n\nif __name__ == '__main__':\n    main()\n"
  }
]