[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      # Check for updates to GitHub Actions every weekday\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/announce.yml",
    "content": "name: Announce Release\non:\n  release:\n    types: [published]\nenv:\n  VER: ${{ github.ref_name }}\n  AUTHOR_NAME: Kenneth Shaw\n  AUTHOR_EMAIL: kenshaw@gmail.com\n  HOMEBREW_REPO: https://kenshaw:${{ secrets.HOMEBREW_TOKEN }}@github.com/xo/homebrew-xo.git\n\n# cribbed from https://github.com/actions/runner/issues/691\njobs:\n  configure:\n    name: Configure\n    runs-on: ubuntu-latest\n    outputs:\n      username: ${{ steps.get-user.outputs.username }}\n    steps:\n      - id: get-user\n        name: Get User\n        run: echo \"username=$(id -n -u)\" >> $GITHUB_OUTPUT\n\n  bump-aur-package:\n    name: Bump AUR Package\n    runs-on: ubuntu-latest\n    needs: configure\n    container:\n      image: docker.io/library/archlinux:latest\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - package: usql\n            repo: aur@aur.archlinux.org:usql.git\n          - package: usql-bin\n            repo: aur@aur.archlinux.org:usql-bin.git\n\n    steps:\n      - name: Setup\n        run: |\n          export USERNAME=${{ needs.configure.outputs.username }}\n          pacman-key --init\n          pacman -Sy --noconfirm archlinux-keyring\n          pacman -Sy --noconfirm git base-devel sudo pacman-contrib devtools\n          useradd -m $USERNAME\n          echo \"${USERNAME} ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers\n\n      - name: Add AUR SSH key\n        uses: shimataro/ssh-key-action@v2\n        with:\n          key: ${{ secrets.AUR_SSH_KEY }}\n          name: id_ed25519\n          known_hosts: |\n            aur.archlinux.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEuBKrPzbawxA/k2g6NcyV5jmqwJ2s+zpgZGZ7tpLIcN\n            aur.archlinux.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDKF9vAFWdgm9Bi8uc+tYRBmXASBb5cB5iZsB7LOWWFeBrLp3r14w0/9S2vozjgqY5sJLDPONWoTTaVTbhe3vwO8CBKZTEt1AcWxuXNlRnk9FliR1/eNB9uz/7y1R0+c1Md+P98AJJSJWKN12nqIDIhjl2S1vOUvm7FNY43fU2knIhEbHybhwWeg+0wxpKwcAd/JeL5i92Uv03MYftOToUijd1pqyVFdJvQFhqD4v3M157jxS5FTOBrccAEjT+zYmFyD8WvKUa9vUclRddNllmBJdy4NyLB8SvVZULUPrP3QOlmzemeKracTlVOUG1wsDbxknF1BwSCU7CmU6UFP90kpWIyz66bP0bl67QAvlIc52Yix7pKJPbw85+zykvnfl2mdROsaT8p8R9nwCdFsBc9IiD0NhPEHcyHRwB8fokXTajk2QnGhL+zP5KnkmXnyQYOCUYo3EKMXIlVOVbPDgRYYT/XqvBuzq5S9rrU70KoI/S5lDnFfx/+lPLdtcnnEPk=\n            aur.archlinux.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLMiLrP8pVi5BFX2i3vepSUnpedeiewE5XptnUnau+ZoeUOPkpoCgZZuYfpaIQfhhJJI5qgnjJmr4hyJbe/zxow=\n\n      - name: Bump AUR Package (${{ matrix.package }})\n        run: |\n          export USERNAME=${{ needs.configure.outputs.username }}\n          export PACKAGE=${{ matrix.package }}\n          export REPO=${{ matrix.repo }}\n          export WORKDIR=$(mktemp -d /tmp/${PACKAGE}.XXXXXX)\n          export REPO_PATH=$WORKDIR/${PACKAGE}\n          export CHANGELOG=$(\n            curl \\\n              -s \\\n              -H 'Accept: application/vnd.github+json' \\\n              https://api.github.com/repos/xo/usql/releases/tags/$VER \\\n              |jq -r .body \\\n              |sed -e 's/\\\\r//g' -e 's/\\[VirusTotal.*//'\n          )\n          git clone $REPO $REPO_PATH\n          git config --global --add safe.directory $REPO_PATH\n          git -C $REPO_PATH config user.name \"$AUTHOR_NAME\"\n          git -C $REPO_PATH config user.email \"$AUTHOR_EMAIL\"\n          sed -i \"s/pkgver=.*$/pkgver=${VER#v}/\" $REPO_PATH/PKGBUILD\n          sed -i \"s/sha256sums\\\\([^=]*\\\\)=.*$/sha256sums\\\\1=('SKIP')/\" $REPO_PATH/PKGBUILD\n          sed -i \"s/pkgrel=.*$/pkgrel=1/\" $REPO_PATH/PKGBUILD\n          chown -R ${USERNAME}:${USERNAME} $WORKDIR\n          pushd $REPO_PATH &> /dev/null\n          sudo -u ${USERNAME} bash -c 'updpkgsums'\n          sudo -u ${USERNAME} bash -c 'makepkg --printsrcinfo > .SRCINFO'\n          popd &> /dev/null\n          git -C $REPO_PATH add PKGBUILD .SRCINFO\n          git -C $REPO_PATH commit -m \"$(printf %b \"Update usql version to ${VER}\\n\\n${CHANGELOG}\")\"\n          git -C $REPO_PATH show -C\n          git -C $REPO_PATH push origin master\n\n  bump-homebrew-formula:\n    name: Bump Homebrew Formula\n    runs-on: ubuntu-latest\n    steps:\n      - name: Bump Homebrew Formula\n        run: |\n          export WORKDIR=$(mktemp -d /tmp/homebrew-xo.XXXXXX)\n          export REPO_PATH=$WORKDIR/homebrew-xo\n          wget -O $WORKDIR/archive.tar.gz https://github.com/xo/usql/archive/${VER}.tar.gz\n          export SHA256SUM=$(sha256sum $WORKDIR/archive.tar.gz|awk '{print $1}')\n          export CHANGELOG=$(\n            curl \\\n              -s \\\n              -H 'Accept: application/vnd.github+json' \\\n              https://api.github.com/repos/xo/usql/releases/tags/$VER \\\n              |jq -r .body \\\n              |sed -e 's/\\\\r//g' -e 's/\\[VirusTotal.*//'\n          )\n          git clone $HOMEBREW_REPO $REPO_PATH\n          git -C $REPO_PATH config user.name \"$AUTHOR_NAME\"\n          git -C $REPO_PATH config user.email \"$AUTHOR_EMAIL\"\n          sed -i \"s%url \\\".*$%url \\\"https://github.com/xo/usql/archive/${VER}.tar.gz\\\"%\" $REPO_PATH/Formula/usql.rb\n          sed -i \"s/sha256 \\\".*$/sha256 \\\"$SHA256SUM\\\"/\" $REPO_PATH/Formula/usql.rb\n          git -C $REPO_PATH add Formula/usql.rb\n          git -C $REPO_PATH commit -m \"$(printf %b \"Update usql version to ${VER}\\n\\n${CHANGELOG}\")\"\n          git -C $REPO_PATH show -C\n          git -C $REPO_PATH push origin master\n\n  announce-discord:\n    name: Announce Discord\n    runs-on: ubuntu-latest\n    steps:\n      - name: Announce Discord\n        run: |\n          curl \\\n            -H 'Content-Type: application/json' \\\n            -d '{\"username\": \"usql\", \"content\": \"> *usql ${{ github.ref_name }}* has been released!\\n\\nGet it here: https://github.com/xo/usql/releases/${{ github.ref_name }}\"}' \\\n            ${{ secrets.DISCORD_WEBHOOK_URL }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non: push\nenv:\n  APP: usql\n  VER: ${{ github.ref_name }}\n  GO_VERSION: stable\n\njobs:\n  build_for_linux:\n    name: Build for Linux\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        arch: [amd64, arm, arm64]\n    steps:\n      - name: Install build dependencies\n        run: |\n          sudo apt-get -qq update\n          sudo apt-get install -y \\\n            build-essential \\\n            qemu-user \\\n            gcc-arm-linux-gnueabihf \\\n            g++-arm-linux-gnueabihf \\\n            gcc-aarch64-linux-gnu \\\n            g++-aarch64-linux-gnu \\\n            libstdc++6-armhf-cross \\\n            libstdc++6-arm64-cross \\\n            libc6-dev-armhf-cross \\\n            libc6-dev-arm64-cross \\\n            file\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Setup Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: ${{ env.GO_VERSION }}\n      - name: Build ${{ matrix.arch }}\n        run: |\n          ./build.sh -v $VER -a ${{ matrix.arch }}\n      - name: Build ${{ matrix.arch }} (static)\n        if: matrix.arch != 'arm'\n        run: |\n          ./build.sh -v $VER -a ${{ matrix.arch }} -s\n      - name: Archive artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: dist-linux-${{ matrix.arch }}\n          path: build/linux/**/*\n          if-no-files-found: error\n\n  build_for_macos:\n    name: Build for macOS\n    runs-on: macos-latest\n    strategy:\n      matrix:\n        arch: [amd64, arm64]\n    steps:\n      - name: Install build dependencies\n        run: |\n          brew install coreutils\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Setup Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: ${{ env.GO_VERSION }}\n      - name: Build ${{ matrix.arch }}\n        run: |\n          ./build.sh -v $VER -a ${{ matrix.arch }}\n      - name: Archive artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: dist-darwin-${{ matrix.arch }}\n          path: build/darwin/**/*\n          if-no-files-found: error\n\n  build_for_macos_universal:\n    name: Build for macOS (universal)\n    needs:\n      - build_for_macos\n    runs-on: macos-latest\n    steps:\n      - name: Install build dependencies\n        run: |\n          brew install coreutils\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n      - name: Build universal\n        run: |\n          if [ \"$VER\" = \"master\" ]; then\n            VER=0.0.0-dev\n          fi\n\n          export WORKDIR=$PWD/build/darwin/universal/$VER\n          mkdir -p $WORKDIR\n\n          gtar -jxvf dist-darwin-amd64/*/*/*.tar.bz2 -C $WORKDIR $APP\n          gtar -jxvf dist-darwin-amd64/*/*/*.tar.bz2 -C $WORKDIR LICENSE\n          mv $WORKDIR/$APP $WORKDIR/$APP-amd64\n\n          gtar -jxvf dist-darwin-arm64/*/*/*.tar.bz2 -C $WORKDIR $APP\n          mv $WORKDIR/$APP $WORKDIR/$APP-arm64\n\n          file $WORKDIR/$APP-{amd64,arm64}\n\n          lipo -create -output $WORKDIR/$APP $WORKDIR/$APP-amd64 $WORKDIR/$APP-arm64\n          chmod +x $WORKDIR/$APP\n          file $WORKDIR/$APP\n\n          rm $WORKDIR/$APP-{amd64,arm64}\n\n          sudo /usr/sbin/purge\n\n          gtar -C $WORKDIR -cjf $WORKDIR/$APP-${VER#v}-darwin-universal.tar.bz2 $APP LICENSE\n          ls -alh $WORKDIR/*\n          sha256sum $WORKDIR/*\n      - name: Archive artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: dist-darwin-universal\n          path: build/darwin/**/*\n          if-no-files-found: error\n\n  build_for_windows:\n    name: Build for Windows\n    runs-on: windows-latest\n    steps:\n      - name: Install build dependencies\n        run: choco install zip\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Setup Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: ${{ env.GO_VERSION }}\n      - name: Build amd64\n        shell: bash\n        run: |\n          ./build.sh -v $VER\n      - name: Archive artifacts\n        uses: actions/upload-artifact@v4\n        with:\n          name: dist-windows\n          path: build/windows/**/*\n          if-no-files-found: error\n\n  draft_release:\n    name: Draft Release\n    needs:\n      - build_for_linux\n      - build_for_macos\n      - build_for_macos_universal\n      - build_for_windows\n    runs-on: ubuntu-latest\n    steps:\n      - name: Download artifacts\n        uses: actions/download-artifact@v4\n      - name: Extract artifacts\n        run: |\n          mkdir /tmp/scan\n          mkdir scan\n          for i in $(find dist-* -name \\*.tar.bz2); do\n            name=$(basename $i|cut -d- -f1)\n            ver=$(sed -e 's/\\.tar\\.bz2$//' <<< $(basename $i)|cut -d- -f2-)\n            echo \"extracting $i ($name $ver)\"\n            tar -C /tmp/scan -jv -f $i -x $name\n            hash=$(sha256sum /tmp/scan/$name|awk '{print $1}')\n            mv /tmp/scan/$name ./scan/$name-$ver-${hash:0:8}\n          done\n          for i in $(find dist-* -name \\*.zip); do\n            name=$(basename $i|cut -d- -f1)\n            ver=$(sed -e 's/\\.zip$//' <<< $(basename $i)|cut -d- -f2-)\n            echo \"extracting $i ($name $ver)\"\n            unzip -d /tmp/scan $i $name.exe\n            hash=$(sha256sum /tmp/scan/$name.exe|awk '{print $1}')\n            mv /tmp/scan/$name.exe ./scan/$name-$ver-${hash:0:8}.exe\n          done\n          file ./scan/*\n          sha256sum ./scan/*\n      - name: Submit to VirusTotal\n        id: virustotal\n        uses: crazy-max/ghaction-virustotal@v4\n        with:\n          vt_api_key: ${{ secrets.VIRUSTOTAL_API_KEY }}\n          request_rate: 4\n          files: |\n            ./scan/*\n      - name: Generate Release Notes\n        id: generate_release_notes\n        uses: softprops/action-gh-release@v2\n        if: startsWith(github.ref, 'refs/tags/v')\n        with:\n          name: ${{ env.APP }} ${{ env.VER }}\n          draft: true\n          generate_release_notes: true\n          files: |\n            dist-*/*/*/*.tar.bz2\n            dist-*/*/*/*.zip\n      - name: Add VirusTotal Info to Release Notes\n        if: startsWith(github.ref, 'refs/tags/v')\n        run: |\n          # github api url\n          url=https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/${{ steps.generate_release_notes.outputs.id }}\n          echo \"url: $url\"\n\n          # get release notes\n          release=$(\n            curl \\\n              -s \\\n              -L \\\n              -H \"Accept: application/vnd.github+json\" \\\n              -H \"Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}\" \\\n              -H \"X-GitHub-Api-Version: 2022-11-28\" \\\n              $url\n          )\n          tag_name=$(jq -r .tag_name <<< \"$release\")\n          target_commitish=$(jq -r .target_commitish <<< \"$release\")\n          body=$(jq -r .body <<< \"$release\")\n\n          echo \"tag_name: $tag_name target_commitish: $target_commitish\"\n\n          # append virustotal details to release notes\n          nl=$'\\n'\n          body+=\"$nl$nl[VirusTotal](https://www.virustotal.com) analysis:$nl\"\n          while read -r -d, line; do\n            name=$(sed -e 's/^\\.\\/scan\\/\\([^=]\\+\\)=.*/\\1/' <<< \"$line\")\n            vturl=$(sed -e 's/.*=\\(https.*\\)/\\1/' <<< \"$line\")\n            body+=\"* [$name]($vturl)$nl\"\n          done <<< \"${{ steps.virustotal.outputs.analysis }},\"\n\n          echo -e \"body:\\n$body\"\n\n          # update release notes\n          export tag_name target_commitish body\n          curl \\\n            -s \\\n            -L \\\n            -X PATCH \\\n            -H \"Accept: application/vnd.github+json\" \\\n            -H \"Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}\" \\\n            -H \"X-GitHub-Api-Version: 2022-11-28\" \\\n            --variable '%tag_name' \\\n            --varable '%target_commitish' \\\n            --variable '%body' \\\n            --expand-data '{\"tag_name\": \"{{tag_name:json}}\", \"body\": \"{{body:trim:json}}\"}' \\\n            $url\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "on: [push, pull_request]\nname: Test usql\njobs:\n  test:\n    name: Build and Test usql\n    runs-on: ubuntu-latest\n    services:\n      cassandra:\n        image: docker.io/usql/cassandra:latest\n        ports:\n          - 9042:9042\n      postgres:\n        image: docker.io/usql/postgres:latest\n        env:\n          POSTGRES_PASSWORD: P4ssw0rd\n        ports:\n          - 5432:5432\n      mysql:\n        image: docker.io/library/mariadb\n        env:\n          MYSQL_ROOT_PASSWORD: P4ssw0rd\n        ports:\n          - 3306:3306\n      sqlserver:\n        image: mcr.microsoft.com/mssql/server:2022-latest\n        env:\n          ACCEPT_EULA: Y\n          MSSQL_PID: Express\n          SA_PASSWORD: Adm1nP@ssw0rd\n        ports:\n          - 1433:1433\n    steps:\n      - name: Install Go\n        uses: actions/setup-go@v5\n        with:\n          go-version: stable\n      - name: Install Packages\n        run: |\n          sudo apt-get -qq update\n          sudo apt-get install -y build-essential libicu-dev unixodbc unixodbc-dev\n      - name: Checkout code\n        uses: actions/checkout@v4\n      - name: Unit Tests\n        run: |\n          go test -v ./stmt\n      - name: Build with all drivers\n        run: |\n          ./build.sh -b -t all\n      - name: Shell Tests\n        run: |\n          go run testcli.go &> output.log\n          ls -alh output.log\n      - name: Archive output\n        uses: actions/upload-artifact@v4\n        if: always()\n        with:\n          name: output\n          path: output.log\n          if-no-files-found: error\n"
  },
  {
    "path": ".gitignore",
    "content": "/usql\n/usql.exe\n/build/\n/coverage.out\n/*.sql\n/*.txt\n\n.usql_history*\n.[a-f0-9]*\n\n*.ini\n*.csv\n*.db\n*.zip\n*.out\n\n*.sqlite3\n*.sqlite3-journal\n*.duckdb\n*.wal\n\n/instantclient*\n/*.pc\n\n.vscode/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing to usql\n====================\n\nAny contributions are welcome. If you found a bug, or a missing feature,\ntake a look at existing [issues](https://github.com/xo/usql/issues)\nand create a new one if needed.\n\nYou can also open up a [pull request](https://github.com/xo/usql/pulls) (PR)\nwith code or documentation changes.\n\n# Adding a new driver\n\n1. Add a new schema in [dburl](https://github.com/xo/dburl).\n1. Create a new go package in `drivers`. It should have an `init()` function, that would call `drivers.Register()`.\n1. Regenerate code in the `internal` package by running `internal/gen.sh`.\n1. Add any new required modules using `go get` or by editing `go.mod` manually and running `go mod tidy`.\n1. Run all tests, build `usql` and see if the new driver works.\n1. Update `README.md`.\n\n> Tip: check out closed PRs for examples, and/or search the codebase\nfor names of databases you're familiar with.\n\n# Enabling metadata introspection for a driver\n\nFor `\\d*` commands to work, `usql` needs to know how to read the structure of a database.\nA driver must provide a metadata reader, by setting the `NewMetadataReader` property\nin the `drivers.Driver` structure passed to `drivers.Register()`. This needs to be a function\nthat given a database and reader options, returns a reader instance for this particular driver.\n\nIf the database has a `information_schema` schema, with standard tables like `tables` and `columns`,\nyou can use an existing reader from the `drivers/informationschema` package.\nSince there are usually minor difference in objects defined in that schema in different databases,\nthere's a set of options to configure this reader. Refer to\nthe [package docs](https://pkg.go.dev/github.com/xo/usql/drivers/metadata/informationschema) for details.\n\nIf you can't use the `informationschema` reader, consider implementing a new one.\nIt should implement at least one of the following reader interfaces:\n* CatalogReader\n* SchemaReader\n* TableReader\n* ColumnReader\n* IndexReader\n* IndexColumnReader\n* FunctionReader\n* FunctionColumnReader\n* SequenceReader\n\nEvery of these interfaces consist of a single function, that takes a `Filter` structure as an argument,\nand returns a set of results and an error.\n\nExample drivers using their own readers include:\n* `sqlite3`\n* `oracle` and `godror` sharing the same reader\n\nIf you want to use the `informationschema` reader, but need to override one or more readers,\nuse the `metadata.NewPluginReader(readers ...Reader)` function. It returns an object calling\nreader functions from the last reader passed in the arguments, that implements it.\n\nExample drivers extending an `informationschema` reader using a plugin reader:\n* `postgres`\n\n`\\d*` commands are actually implemented by a metadata writer. There's currently only one,\nbut it too can be replaced and/or extended.\n\n# Enabling autocomplete for a driver\n\nIf a driver provides a metadata reader, the default completer will use it.\nA driver can provide it's own completer, by setting the `NewCompleter` property\nin the `drivers.Driver` structure passed to `drivers.Register()`.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015-2025 Kenneth Shaw\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/xo/usql-logo/master/usql.png\" height=\"120\">\n</div>\n\n<div align=\"center\">\n  <a href=\"#installing\" title=\"Installing\">Installing</a> |\n  <a href=\"#building\" title=\"Building\">Building</a> |\n  <a href=\"#database-support\" title=\"Database Support\">Database Support</a> |\n  <a href=\"#using\" title=\"Using\">Using</a> |\n  <a href=\"#features-and-compatibility\" title=\"Features and Compatibility\">Features and Compatibility</a> |\n  <a href=\"https://github.com/xo/usql/releases\" title=\"Releases\">Releases</a> |\n  <a href=\"#contributing\" title=\"Contributing\">Contributing</a>\n</div>\n\n<br/>\n\n`usql` is a universal command-line interface for PostgreSQL, MySQL, Oracle\nDatabase, SQLite3, Microsoft SQL Server, [and many other databases][databases]\nincluding NoSQL and non-relational databases!\n\n`usql` provides a simple way to work with [SQL and NoSQL databases][databases]\nvia a command-line inspired by PostgreSQL's `psql`. `usql` supports most of the\ncore `psql` features, such as [variables][variables], [backticks][backticks],\n[backslash commands][commands] and has additional features that `psql` does\nnot, such as [multiple database support][databases], [copying between databases][copying],\n[syntax highlighting][highlighting], [context-based completion][completion],\nand [terminal graphics][termgraphics].\n\nDatabase administrators and developers that would prefer to work with a tool\nlike `psql` with non-PostgreSQL databases, will find `usql` intuitive,\neasy-to-use, and a great replacement for the command-line clients/tools\nfor other databases.\n\n[![Unit Tests][usql-ci-status]][usql-ci]\n[![Go Reference][goref-usql-status]][goref-usql]\n[![Releases][release-status]][Releases]\n[![Discord Discussion][discord-status]][discord]\n\n[usql-ci]: https://github.com/xo/usql/actions/workflows/test.yml \"Test CI\"\n[usql-ci-status]: https://github.com/xo/usql/actions/workflows/test.yml/badge.svg \"Test CI\"\n[goref-usql]: https://pkg.go.dev/github.com/xo/usql \"Go Reference\"\n[goref-usql-status]: https://pkg.go.dev/badge/github.com/xo/usql.svg \"Go Reference\"\n[release-status]: https://img.shields.io/github/v/release/xo/usql?display_name=tag&sort=semver \"Latest Release\"\n[discord]: https://discord.gg/WDWAgXwJqN \"Discord Discussion\"\n[discord-status]: https://img.shields.io/discord/829150509658013727.svg?label=Discord&logo=Discord&colorB=7289da&style=flat-square \"Discord Discussion\"\n[installing]: #installing \"Installing\"\n[databases]: #database-support \"Database Support\"\n[releases]: https://github.com/xo/usql/releases \"Releases\"\n\n## Installing\n\n`usql` can be installed [via Release][], [via Homebrew][], [via AUR][], [via\nScoop][], [via Go][], or [via Docker][]:\n\n[via Release]: #installing-via-release\n[via Homebrew]: #installing-via-homebrew-macos-and-linux\n[via AUR]: #installing-via-aur-arch-linux\n[via Scoop]: #installing-via-scoop-windows\n[via Go]: #installing-via-go\n[via Docker]: #installing-via-docker\n\n### Installing via Release\n\n1. [Download a release for your platform][releases]\n2. Extract the `usql` or `usql.exe` file from the `.tar.bz2` or `.zip` file\n3. Move the extracted executable to somewhere on your `$PATH` (Linux/macOS) or\n   `%PATH%` (Windows)\n\n### Installing via Homebrew (macOS and Linux)\n\nInstall `usql` from the [`xo/xo` tap][xo-tap] in the usual way with the [`brew`\ncommand][homebrew]:\n\n```sh\n# install usql with most drivers\n$ brew install xo/xo/usql\n```\n\nSupport for [ODBC databases][databases] is available through the `--with-odbc`\ninstall flag:\n\n```sh\n# add xo tap\n$ brew tap xo/xo\n\n# install usql with odbc support\n$ brew install --with-odbc usql\n```\n\n### Installing via AUR (Arch Linux)\n\nInstall `usql` from the [Arch Linux AUR][aur] in the usual way with the [`yay`\ncommand][yay]:\n\n```sh\n# install usql with most drivers\n$ yay -S usql\n```\n\nAlternately, build and [install using `makepkg`][arch-makepkg]:\n\n```sh\n$ git clone https://aur.archlinux.org/usql.git && cd usql\n$ makepkg -si\n==> Making package: usql 0.12.10-1 (Fri 26 Aug 2022 05:56:09 AM WIB)\n==> Checking runtime dependencies...\n==> Checking buildtime dependencies...\n==> Retrieving sources...\n  -> Downloading usql-0.12.10.tar.gz...\n...\n```\n\n### Installing via Scoop (Windows)\n\nInstall `usql` using [Scoop](https://scoop.sh):\n\n```powershell\n# Optional: Needed to run a remote script the first time\n> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser\n\n# install scoop if not already installed\n> irm get.scoop.sh | iex\n\n# install usql with scoop\n> scoop install usql\n```\n\n### Installing via Go\n\nInstall `usql` in the usual Go fashion:\n\n```sh\n# install latest usql version with base drivers\n$ go install github.com/xo/usql@latest\n\n# alternately, install usql with most drivers (see below for info about build tags)\n$ go install -tags most github.com/xo/usql@latest\n```\n\nSee [below for information](#building) on `usql` build tags.\n\n### Installing via Docker\n\nAn [official container image (`docker.io/usql/usql`)][docker-hub] is maintained\nby the `usql` team, and can be used with Docker, Podman, or other container\nruntime.\n\n[docker-hub]: https://hub.docker.com/r/usql/usql\n\nInstall `usql` with Docker, Podman, or other container runtime:\n\n```sh\n# run interactive shell and mount the $PWD/data directory as a volume for use\n# within the container\n$ docker run --rm -it --volume $(pwd)/data:/data docker.io/usql/usql:latest sqlite3://data/test.db\nTrying to pull docker.io/usql/usql:latest...\nGetting image source signatures\nCopying blob af48168d69d8 done   |\nCopying blob efc2b5ad9eec skipped: already exists\nCopying config 917ceb411d done   |\nWriting manifest to image destination\nConnected with driver sqlite3 (SQLite3 3.45.1)\nType \"help\" for help.\n\nsq:data/test.db=> \\q\n\n# run postgres locally\n$ docker run --detach --rm --name=postgres --publish=5432:5432 --env=POSTGRES_PASSWORD=P4ssw0rd docker.io/usql/postgres\n\n# connect to local postgres instance\n$ docker run --rm --network host -it docker.io/usql/usql:latest postgres://postgres:P4ssw0rd@localhost\nConnected with driver postgres (PostgreSQL 16.3 (Debian 16.3-1.pgdg120+1))\nType \"help\" for help.\n\npg:postgres@localhost=> \\q\n\n# run specific usql version\n$ docker run --rm -it docker.io/usql/usql:0.19.3\n```\n\n## Building\n\nWhen building `usql` out-of-the-box with `go build` or `go install`, only the\n[`base` drivers][databases] for PostgreSQL, MySQL, SQLite3, Microsoft SQL\nServer, Oracle, CSVQ will be included in the build:\n\n```sh\n# build/install with base drivers (PostgreSQL, MySQL, SQLite3, Microsoft SQL Server,\n# Oracle, CSVQ)\n$ go install github.com/xo/usql@master\n```\n\nOther databases can be enabled by specifying the [build tag for their database\ndriver][databases].\n\n```sh\n# build/install with base, Avatica, and ODBC drivers\n$ go install -tags 'avatica odbc' github.com/xo/usql@master\n```\n\nFor every build tag `<driver>`, there is also a `no_<driver>` build tag\nthat will disable the driver:\n\n```sh\n# build/install most drivers, excluding Avatica, Couchbase, and PostgreSQL\n$ go install -tags 'most no_avatica no_couchbase no_postgres' github.com/xo/usql@master\n```\n\nBy specifying the build tags `most` or `all`, the build will include most, and\nall SQL drivers, respectively:\n\n```sh\n# build/install with most drivers (excludes CGO drivers and problematic drivers)\n$ go install -tags most github.com/xo/usql@master\n\n# build/install all drivers (includes CGO drivers and problematic drivers)\n$ go install -tags all github.com/xo/usql@master\n```\n\n## Database Support\n\n`usql` works with all Go standard library compatible SQL drivers supported by\n[`github.com/xo/dburl`][dburl].\n\nThe list of drivers that `usql` was built with can be displayed with the\n[`\\drivers` command][commands]:\n\n```sh\n$ cd $GOPATH/src/github.com/xo/usql\n\n# build excluding the base drivers, and including cassandra and moderncsqlite\n$ go build -tags 'no_postgres no_oracle no_sqlserver no_sqlite3 cassandra moderncsqlite'\n\n# show built driver support\n$ ./usql -c '\\drivers'\nAvailable Drivers:\n  cql [ca, scy, scylla, datastax, cassandra]\n  memsql (mysql) [me]\n  moderncsqlite [mq, sq, file, sqlite, sqlite3, modernsqlite]\n  mysql [my, maria, aurora, mariadb, percona]\n  tidb (mysql) [ti]\n  vitess (mysql) [vt]\n```\n\nThe above shows that `usql` was built with only the `mysql`, `cassandra` (ie,\n`cql`), and `moderncsqlite` drivers. The output above reflects information\nabout the drivers available to `usql`, specifically the internal driver name,\nits primary URL scheme, the driver's available scheme aliases (shown in\n`[...]`), and the real/underlying driver (shown in `(...)`) for wire compatible\ndrivers.\n\n### Supported Database Schemes and Aliases\n\nThe following are the [Go SQL drivers][go-sql] that `usql` supports, the\nassociated database, scheme / build tag, and scheme aliases:\n\n<!-- DRIVER DETAILS START -->\n\n| Database             | Scheme / Tag    | Scheme Aliases                                  | Driver Package / Notes                                                      |\n| -------------------- | --------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |\n| PostgreSQL           | `postgres`      | `pg`, `pgsql`, `postgresql`                     | [github.com/lib/pq][d-postgres]                                             |\n| MySQL                | `mysql`         | `my`, `maria`, `aurora`, `mariadb`, `percona`   | [github.com/go-sql-driver/mysql][d-mysql]                                   |\n| Microsoft SQL Server | `sqlserver`     | `ms`, `mssql`, `azuresql`                       | [github.com/microsoft/go-mssqldb][d-sqlserver]                              |\n| Oracle Database      | `oracle`        | `or`, `ora`, `oci`, `oci8`, `odpi`, `odpi-c`    | [github.com/sijms/go-ora/v2][d-oracle]                                      |\n| SQLite3              | `sqlite3`       | `sq`, `sqlite`, `file`                          | [github.com/mattn/go-sqlite3][d-sqlite3] <sup>[†][f-cgo]</sup>              |\n| ClickHouse           | `clickhouse`    | `ch`                                            | [github.com/ClickHouse/clickhouse-go/v2][d-clickhouse]                      |\n| CSVQ                 | `csvq`          | `cs`, `csv`, `tsv`, `json`                      | [github.com/mithrandie/csvq-driver][d-csvq]                                 |\n|                      |                 |                                                 |                                                                             |\n| Alibaba MaxCompute   | `maxcompute`    | `mc`                                            | [sqlflow.org/gomaxcompute][d-maxcompute]                                    |\n| Alibaba Tablestore   | `ots`           | `ot`, `tablestore`                              | [github.com/aliyun/aliyun-tablestore-go-sql-driver][d-ots]                  |\n| Apache Avatica       | `avatica`       | `av`, `phoenix`                                 | [github.com/apache/calcite-avatica-go/v5][d-avatica]                        |\n| Apache H2            | `h2`            |                                                 | [github.com/jmrobles/h2go][d-h2]                                            |\n| Apache Hive          | `hive`          | `hi`, `hive2`                                   | [sqlflow.org/gohive][d-hive]                                                |\n| Apache Ignite        | `ignite`        | `ig`, `gridgain`                                | [github.com/amsokol/ignite-go-client/sql][d-ignite]                         |\n| Apache Impala        | `impala`        | `im`                                            | [github.com/sclgo/impala-go][d-impala]                                      |\n| AWS Athena           | `athena`        | `s3`, `aws`, `awsathena`                        | [github.com/uber/athenadriver/go][d-athena]                                 |\n| Azure CosmosDB       | `cosmos`        | `cm`, `gocosmos`                                | [github.com/btnguyen2k/gocosmos][d-cosmos]                                  |\n| Cassandra            | `cassandra`     | `ca`, `scy`, `scylla`, `datastax`, `cql`        | [github.com/MichaelS11/go-cql-driver][d-cassandra]                          |\n| ChaiSQL              | `chai`          | `ci`, `genji`, `chaisql`                        | [github.com/chaisql/chai][d-chai]                                           |\n| Couchbase            | `couchbase`     | `n1`, `n1ql`                                    | [github.com/couchbase/go_n1ql][d-couchbase]                                 |\n| Cznic QL             | `ql`            | `cznic`, `cznicql`                              | [modernc.org/ql][d-ql]                                                      |\n| Databend             | `databend`      | `dd`, `bend`                                    | [github.com/datafuselabs/databend-go][d-databend]                           |\n| Databricks           | `databricks`    | `br`, `brick`, `bricks`, `databrick`            | [github.com/databricks/databricks-sql-go][d-databricks]                     |\n| DuckDB               | `duckdb`        | `dk`, `ddb`, `duck`, `file`                     | [github.com/duckdb/duckdb-go/v2][d-duckdb] <sup>[†][f-cgo]</sup>            |\n| DynamoDb             | `dynamodb`      | `dy`, `dyn`, `dynamo`, `dynamodb`               | [github.com/btnguyen2k/godynamo][d-dynamodb]                                |\n| Exasol               | `exasol`        | `ex`, `exa`                                     | [github.com/exasol/exasol-driver-go][d-exasol]                              |\n| Firebird             | `firebird`      | `fb`, `firebirdsql`                             | [github.com/nakagami/firebirdsql][d-firebird]                               |\n| FlightSQL            | `flightsql`     | `fl`, `flight`                                  | [github.com/apache/arrow/go/v17/arrow/flight/flightsql/driver][d-flightsql] |\n| Google BigQuery      | `bigquery`      | `bq`                                            | [gorm.io/driver/bigquery/driver][d-bigquery]                                |\n| Google Spanner       | `spanner`       | `sp`                                            | [github.com/googleapis/go-sql-spanner][d-spanner]                           |\n| Microsoft ADODB      | `adodb`         | `ad`, `ado`                                     | [github.com/mattn/go-adodb][d-adodb]                                        |\n| ModernC SQLite3      | `moderncsqlite` | `mq`, `modernsqlite`                            | [modernc.org/sqlite][d-moderncsqlite]                                       |\n| MySQL MyMySQL        | `mymysql`       | `zm`, `mymy`                                    | [github.com/ziutek/mymysql/godrv][d-mymysql]                                |\n| Netezza              | `netezza`       | `nz`, `nzgo`                                    | [github.com/IBM/nzgo/v12][d-netezza]                                        |\n| PostgreSQL PGX       | `pgx`           | `px`                                            | [github.com/jackc/pgx/v5/stdlib][d-pgx]                                     |\n| Presto               | `presto`        | `pr`, `prs`, `prestos`, `prestodb`, `prestodbs` | [github.com/prestodb/presto-go-client/presto][d-presto]                     |\n| RamSQL               | `ramsql`        | `rm`, `ram`                                     | [github.com/proullon/ramsql/driver][d-ramsql]                               |\n| SAP ASE              | `sapase`        | `ax`, `ase`, `tds`                              | [github.com/thda/tds][d-sapase]                                             |\n| SAP HANA             | `saphana`       | `sa`, `sap`, `hana`, `hdb`                      | [github.com/SAP/go-hdb/driver][d-saphana]                                   |\n| Snowflake            | `snowflake`     | `sf`                                            | [github.com/snowflakedb/gosnowflake][d-snowflake]                           |\n| Trino                | `trino`         | `tr`, `trs`, `trinos`                           | [github.com/trinodb/trino-go-client/trino][d-trino]                         |\n| Vertica              | `vertica`       | `ve`                                            | [github.com/vertica/vertica-sql-go][d-vertica]                              |\n| VoltDB               | `voltdb`        | `vo`, `vdb`, `volt`                             | [github.com/VoltDB/voltdb-client-go/voltdbclient][d-voltdb]                 |\n| YDB                  | `ydb`           | `yd`, `yds`, `ydbs`                             | [github.com/ydb-platform/ydb-go-sdk/v3][d-ydb]                              |\n|                      |                 |                                                 |                                                                             |\n| GO DRiver for ORacle | `godror`        | `gr`                                            | [github.com/godror/godror][d-godror] <sup>[†][f-cgo]</sup>                  |\n| ODBC                 | `odbc`          | `od`                                            | [github.com/alexbrainman/odbc][d-odbc] <sup>[†][f-cgo]</sup>                |\n|                      |                 |                                                 |                                                                             |\n| Amazon Redshift      | `postgres`      | `rs`, `redshift`                                | [github.com/lib/pq][d-postgres] <sup>[‡][f-wire]</sup>                      |\n| CockroachDB          | `postgres`      | `cr`, `cdb`, `crdb`, `cockroach`, `cockroachdb` | [github.com/lib/pq][d-postgres] <sup>[‡][f-wire]</sup>                      |\n| OLE ODBC             | `adodb`         | `oo`, `ole`, `oleodbc`                          | [github.com/mattn/go-adodb][d-adodb] <sup>[‡][f-wire]</sup>                 |\n| SingleStore MemSQL   | `mysql`         | `me`, `memsql`                                  | [github.com/go-sql-driver/mysql][d-mysql] <sup>[‡][f-wire]</sup>            |\n| TiDB                 | `mysql`         | `ti`, `tidb`                                    | [github.com/go-sql-driver/mysql][d-mysql] <sup>[‡][f-wire]</sup>            |\n| Vitess Database      | `mysql`         | `vt`, `vitess`                                  | [github.com/go-sql-driver/mysql][d-mysql] <sup>[‡][f-wire]</sup>            |\n|                      |                 |                                                 |                                                                             |\n|                      |                 |                                                 |                                                                             |\n|                      |                 |                                                 |                                                                             |\n| **NO DRIVERS**       | `no_base`       |                                                 | _no base drivers (useful for development)_                                  |\n| **MOST DRIVERS**     | `most`          |                                                 | _all stable drivers_                                                        |\n| **ALL DRIVERS**      | `all`           |                                                 | _all drivers, excluding bad drivers_                                        |\n| **BAD DRIVERS**      | `bad`           |                                                 | _bad drivers (broken/non-working drivers)_                                  |\n| **NO &lt;TAG&gt;**   | `no_<tag>`      |                                                 | _exclude driver with `<tag>`_                                               |\n\n[d-adodb]: https://github.com/mattn/go-adodb\n[d-athena]: https://github.com/uber/athenadriver\n[d-avatica]: https://github.com/apache/calcite-avatica-go\n[d-bigquery]: https://github.com/go-gorm/bigquery\n[d-cassandra]: https://github.com/MichaelS11/go-cql-driver\n[d-chai]: https://github.com/chaisql/chai\n[d-clickhouse]: https://github.com/ClickHouse/clickhouse-go\n[d-cosmos]: https://github.com/btnguyen2k/gocosmos\n[d-couchbase]: https://github.com/couchbase/go_n1ql\n[d-csvq]: https://github.com/mithrandie/csvq-driver\n[d-databend]: https://github.com/datafuselabs/databend-go\n[d-databricks]: https://github.com/databricks/databricks-sql-go\n[d-duckdb]: https://github.com/duckdb/duckdb-go\n[d-dynamodb]: https://github.com/btnguyen2k/godynamo\n[d-exasol]: https://github.com/exasol/exasol-driver-go\n[d-firebird]: https://github.com/nakagami/firebirdsql\n[d-flightsql]: https://github.com/apache/arrow/tree/main/go/arrow/flight/flightsql/driver\n[d-godror]: https://github.com/godror/godror\n[d-h2]: https://github.com/jmrobles/h2go\n[d-hive]: https://github.com/sql-machine-learning/gohive\n[d-ignite]: https://github.com/amsokol/ignite-go-client\n[d-impala]: https://github.com/sclgo/impala-go\n[d-maxcompute]: https://github.com/sql-machine-learning/gomaxcompute\n[d-moderncsqlite]: https://gitlab.com/cznic/sqlite\n[d-mymysql]: https://github.com/ziutek/mymysql\n[d-mysql]: https://github.com/go-sql-driver/mysql\n[d-netezza]: https://github.com/IBM/nzgo\n[d-odbc]: https://github.com/alexbrainman/odbc\n[d-oracle]: https://github.com/sijms/go-ora\n[d-ots]: https://github.com/aliyun/aliyun-tablestore-go-sql-driver\n[d-pgx]: https://github.com/jackc/pgx\n[d-postgres]: https://github.com/lib/pq\n[d-presto]: https://github.com/prestodb/presto-go-client\n[d-ql]: https://gitlab.com/cznic/ql\n[d-ramsql]: https://github.com/proullon/ramsql\n[d-sapase]: https://github.com/thda/tds\n[d-saphana]: https://github.com/SAP/go-hdb\n[d-snowflake]: https://github.com/snowflakedb/gosnowflake\n[d-spanner]: https://github.com/googleapis/go-sql-spanner\n[d-sqlite3]: https://github.com/mattn/go-sqlite3\n[d-sqlserver]: https://github.com/microsoft/go-mssqldb\n[d-trino]: https://github.com/trinodb/trino-go-client\n[d-vertica]: https://github.com/vertica/vertica-sql-go\n[d-voltdb]: https://github.com/VoltDB/voltdb-client-go\n[d-ydb]: https://github.com/ydb-platform/ydb-go-sdk\n\n<!-- DRIVER DETAILS END -->\n\n[f-cgo]: #f-cgo \"Requires CGO\"\n[f-wire]: #f-wire \"Wire compatible\"\n\n<p>\n  <i>\n    <a id=\"f-cgo\"><sup>†</sup> Requires CGO</a><br>\n    <a id=\"f-wire\"><sup>‡</sup> Wire compatible (see respective driver)</a>\n  </i>\n</p>\n\nAny of the protocol schemes/aliases above can be used in conjunction when\nconnecting to a database via the command-line or with the [`\\connect` and\n`\\copy` commands][commands]:\n\n```sh\n# connect to a vitess database:\n$ usql vt://user:pass@host:3306/mydatabase\n\n$ usql\n(not connected)=> \\c vitess://user:pass@host:3306/mydatabase\n\n$ usql\n(not connected)=> \\copy csvq://. pg://localhost/ 'select * ....' 'myTable'\n```\n\nSee [the section below on connecting to databases][connecting] for further\ndetails building DSNs/URLs for use with `usql`.\n\n## Using\n\nAfter [installing][], `usql` can be used similarly to the following:\n\n```sh\n# connect to a postgres database\n$ usql postgres://booktest@localhost/booktest\n\n# connect to an oracle database\n$ usql oracle://user:pass@host/oracle.sid\n\n# connect to a postgres database and run the commands contained in script.sql\n$ usql pg://localhost/ -f script.sql\n```\n\n### Command-line Options\n\nSupported command-line options:\n\n```sh\n$ usql --help\nusql, the universal command-line interface for SQL databases\n\nUsage:\n  usql [flags]... [DSN]\n\nArguments:\n  DSN   database url or connection name\n\nFlags:\n  -c, --command COMMAND                     run only single command (SQL or internal) and exit\n  -f, --file FILE                           execute commands from file and exit\n  -w, --no-password                         never prompt for password\n  -X, --no-init                             do not execute initialization scripts (aliases: --no-rc --no-psqlrc --no-usqlrc)\n  -o, --out FILE                            output file\n  -W, --password                            force password prompt (should happen automatically)\n  -1, --single-transaction                  execute as a single transaction (if non-interactive)\n  -v, --set NAME=VALUE                      set variable NAME to VALUE (see \\set command, aliases: --var --variable)\n  -N, --cset NAME=DSN                       set named connection NAME to DSN (see \\cset command)\n  -P, --pset VAR=ARG                        set printing option VAR to ARG (see \\pset command)\n  -F, --field-separator FIELD-SEPARATOR     field separator for unaligned and CSV output (default \"|\" and \",\")\n  -R, --record-separator RECORD-SEPARATOR   record separator for unaligned and CSV output (default \\n)\n  -T, --table-attr TABLE-ATTR               set HTML table tag attributes (e.g., width, border)\n  -A, --no-align                            unaligned table output mode\n  -H, --html                                HTML table output mode\n  -t, --tuples-only                         print rows only\n  -x, --expanded                            turn on expanded table output\n  -z, --field-separator-zero                set field separator for unaligned and CSV output to zero byte\n  -0, --record-separator-zero               set record separator for unaligned and CSV output to zero byte\n  -J, --json                                JSON output mode\n  -C, --csv                                 CSV output mode\n  -G, --vertical                            vertical output mode\n  -q, --quiet                               run quietly (no messages, only query output)\n      --config string                       config file\n  -V, --version                             output version information, then exit\n  -?, --help                                show this help, then exit\n```\n\n### Connecting to Databases\n\n`usql` opens a database connection by [parsing a URL][dburl] and passing the\nresulting connection string to [a database driver][databases]. Database\nconnection strings (aka \"data source name\" or DSNs) have the same parsing rules\nas URLs, and can be passed to `usql` via command-line, or to the [`\\connect`,\n`\\c`, and `\\copy` commands][commands].\n\nDatabase connections can be defined with [the `\\cset` command][connection-vars]\nor in [the `config.yaml` configuration file][config].\n\n#### Database Connection Strings\n\nDatabase connection strings look like the following:\n\n```txt\n  driver+transport://user:pass@host/dbname?opt1=a&opt2=b\n  driver:/path/to/file\n  /path/to/file\n  name\n```\n\nWhere the above are:\n\n| Component                       | Description                                                                          |\n| ------------------------------- | ------------------------------------------------------------------------------------ |\n| `driver`                        | driver scheme name or scheme alias                                                   |\n| `transport`                     | `tcp`, `udp`, `unix` or driver name <i>(for ODBC and ADODB)</i>                      |\n| `user`                          | username                                                                             |\n| `pass`                          | password                                                                             |\n| `host`                          | hostname                                                                             |\n| `dbname` <sup>[±][f-path]</sup> | database name, instance, or service name/ID                                          |\n| `?opt1=a&...`                   | additional database driver options (see respective SQL driver for available options) |\n| `/path/to/file`                 | a path on disk                                                                       |\n| `name`                          | a connection name set by [`\\cset`][connection-vars] or in [`config.yaml`][config]    |\n\n[f-path]: #f-path \"URL Paths for Databases\"\n\n<p>\n  <i>\n    <a id=\"f-path\">\n      <sup>±</sup> Some databases, such as Microsoft SQL Server, or Oracle\n      Database support a path component (ie, <code>/dbname</code>) in the form\n      of <code>/instance/dbname</code>, where <code>/instance</code> is the\n      optional service identifier (aka \"SID\") or database instance\n    </a>\n  </i>\n</p>\n\n#### Driver Aliases\n\n`usql` supports the same driver names and aliases as [the `dburl`\npackage][dburl]. Databases have at least one or more aliases. See [`dburl`'s\nscheme documentation][dburl-schemes] for a list of all supported aliases.\n\n##### Short Aliases\n\nAll database drivers have a two character short form that is usually the first\ntwo letters of the database driver. For example, `pg` for `postgres`, `my` for\n`mysql`, `ms` for `sqlserver`, `or` for `oracle`, or `sq` for `sqlite3`.\n\n#### Passing Driver Options\n\nDriver options are specified as standard URL query options in the form of\n`?opt1=a&opt2=b`. Refer to the [relevant database driver's documentation][databases]\nfor available options.\n\n#### Paths on Disk\n\nIf a URL does not have a `driver:` scheme, `usql` will check if it is a path on\ndisk. If the path exists, `usql` will attempt to use an appropriate database\ndriver to open the path.\n\nWhen the path is a Unix Domain Socket, `usql` will attempt to open it with the\nMySQL driver. When the path is a directory, `usql` will attempt to open it\nusing the PostgreSQL driver. And, lastly, when the path is a regular file,\n`usql` will attempt to open the file using the SQLite3 or DuckDB drivers.\n\n#### Driver Defaults\n\nAs with URLs, most components in the URL are optional and many components can\nbe left out. `usql` will attempt connecting using defaults where possible:\n\n```sh\n# connect to postgres using the local $USER and the unix domain socket in /var/run/postgresql\n$ usql pg://\n```\n\nSee the relevant documentation [on database drivers][databases] for more\ninformation.\n\n### Connection Examples\n\nThe following are example connection strings and additional ways to connect to\ndatabases using `usql`:\n\n```sh\n# connect to a postgres database\n$ usql pg://user:pass@host/dbname\n$ usql pgsql://user:pass@host/dbname\n$ usql postgres://user:pass@host:port/dbname\n$ usql pg://\n$ usql /var/run/postgresql\n$ usql pg://user:pass@host/dbname?sslmode=disable # Connect without SSL\n\n# connect to a mysql database\n$ usql my://user:pass@host/dbname\n$ usql mysql://user:pass@host:port/dbname\n$ usql my://\n$ usql /var/run/mysqld/mysqld.sock\n\n# connect to a sqlserver database\n$ usql sqlserver://user:pass@host/instancename/dbname\n$ usql ms://user:pass@host/dbname\n$ usql ms://user:pass@host/instancename/dbname\n$ usql mssql://user:pass@host:port/dbname\n$ usql ms://\n\n# connect to a sqlserver database using Windows domain authentication\n$ runas /user:ACME\\wiley /netonly \"usql mssql://host/dbname/\"\n\n# connect to a oracle database\n$ usql or://user:pass@host/sid\n$ usql oracle://user:pass@host:port/sid\n$ usql or://\n\n# connect to a cassandra database\n$ usql ca://user:pass@host/keyspace\n$ usql cassandra://host/keyspace\n$ usql cql://host/\n$ usql ca://\n\n# connect to a sqlite database that exists on disk\n$ usql dbname.sqlite3\n\n# Note: when connecting to a SQLite database, if the \"driver://\" or\n# \"driver:\" scheme/alias is omitted, the file must already exist on disk.\n#\n# if the file does not yet exist, the URL must incorporate file:, sq:, sqlite3:,\n# or any other recognized sqlite3 driver alias to force usql to create a new,\n# empty database at the specified path:\n$ usql sq://path/to/dbname.sqlite3\n$ usql sqlite3://path/to/dbname.sqlite3\n$ usql file:/path/to/dbname.sqlite3\n\n# connect to a adodb ole resource (windows only)\n$ usql adodb://Microsoft.Jet.OLEDB.4.0/myfile.mdb\n$ usql \"adodb://Microsoft.ACE.OLEDB.12.0/?Extended+Properties=\\\"Text;HDR=NO;FMT=Delimited\\\"\"\n\n# connect to a named connection in $HOME/.config/usql/config.yaml\n$ cat $HOME/.config/usql/config.yaml\nconnections:\n  my_named_connection: sqlserver://user:pass@localhost/\n$ usql my_named_connection\n\n# connect with ODBC driver (requires building with odbc tag)\n$ cat /etc/odbcinst.ini\n[DB2]\nDescription=DB2 driver\nDriver=/opt/db2/clidriver/lib/libdb2.so\nFileUsage = 1\nDontDLClose = 1\n\n[PostgreSQL ANSI]\nDescription=PostgreSQL ODBC driver (ANSI version)\nDriver=psqlodbca.so\nSetup=libodbcpsqlS.so\nDebug=0\nCommLog=1\nUsageCount=1\n\n# connect to db2, postgres databases using odbc config above\n$ usql odbc+DB2://user:pass@localhost/dbname\n$ usql odbc+PostgreSQL+ANSI://user:pass@localhost/dbname?TraceFile=/path/to/trace.log\n```\n\nSee the [section on connection variables][connection-vars] for information on\ndefining connection names.\n\n### Executing Queries and Commands\n\nThe interactive interpreter reads queries and [backslash meta (`\\`) commands][commands],\nsending the query to the connected database:\n\n```sh\n$ usql sqlite://example.sqlite3\nConnected with driver sqlite3 (SQLite3 3.17.0)\nType \"help\" for help.\n\nsq:example.sqlite3=> create table test (test_id int, name string);\nCREATE TABLE\nsq:example.sqlite3=> insert into test (test_id, name) values (1, 'hello');\nINSERT 1\nsq:example.sqlite3=> select * from test;\n  test_id | name\n+---------+-------+\n        1 | hello\n(1 rows)\n\nsq:example.sqlite3=> select * from test\nsq:example.sqlite3-> \\p\nselect * from test\nsq:example.sqlite3-> \\g\n  test_id | name\n+---------+-------+\n        1 | hello\n(1 rows)\n\nsq:example.sqlite3=> \\c postgres://booktest@localhost\nerror: pq: 28P01: password authentication failed for user \"booktest\"\nEnter password:\nConnected with driver postgres (PostgreSQL 9.6.6)\npg:booktest@localhost=> select * from authors;\n  author_id |      name\n+-----------+----------------+\n          1 | Unknown Master\n          2 | blah\n          3 | foobar\n(3 rows)\n\npg:booktest@localhost=>\n```\n\nCommands may accept one or more parameter, and can be quoted using either `'`\nor `\"`. Command parameters [may also be backticked][backticks].\n\n### Backslash Commands\n\n`usql` supports interleaved backslash (`\\`) meta commands to modify or alter\nthe way that `usql` interprets queries, formats its output, and changes the\nresulting interactive flow.\n\n```sh\n(not connected)=> \\c postgres://user:pass@localhost\npg:user@localhost=> select * from my_table \\G\n```\n\nAvailable backslash meta commands can be displayed with `\\?`:\n\n```sh\n$ usql\nType \"help\" for help.\n\n(not connected)=> \\?\nGeneral\n  \\q                                quit usql\n  \\quit                             alias for \\q\n  \\copyright                        show usage and distribution terms for usql\n  \\drivers                          show database drivers available to usql\n\nHelp\n  \\? [commands]                     show help on usql's meta (backslash) commands\n  \\? options                        show help on usql command-line options\n  \\? variables                      show help on special usql variables\n\nConnection\n  \\c DSN or \\c NAME                 connect to dsn or named database connection\n  \\c DRIVER PARAMS...               connect to database with driver and parameters\n  \\connect                          alias for \\c\n  \\Z                                close (disconnect) database connection\n  \\disconnect                       alias for \\Z\n  \\password [USER]                  change password for user\n  \\passwd                           alias for \\password\n  \\conninfo                         display information about the current database connection\n\nQuery Execute\n  \\g [(OPTIONS)] [FILE] or ;        execute query (and send results to file or |pipe)\n  \\go                               alias for \\g\n  \\G [(OPTIONS)] [FILE]             as \\g, but forces vertical output mode\n  \\ego                              alias for \\G\n  \\gx [(OPTIONS)] [FILE]            as \\g, but forces expanded output mode\n  \\gexec                            execute query and execute each value of the result\n  \\gset [PREFIX]                    execute query and store results in usql variables\n  \\bind [PARAM]...                  set query parameters\n  \\timing [on|off]                  toggle timing of commands\n\nQuery View\n  \\crosstab [(OPTIONS)] [COLUMNS]   execute query and display results in crosstab\n  \\crosstabview                     alias for \\crosstab\n  \\xtab                             alias for \\crosstab\n  \\chart CHART [(OPTIONS)]          execute query and display results as a chart\n  \\watch [(OPTIONS)] [INTERVAL]     execute query every specified interval\n\nQuery Buffer\n  \\e [-raw|-exec] [FILE] [LINE]     edit the query buffer, raw (non-interpolated) buffer, the\n                                    exec buffer, or a file with external editor\n  \\edit                             alias for \\e\n  \\p [-raw|-exec]                   show the contents of the query buffer, the raw\n                                    (non-interpolated) buffer or the exec buffer\n  \\print                            alias for \\p\n  \\raw                              alias for \\p\n  \\exec                             alias for \\p\n  \\w [-raw|-exec] FILE              write the contents of the query buffer, raw\n                                    (non-interpolated) buffer, or exec buffer to file\n  \\write                            alias for \\w\n  \\r                                reset (clear) the query buffer\n  \\reset                            alias for \\r\n\nInformational\n  \\d[S+] [NAME]                     list tables, views, and sequences or describe table, view,\n                                    sequence, or index\n  \\da[S+] [PATTERN]                 list aggregates\n  \\df[S+] [PATTERN]                 list functions\n  \\di[S+] [PATTERN]                 list indexes\n  \\dm[S+] [PATTERN]                 list materialized views\n  \\dn[S+] [PATTERN]                 list schemas\n  \\dp[S] [PATTERN]                  list table, view, and sequence access privileges\n  \\ds[S+] [PATTERN]                 list sequences\n  \\dt[S+] [PATTERN]                 list tables\n  \\dv[S+] [PATTERN]                 list views\n  \\l[+]                             list databases\n  \\ss[+] [TABLE|QUERY] [k]          show stats for a table or a query\n\nVariables\n  \\set [NAME [VALUE]]               set usql application variable, or show all usql application\n                                    variables if no parameters\n  \\unset NAME                       unset (delete) usql application variable\n  \\pset [NAME [VALUE]]              set table print formatting option, or show all print\n                                    formatting options if no parameters\n  \\a                                toggle between unaligned and aligned output mode\n  \\C [TITLE]                        set table title, or unset if none\n  \\f [SEPARATOR]                    show or set field separator for unaligned query output\n  \\H                                toggle HTML output mode\n  \\T [ATTRIBUTES]                   set HTML <table> tag attributes, or unset if none\n  \\t [on|off]                       show only rows\n  \\x [on|off|auto]                  toggle expanded output\n  \\cset [NAME [URL]]                set named connection, or show all named connections if no\n                                    parameters\n  \\cset NAME DRIVER PARAMS...       set named connection for driver and parameters\n  \\prompt [-TYPE] VAR [PROMPT]      prompt user to set application variable\n\nInput/Output\n  \\echo [-n] [MESSAGE]...           write message to standard output (-n for no newline)\n  \\qecho [-n] [MESSAGE]...          write message to \\o output stream (-n for no newline)\n  \\warn [-n] [MESSAGE]...           write message to standard error (-n for no newline)\n  \\o [FILE]                         send all query results to file or |pipe\n  \\out                              alias for \\o\n  \\copy SRC DST QUERY TABLE         copy results of query from source database into table on\n                                    destination database\n  \\copy SRC DST QUERY TABLE(A,...)  copy results of query from source database into table's\n                                    columns on destination database\n\nControl/Conditional\n  \\i FILE                           execute commands from file\n  \\include                          alias for \\i\n  \\ir FILE                          as \\i, but relative to location of current script\n  \\include_relative                 alias for \\ir\n  \\if EXPR                          begin conditional block\n  \\elif EXPR                        alternative within current conditional block\n  \\else                             final alternative within current conditional block\n  \\endif                            end conditional block\n\nTransaction\n  \\begin [-read-only [ISOLATION]]   begin transaction, with optional isolation level\n  \\commit                           commit current transaction\n  \\rollback                         rollback (abort) current transaction\n  \\abort                            alias for \\rollback\n\nOperating System/Environment\n  \\! [COMMAND]                      execute command in shell or start interactive shell\n  \\cd [DIR]                         change the current working directory\n  \\getenv VARNAME ENVVAR            fetch environment variable\n  \\setenv NAME [VALUE]              set or unset environment variable\n```\n\nParameters passed to commands [can be backticked][backticks].\n\n## Features and Compatibility\n\nAn overview of `usql`'s features, functionality, and compatibility with `psql`:\n\n- [Configuration][config]\n- [Variables][variables]\n- [Backticks][backticks]\n- [Copying Between Databases][copying]\n- [Syntax Highlighting][highlighting]\n- [Time Formatting][timefmt]\n- [Context Completion][completion]\n- [Host Connection Information](#host-connection-information)\n- [Passwords][usqlpass]\n- [Runtime Configuration (RC) File][usqlrc]\n\nThe `usql` project's goal is to support as much of `psql`'s core features and\nfunctionality, and aims to be as compatible as possible - [contributions are\nalways appreciated][contributing]!\n\n#### Configuration\n\nDuring its initialization phase, `usql` reads a standard [YAML configuration][yaml]\nfile [`config.yaml`](contrib/config.yaml). On Windows this is `%AppData%/usql/config.yaml`,\non macOS this is `$HOME/Library/Application Support/usql/config.yaml`, and on\nLinux and other Unix systems this is normally `$HOME/.config/usql/config.yaml`.\n\n##### `connections:`\n\n[Named connection DSNs][connecting] can be defined under `connections:` as a string\nor as a map:\n\n```yaml\nconnections:\n  my_couchbase_conn: couchbase://Administrator:P4ssw0rd@localhost\n  my_clickhouse_conn: clickhouse://clickhouse:P4ssw0rd@localhost\n  my_godror_conn:\n    protocol: godror\n    username: system\n    password: P4ssw0rd\n    hostname: localhost\n    port: 1521\n    database: free\n```\n\nDefined `connections:` can be used on the command-line with `\\connect`, `\\c`,\n`\\copy`, and [other commands][commands]:\n\n```sh\n$ usql my_godror_conn\nConnected with driver godror (Oracle Database 23.0.0.0.0)\nType \"help\" for help.\n\ngr:system@localhost/free=>\n```\n\n##### `init:`\n\nAn initialization script can be defined as `init:` as a string:\n\n```yaml\ninit: |\n  \\echo welcome to the jungle `date`\n  \\set SYNTAX_HL_STYLE paraiso-dark\n  \\set PROMPT1 '\\033[32m%S%M%/%R%#\\033[0m '\n```\n\nThe `init:` script is commonly used to set [environment variables][variables]\nor other configuration, and can be disabled on the command-line using the\n`--no-init` / `-X` flag. The script will be executed prior to any `-c` /\n`--command` / `-f` / `--file` flag and before starting the interactive\ninterpreter.\n\n##### Other Options\n\nPlease see [`contrib/config.yaml`](contrib/config.yaml) for an overview of\navailable configuration options.\n\n#### Variables\n\n`usql` supports [runtime][runtime-vars], [connection][connection-vars], and\n[display formatting][print-vars] variables that can be `\\set`, `\\cset`, or\n`\\pset` respectively.\n\n##### Runtime Variables\n\nRuntime variables are managed with the `\\set` and `\\unset` [commands][commands]:\n\n```sh\n(not connected)=> \\unset FOO\n(not connected)=> \\set FOO bar\n```\n\nRuntime variables can be displayed with `\\set`:\n\n```sh\n(not connected)=> \\set\nFOO = 'bar'\n```\n\n###### Variable Interpolation\n\nWhen a runtime variable `NAME` has been `\\set`, then `:NAME`, `:'NAME'`, and\n`:\"NAME\"` will be interpolated into the query buffer:\n\n```sh\npg:booktest@localhost=> \\set FOO bar\npg:booktest@localhost=> select * from authors where name = :'FOO';\n  author_id | name\n+-----------+------+\n          7 | bar\n(1 rows)\n```\n\nWhere a runtime variable is used as `:'NAME'` or `:\"NAME\"` the interpolated\nvalue will be quoted using `'` or `\"` respectively:\n\n```sh\npg:booktest@localhost=> \\set TBLNAME authors\npg:booktest@localhost=> \\set COLNAME name\npg:booktest@localhost=> \\set FOO bar\npg:booktest@localhost=> select * from :TBLNAME where :\"COLNAME\" = :'FOO'\n```\n\nThe query buffer and interpolated values can be displayed with `\\p` and\n`\\print`, or the raw query buffer can be displayed with `\\raw`:\n\n```sh\npg:booktest@localhost-> \\p\nselect * from authors where \"name\" = 'bar'\npg:booktest@localhost-> \\raw\nselect * from :TBLNAME where :\"COLNAME\" = :'FOO'\n```\n\n<hr/>\n\n> **Note**\n>\n> Variables contained within other strings <b><u>will not</b></u> be interpolated:\n\n```sh\npg:booktest@localhost=> select ':FOO';\n  ?column?\n+----------+\n  :FOO\n(1 rows)\n\npg:booktest@localhost=> \\p\nselect ':FOO';\n```\n\n<hr/>\n\n##### Connection Variables\n\nConnection variables work similarly to runtime variables, and are managed with\n`\\cset`. Connection variables can be used with the `\\c`, `\\connect`, `\\copy`,\nor [other commands][commands]:\n\n```sh\n(not connected)=> \\cset my_conn postgres://user:pass@localhost\n(not connected)=> \\c my_conn\nConnected with driver postgres (PostgreSQL 16.2 (Debian 16.2-1.pgdg120+2))\npg:postgres@localhost=>\n```\n\nConnection variables are not interpolated into queries. See the [configuration\nsection for information on defining persistent connection variables][config].\n\nConnection variables can be displayed with `\\cset`:\n\n```sh\n(not connected)=> \\cset\nmy_conn = 'postgres://user:pass@localhost'\n```\n\n##### Display Formatting (Print) Variables\n\nDisplay formatting variables can be set using `\\pset` and [other\ncommands][commands]:\n\n```sh\n(not connected)=> \\pset time Kitchen\nTime display is \"Kitchen\" (\"3:04PM\").\n(not connected)=> \\a\nOutput format is unaligned.\n```\n\nDisplay formatting variables can be displayed with `\\pset`:\n\n```sh\n(not connected)=> \\pset\ntime                     Kitchen\n```\n\n##### Other Variables\n\nRuntime behavior, such as [enabling or disabling syntax\nhighlighting][highlighting] can be modified through special variables like\n[`SYNTAX_HL`][highlighting].\n\nUse the `\\? variables` [command][commands] to display variable help information\nand to list special variables recognized by `usql`:\n\n```sh\n(not connected)=> \\? variables\n```\n\n#### Backticks\n\n[Backslash (`\\`) meta commands][commands] support backticks on parameters:\n\n```sh\n(not connected)=> \\echo Welcome `echo $USER` -- 'currently:' \"(\" `date` \")\"\nWelcome ken -- currently: ( Wed Jun 13 12:10:27 WIB 2018 )\n(not connected)=>\n```\n\nBackticked parameters will be passed to the user's `SHELL`, exactly as written,\nand can be combined with `\\set`:\n\n```sh\npg:booktest@localhost=> \\set MYVAR `date`\npg:booktest@localhost=> \\set\nMYVAR = 'Wed Jun 13 12:17:11 WIB 2018'\npg:booktest@localhost=> \\echo :MYVAR\nWed Jun 13 12:17:11 WIB 2018\npg:booktest@localhost=>\n```\n\n#### Copying Between Databases\n\n`usql` provides a `\\copy` command that reads data from a source database DSN\nand writes to a destination database DSN:\n\n```sh\n(not connected)=> \\cset PGDSN postgres://user:pass@localhost\n(not connected)=> \\cset MYDSN mysql://user:pass@localhost\n(not connected)=> \\copy PGDSN MYDSN 'select book_id, author_id from books' 'books(id, author_id)'\n```\n\nAs demonstrated above, the `\\copy` command does not require being connected to\na database, and will not modify or change the current open database connection\nor state.\n\nAny valid URL or DSN name maybe used for the source and destination database:\n\n```sh\n(not connected)=> \\cset MYDSN mysql://user:pass@localhost\n(not connected)=> \\copy postgres://user:pass@localhost MYDSN 'select book_id, author_id from books' 'books(id, author_id)'\n```\n\n<hr/>\n\n> **Note**\n>\n> `usql`'s `\\copy` is distinct from and <b><u>does not</u></b> function like\n> `psql`'s `\\copy`.\n\n<hr/>\n\n##### Copy Parameters\n\nThe `\\copy` command has two parameter forms:\n\n```txt\n\\copy SRC DST QUERY TABLE\n\\copy SRC DST QUERY TABLE(COL1, COL2, ..., COLN)\n```\n\nWhere:\n\n- `SRC` - is the [source database URL][connecting] to connect to, and where the\n  `QUERY` will be executed\n- `DST` - is the [destination database URL][connecting] to connect to, and where\n  the destination `TABLE` resides\n- `QUERY` - is the query to execute on the `SRC` connection, the results of which\n  will be copied to `TABLE`\n- `TABLE` - is the destination table name, followed by an optional SQL-like column\n  list of the form `(COL1, COL2, ..., COLN)`\n- `(COL1, COL2, ..., COLN)` - a list of the destination column names, 1-to-N\n\nThe usual rules for [variables, interpolation, and quoting][variables] apply to\n`\\copy`'s parameters.\n\n###### Quoting\n\n`QUERY` and `TABLE` **_must_** be quoted when containing spaces:\n\n```sh\n$ usql\n(not connected)=> echo :SOURCE_DSN :DESTINATION_DSN\npg://postgres:P4ssw0rd@localhost/ mysql://localhost\n(not connected)=> \\copy :SOURCE_DSN :DESTINATION_DSN 'select * from mySourceTable' 'myDestination(colA, colB)'\nCOPY 2\n```\n\n###### Column Counts\n\nThe `QUERY` **_must_** return the same number of columns as defined by\nthe `TABLE` expression:\n\n```sh\n$ usql\n(not connected)=> \\copy csvq:. sq:test.db 'select * from authors' authors\nerror: failed to prepare insert query: 2 values for 1 columns\n(not connected)=> \\copy csvq:. sq:test.db 'select name from authors' authors(name)\nCOPY 2\n```\n\n###### Datatype Compatibility and Casting\n\nThe `\\copy` command does not attempt to perform any kind of datatype\nconversion.\n\nIf a `QUERY` returns columns with different datatypes than expected by the\n`TABLE`'s column, the `QUERY` can use the source database's conversion/casting\nfunctionality to cast columns to a datatype that will work for `TABLE`'s\ncolumns:\n\n```sh\n$ usql\n(not connected)=> \\copy postgres://user:pass@localhost mysql://user:pass@localhost 'SELECT uuid_column::TEXT FROM myPgTable' myMyTable\nCOPY 1\n```\n\n###### Importing Data from CSV\n\nThe `\\copy` command is capable of importing data from CSV's (or any other\ndatabase!) using the `csvq` driver:\n\n```sh\n$ cat authors.csv\nauthor_id,name\n1,Isaac Asimov\n2,Stephen King\n$ cat books.csv\nbook_id,author_id,title\n1,1,I Robot\n2,2,Carrie\n3,2,Cujo\n$ usql\n(not connected)=> -- setting variables to make connections easier\n(not connected)=> \\set SOURCE_DSN csvq://.\n(not connected)=> \\set DESTINATION_DSN sqlite3:booktest.db\n(not connected)=> -- connecting to the destination and creating the schema\n(not connected)=> \\c :DESTINATION_DSN\nConnected with driver sqlite3 (SQLite3 3.38.5)\n(sq:booktest.db)=> create table authors (author_id integer, name text);\nCREATE TABLE\n(sq:booktest.db)=> create table books (book_id integer not null primary key autoincrement, author_id integer, title text);\nCREATE TABLE\n(sq:booktest.db)=> -- adding an extra row to books prior to copying\n(sq:booktest.db)=> insert into books (author_id, title) values (1, 'Foundation');\nINSERT 1\n(sq:booktest.db)=> -- disconnecting to demonstrate that \\copy opens new database connections\n(sq:booktest.db)=> \\disconnect\n(not connected)=> -- copying data from SOURCE -> DESTINATION\n(not connected)=> \\copy :SOURCE_DSN :DESTINATION_DSN 'select * from authors' authors\nCOPY 2\n(not connected)=> \\copy :SOURCE_DSN :DESTINATION_DSN 'select author_id, title from books' 'books(author_id, title)'\nCOPY 3\n(not connected)=> \\c :DESTINATION_DSN\nConnected with driver sqlite3 (SQLite3 3.38.5)\n(sq:booktest.db)=> select * from authors;\n author_id |     name\n-----------+--------------\n         1 | Isaac Asimov\n         2 | Stephen King\n(2 rows)\n\nsq:booktest.db=> select * from books;\n book_id | author_id |   title\n---------+-----------+------------\n       1 |         1 | Foundation\n       2 |         1 | I Robot\n       3 |         2 | Carrie\n       4 |         2 | Cujo\n(4 rows)\n```\n\n<hr/>\n\n> **Note**\n>\n> When importing large datasets (> 1GiB) from one database to another, it is\n> better to use a database's native clients and tools.\n\n<hr/>\n\n###### Reusing Connections with Copy\n\nThe `\\copy` command (and all `usql` commands) [works with variables][variables].\nWhen scripting, or when needing to perform multiple `\\copy` operations from/to\nmultiple sources/destinations, the best practice is to `\\set` connection\nvariables either in a script or in [the `$HOME/.usqlrc` RC script][usqlrc].\n\nSimilarly, passwords can be stored for easy reuse (and kept out of scripts) by\nstoring in [the `$HOME/.usqlpass` password file][usqlpass].\n\nFor example:\n\n```sh\n$ cat $HOME/.usqlpass\npostgres:*:*:*:postgres:P4ssw0rd\ngodror:*:*:*:system:P4ssw0rd\n$ usql\nType \"help\" for help.\n\n(not connected)=> \\set pglocal postgres://postgres@localhost:49153?sslmode=disable\n(not connected)=> \\set orlocal godror://system@localhost:1521/orasid\n(not connected)=> \\copy :pglocal :orlocal 'select staff_id, first_name from staff' 'staff(staff_id, first_name)'\nCOPY 18\n```\n\n#### Syntax Highlighting\n\nInteractive queries will be syntax highlighted by default, using\n[Chroma][chroma]. There are a number of [variables][] that control syntax\nhighlighting:\n\n| Variable                | Default                         | Values            | Description                                                  |\n| ----------------------- | ------------------------------- | ----------------- | ------------------------------------------------------------ |\n| `SYNTAX_HL`             | `true`                          | `true` or `false` | enables syntax highlighting                                  |\n| `SYNTAX_HL_FORMAT`      | _dependent on terminal support_ | formatter name    | [Chroma formatter name][chroma-formatter]                    |\n| `SYNTAX_HL_OVERRIDE_BG` | `true`                          | `true` or `false` | enables overriding the background color of the chroma styles |\n| `SYNTAX_HL_STYLE`       | `monokai`                       | style name        | [Chroma style name][chroma-style]                            |\n\nThe `SYNTAX_*` variables are regular `usql` variables, and can be `\\set` and\n`\\unset`:\n\n```sh\n$ usql\n(not connected)=> \\set SYNTAX_HL_STYLE dracula\n(not connected)=> \\unset SYNTAX_HL_OVERRIDE_BG\n```\n\n#### Context Completion\n\nWhen using the interactive shell, context completion is available in `usql` by\nhitting the `<Tab>` key. For example, hitting `<Tab>` can complete some parts\nof `SELECT` queries on a PostgreSQL databases:\n\n```sh\n$ usql\nConnected with driver postgres (PostgreSQL 14.4 (Debian 14.4-1.pgdg110+1))\nType \"help\" for help.\n\npg:postgres@=> select * f<Tab>\nfetch            from             full outer join\n```\n\nOr, for example completing [backslash commands][commands] while connected to a\ndatabase:\n\n```sh\n$ usql my://\nConnected with driver mysql (10.8.3-MariaDB-1:10.8.3+maria~jammy)\nType \"help\" for help.\n\nmy:root@=> \\g<Tab>\n\\g     \\gexec \\gset  \\gx\n```\n\nNot all commands, contexts, or databases support completion. If you're\ninterested in helping to make `usql`'s completion better, see [the section\nbelow on contributing][contributing].\n\nCommand completion can be canceled with `<Control-C>`.\n\n#### Time Formatting\n\nSome databases support time/date columns that [support formatting][go-time]. By\ndefault, `usql` formats time/date columns as [RFC3339Nano][go-time], and can be\nset using `\\pset time FORMAT`:\n\n```sh\n$ usql pg://\nConnected with driver postgres (PostgreSQL 13.2 (Debian 13.2-1.pgdg100+1))\nType \"help\" for help.\n\npg:postgres@=> \\pset\ntime                     RFC3339Nano\npg:postgres@=> select now();\n             now\n-----------------------------\n 2021-05-01T22:21:44.710385Z\n(1 row)\n\npg:postgres@=> \\pset time Kitchen\nTime display is \"Kitchen\" (\"3:04PM\").\npg:postgres@=> select now();\n   now\n---------\n 10:22PM\n(1 row)\n\npg:postgres@=>\n```\n\n`usql`'s time format supports any [Go supported time format][go-time], or can\nbe any standard Go const name, such as `Kitchen` above. See below for an\noverview of the [available time constants](#time-constants).\n\n##### Time Constants\n\nThe following are the time constant names available in `usql`, corresponding\ntime format value, and example display output:\n\n| Constant    |                                Format |        Display <sup>[↓][f-ts]</sup> |\n| ----------- | ------------------------------------: | ----------------------------------: |\n| ANSIC       |            `Mon Jan _2 15:04:05 2006` |          `Wed Aug  3 20:12:48 2022` |\n| UnixDate    |        `Mon Jan _2 15:04:05 MST 2006` |      `Wed Aug  3 20:12:48 UTC 2022` |\n| RubyDate    |      `Mon Jan 02 15:04:05 -0700 2006` |    `Wed Aug 03 20:12:48 +0000 2022` |\n| RFC822      |                 `02 Jan 06 15:04 MST` |               `03 Aug 22 20:12 UTC` |\n| RFC822Z     |               `02 Jan 06 15:04 -0700` |             `03 Aug 22 20:12 +0000` |\n| RFC850      |      `Monday, 02-Jan-06 15:04:05 MST` | `Wednesday, 03-Aug-22 20:12:48 UTC` |\n| RFC1123     |       `Mon, 02 Jan 2006 15:04:05 MST` |     `Wed, 03 Aug 2022 20:12:48 UTC` |\n| RFC1123Z    |     `Mon, 02 Jan 2006 15:04:05 -0700` |   `Wed, 03 Aug 2022 20:12:48 +0000` |\n| RFC3339     |           `2006-01-02T15:04:05Z07:00` |              `2022-08-03T20:12:48Z` |\n| RFC3339Nano | `2006-01-02T15:04:05.999999999Z07:00` |       `2022-08-03T20:12:48.693257Z` |\n| Kitchen     |                              `3:04PM` |                            `8:12PM` |\n| Stamp       |                     `Jan _2 15:04:05` |                   `Aug  3 20:12:48` |\n| StampMilli  |                 `Jan _2 15:04:05.000` |               `Aug  3 20:12:48.693` |\n| StampMicro  |              `Jan _2 15:04:05.000000` |            `Aug  3 20:12:48.693257` |\n| StampNano   |           `Jan _2 15:04:05.000000000` |         `Aug  3 20:12:48.693257000` |\n\n[f-ts]: #f-ts \"Timestamp Value\"\n\n<p>\n  <i>\n    <a id=\"f-ts\"><sup>↓</sup> Generated using timestamp <code>2022-08-03T20:12:48.693257Z</code></a><br>\n  </i>\n</p>\n\n#### Host Connection Information\n\nBy default, `usql` displays connection information when connecting to a\ndatabase. This might cause problems with some databases or connections. This\ncan be disabled by setting the system environment variable `USQL_SHOW_HOST_INFORMATION`\nto `false`:\n\n```sh\n$ export USQL_SHOW_HOST_INFORMATION=false\n$ usql pg://booktest@localhost\nType \"help\" for help.\n\npg:booktest@=>\n```\n\n`SHOW_HOST_INFORMATION` is a standard [`usql` variable][variables],\nand can be `\\set` or `\\unset`. Additionally, it can be passed via the\ncommand-line using `-v` or `--set`:\n\n```sh\n$ usql --set SHOW_HOST_INFORMATION=false pg://\nType \"help\" for help.\n\npg:booktest@=> \\set SHOW_HOST_INFORMATION true\npg:booktest@=> \\connect pg://\nConnected with driver postgres (PostgreSQL 9.6.9)\npg:booktest@=>\n```\n\n#### Terminal Graphics\n\n`usql` supports terminal graphics for [Kitty][kitty-graphics], [iTerm][iterm-graphics],\nand [Sixel][sixel-graphics] enabled terminals using the [`github.com/kenshaw/rasterm` package][rasterm].\nTerminal graphics are only available when using the interactive shell.\n\n##### Detection and Support\n\n`usql` will attempt to detect when terminal graphics support is available using\nthe `USQL_TERM_GRAPHICS`, `TERM_GRAPHICS` and other environment variables\nunique to various terminals.\n\nWhen support is available, the logo will be displayed at the start of an\ninteractive session:\n\n<div style=\"padding-left: 20px;\">\n  <img src=\"https://raw.githubusercontent.com/xo/usql-logo/master/usql-interactive.png\" height=\"120\">\n</div>\n\n##### Charts and Graphs\n\nThe [`\\chart` command][chart-command] can be used to display a chart\ndirectly in the terminal:\n\n<div style=\"padding-left: 20px;\">\n  <img src=\"https://raw.githubusercontent.com/xo/usql-logo/master/chart-example.png\" height=\"120\">\n</div>\n\nSee [the section on the `\\chart` meta command][chart-command] for details.\n\n##### Enabling/Disabling Terminal Graphics\n\nTerminal graphics can be forced enabled or disabled by setting the\n`USQL_TERM_GRAPHICS` or the `TERM_GRAPHICS` environment variable:\n\n```sh\n# disable\n$ USQL_TERM_GRAPHICS=none usql\n\n# force iterm graphics\n$ TERM_GRAPHICS=iterm usql\n```\n\n| Variable        | Default | Values                                | Description                    |\n| --------------- | ------- | ------------------------------------- | ------------------------------ |\n| `TERM_GRAPHICS` | ``      | ``, `kitty`, `iterm`, `sixel`, `none` | enables/disables term graphics |\n\n##### Terminals with Graphics Support\n\nThe following terminals have been tested with `usql`:\n\n- [WezTerm][wezterm] is a cross-platform terminal for Windows, macOS, Linux, and\n  many other platforms that supports [iTerm][iterm-graphics] graphics\n\n- [iTerm2][iterm2] is a macOS terminal that supports [iTerm][iterm-graphics]\n  graphics\n\n- [kitty][kitty] is a terminal for Linux, macOS, and various BSDs that supports\n  [Kitty][kitty-graphics] graphics\n\n- [foot][foot] is a Wayland terminal for Linux (and other Wayland hosts) that\n  supports [Sixel][sixel-graphics] graphics\n\nAdditional terminals that support [Sixel][sixel-graphics] graphics are\ncatalogued on the [Are We Sixel Yet?][arewesixelyet] website.\n\n#### Passwords\n\n`usql` supports reading passwords for databases from a `.usqlpass` file\ncontained in the user's `HOME` directory at startup:\n\n```sh\n$ cat $HOME/.usqlpass\n# format is:\n# protocol:host:port:dbname:user:pass\npostgres:*:*:*:booktest:booktest\n$ usql pg://\nConnected with driver postgres (PostgreSQL 9.6.9)\nType \"help\" for help.\n\npg:booktest@=>\n```\n\nWhile the `.usqlpass` functionality will not be removed, it is recommended to\n[define named connections][connection-vars] preferably via [the `config.yaml`\nfile][config].\n\n<hr/>\n\n> **Note**\n>\n> The `.usqlpass` file cannot be readable by other users, and the permissions\n> should be set accordingly:\n\n```sh\nchmod 0600 ~/.usqlpass\n```\n\n<hr/>\n\n#### Runtime Configuration (RC) File\n\n`usql` supports executing a `.usqlrc` runtime configuration (RC) file contained\nin the user's `HOME` directory:\n\n```sh\n$ cat $HOME/.usqlrc\n\\echo WELCOME TO THE JUNGLE `date`\n\\set SYNTAX_HL_STYLE paraiso-dark\n\n-- set color prompt (default is prompt is \"%S%m%/%R%#\" )\n\\set PROMPT1 \"\\033[32m%S%m%/%R%#\\033[0m\"\n$ usql\nWELCOME TO THE JUNGLE Thu Jun 14 02:36:53 WIB 2018\nType \"help\" for help.\n\n(not connected)=> \\set\nSYNTAX_HL_STYLE = 'paraiso-dark'\n(not connected)=>\n```\n\nThe `.usqlrc` file is read at startup in the same way as a file passed on the\ncommand-line with `-f` / `--file`. It is commonly used to set startup\nenvironment variables and settings.\n\nRC-file execution can be temporarily disabled at startup by passing `-X` or\n`--no-init` on the command-line:\n\n```sh\n$ usql --no-init pg://\n```\n\nWhile the `.usqlrc` functionality will not be removed, it is recommended to set\nan `init` script in [the `config.yaml` file][config].\n\n## Additional Notes\n\nThe following are additional notes and miscellania related to `usql`:\n\n### Release Builds\n\n[Release builds][releases] are built with the `most` build tag and with\nadditional [SQLite3 build tags (see: `build.sh`)](build.sh).\n\n### macOS\n\nThe recommended installation method on macOS is [via `brew`][via Homebrew] due\nto the way library dependencies for the `sqlite3` driver are done on macOS. If\nthe following (or similar) error is encountered when attempting to run `usql`:\n\n```sh\n$ usql\ndyld: Library not loaded: /usr/local/opt/icu4c/lib/libicuuc.68.dylib\n  Referenced from: /Users/user/.local/bin/usql\n  Reason: image not found\nAbort trap: 6\n```\n\nThen missing library dependency can be fixed by installing\n[`icu4c`](http://site.icu-project.org) using `brew`:\n\n```sh\n$ brew install icu4c\nRunning `brew update --auto-update`...\n==> Downloading ...\n...\n\n$ usql\n(not connected)=>\n```\n\n## Contributing\n\n`usql` is currently a WIP, and is aiming towards a 1.0 release soon.\nWell-written PRs are always welcome -- and there is a clear backlog of issues\nmarked `help wanted` on the GitHub issue tracker! For [technical details on\ncontributing, see CONTRIBUTING.md](CONTRIBUTING.md).\n\n[_Pick up an issue today, and submit a PR tomorrow!_][help-wanted]\n\n## Related Projects\n\n- [dburl][dburl] - Go package providing a standard, URL-style mechanism for parsing\n  and opening database connection URLs\n- [xo][xo] - Go command-line tool to generate Go code from a database schema\n\n[dburl]: https://github.com/xo/dburl\n[dburl-schemes]: https://github.com/xo/dburl#protocol-schemes-and-aliases\n[go-time]: https://pkg.go.dev/time#pkg-constants\n[go-sql]: https://pkg.go.dev/database/sql\n[homebrew]: https://brew.sh/\n[xo]: https://github.com/xo/xo\n[xo-tap]: https://github.com/xo/homebrew-xo\n[chroma]: https://github.com/alecthomas/chroma\n[chroma-formatter]: https://github.com/alecthomas/chroma#formatters\n[chroma-style]: https://xyproto.github.io/splash/docs/all.html\n[help-wanted]: https://github.com/xo/usql/issues?q=is:open+is:issue+label:%22help+wanted%22\n[aur]: https://aur.archlinux.org/packages/usql\n[yay]: https://github.com/Jguer/yay\n[arch-makepkg]: https://wiki.archlinux.org/title/makepkg\n[backticks]: #backticks \"Backticks\"\n[config]: #configuration \"Configuration\"\n[commands]: #backslash-commands \"Backslash Commands\"\n[completion]: #context-completion \"Context Completion\"\n[connecting]: #connecting-to-databases \"Connecting to Databases\"\n[contributing]: #contributing \"Contributing\"\n[copying]: #copying-between-databases \"Copying Between Databases\"\n[highlighting]: #syntax-highlighting \"Syntax Highlighting\"\n[termgraphics]: #terminal-graphics \"Terminal Graphics\"\n[timefmt]: #time-formatting \"Time Formatting\"\n[usqlpass]: #passwords \"Passwords\"\n[usqlrc]: #runtime-configuration-rc-file \"Runtime Configuration File\"\n[variables]: #variables \"Variables\"\n[runtime-vars]: #runtime-variables \"Runtime Variables\"\n[connection-vars]: #connection-variables \"Connection Variables\"\n[print-vars]: #display-formatting-(print)-variables \"Display Formatting (print) Variables\"\n[kitty-graphics]: https://sw.kovidgoyal.net/kitty/graphics-protocol.html\n[iterm-graphics]: https://iterm2.com/documentation-images.html\n[sixel-graphics]: https://saitoha.github.io/libsixel/\n[rasterm]: https://github.com/kenshaw/rasterm\n[wezterm]: https://wezfurlong.org/wezterm/\n[iterm2]: https://iterm2.com\n[foot]: https://codeberg.org/dnkl/foot\n[kitty]: https://sw.kovidgoyal.net/kitty/\n[arewesixelyet]: https://www.arewesixelyet.com\n[chart-command]: #chart-command \"\\\\chart meta command\"\n[yaml]: https://yaml.org\n"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\n\nset -e\n\nSRC=$(realpath $(cd -P \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd))\n\nNAME=$(basename $SRC)\nVER=\nSTATIC=0\nFORCE=0\nCHECK=1\nINSTALL=0\nBUILDONLY=0\nVERBOSE=false\nCGO_ENABLED=1\nLDNAME=github.com/xo/usql/text.CommandName\nLDVERSION=github.com/xo/usql/text.CommandVersion\nPLATFORM=$(go env GOOS)\nARCH=$(go env GOARCH)\nGOARCH=$ARCH\n\nTAGS=(\n  most\n  sqlite_app_armor\n  sqlite_fts5\n  sqlite_introspect\n  sqlite_json1\n  sqlite_math_functions\n  sqlite_stat4\n  sqlite_vtable\n)\n\nlatest_tag() {\n  # get latest tag version\n  pushd $SRC &> /dev/null\n  git tag -l|grep -E '^v[0-9]+\\.[0-9]+\\.[0-9]+(\\.[0-9]+)?$'|sort -r -V|head -1||:\n  popd &> /dev/null\n}\n\nOPTIND=1\nwhile getopts \"a:v:sfnibxt:r\" opt; do\ncase \"$opt\" in\n  a) ARCH=$OPTARG ;;\n  v) VER=$OPTARG ;;\n  s) STATIC=1 ;;\n  f) FORCE=1 ;;\n  n) CHECK=0 ;;\n  i) INSTALL=1 ;;\n  b) BUILDONLY=1 ;;\n  x) VERBOSE=true ;;\n  t) TAGS=($OPTARG) ;;\n  r) VER=$(latest_tag) ;;\nesac\ndone\n\n# neither -v or -r specified, or -v=master, set FORCE and VER\nif [[ \"$VER\" = \"\" || \"$VER\" == \"master\" ]]; then\n  VER=0.0.0-dev\n  FORCE=1\nfi\n\nVER=\"${VER#v}\"\n\nBUILD=$SRC/build\nDIR=$BUILD/$PLATFORM/$ARCH/$VER\n\nTAR=tar\nEXT=tar.bz2\nBIN=$DIR/$NAME\n\ncase $PLATFORM in\n  linux)\n    TAGS+=(no_adodb)\n  ;;\n  windows)\n    EXT=zip\n    BIN=$BIN.exe\n  ;;\n  darwin)\n    TAGS+=(no_adodb)\n    TAR=gtar\n  ;;\nesac\nOUT=$DIR/$NAME-$VER-$PLATFORM-$ARCH.$EXT\n\nCARCH=\nQEMUARCH=\nGNUTYPE=\nCC=\nCXX=\nEXTLD=g++\n\nif [[ \"$PLATFORM\" == \"linux\" && \"$ARCH\" != \"$GOARCH\" ]]; then\n  case $ARCH in\n    arm)   CARCH=armhf   QEMUARCH=arm     GNUTYPE=gnueabihf ;;\n    arm64) CARCH=aarch64 QEMUARCH=aarch64 GNUTYPE=gnu ;;\n    *)\n      echo \"error: unknown arch $ARCH\"\n      exit 1\n    ;;\n  esac\n  LDARCH=$CARCH\n  if [[ \"$ARCH\" == \"arm\" ]]; then\n    TAGS+=(no_netezza no_chai)\n    if [ -d /usr/arm-linux-$GNUTYPE ]; then\n      LDARCH=arm\n    elif [ -d /usr/arm-none-linux-$GNUTYPE ]; then\n      LDARCH=arm-none\n    fi\n  fi\n  CC=$LDARCH-linux-$GNUTYPE-gcc\n  CXX=$LDARCH-linux-$GNUTYPE-c++\n  EXTLD=$LDARCH-linux-$GNUTYPE-g++\nfi\n\nif [[ \"$PLATFORM\" == \"linux\" && \"$ARCH\" != \"amd64\" ]] || [[ \"$PLATFORM\" == \"windows\" ]]; then\n  TAGS+=(no_duckdb)\nfi\n\nLDFLAGS=(\n  -s\n  -w\n  -X $LDNAME=$NAME\n  -X $LDVERSION=$VER\n)\n\nif [ \"$STATIC\" = \"1\" ]; then\n  OUT=$DIR/${NAME}_static-$VER-$PLATFORM-$ARCH.$EXT\n  BIN=$DIR/${NAME}_static\n  case $PLATFORM in\n    linux)\n      TAGS+=(\n        netgo\n        osusergo\n        minicore_disabled\n      )\n      EXTLDFLAGS=(\n        -static\n        -lm\n        -ldl\n      )\n      EXTLDFLAGS=\"${EXTLDFLAGS[@]}\"\n      LDFLAGS+=(\n        -linkmode=external\n        -extldflags \\'$EXTLDFLAGS\\'\n        -extld $EXTLD\n      )\n    ;;\n    *)\n      echo \"ERROR: fully static builds not currently supported for $PLATFORM/$ARCH\"\n      exit 1\n    ;;\n  esac\nfi\n\n# check not overwriting existing build artifacts\nif [[ -e $OUT && \"$FORCE\" != \"1\" && \"$INSTALL\" == \"0\" ]]; then\n  echo \"ERROR: $OUT exists and FORCE != 1 (try $0 -f)\"\n  exit 1\nfi\n\nTAGS=\"${TAGS[@]}\"\nLDFLAGS=\"${LDFLAGS[@]}\"\n\necho \"APP:         $NAME/${VER} ($PLATFORM/$ARCH)\"\nif [ \"$STATIC\" = \"1\" ]; then\n  echo \"STATIC:      yes\"\nfi\necho \"BUILD TAGS:  $TAGS\"\necho \"LDFLAGS:     $LDFLAGS\"\n\npushd $SRC &> /dev/null\n\nif [ -f $OUT ]; then\n  echo \"REMOVING:    $OUT\"\n  rm -rf $OUT\nfi\nmkdir -p $DIR\necho \"BUILDING:    $BIN\"\n\n# build\necho \"BUILD:\"\n\nVERB=build\nOUTPUT=\"-o $BIN\"\nif [ \"$INSTALL\" = \"1\" ]; then\n  VERB=install OUTPUT=\"\"\nelif [ \"$BUILDONLY\" = \"1\" ]; then\n  OUTPUT=\"\"\nfi\n(set -x;\n  CC=$CC \\\n  CXX=$CXX \\\n  CGO_ENABLED=$CGO_ENABLED \\\n  GOARCH=$ARCH \\\n  go $VERB \\\n    -v=$VERBOSE \\\n    -x=$VERBOSE \\\n    -ldflags=\"$LDFLAGS\" \\\n    -tags=\"$TAGS\" \\\n    -trimpath \\\n    $OUTPUT\n)\n\nif [[ \"$INSTALL\" == \"1\" || \"$BUILDONLY\" == \"1\" ]]; then\n  exit\nfi\n\n(set -x;\n  file $BIN\n)\nif [[ \"$PLATFORM\" != \"windows\" ]]; then\n  (set -x;\n    chmod +x $BIN\n  )\nfi\n\n# purge disk cache\nif [[ \"$PLATFORM\" == \"darwin\" && \"$CI\" == \"true\" ]]; then\n  (set -x;\n    sudo /usr/sbin/purge\n  )\nfi\n\nbuilt_ver() {\n  if [[ \"$PLATFORM\" == \"linux\" && \"$ARCH\" != \"$GOARCH\" ]]; then\n    EXTRA=\n    if [ -d /usr/$LDARCH-linux-$GNUTYPE/libc ]; then\n      EXTRA=\"-L /usr/$LDARCH-linux-$GNUTYPE/libc\"\n    fi\n    qemu-$QEMUARCH \\\n      -L /usr/$LDARCH-linux-$GNUTYPE \\\n      $EXTRA \\\n      $BIN --version\n  elif [[ \"$PLATFORM\" == \"darwin\" && \"$ARCH\" != \"$GOARCH\" ]]; then\n    echo \"$NAME ${VER#v}\"\n  else\n    $BIN --version\n  fi\n}\n\n# check build\nif [[ \"$CHECK\" == \"1\" ]]; then\n  BUILT_VER=$(built_ver)\n  if [ \"$BUILT_VER\" != \"$NAME ${VER#v}\" ]; then\n    echo -e \"\\n\\nERROR: expected $NAME --version to report '$NAME ${VER#v}', got: '$BUILT_VER'\"\n    exit 1\n  fi\n  echo \"REPORTED:    $BUILT_VER\"\nfi\n\n# pack\ncp $SRC/LICENSE $DIR\ncase $EXT in\n  tar.bz2) $TAR -C $DIR -cjf $OUT $(basename $BIN) LICENSE ;;\n  zip) zip $OUT -j $BIN LICENSE ;;\nesac\n\n# report\necho \"PACKED:      $OUT ($(du -sh $OUT|awk '{print $1}'))\"\n\ncase $EXT in\n  tar.bz2) (set -x; $TAR  -jvtf $OUT) ;;\n  zip)     (set -x; unzip -l    $OUT) ;;\nesac\n\n(set -x;\n  sha256sum $DIR/*\n)\n\npopd &> /dev/null\n"
  },
  {
    "path": "contrib/adodb/adodb.sh",
    "content": "#!/bin/bash\n\nrm -f example.csv\n\nusql \"adodb://Microsoft.ACE.OLEDB.12.0/?Extended+Properties=\\\"Text;HDR=NO;FMT=Delimited\\\"\" \\\n  -c \"create table example.csv(f1 text, f2 text, f3 text);\" \\\n  -c \"insert into example.csv(f1, f2, f3) values ('a', 'b', 'c');\" \\\n  -c \"select * from example.csv;\"\n"
  },
  {
    "path": "contrib/adodb/usql-config",
    "content": ""
  },
  {
    "path": "contrib/cassandra/podman-config",
    "content": "NAME=cassandra\nIMAGE=docker.io/usql/cassandra\nPUBLISH=9042:9042\n"
  },
  {
    "path": "contrib/cassandra/test.sql",
    "content": "USE cycling;\nCREATE KEYSPACE IF NOT EXISTS cycling WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };\n\n// Q1:\n-- Find a cyclist's name given an ID number\n// CREATE TABLE SIMPLE PRIMARY KEY\nCREATE TABLE cycling.cyclist_name ( id UUID PRIMARY KEY, lastname text, firstname text );\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (5b6962dd-3f90-4c93-8f61-eabfa4a803e2, 'VOS','Marianne');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (e7cd5752-bc0d-4157-a80f-7523add8dbcd, 'VAN DER BREGGEN','Anna');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (e7ae5cf3-d358-4d99-b900-85902fda9bb0, 'FRAME','Alex');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (220844bf-4860-49d6-9a4b-6b5d3a79cbfb, 'TIRALONGO','Paolo');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47, 'KRUIKSWIJK','Steven');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (fb372533-eb95-4bb4-8685-6ef61e994caa, 'MATTHEWS', 'Michael');\nSELECT * FROM cycling.cyclist_name;\nSELECT lastname, firstname FROM cycling.cyclist_name WHERE id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47;\n\n-- Q2:\n-- Find cyclists that fit a particular category\n// CREATE TABLE CLUSTERING ORDER, PRIMARY KEY: PARTITION KEY + 1 CLUSTERING COLUMN, SIMPLE WHERE QUERY\nCREATE TABLE cycling.cyclist_category ( category text, points int, id UUID, lastname text, PRIMARY KEY (category, points)) WITH CLUSTERING ORDER BY (points DESC);\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('GC',1269,220844bf-4860-49d6-9a4b-6b5d3a79cbfb,'TIRALONGO');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('One-day-races',367,220844bf-4860-49d6-9a4b-6b5d3a79cbfb,'TIRALONGO');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('Time-trial',182,220844bf-4860-49d6-9a4b-6b5d3a79cbfb,'TIRALONGO');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('Sprint',0,220844bf-4860-49d6-9a4b-6b5d3a79cbfb,'TIRALONGO');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('GC',1324,6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47,'KRUIJSWIJK');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('One-day-races',198,6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47,'KRUIJSWIJK');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('Sprint',39,6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47,'KRUIJSWIJK');\nINSERT INTO cycling.cyclist_category (category, points, id, lastname) VALUES ('Time-trial',3,6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47,'KRUIJSWIJK');\nSELECT * FROM cycling.cyclist_category;\nSELECT lastname, points FROM cycling.cyclist_category WHERE category = 'One-day-races';\n\n-- Q3:\n-- Store race information by year and race name using a COMPOSITE PARTITION KEY\nCREATE TABLE cycling.rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, PRIMARY KEY ((race_year, race_name), rank) );\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2015, 'Tour of Japan - Stage 4 - Minami > Shinshu', 'Benjamin PRADES', 1);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2015, 'Tour of Japan - Stage 4 - Minami > Shinshu', 'Adam PHELAN', 2);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2015, 'Tour of Japan - Stage 4 - Minami > Shinshu', 'Thomas LEBAS', 3);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2015, 'Giro d''Italia - Stage 11 - Forli > Imola', 'Ilnur ZAKARIN', 1);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2015, 'Giro d''Italia - Stage 11 - Forli > Imola', 'Carlos BETANCUR', 2);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2014, '4th Tour of Beijing', 'Phillippe GILBERT', 1);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2014, '4th Tour of Beijing', 'Daniel MARTIN', 2);\nINSERT INTO cycling.rank_by_year_and_name (race_year, race_name, cyclist_name, rank) VALUES (2014, '4th Tour of Beijing', 'Johan Esteban CHAVES', 3);\nSELECT * FROM cycling.rank_by_year_and_name;\nSELECT * FROM cycling.rank_by_year_and_name WHERE race_year=2015 AND race_name='Tour of Japan - Stage 4 - Minami > Shinshu';\n\n-- New C* 3.6\n-- PER PARTITION LIMIT\n-- To get the Top Two for each race_year-race_name pair\nSELECT * FROM cycling.rank_by_year_and_name PER PARTITION LIMIT 2;\n\n-- Q4:\n-- Find a cyclist's id given lastname and firstname\n-- Another CREATE TABLE using COMPOSITE PARTITION KEY\n-- 2i INDEX ALSO GOOD FOR THIS TABLE\nCREATE TABLE cycling.cyclist_id ( lastname text, firstname text, age int, id UUID, PRIMARY KEY ((lastname, firstname), age) );\nINSERT INTO cycling.cyclist_id (lastname, firstname, age, id) VALUES ('EENKHOORN','Pascal',18, ffdfa2a7-5fc6-49a7-bfdc-3fcdcfdd7156);\nINSERT INTO cycling.cyclist_id (lastname, firstname, age, id) VALUES ('WELTEN','Bram',18, 18f471bf-f631-4bc4-a9a2-d6f6cf5ea503);\nINSERT INTO cycling.cyclist_id (lastname, firstname, age, id) VALUES ('COSTA','Adrien',17, 15a116fc-b833-4da6-ab9a-4a7775752836);\nSELECT * FROM cycling.cyclist_id WHERE lastname = 'COSTA' AND firstname = 'Adrien';\n-- If you want to search by age, an index can be added\nCREATE INDEX c_age ON cycling.cyclist_id (age);\nSELECT * FROM cycling.cyclist_id WHERE age = 18;\n\n-- Q5:\n-- Display flag for riders\n-- CREATE TABLE WITH STATIC COLUMN, example uses an integer to identify flag, but it could be a blob\nCREATE TABLE cycling.country_flag (country text, cyclist_name text, flag int STATIC, PRIMARY KEY (country, cyclist_name));\nINSERT INTO cycling.country_flag (country, cyclist_name, flag) VALUES ('Belgium', 'Jacques', 1);\nINSERT INTO cycling.country_flag (country, cyclist_name) VALUES ('Belgium', 'Andre');\nINSERT INTO cycling.country_flag (country, cyclist_name, flag) VALUES ('France', 'Andre', 2);\nINSERT INTO cycling.country_flag (country, cyclist_name, flag) VALUES ('France', 'George', 3);\n-- USE SELECT REPEATEDLY TO SHOW CHANGING (OR UNCHANGING) NATURE OF the column 'flag'\nSELECT * FROM cycling.country_flag;\n\n-- Q6:\n-- Find all teams that a cyclist has been a member of\n--CREATE TABLE WITH SET\nCREATE TABLE cycling.cyclist_career_teams ( id UUID PRIMARY KEY, lastname text, teams set<text> );\nINSERT INTO cycling.cyclist_career_teams (id,lastname,teams) VALUES (5b6962dd-3f90-4c93-8f61-eabfa4a803e2, 'VOS', { 'Rabobank-Liv Woman Cycling Team','Rabobank-Liv Giant','Rabobank Women Team','Nederland bloeit' } );\nINSERT INTO cycling.cyclist_career_teams (id,lastname,teams) VALUES (e7cd5752-bc0d-4157-a80f-7523add8dbcd, 'VAN DER BREGGEN', { 'Rabobank-Liv Woman Cycling Team','Sengers Ladies Cycling Team','Team Flexpoint' } );\nINSERT INTO cycling.cyclist_career_teams (id,lastname,teams) VALUES (cb07baad-eac8-4f65-b28a-bddc06a0de23, 'ARMITSTEAD', { 'Boels-Dolmans Cycling Team','AA Drink - Leontien.nl','Team Garmin - Cervelo' } );\nINSERT INTO cycling.cyclist_career_teams (id,lastname,teams) VALUES (1c9ebc13-1eab-4ad5-be87-dce433216d40, 'BRAND', { 'Rabobank-Liv Woman Cycling Team','Rabobank-Liv Giant','AA Drink - Leontien.nl','Leontien.nl' } );\nSELECT lastname,teams FROM cycling.cyclist_career_teams;\nSELECT lastname, teams FROM cycling.cyclist_career_teams WHERE id=5b6962dd-3f90-4c93-8f61-eabfa4a803e2;\n\n-- NOT A QUERY, JUST A TABLE FOR QUERIES\n-- CREATE TABLE WITH LIST FOR UPDATE\n-- The SELECT statements that use this table can be found below\nCREATE TABLE cycling.calendar (race_id int, race_name text, race_start_date timestamp, race_end_date timestamp, PRIMARY KEY (race_id, race_start_date, race_end_date));\nINSERT INTO cycling.calendar (race_id, race_name, race_start_date, race_end_date) VALUES (100, 'Giro d''Italia','2015-05-09','2015-05-31');\nINSERT INTO cycling.calendar (race_id, race_name, race_start_date, race_end_date) VALUES (101, 'Criterium du Dauphine','2015-06-07','2015-06-14');\nINSERT INTO cycling.calendar (race_id, race_name, race_start_date, race_end_date) VALUES (102, 'Tour de Suisse','2015-06-13','2015-06-21');\nINSERT INTO cycling.calendar (race_id, race_name, race_start_date, race_end_date) VALUES (103, 'Tour de France','2015-07-04','2015-07-26');\nSELECT * FROM cycling.calendar;\n\n-- NEW FOR C*3.6\n-- Clustering columns can be used in a WHERE clause with ALLOW FILTERING without secondary indexes\n-- This query uses the clustering column \"race_start_date\" without an index and without using the partition key\n-- but using ALLOW FILTERING\nSELECT * FROM cycling.calendar WHERE race_start_date='2015-06-13' ALLOW FILTERING;\n\n-- Q7:\n-- Find all calendar events for a particular year and month\nCREATE TABLE cycling.upcoming_calendar ( year int, month int, events list<text>, PRIMARY KEY ( year, month ));\nINSERT INTO cycling.upcoming_calendar (year, month, events) VALUES (2015, 06, ['Criterium du Dauphine','Tour de Suisse']);\nINSERT INTO cycling.upcoming_calendar (year, month, events) VALUES (2015, 07, ['Tour de France']);\nSELECT * FROM cycling.upcoming_calendar WHERE year=2015 AND month=06;\n\n-- Q8:\n-- SIMPLE USER-DEFINED TYPE\nCREATE TYPE cycling.fullname ( firstname text, lastname text );\nCREATE TABLE cycling.race_winners (race_name text, race_position int, cyclist_name FROZEN<fullname>, PRIMARY KEY (race_name, race_position));\nINSERT INTO cycling.race_winners (race_name, race_position, cyclist_name) VALUES ('National Championships South Africa WJ-ITT (CN)', 1, {firstname:'Frances',lastname:'DU TOUT'});\nINSERT INTO cycling.race_winners (race_name, race_position, cyclist_name) VALUES ('National Championships South Africa WJ-ITT (CN)', 2, {firstname:'Lynette',lastname:'BENSON'});\nINSERT INTO cycling.race_winners (race_name, race_position, cyclist_name) VALUES ('National Championships South Africa WJ-ITT (CN)', 3, {firstname:'Anja',lastname:'GERBER'});\nINSERT INTO cycling.race_winners (race_name, race_position, cyclist_name) VALUES ('National Championships South Africa WJ-ITT (CN)', 4, {firstname:'Ame',lastname:'VENTER'});\nINSERT INTO cycling.race_winners (race_name, race_position, cyclist_name) VALUES ('National Championships South Africa WJ-ITT (CN)', 5, {firstname:'Danielle',lastname:'VAN NIEKERK'});\nSELECT * FROM cycling.race_winners WHERE race_name = 'National Championships South Africa WJ-ITT (CN)';\n\n-- Q9:\n-- Find all races for a particular cyclist\n-- CREATE TYPE - User-Defined Type, race\n-- CREATE TABLE WITH LIST, SIMPLE PRIMARY KEY\nCREATE TYPE cycling.race (race_title text, race_date timestamp, race_time text);\nCREATE TABLE cycling.cyclist_races ( id UUID PRIMARY KEY, lastname text, firstname text, races list<FROZEN <race>> );\nINSERT INTO cycling.cyclist_races (id, lastname, firstname, races) VALUES (5b6962dd-3f90-4c93-8f61-eabfa4a803e2, 'VOS', 'Marianne', [ {race_title:'Rabobank 7-Dorpenomloop Aalburg',race_date:'2015-05-09',race_time:'02:58:33'},{race_title:'Ronde van Gelderland',race_date:'2015-04-19',race_time:'03:22:23'}\n]);\nINSERT INTO cycling.cyclist_races (id, lastname, firstname, races) VALUES (e7cd5752-bc0d-4157-a80f-7523add8dbcd, 'VAN DER BREGGEN', 'Anna', [ {race_title:'Festival Luxembourgeois du cyclisme feminin Elsy Jacobs - Prologue - Garnich > Garnich',race_date:'2015-05-01',race_time:'08:13:00'},{race_title:'Fest\nival Luxembourgeois du cyclisme feminin Elsy Jacobs - Stage 2 - Garnich > Garnich',race_date:'2015-05-02',race_time:'02:41:52'},{race_title:'Festival Luxembourgeois du cyclisme feminin Elsy Jacobs - Stage 3 - Mamer > Mamer',race_date:'2015-05-03',race_time:'02:31:24'} ]);\nSELECT * FROM cycling.cyclist_races;\nSELECT lastname, races FROM cycling.cyclist_races WHERE id = e7cd5752-bc0d-4157-a80f-7523add8dbcd;\n\n-- Q10:\n-- Find all teams for a particular cyclist associated with the year of membership\n-- teams map<int, text> is map<year, team_name>\n-- CREATE TABLE WITH MAP, SIMPLE PRIMARY KEY\nCREATE TABLE cycling.cyclist_teams ( id UUID PRIMARY KEY, lastname text, firstname text, teams map<int,text> );\nINSERT INTO cycling.cyclist_teams (id, lastname, firstname, teams) VALUES (5b6962dd-3f90-4c93-8f61-eabfa4a803e2,'VOS', 'Marianne', {2015 : 'Rabobank-Liv Woman Cycling Team', 2014 : 'Rabobank-Liv Woman Cycling Team', 2013 : 'Rabobank-Liv Giant', 2012 : 'Rabobank Women Team', 2011 : 'Nederland bloeit' });\nINSERT INTO cycling.cyclist_teams (id, lastname, firstname, teams) VALUES (e7cd5752-bc0d-4157-a80f-7523add8dbcd,'VAN DER BREGGEN', 'Anna', {2015 : 'Rabobank-Liv Woman Cycling Team', 2014 : 'Rabobank-Liv Woman Cycling Team', 2013 : 'Sengers Ladies Cycling Team', 2012 : 'Sengers Ladies Cycling Team', 2009 : 'Team Flexpoint' });\nINSERT INTO cycling.cyclist_teams (id, lastname, firstname, teams) VALUES (cb07baad-eac8-4f65-b28a-bddc06a0de23,'ARMITSTEAD', 'Elizabeth', {2015 : 'Boels-Dolmans Cycling Team', 2014 : 'Boels-Dolmans Cycling Team', 2013 : 'Boels-Dolmans Cycling Team', 2012 : 'AA Drink - Leontien.nl', 2011 : 'Team Garmin - Cervelo' });\nSELECT lastname, firstname, teams FROM cycling.cyclist_teams;\nSELECT lastname, firstname, teams FROM cycling.cyclist_teams WHERE id=5b6962dd-3f90-4c93-8f61-eabfa4a803e2;\n\n-- Q11:\n-- Find all stats for a particular cyclist\n-- CREATE TYPE -  UDT, basic_info\n-- CREATE TABLE with UDT, SIMPLE PRIMARY KEY\nCREATE TYPE cycling.basic_info ( birthday timestamp, nationality text, weight text, height text );\nCREATE TABLE cycling.cyclist_stats ( id UUID, lastname text, basics FROZEN <basic_info>, PRIMARY KEY (id) );\nINSERT INTO cycling.cyclist_stats (id, lastname, basics) VALUES (e7ae5cf3-d358-4d99-b900-85902fda9bb0, 'FRAME', { birthday:'1993-06-18',nationality:'New Zealand',weight:null,height:null });\nINSERT INTO cycling.cyclist_stats (id, lastname, basics) VALUES (6cbc55e9-1943-47dc-91f2-f8f9e95992eb, 'VIGANO', { birthday:'1984-06-12',nationality:'Italy',weight:'67 kg',height:'1.82 m' });\nINSERT INTO cycling.cyclist_stats (id, lastname, basics) VALUES (220844bf-4860-49d6-9a4b-6b5d3a79cbfb, 'TIRALONGO', { birthday:'1977-07-08',nationality:'Italy',weight:'63 kg',height:'1.78 m' });\nSELECT * FROM cycling.cyclist_stats;\nSELECT * FROM cycling.cyclist_stats WHERE id = 220844bf-4860-49d6-9a4b-6b5d3a79cbfb;\n\n-- NEW IN C* 3.6\n-- UPDATE AND DELETE single fields in UDTs with only non-collection fields\n-- CHANGE \"CREATE TABLE IN LAST EXAMPLE TO non-frozen\nCREATE TABLE cycling.cyclist_stats ( id UUID, lastname text, basics basic_info, PRIMARY KEY (id) );\n-- Now birthday can be updated separate from nationality, weight, and height\nUPDATE cycling.cyclist_stats SET basics.birthday = '2000-12-12' WHERE id = 220844bf-4860-49d6-9a4b-6b5d3a79cbfb;\n\n-- Q12:\n-- Find total number of PCS points for a particular cyclist\n-- CREATE TABLE WITH PRIMARY KEY: PARTITION KEY + 1 CLUSTERING COLUMN\n-- USE STANDARD AGGREGATE IN QUERY\nCREATE TABLE cycling.cyclist_points (id UUID, firstname text, lastname text, race_title text, race_points int, PRIMARY KEY (id, race_points ));\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (e3b19ec4-774a-4d1c-9e5a-decec1e30aac, 'Giorgia','BRONZINI', 'Tour of Chongming Island World Cup', 120);\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (e3b19ec4-774a-4d1c-9e5a-decec1e30aac, 'Giorgia','BRONZINI', 'Trofeo Alfredo Binda - Comune di Cittiglio', 6);\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (e3b19ec4-774a-4d1c-9e5a-decec1e30aac, 'Giorgia','BRONZINI', 'Acht van Westerveld', 75);\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (220844bf-4860-49d6-9a4b-6b5d3a79cbfb, 'Paolo','TIRALONGO', '98th Giro d''Italia - Stage 15', 2);\nSELECT sum(race_points) FROM cycling.cyclist_points WHERE id=e3b19ec4-774a-4d1c-9e5a-decec1e30aac;\n\n-- Q13:\n-- USES TABLE cycling.cyclist_points\n-- Find total number of PCS points for a particular cyclist using a user-defined function (UDF) created using java function log\n-- cassandra.yaml must be modified to allow UDFs to work\n-- enable_user_defined_functions: true (false by default)\n-- CREATE UDF\nCREATE TABLE cycling.cyclist_points (id UUID, firstname text, lastname text, race_title text, race_points double, PRIMARY KEY (id, race_points ));\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (e3b19ec4-774a-4d1c-9e5a-decec1e30aac, 'Giorgia','BRONZINI', 'Tour of Chongming Island World Cup', 120);\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (e3b19ec4-774a-4d1c-9e5a-decec1e30aac, 'Giorgia','BRONZINI', 'Trofeo Alfredo Binda - Comune di Cittiglio', 6);\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (e3b19ec4-774a-4d1c-9e5a-decec1e30aac, 'Giorgia','BRONZINI', 'Acht van Westerveld', 75);\nINSERT INTO cycling.cyclist_points (id, firstname, lastname, race_title, race_points) VALUES (220844bf-4860-49d6-9a4b-6b5d3a79cbfb, 'Paolo','TIRALONGO', '98th Giro d''Italia - Stage 15', 2);\nCREATE OR REPLACE FUNCTION cycling.fLog (input double) CALLED ON NULL INPUT RETURNS double LANGUAGE java AS 'return Double.valueOf(Math.log(input.doubleValue()));';\nSELECT id, lastname, fLog(race_points) FROM cycling.cyclist_points;\n\n-- Q14:\n--Find the average race_time in seconds for a particular race for a particular team.\n-- CREATE UDA that computes the average value\n--CREATE TABLE WITH SIMPLE PRIMARY KEY: PARTITION KEY + 2 CLUSTERING COLUMNS\nCREATE OR REPLACE FUNCTION cycling.avgState ( state tuple<int,bigint>, val int ) CALLED ON NULL INPUT RETURNS tuple<int,bigint> LANGUAGE java AS 'if (val !=null) { state.setInt(0, state.getInt(0)+1); state.setLong(1, state.getLong(1)+val.intValue()); } return state;';\nCREATE OR REPLACE FUNCTION cycling.avgFinal ( state tuple<int,bigint> ) CALLED ON NULL INPUT RETURNS double LANGUAGE java AS 'double r = 0; if (state.getInt(0) == 0) return null; r = state.getLong(1); r/= state.getInt(0); return Double.valueOf(r);';\nCREATE AGGREGATE cycling.average ( int ) SFUNC avgState STYPE tuple<int,bigint> FINALFUNC avgFinal INITCOND (0,0);\nCREATE TABLE cycling.team_average (team_name text, cyclist_name text, cyclist_time_sec int, race_title text, PRIMARY KEY (team_name, race_title,cyclist_name));\nINSERT INTO cycling.team_average (team_name, cyclist_name, cyclist_time_sec, race_title) VALUES ('UnitedHealthCare Pro Cycling Womens Team','Katie HALL',11449,'Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe');\nINSERT INTO cycling.team_average (team_name, cyclist_name, cyclist_time_sec, race_title) VALUES ('UnitedHealthCare Pro Cycling Womens Team','Linda VILLUMSEN',11485,'Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe');\nINSERT INTO cycling.team_average (team_name, cyclist_name, cyclist_time_sec, race_title) VALUES ('UnitedHealthCare Pro Cycling Womens Team','Hannah BARNES',11490,'Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe');\nINSERT INTO cycling.team_average (team_name, cyclist_name, cyclist_time_sec, race_title) VALUES ('Velocio-SRAM','Alena AMIALIUSIK',11451,'Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe');\nINSERT INTO cycling.team_average (team_name, cyclist_name, cyclist_time_sec, race_title) VALUES ('Velocio-SRAM','Trixi WORRACK',11453,'Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe');\nINSERT INTO cycling.team_average (team_name, cyclist_name, cyclist_time_sec, race_title) VALUES ('TWENTY16 presented by Sho-Air','Lauren KOMANSKI',11451,'Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe');\nSELECT cycling.average(cyclist_time_sec) FROM cycling.team_average WHERE team_name='UnitedHealthCare Pro Cycling Womens Team' AND race_title='Amgen Tour of California Women''s Race presented by SRAM - Stage 1 - Lake Tahoe > Lake Tahoe';\n\n-- Q15:\n-- CREATE INDEX - PARTITION KEY\n-- Uses cycling.rank_by_year_and_name\n-- Find rank for all races for a particular race year\nCREATE INDEX ryear ON cycling.rank_by_year_and_name (race_year);\n-- This will not work without the index, because the table has a composite partition key\nSELECT * FROM cycling.rank_by_year_and_name WHERE race_year=2015;\n-- INDEX on clustering column\nCREATE INDEX rrank ON cycling.rank_by_year_and_name (rank);\nSELECT * FROM cycling.rank_by_year_and_name WHERE rank = 1;\n\n-- Q16:\n-- CREATE INDEX - COLLECTION - SET\n-- Find all the cyclists that have been on a particular team\nCREATE INDEX team ON cycling.cyclist_career_teams (teams);\nSELECT * FROM cycling.cyclist_career_teams WHERE teams CONTAINS 'Nederland bloeit';\nSELECT * FROM cycling.cyclist_career_teams WHERE teams CONTAINS 'Rabobank-Liv Giant';\n\n-- Q17:\n-- CREATE INDEX - COLLECTION ON MAP KEYS\n-- Find all cyclist/team combinations for a particular year\n-- CREATE TABLE cycling.cyclist_teams ( id UUID PRIMARY KEY, lastname text, firstname text, teams map<int,text> );\nCREATE INDEX team_year ON cycling.cyclist_teams (KEYS(teams));\nSELECT * FROM cycling.cyclist_teams WHERE teams CONTAINS KEY 2015;\n\n-- Q35:\n-- CREATE INDEX - ENTRIES ON MAP KEYS\n-- ONLY VALID FOR MAP TYPE\nCREATE TABLE cycling.birthday_list (cyclist_name text PRIMARY KEY, blist map<text,text>);\nINSERT INTO cycling.birthday_list (cyclist_name, blist) VALUES ('Allan DAVIS', {'age':'35', 'bday':'27/07/1980', 'nation':'AUSTRALIA'});\nINSERT INTO cycling.birthday_list (cyclist_name, blist) VALUES ('Claudio VANDELLI', {'age':'54', 'bday':'27/07/1961', 'nation':'ITALY'});\nINSERT INTO cycling.birthday_list (cyclist_name, blist) VALUES ('Laurence BOURQUE', {'age':'23', 'bday':'27/07/1992', 'nation':'CANADA'});\nINSERT INTO cycling.birthday_list (cyclist_name, blist) VALUES ('Claudio HEINEN', {'age':'23', 'bday':'27/07/1992', 'nation':'GERMANY'});\nINSERT INTO cycling.birthday_list (cyclist_name, blist) VALUES ('Luc HAGENAARS', {'age':'28', 'bday':'27/07/1987', 'nation':'NETHERLANDS'});\nINSERT INTO cycling.birthday_list (cyclist_name, blist) VALUES ('Toine POELS', {'age':'52', 'bday':'27/07/1963', 'nation':'NETHERLANDS'});\nCREATE INDEX blist_idx ON cycling.birthday_list (ENTRIES(blist));\nSELECT * FROM cycling.birthday_list WHERE blist['age'] = '23';\nSELECT * FROM cycling.birthday_list WHERE blist['nation'] = 'GERMANY';\nSELECT * FROM cycling.birthday_list WHERE blist['bday'] = '27/07/1992';\n\n-- Q36:\n-- CREATE INDEX - FULL ON FROZEN COLLECTION\n-- ONLY VALID FOR FROZEN COLLECTIONS (SET, LIST, MAP)\nCREATE TABLE cycling.race_starts (cyclist_name text PRIMARY KEY, rnumbers FROZEN<LIST<int>>);\nCREATE INDEX rnumbers_idx ON cycling.race_starts (FULL(rnumbers));\nINSERT INTO cycling.race_starts (cyclist_name,rnumbers) VALUES ('Alexander KRISTOFF',[40,5,14]);\nINSERT INTO cycling.race_starts (cyclist_name,rnumbers) VALUES ('Alejandro VALVERDE',[67,17,20]);\nINSERT INTO cycling.race_starts (cyclist_name,rnumbers) VALUES ('Alberto CONTADOR',[61,14,7]);\nINSERT INTO cycling.race_starts (cyclist_name,rnumbers) VALUES ('Christopher FROOME',[28,10,6]);\nINSERT INTO cycling.race_starts (cyclist_name,rnumbers) VALUES ('John DEGENKOLB',[39,7,14]);\nSELECT * FROM cycling.race_starts WHERE rnumbers = [39,7,14];\n\n-- NOT A QUERY, JUST AN EXAMPLE\n-- INSERT DATA IN JSON FORMAT\nINSERT INTO cycling.cyclist_category JSON '{ \"category\" : \"GC\", \"points\" : 780, \"id\" : \"829aa84a-4bba-411f-a4fb-38167a987cda\", \"lastname\" : \"SUTHERLAND\" }';\n-- null INSERTION EXAMPLE\nINSERT INTO cycling.cyclist_category JSON '{ \"category\" : \"Sprint\", \"points\" : 700, \"id\" : \"829aa84a-4bba-411f-a4fb-38167a987cda\" }';\n\n-- NOT A QUERY, JUST AN EXAMPLE\n-- UPDATE SET\n-- Can only be +\n-- Add team to a cyclist's list of teams, order doesn't matter; this example adds it to the end\nUPDATE cycling.cyclist_career_teams SET teams = teams + {'Team DSB - Ballast Nedam'} WHERE id=5b6962dd-3f90-4c93-8f61-eabfa4a803e2;\n\n-- NOT A QUERY, JUST AN EXAMPLE\n-- UPDATE LIST\n-- Add events to the events list with either +/- or a specific place in the list like events[2]\nUPDATE cycling.upcoming_calendar SET events = ['The Parx Casino Philly Cycling Classic'] + events WHERE year = 2015 AND month = 06;\nUPDATE cycling.upcoming_calendar SET events[2] = 'Vuelta Ciclista a Venezuela' WHERE year = 2015 AND month = 06;\n\n-- NOT A QUERY, JUST AN EXAMPLE\n-- UPDATE MAP\n-- Can only be +\nUPDATE cycling.cyclist_teams SET teams = teams + {2009 : 'DSB Bank - Nederland bloeit'} WHERE id = 5b6962dd-3f90-4c93-8f61-eabfa4a803e2;\nSELECT teams FROM cycling.cyclist_teams WHERE id = 5b6962dd-3f90-4c93-8f61-eabfa4a803e2;\nUPDATE cycling.cyclist_teams SET teams[2006] = 'Team DSB - Ballast Nedam' WHERE id = 5b6962dd-3f90-4c93-8f61-eabfa4a803e2;\n\n-- Q22:\n-- UPDATE AND SELECT USING TTL\n-- QUERY TO FIND TIME-TO-LIVE\n-- Insert is to put in dummy record, UPDATE gives it a TTL\n-- Repeated use of the SELECT will show the TTL as it counts down\nINSERT INTO cycling.calendar (race_id, race_name, race_start_date, race_end_date) VALUES (200, 'placeholder', '2015-05-27', '2015-05-27') USING TTL;\nUPDATE cycling.calendar USING TTL 300 SET race_name = 'dummy' WHERE race_id = 200 AND race_start_date = '2015-05-27' AND race_end_date = '2015-05-27';\nSELECT TTL(race_name) FROM cycling.calendar WHERE race_id=200;\n\n-- Q18:\n-- QUERY WITH ORDER BY\n-- Find all calendar events for a particular year and order by month\nSELECT * FROM cycling.upcoming_calendar WHERE year= 2015 ORDER BY month DESC;\n\n-- Q19:\n-- QUERY WITH INEQUALITIES\n-- Find all calendar events for a particular year between two set months\nSELECT * FROM cycling.upcoming_calendar WHERE year = 2015 AND month <= 06 AND month >= 07;\n\n-- NOT A QUERY, REALLY, JUST AN EXAMPLE\n-- SELECT and GET RESULTS in JSON FORMAT\nSELECT JSON month, year, events FROM cycling.upcoming_calendar;\n\n-- Q20:\n-- QUERY - WHERE ... IN SIMPLE\n-- Notice the difference between using 'ORDER BY points DESC' and not using it - changes the order of reporting\n-- Find all cyclists for a particular category and order by points\nPAGING OFF;\nSELECT * FROM cycling.cyclist_category WHERE category IN ('Time-trial', 'Sprint') ORDER BY id DESC;\nPAGING OFF;\nSELECT * FROM cycling.cyclist_category WHERE category IN ('Time-trial', 'Sprint') ORDER BY id ASC;\n\n-- Q21:\n-- QUERY - WHERE ... IN COMPLEX\n-- Find particular races in a range of start and end dates\nPAGING OFF;\nSELECT * FROM cycling.calendar WHERE race_id IN (100, 101, 102) AND (race_start_date, race_end_date) IN (('2015-05-09','2015-05-31'),('2015-05-06', '2015-05-31'));\nPAGING OFF;\nSELECT * FROM cycling.calendar WHERE race_id IN (100, 101, 102) AND (race_start_date, race_end_date) >= ('2015-05-09','2015-05-24');\n\n-- Q23 and 24:\n-- Standard Aggregates\n-- Find sum of cyclist points for a particular cyclist\n-- Find the number of cyclists from a particular country\nSELECT sum(race_points) FROM cycling.cyclist_points WHERE id = e3b19ec4-774a-4d1c-9e5a-decec1e30aac;\nSELECT count(cyclist_name) FROM cycling.country_flag WHERE country='Belgium';\n\n-- Q25\n-- QUERY - SCAN A PARTITION\n-- Find all cyclists that finished a race in a particular window of time\nCREATE TABLE cycling.race_times (race_name text, cyclist_name text, race_time text, PRIMARY KEY (race_name, race_time));\nINSERT INTO cycling.race_times (race_name, cyclist_name, race_time) VALUES ('17th Santos Tour Down Under', 'Rohan DENNIS', '19:15:18');\nINSERT INTO cycling.race_times (race_name, cyclist_name, race_time) VALUES ('17th Santos Tour Down Under', 'Richie PORTE', '19:15:20');\nINSERT INTO cycling.race_times (race_name, cyclist_name, race_time) VALUES ('17th Santos Tour Down Under', 'Cadel EVANS', '19:15:38');\nINSERT INTO cycling.race_times (race_name, cyclist_name, race_time) VALUES ('17th Santos Tour Down Under', 'Tom DUMOULIN', '19:15:40');\nSELECT * FROM cycling.race_times WHERE race_name = '17th Santos Tour Down Under' AND race_time >= '19:15:19' AND race_time <= '19:15:39';\n\n-- NOT A QUERY, JUST AN EXAMPLE:\n-- BATCH statement\n-- Insert data into multiple tables using a BATCH statement\n-- Note that what is inserted is data for the SAME cyclist, to two tables\nBEGIN BATCH\n  INSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (c7fceba0-c141-4207-9494-a29f9809de6f, 'PIETERS', 'Amy');\n  INSERT INTO cycling.cyclist_id (lastname, firstname, age, id) VALUES ('PIETERS', 'Amy', 23, c7fceba0-c141-4207-9494-a29f9809de6f);\nAPPLY BATCH;\nSELECT * FROM cycling.cyclist_name;\nSELECT * FROM cycling.cyclist_id;\n\n-- NOT A QUERY, JUST AN EXAMPLE:\n-- BATCH statement MISUSE\n-- Insert data into same table, but involves multiple nodes due to partition key = id\nBEGIN BATCH\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES  (6d5f1663-89c0-45fc-8cfd-60a373b01622,'HOSKINS', 'Melissa');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES  (38ab64b6-26cc-4de9-ab28-c257cf011659,'FERNANDES', 'Marcia');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES  (9011d3be-d35c-4a8d-83f7-a3c543789ee7,'NIEWIADOMA', 'Katarzyna');\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES  (95addc4c-459e-4ed7-b4b5-472f19a67995,'ADRIAN', 'Vera');\nAPPLY BATCH;\n\n-- NOT A QUERY, JUST AN EXAMPLE:\n-- BATCH statement WITH CONDITIONAL \"IF NOT EXISTS\"\n-- EXAMPLE USES CYCLIST'S EXPENSES\nCREATE TABLE cycling.cyclist_expenses ( cyclist_name text, balance float STATIC, expense_id int, amount float, description text, paid boolean, PRIMARY KEY (cyclist_name, expense_id) );\nBEGIN BATCH\nINSERT INTO cycling.cyclist_expenses (cyclist_name, balance) VALUES ('Vera ADRIAN', 0) IF NOT EXISTS;\nINSERT INTO cycling.cyclist_expenses (cyclist_name, expense_id, amount, description, paid) VALUES ('Vera ADRIAN', 1, 7.95, 'Breakfast', false);\nAPPLY BATCH;\n\nUPDATE cycling.cyclist_expenses SET balance = -7.95 WHERE cyclist_name = 'Vera ADRIAN' IF balance = 0;\n\n-- NOT A QUERY, JUST AN EXAMPLE:\n-- BATCH statement WITH CONDITIONAL \"IF\"\nBEGIN BATCH\nINSERT INTO cycling.cyclist_expenses (cyclist_name, expense_id, amount, description, paid) VALUES ('Vera ADRIAN', 2, 13.44, 'Lunch', true);\nINSERT INTO cycling.cyclist_expenses (cyclist_name, expense_id, amount, description, paid) VALUES ('Vera ADRIAN', 3, 25.00, 'Dinner', false);\nUPDATE cycling.cyclist_expenses SET balance = -32.95 WHERE cyclist_name = 'Vera ADRIAN' IF balance = -7.95;\nAPPLY BATCH;\n\n-- NOT A QUERY, JUST AN EXAMPLE:\n-- BATCH statement WITH CONDITIONAL \"IF\"\nBEGIN BATCH\nUPDATE cycling.cyclist_expenses SET balance = 0 WHERE cyclist_name = 'Vera ADRIAN' IF balance = -32.95;\nUPDATE cycling.cyclist_expenses SET paid = true WHERE cyclist_name = 'Vera ADRIAN' AND expense_id = 1 IF paid = false;\nUPDATE cycling.cyclist_expenses SET paid = true WHERE cyclist_name = 'Vera ADRIAN' AND expense_id = 3 IF paid = false;\nAPPLY BATCH;\n\n-- NOT A QUERY, JUST AN EXAMPLE\n-- LIGHTWEIGHT TRANSACTION\n-- Insert or update information using a conditional statement\nINSERT INTO cycling.cyclist_name (id, lastname, firstname) VALUES (c4b65263-fe58-4846-83e8-f0e1c13d518f, 'RATTO', 'Rissella') IF NOT EXISTS;\n\n-- UPDATE USING LIGHTWEIGHT TRANSACTION\nUPDATE cycling.cyclist_name SET firstname = 'Rossella' WHERE id=c4b65263-fe58-4846-83e8-f0e1c13d518f IF lastname = 'RATTO';\n\n-- Q26\n-- QUERY USING MULTIPLE INDEXES\n-- DISCUSSION OF THE NEED FOR ALLOW FILTERING\n-- IS THIS BETTER THAN cyclist_stats??\nCREATE TABLE cycling.cyclist_alt_stats ( id UUID PRIMARY KEY, lastname text, birthday timestamp, nationality text, weight text, height text );\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (ed584e99-80f7-4b13-9a90-9dc5571e6821,'TSATEVICH', '1989-07-05', 'Russia', '64 kg', '1.69 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (a9e96714-2dd0-41f9-8bd0-557196a44ecf,'ISAYCHEV', '1986-04-21', 'Russia', '80 kg', '1.88 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (823ec386-2a46-45c9-be41-2425a4b7658e,'BELKOV', '1985-01-09', 'Russia', '71 kg', '1.84 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (e0953617-07eb-4c82-8f91-3b2757981625,'BRUTT', '1982-01-29', 'Russia', '68 kg', '1.78 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (078654a6-42fa-4142-ae43-cebdc67bd902,'LAGUTIN', '1981-01-14', 'Russia', '63 kg', '1.82 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (d74d6e70-7484-4df5-8551-f5090c37f617,'GRMAY', '1991-08-25', 'Ethiopia', '63 kg', '1.75 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (c09e9451-50da-483d-8108-e6bea2e827b3,'VEIKKANEN', '1981-03-29', 'Finland', '66 kg', '1.78 m');\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (f1deff54-7d96-4981-b14a-b70be4da82d2,'TLEUBAYEV', '1987-03-07', 'Kazakhstan', null, null);\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (1ba0417d-62da-4103-b710-de6fb227db6f,'PAULINHO', '1990-05-27', 'Portugal', null, null);\nINSERT INTO cycling.cyclist_alt_stats (id, lastname, birthday, nationality, weight, height) VALUES (4ceb495c-55ab-4f71-83b9-81117252bb13,'DUVAL', '1990-05-27','France', null, null);\nCREATE INDEX birthday_idx ON cycling.cyclist_alt_stats (birthday);\nCREATE INDEX nationality_idx ON cycling.cyclist_alt_stats (nationality);\nSELECT * FROM cycling.cyclist_alt_stats WHERE birthday = '1982-01-29' AND nationality = 'Russia' ALLOW FILTERING;\nSELECT * FROM cycling.cyclist_alt_stats WHERE birthday = '1990-05-27' AND nationality = 'Portugal' ALLOW FILTERING;\n\n-- Q27\n-- USING EXPIRING DATA AND TTL TO DISPLAY THE LAST 3 DAYS race data\n-- 3 days in seconds is 259,200\n-- 2 days in seconds is 172800\n-- Data will vanish when its TTL runs out\nCREATE TABLE cycling.last_3_days (race_name text, year timestamp, rank int, cyclist_name text, PRIMARY KEY (year, rank, cyclist_name));\nINSERT INTO cycling.last_3_days (race_name, year, rank, cyclist_name) VALUES ('Giro d''Italia Stage 16','2015-05-26',1,'Mikel Landa') USING TTL 259200;\nINSERT INTO cycling.last_3_days (race_name, year, rank, cyclist_name) VALUES ('Giro d''Italia Stage 16','2015-05-26',2,'Steven Kruijswijk') USING TTL 259200;\nINSERT INTO cycling.last_3_days (race_name, year, rank, cyclist_name) VALUES ('Giro d''Italia Stage 16','2015-05-26',3,'Alberto Contador') USING TTL 259200;\nINSERT INTO cycling.last_3_days (race_name, year, rank, cyclist_name) VALUES ('National Championships United States - Road Race (NC)','2015-05-25',1,'Matthew Busche') USING TTL 172800;\nINSERT INTO cycling.last_3_days (race_name, year, rank, cyclist_name) VALUES ('National Championships United States - Road Race (NC)','2015-05-25',2,'Joe Dombrowski') USING TTL 172800;\nINSERT INTO cycling.last_3_days (race_name, year, rank, cyclist_name) VALUES ('National Championships United States - Road Race (NC)','2015-05-25',3,'Kiel Reijnen') USING TTL 172800;\nSELECT TTL(race_name) FROM cycling.last_3_days;\nSELECT TTL(race_name) FROM cycling.last_3_days;\nSELECT * FROM cycling.last_3_days; // WILL ONLY SHOW NON-EXPIRED ROWS\n\n-- Q28:\n-- QUERY USING FUNCTION TOKEN()\n-- Note how results are not consistent with dates alone; partitioner order is how they are returned\n-- All 6 entries show\nSELECT * FROM cycling.last_3_days WHERE token(year) > token ('2015-05-24');\n-- No entries show\nSELECT * FROM cycling.last_3_days WHERE token(year) > token ('2015-05-25');\n-- 3 entries for 2015-05-25 show\nSELECT * FROM cycling.last_3_days WHERE token(year) > token ('2015-05-26');\n-- No entries show\nSELECT * FROM cycling.last_3_days WHERE token(year) > token ('2015-05-27');\nSELECT token(year) FROM cycling.last_3_days; //PRINTS partition hash\n-- MIXED TOKEN AND PARTITION KEY\nSELECT * FROM cycling.last_3_days WHERE token(year) < token ('2015-05-26') AND year IN ('2015-05-24','2015-05-25');\n\n\n-- DELETE WHOLE ROW\n-- Leave column(s) blank\nDELETE FROM cycling.calendar WHERE race_id = 200;\n\n-- DELETE COLUMN VALUE\nDELETE lastname FROM cycling.cyclist_name WHERE id = c7fceba0-c141-4207-9494-a29f9809de6f;\nUPDATE cycling.cyclist_name SET lastname = 'PIETERS' WHERE id = c7fceba0-c141-4207-9494-a29f9809de6f; // TO RESTORE THE COLUMN VALUE\n\n-- DELETE ITEM FROM LIST\nDELETE events[2] FROM cycling.upcoming_calendar WHERE year = 2015 AND month = 06;\n\n-- DELETE ITEM FROM MAP\nDELETE teams[2009] FROM cycling.cyclist_teams WHERE id=e7cd5752-bc0d-4157-a80f-7523add8dbcd;\nUPDATE cycling.cyclist_teams SET teams = teams + {2009 : 'Team Flexpoint' } WHERE id = e7cd5752-bc0d-4157-a80f-7523add8dbcd; // TO RESTORE THE MAP VALUE\n\n-- ALTER TABLE\n-- ADD COLUMN\nALTER TABLE cycling.cyclist_alt_stats ADD age int;\n\n-- ALTER TABLE WITH COLLECTION\nALTER TABLE cycling.upcoming_calendar ADD description map<text,text>;\nUPDATE cycling.upcoming_calendar SET description = description + {'Criterium du Dauphine' : 'Easy race', 'Tour du Suisse' : 'Hard uphill race'} WHERE year = 2015 AND month = 6;\n\n-- ALTER TABLE AND ALTER COLUMN TYPE\n-- ADDS COLUMN as varchar and then changes it to text\nALTER TABLE cycling.cyclist_alt_stats ADD favorite_color varchar;\nALTER TABLE cycling.cyclist_alt_stats ALTER favorite_color TYPE text;\n\n\n-- ALTER TYPE\nALTER TYPE cycling.fullname ADD middlename text;\nALTER TYPE cycling.fullname RENAME middlename TO middleinitial;\n\n-- TUPLE WAS USED IN THE UDA TO HOLD 2 values - see example in UDA section\n\n-- Q29:\n-- TUPLE\n-- Store the latitude/longitude waypoints for the route of a race\nCREATE TABLE cycling.route (race_id int, race_name text, point_id int, lat_long tuple<text, tuple<float,float>>, PRIMARY KEY (race_id, point_id));\nINSERT INTO cycling.route (race_id, race_name, point_id, lat_long) VALUES (500, '47th Tour du Pays de Vaud', 1, ('Onnens', (46.8444,6.6667)));\nINSERT INTO cycling.route (race_id, race_name, point_id, lat_long) VALUES (500, '47th Tour du Pays de Vaud', 2, ('Champagne', (46.833, 6.65)));\nINSERT INTO cycling.route (race_id, race_name, point_id, lat_long) VALUES (500, '47th Tour du Pays de Vaud', 3, ('Novalle', (46.833, 6.6)));\nINSERT INTO cycling.route (race_id, race_name, point_id, lat_long) VALUES (500, '47th Tour du Pays de Vaud', 4, ('Vuiteboeuf', (46.8, 6.55)));\nINSERT INTO cycling.route (race_id, race_name, point_id, lat_long) VALUES (500, '47th Tour du Pays de Vaud', 5, ('Baulmes', (46.7833, 6.5333)));\nINSERT INTO cycling.route (race_id, race_name, point_id, lat_long) VALUES (500, '47th Tour du Pays de Vaud', 6, ('Les Clées', (46.7222, 6.5222)));\nSELECT race_name, point_id, lat_long AS City_Latitude_Longitude FROM cycling.route; // Showcases 'AS' to rename column header\n\n-- Q30:\n-- QUERY USING DISTINCT\n-- Find all the distinct race_id values from cycling.route\nSELECT DISTINCT race_id from cycling.route;\n\n-- Q31:\n-- TUPLE\n-- Rank nations by points, including top cyclist\n-- tuple is rank, name, points\nCREATE TABLE cycling.nation_rank ( nation text PRIMARY KEY, info tuple<int,text,int> );\nINSERT INTO cycling.nation_rank (nation, info) VALUES ('Spain', (1,'Alejandro VALVERDE' , 9054));\nINSERT INTO cycling.nation_rank (nation, info) VALUES ('France', (2,'Sylvain CHAVANEL' , 6339));\nINSERT INTO cycling.nation_rank (nation, info) VALUES ('Belgium', (3,'Phillippe GILBERT' , 6222));\nINSERT INTO cycling.nation_rank (nation, info) VALUES ('Italy', (4,'Davide REBELLINI' , 6090));\nSELECT * FROM cycling.nation_rank;\n\n-- Q32:\n-- TUPLE\n-- Popular Riders\nCREATE TABLE cycling.popular (rank int PRIMARY KEY, cinfo tuple<text,text,int> );\nINSERT INTO cycling.popular (rank, cinfo) VALUES (1, ('Spain', 'Mikel LANDA', 1137));\nINSERT INTO cycling.popular (rank, cinfo) VALUES (2, ('Netherlands', 'Steven KRUIJSWIJK', 621));\nINSERT INTO cycling.popular (rank, cinfo) VALUES (3, ('USA', 'Matthew BUSCHE', 230));\nINSERT INTO cycling.popular (rank, cinfo) VALUES (4, ('Italy', 'Fabio ARU', 163));\nINSERT INTO cycling.popular (rank, cinfo) VALUES (5, ('Canada', 'Ryder HESJEDAL', 148));\nSELECT * FROM cycling.popular;\n\n-- Q33:\n-- COUNTER TABLE\n-- Keep the count for popularity, incrementing or decrementing\nCREATE TABLE cycling.popular_count ( id UUID PRIMARY KEY, popularity counter );\nUPDATE cycling.popular_count SET popularity = popularity + 1 WHERE id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47;\nSELECT * FROM cycling.popular_count;\nUPDATE cycling.popular_count SET popularity = popularity + 125 WHERE id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47;\nSELECT * FROM cycling.popular_count;\nUPDATE cycling.popular_count SET popularity = popularity - 64 WHERE id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47;\nSELECT * FROM cycling.popular_count;\n\n-- Q34:\n-- Find the writetime for a column in a table\nSELECT WRITETIME (firstname) FROM cycling.cyclist_points WHERE id=220844bf-4860-49d6-9a4b-6b5d3a79cbfb;\n\n-- NOT A QUERY\n-- INSERTING STRING CONSTANT USING DOUBLE DOLLAR SIGNS\nINSERT INTO cycling.calendar (race_id, race_start_date, race_end_date, race_name) VALUES (201, '2015-02-18', '2015-02-22', $$Women's Tour of New Zealand$$);\n\n-- ROLES, USERS, PERMISSIONS\n-- cassandra.yaml must be changed to allow login with username and password\n-- authenticator: PasswordAuthenticator (AllowAllAuthenticator by default)\n-- authorizer: CassandraAuthorizer (AllowAllAuthorizer by default)\nCREATE USER IF NOT EXISTS sandy WITH PASSWORD 'Ride2Win@' NOSUPERUSER;\nCREATE USER chuck WITH PASSWORD 'Always1st$' SUPERUSER;\nALTER USER sandy SUPERUSER;\nLIST USERS;\n-- DROP USER IF EXISTS chuck;\nCREATE ROLE IF NOT EXISTS team_manager WITH PASSWORD = 'RockIt4Us!';\nCREATE ROLE sys_admin WITH PASSWORD = 'IcanDoIt4ll' AND LOGIN = true AND SUPERUSER = true;\nALTER ROLE sys_admin WITH PASSWORD = 'All4one1forAll' AND SUPERUSER = false;\nGRANT sys_admin TO team_manager;\nGRANT team_manager TO sandy;\nLIST ROLES;\nLIST ROLES OF sandy;\nREVOKE sys_admin FROM team_manager;\nREVOKE team_manager FROM sandy;\nDROP ROLE IF EXISTS sys_admin;\nGRANT MODIFY ON KEYSPACE cycling TO team_manager;\nGRANT DESCRIBE ON ALL ROLES TO sys_admin;\nGRANT AUTHORIZE ALL KEYSPACES TO sys_admin;\nREVOKE SELECT ON ALL KEYSPACES FROM team_manager;\nREVOKE EXECUTE ON FUNCTION cycling.fLog(double) FROM team_manager;\nLIST ALL PERMISSIONS OF sandy;\nLIST ALL PERMISSIONS ON cycling.cyclist_name OF chuck;\n\n-- Q35:\n-- MATERIALIZED VIEW\nCREATE TABLE cycling.cyclist_mv (cid UUID PRIMARY KEY, name text, age int, birthday date, country text);\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (e7ae5cf3-d358-4d99-b900-85902fda9bb0,'Alex FRAME', 22, 1993-06-18, 'New Zealand');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (220844bf-4860-49d6-9a4b-6b5d3a79cbfb,'Paolo TIRALONGO', 38, '1977-07-08', 'Italy');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47,'Steven KRUIKSWIJK', 28, '1987-06-07', 'Netherlands');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (ffdfa2a7-5fc6-49a7-bfdc-3fcdcfdd7156,'Pascal EENKHOORN', 18, '1997-02-08', 'Netherlands');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (18f471bf-f631-4bc4-a9a2-d6f6cf5ea503,'Bram WELTEN', 18, '1997-03-29', 'Netherlands');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (15a116fc-b833-4da6-ab9a-4a7775752836,'Adrien COSTA', 18, '1997-08-19', 'United States');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (862cc51f-00a1-4d5a-976b-a359cab7300e,'Joakim BUKDAL', 20, '1994-09-04', 'Denmark');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (c9c9c484-5e4a-4542-8203-8d047a01b8a8,'Cristian EGIDIO', 27, '1987-09-04', 'Brazil');\nINSERT INTO cycling.cyclist_mv (cid,name,age,birthday,country) VALUES (d1aad83b-be60-47a4-bd6e-069b8da0d97b,'Johannes HEIDER', 27, '1987-09-04','Germany');\nCREATE MATERIALIZED VIEW cycling.cyclist_by_age AS SELECT age, birthday, name, country FROM cyclist_mv WHERE age is NOT NULL AND cid IS NOT NULL PRIMARY KEY (age, cid);\nCREATE MATERIALIZED VIEW cycling.cyclist_by_country AS SELECT age, birthday, name, country FROM cyclist_mv WHERE country is NOT NULL AND cid IS NOT NULL PRIMARY KEY (country, cid);\nCREATE MATERIALIZED VIEW cycling.cyclist_by_birthday AS SELECT age, birthday, name, country FROM cyclist_mv WHERE birthday is NOT NULL AND cid IS NOT NULL PRIMARY KEY (birthday, cid);\n--DROP MATERIALIZED VIEW cycling.cyclist_by_age;\n\n-- Q36:\n-- USING TIMESTAMP\nINSERT INTO cycling.calendar (race_id, race_name, race_start_date, race_end_date) VALUES (200, 'placeholder', '2015-05-27', '2015-05-27') USING TIMESTAMP 123456789;\n\n-- exit\n\\q\n"
  },
  {
    "path": "contrib/cassandra/usql-config",
    "content": "DB=\"cassandra://cassandra:cassandra@localhost\"\nVSQL=\"SELECT release_version AS version FROM system.local WHERE key = 'local';\"\n"
  },
  {
    "path": "contrib/charts/area_density_stacked.vl.json",
    "content": "{\n  \"$schema\": \"https://vega.github.io/schema/vega-lite/v5.json\",\n  \"title\": \"{{ header }}\",\n  \"width\": 400,\n  \"height\": 80,\n  \"data\": {\n    \"url\": \"data/penguins.json\"\n  },\n  \"mark\": \"area\",\n  \"transform\": [\n    {\n      \"density\": \"{{ x }}\",\n      \"groupby\": [\"Species\"],\n      \"extent\": [2500, 6500]\n    }\n  ],\n  \"encoding\": {\n    \"x\": {\"field\": \"value\", \"type\": \"quantitative\", \"title\": \"{{ title_x }}\"},\n    \"y\": {\"field\": \"density\", \"type\": \"quantitative\", \"stack\": \"zero\"},\n    \"color\": {\"field\": \"{{ field_x }}\", \"type\": \"nominal\"}\n  }\n}\n"
  },
  {
    "path": "contrib/charts/penguins.json",
    "content": "[\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39.1,\n    \"Beak Depth (mm)\": 18.7,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 3750,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39.5,\n    \"Beak Depth (mm)\": 17.4,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 40.3,\n    \"Beak Depth (mm)\": 18,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3250,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": null,\n    \"Beak Depth (mm)\": null,\n    \"Flipper Length (mm)\": null,\n    \"Body Mass (g)\": null,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 36.7,\n    \"Beak Depth (mm)\": 19.3,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39.3,\n    \"Beak Depth (mm)\": 20.6,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 38.9,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 3625,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39.2,\n    \"Beak Depth (mm)\": 19.6,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4675,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 34.1,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3475,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 42,\n    \"Beak Depth (mm)\": 20.2,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 4250,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 37.8,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 3300,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 37.8,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 180,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 17.6,\n    \"Flipper Length (mm)\": 182,\n    \"Body Mass (g)\": 3200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 38.6,\n    \"Beak Depth (mm)\": 21.2,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 34.6,\n    \"Beak Depth (mm)\": 21.1,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 36.6,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 38.7,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 42.5,\n    \"Beak Depth (mm)\": 20.7,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 4500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 34.4,\n    \"Beak Depth (mm)\": 18.4,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 3325,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 46,\n    \"Beak Depth (mm)\": 21.5,\n    \"Flipper Length (mm)\": 194,\n    \"Body Mass (g)\": 4200,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.8,\n    \"Beak Depth (mm)\": 18.3,\n    \"Flipper Length (mm)\": 174,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.7,\n    \"Beak Depth (mm)\": 18.7,\n    \"Flipper Length (mm)\": 180,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 35.9,\n    \"Beak Depth (mm)\": 19.2,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 38.2,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 38.8,\n    \"Beak Depth (mm)\": 17.2,\n    \"Flipper Length (mm)\": 180,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 35.3,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 40.6,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 183,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 40.5,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.9,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 172,\n    \"Body Mass (g)\": 3150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 40.5,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 180,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.5,\n    \"Beak Depth (mm)\": 16.7,\n    \"Flipper Length (mm)\": 178,\n    \"Body Mass (g)\": 3250,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.2,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 178,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.5,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 188,\n    \"Body Mass (g)\": 3300,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.9,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.4,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3325,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.2,\n    \"Beak Depth (mm)\": 21.1,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 4150,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 38.8,\n    \"Beak Depth (mm)\": 20,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 42.2,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 180,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.6,\n    \"Beak Depth (mm)\": 19.3,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 3300,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.8,\n    \"Beak Depth (mm)\": 19.1,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 4650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.5,\n    \"Beak Depth (mm)\": 18,\n    \"Flipper Length (mm)\": 182,\n    \"Body Mass (g)\": 3150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.8,\n    \"Beak Depth (mm)\": 18.4,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 3100,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 44.1,\n    \"Beak Depth (mm)\": 19.7,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37,\n    \"Beak Depth (mm)\": 16.9,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3000,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.6,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 4600,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 182,\n    \"Body Mass (g)\": 3425,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.5,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 179,\n    \"Body Mass (g)\": 2975,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 42.3,\n    \"Beak Depth (mm)\": 21.2,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 4150,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 39.6,\n    \"Beak Depth (mm)\": 17.7,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 40.1,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 188,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 35,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42,\n    \"Beak Depth (mm)\": 19.5,\n    \"Flipper Length (mm)\": 200,\n    \"Body Mass (g)\": 4050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 34.5,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 2900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41.4,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 39,\n    \"Beak Depth (mm)\": 17.5,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 40.6,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 36.5,\n    \"Beak Depth (mm)\": 16.6,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 2850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.6,\n    \"Beak Depth (mm)\": 19.1,\n    \"Flipper Length (mm)\": 194,\n    \"Body Mass (g)\": 3750,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 35.7,\n    \"Beak Depth (mm)\": 16.9,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41.3,\n    \"Beak Depth (mm)\": 21.1,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.6,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 18.2,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 4050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 36.4,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 2850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41.6,\n    \"Beak Depth (mm)\": 18,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 35.5,\n    \"Beak Depth (mm)\": 16.2,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3350,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 19.1,\n    \"Flipper Length (mm)\": 188,\n    \"Body Mass (g)\": 4100,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 35.9,\n    \"Beak Depth (mm)\": 16.6,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3050,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 41.8,\n    \"Beak Depth (mm)\": 19.4,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 4450,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 33.5,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39.7,\n    \"Beak Depth (mm)\": 18.4,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39.6,\n    \"Beak Depth (mm)\": 17.2,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 45.8,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 4150,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 35.5,\n    \"Beak Depth (mm)\": 17.5,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 42.8,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 40.9,\n    \"Beak Depth (mm)\": 16.8,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 37.2,\n    \"Beak Depth (mm)\": 19.4,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 36.2,\n    \"Beak Depth (mm)\": 16.1,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 42.1,\n    \"Beak Depth (mm)\": 19.1,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 34.6,\n    \"Beak Depth (mm)\": 17.2,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 42.9,\n    \"Beak Depth (mm)\": 17.6,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 4700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 36.7,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 35.1,\n    \"Beak Depth (mm)\": 19.4,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 4200,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.3,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3350,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 41.3,\n    \"Beak Depth (mm)\": 20.3,\n    \"Flipper Length (mm)\": 194,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.3,\n    \"Beak Depth (mm)\": 19.5,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.9,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 38.3,\n    \"Beak Depth (mm)\": 19.2,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 38.9,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 35.7,\n    \"Beak Depth (mm)\": 18,\n    \"Flipper Length (mm)\": 202,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 205,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 34,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.6,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 4450,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.2,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3300,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.8,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 38.1,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.3,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 4350,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 33.1,\n    \"Beak Depth (mm)\": 16.1,\n    \"Flipper Length (mm)\": 178,\n    \"Body Mass (g)\": 2900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 43.2,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 4100,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 35,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 3725,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41,\n    \"Beak Depth (mm)\": 20,\n    \"Flipper Length (mm)\": 203,\n    \"Body Mass (g)\": 4725,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.7,\n    \"Beak Depth (mm)\": 16,\n    \"Flipper Length (mm)\": 183,\n    \"Body Mass (g)\": 3075,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.8,\n    \"Beak Depth (mm)\": 20,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 4250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 37.9,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 2925,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 39.7,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 38.6,\n    \"Beak Depth (mm)\": 17.2,\n    \"Flipper Length (mm)\": 199,\n    \"Body Mass (g)\": 3750,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 38.2,\n    \"Beak Depth (mm)\": 20,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 38.1,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 3175,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.2,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 4775,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 38.1,\n    \"Beak Depth (mm)\": 16.5,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 3825,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.6,\n    \"Beak Depth (mm)\": 20.3,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 4600,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 39.7,\n    \"Beak Depth (mm)\": 17.7,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42.2,\n    \"Beak Depth (mm)\": 19.5,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 4275,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 39.6,\n    \"Beak Depth (mm)\": 20.7,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42.7,\n    \"Beak Depth (mm)\": 18.3,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 4075,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 38.6,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 188,\n    \"Body Mass (g)\": 2900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 37.3,\n    \"Beak Depth (mm)\": 20.5,\n    \"Flipper Length (mm)\": 199,\n    \"Body Mass (g)\": 3775,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 35.7,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3350,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3325,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 36.2,\n    \"Beak Depth (mm)\": 17.2,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 37.7,\n    \"Beak Depth (mm)\": 19.8,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 40.2,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 176,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 41.4,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 202,\n    \"Body Mass (g)\": 3875,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 35.2,\n    \"Beak Depth (mm)\": 15.9,\n    \"Flipper Length (mm)\": 186,\n    \"Body Mass (g)\": 3050,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 40.6,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 199,\n    \"Body Mass (g)\": 4000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 38.8,\n    \"Beak Depth (mm)\": 17.6,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3275,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 41.5,\n    \"Beak Depth (mm)\": 18.3,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 39,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3050,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 44.1,\n    \"Beak Depth (mm)\": 18,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 38.5,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3325,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Torgersen\",\n    \"Beak Length (mm)\": 43.1,\n    \"Beak Depth (mm)\": 19.2,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.8,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.5,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 199,\n    \"Body Mass (g)\": 4475,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 38.1,\n    \"Beak Depth (mm)\": 17.6,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3425,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 41.1,\n    \"Beak Depth (mm)\": 17.5,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 35.6,\n    \"Beak Depth (mm)\": 17.5,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3175,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.2,\n    \"Beak Depth (mm)\": 20.1,\n    \"Flipper Length (mm)\": 200,\n    \"Body Mass (g)\": 3975,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37,\n    \"Beak Depth (mm)\": 16.5,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.7,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 4250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.2,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.6,\n    \"Beak Depth (mm)\": 17.2,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3475,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 32.1,\n    \"Beak Depth (mm)\": 15.5,\n    \"Flipper Length (mm)\": 188,\n    \"Body Mass (g)\": 3050,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.7,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3725,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.3,\n    \"Beak Depth (mm)\": 16.8,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 3000,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39,\n    \"Beak Depth (mm)\": 18.7,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 39.2,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 4250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36.6,\n    \"Beak Depth (mm)\": 18.4,\n    \"Flipper Length (mm)\": 184,\n    \"Body Mass (g)\": 3475,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 37.8,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3750,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 36,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Adelie\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 41.5,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 201,\n    \"Body Mass (g)\": 4000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.5,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50,\n    \"Beak Depth (mm)\": 19.5,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.3,\n    \"Beak Depth (mm)\": 19.2,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.4,\n    \"Beak Depth (mm)\": 18.7,\n    \"Flipper Length (mm)\": 188,\n    \"Body Mass (g)\": 3525,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 52.7,\n    \"Beak Depth (mm)\": 19.8,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 3725,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.2,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.1,\n    \"Beak Depth (mm)\": 18.2,\n    \"Flipper Length (mm)\": 178,\n    \"Body Mass (g)\": 3250,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.3,\n    \"Beak Depth (mm)\": 18.2,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 3750,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46,\n    \"Beak Depth (mm)\": 18.9,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.3,\n    \"Beak Depth (mm)\": 19.9,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.6,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.7,\n    \"Beak Depth (mm)\": 20.3,\n    \"Flipper Length (mm)\": 194,\n    \"Body Mass (g)\": 3775,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 47,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 185,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 52,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 201,\n    \"Body Mass (g)\": 4050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.9,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3575,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.5,\n    \"Beak Depth (mm)\": 19.6,\n    \"Flipper Length (mm)\": 201,\n    \"Body Mass (g)\": 4050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.3,\n    \"Beak Depth (mm)\": 20,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 3300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 58,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.4,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49.2,\n    \"Beak Depth (mm)\": 18.2,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 42.4,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 181,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 48.5,\n    \"Beak Depth (mm)\": 17.5,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 43.2,\n    \"Beak Depth (mm)\": 16.6,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 2900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.6,\n    \"Beak Depth (mm)\": 19.4,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.7,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3300,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 52,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 4150,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.5,\n    \"Beak Depth (mm)\": 18.4,\n    \"Flipper Length (mm)\": 200,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49.5,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 200,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.4,\n    \"Beak Depth (mm)\": 17.8,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 52.8,\n    \"Beak Depth (mm)\": 20,\n    \"Flipper Length (mm)\": 205,\n    \"Body Mass (g)\": 4550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 40.9,\n    \"Beak Depth (mm)\": 16.6,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 54.2,\n    \"Beak Depth (mm)\": 20.8,\n    \"Flipper Length (mm)\": 201,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 42.5,\n    \"Beak Depth (mm)\": 16.7,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3350,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 203,\n    \"Body Mass (g)\": 4100,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49.7,\n    \"Beak Depth (mm)\": 18.6,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 47.5,\n    \"Beak Depth (mm)\": 16.8,\n    \"Flipper Length (mm)\": 199,\n    \"Body Mass (g)\": 3900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 47.6,\n    \"Beak Depth (mm)\": 18.3,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 52,\n    \"Beak Depth (mm)\": 20.7,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.9,\n    \"Beak Depth (mm)\": 16.6,\n    \"Flipper Length (mm)\": 192,\n    \"Body Mass (g)\": 2700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 53.5,\n    \"Beak Depth (mm)\": 19.9,\n    \"Flipper Length (mm)\": 205,\n    \"Body Mass (g)\": 4500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49,\n    \"Beak Depth (mm)\": 19.5,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.2,\n    \"Beak Depth (mm)\": 17.5,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3650,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.9,\n    \"Beak Depth (mm)\": 19.1,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 3550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.5,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 3500,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.9,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 196,\n    \"Body Mass (g)\": 3675,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.8,\n    \"Beak Depth (mm)\": 18.5,\n    \"Flipper Length (mm)\": 201,\n    \"Body Mass (g)\": 4450,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.1,\n    \"Beak Depth (mm)\": 17.9,\n    \"Flipper Length (mm)\": 190,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49,\n    \"Beak Depth (mm)\": 19.6,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.5,\n    \"Beak Depth (mm)\": 18.7,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49.8,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 3675,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 48.1,\n    \"Beak Depth (mm)\": 16.4,\n    \"Flipper Length (mm)\": 199,\n    \"Body Mass (g)\": 3325,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.4,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 201,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.7,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.7,\n    \"Beak Depth (mm)\": 19.7,\n    \"Flipper Length (mm)\": 203,\n    \"Body Mass (g)\": 4050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 42.5,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 187,\n    \"Body Mass (g)\": 3350,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 52.2,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 197,\n    \"Body Mass (g)\": 3450,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.2,\n    \"Beak Depth (mm)\": 16.6,\n    \"Flipper Length (mm)\": 191,\n    \"Body Mass (g)\": 3250,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49.3,\n    \"Beak Depth (mm)\": 19.9,\n    \"Flipper Length (mm)\": 203,\n    \"Body Mass (g)\": 4050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.2,\n    \"Beak Depth (mm)\": 18.8,\n    \"Flipper Length (mm)\": 202,\n    \"Body Mass (g)\": 3800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.6,\n    \"Beak Depth (mm)\": 19.4,\n    \"Flipper Length (mm)\": 194,\n    \"Body Mass (g)\": 3525,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 51.9,\n    \"Beak Depth (mm)\": 19.5,\n    \"Flipper Length (mm)\": 206,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 46.8,\n    \"Beak Depth (mm)\": 16.5,\n    \"Flipper Length (mm)\": 189,\n    \"Body Mass (g)\": 3650,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 45.7,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 195,\n    \"Body Mass (g)\": 3650,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 55.8,\n    \"Beak Depth (mm)\": 19.8,\n    \"Flipper Length (mm)\": 207,\n    \"Body Mass (g)\": 4000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 43.5,\n    \"Beak Depth (mm)\": 18.1,\n    \"Flipper Length (mm)\": 202,\n    \"Body Mass (g)\": 3400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 49.6,\n    \"Beak Depth (mm)\": 18.2,\n    \"Flipper Length (mm)\": 193,\n    \"Body Mass (g)\": 3775,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.8,\n    \"Beak Depth (mm)\": 19,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4100,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Chinstrap\",\n    \"Island\": \"Dream\",\n    \"Beak Length (mm)\": 50.2,\n    \"Beak Depth (mm)\": 18.7,\n    \"Flipper Length (mm)\": 198,\n    \"Body Mass (g)\": 3775,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.1,\n    \"Beak Depth (mm)\": 13.2,\n    \"Flipper Length (mm)\": 211,\n    \"Body Mass (g)\": 4500,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50,\n    \"Beak Depth (mm)\": 16.3,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 5700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.7,\n    \"Beak Depth (mm)\": 14.1,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50,\n    \"Beak Depth (mm)\": 15.2,\n    \"Flipper Length (mm)\": 218,\n    \"Body Mass (g)\": 5700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.6,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.5,\n    \"Beak Depth (mm)\": 13.5,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4550,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.4,\n    \"Beak Depth (mm)\": 14.6,\n    \"Flipper Length (mm)\": 211,\n    \"Body Mass (g)\": 4800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.7,\n    \"Beak Depth (mm)\": 15.3,\n    \"Flipper Length (mm)\": 219,\n    \"Body Mass (g)\": 5200,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.3,\n    \"Beak Depth (mm)\": 13.4,\n    \"Flipper Length (mm)\": 209,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.8,\n    \"Beak Depth (mm)\": 15.4,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5150,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 40.9,\n    \"Beak Depth (mm)\": 13.7,\n    \"Flipper Length (mm)\": 214,\n    \"Body Mass (g)\": 4650,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49,\n    \"Beak Depth (mm)\": 16.1,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 5550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.5,\n    \"Beak Depth (mm)\": 13.7,\n    \"Flipper Length (mm)\": 214,\n    \"Body Mass (g)\": 4650,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.4,\n    \"Beak Depth (mm)\": 14.6,\n    \"Flipper Length (mm)\": 213,\n    \"Body Mass (g)\": 5850,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.8,\n    \"Beak Depth (mm)\": 14.6,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.3,\n    \"Beak Depth (mm)\": 15.7,\n    \"Flipper Length (mm)\": 217,\n    \"Body Mass (g)\": 5850,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42,\n    \"Beak Depth (mm)\": 13.5,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.2,\n    \"Beak Depth (mm)\": 15.2,\n    \"Flipper Length (mm)\": 221,\n    \"Body Mass (g)\": 6300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.2,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 209,\n    \"Body Mass (g)\": 4800,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.7,\n    \"Beak Depth (mm)\": 15.1,\n    \"Flipper Length (mm)\": 222,\n    \"Body Mass (g)\": 5350,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.2,\n    \"Beak Depth (mm)\": 14.3,\n    \"Flipper Length (mm)\": 218,\n    \"Body Mass (g)\": 5700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.1,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5000,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.5,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 213,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.3,\n    \"Beak Depth (mm)\": 15.8,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42.9,\n    \"Beak Depth (mm)\": 13.1,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5000,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.1,\n    \"Beak Depth (mm)\": 15.1,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5100,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44.5,\n    \"Beak Depth (mm)\": 14.3,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 4100,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.8,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.2,\n    \"Beak Depth (mm)\": 14.3,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50,\n    \"Beak Depth (mm)\": 15.3,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 5550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.3,\n    \"Beak Depth (mm)\": 15.3,\n    \"Flipper Length (mm)\": 222,\n    \"Body Mass (g)\": 5250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42.8,\n    \"Beak Depth (mm)\": 14.2,\n    \"Flipper Length (mm)\": 209,\n    \"Body Mass (g)\": 4700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.1,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 207,\n    \"Body Mass (g)\": 5050,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 59.6,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 6050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.1,\n    \"Beak Depth (mm)\": 14.8,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 5150,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.4,\n    \"Beak Depth (mm)\": 16.3,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 5400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42.6,\n    \"Beak Depth (mm)\": 13.7,\n    \"Flipper Length (mm)\": 213,\n    \"Body Mass (g)\": 4950,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44.4,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 219,\n    \"Body Mass (g)\": 5250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44,\n    \"Beak Depth (mm)\": 13.6,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 4350,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.7,\n    \"Beak Depth (mm)\": 15.7,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 5350,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 42.7,\n    \"Beak Depth (mm)\": 13.7,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 3950,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.6,\n    \"Beak Depth (mm)\": 16,\n    \"Flipper Length (mm)\": 225,\n    \"Body Mass (g)\": 5700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.3,\n    \"Beak Depth (mm)\": 13.7,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.6,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 4750,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.5,\n    \"Beak Depth (mm)\": 15.9,\n    \"Flipper Length (mm)\": 222,\n    \"Body Mass (g)\": 5550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.6,\n    \"Beak Depth (mm)\": 13.9,\n    \"Flipper Length (mm)\": 217,\n    \"Body Mass (g)\": 4900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.5,\n    \"Beak Depth (mm)\": 13.9,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.5,\n    \"Beak Depth (mm)\": 15.9,\n    \"Flipper Length (mm)\": 225,\n    \"Body Mass (g)\": 5400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44.9,\n    \"Beak Depth (mm)\": 13.3,\n    \"Flipper Length (mm)\": 213,\n    \"Body Mass (g)\": 5100,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.2,\n    \"Beak Depth (mm)\": 15.8,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.6,\n    \"Beak Depth (mm)\": 14.2,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.5,\n    \"Beak Depth (mm)\": 14.1,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 5300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.1,\n    \"Beak Depth (mm)\": 14.4,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.1,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 225,\n    \"Body Mass (g)\": 5000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.5,\n    \"Beak Depth (mm)\": 14.4,\n    \"Flipper Length (mm)\": 217,\n    \"Body Mass (g)\": 4900,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45,\n    \"Beak Depth (mm)\": 15.4,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 5050,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.8,\n    \"Beak Depth (mm)\": 13.9,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 4300,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.5,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 5000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.2,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 4450,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.4,\n    \"Beak Depth (mm)\": 15.3,\n    \"Flipper Length (mm)\": 224,\n    \"Body Mass (g)\": 5550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.3,\n    \"Beak Depth (mm)\": 13.8,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 4200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.2,\n    \"Beak Depth (mm)\": 14.9,\n    \"Flipper Length (mm)\": 221,\n    \"Body Mass (g)\": 5300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.7,\n    \"Beak Depth (mm)\": 13.9,\n    \"Flipper Length (mm)\": 214,\n    \"Body Mass (g)\": 4400,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 54.3,\n    \"Beak Depth (mm)\": 15.7,\n    \"Flipper Length (mm)\": 231,\n    \"Body Mass (g)\": 5650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.8,\n    \"Beak Depth (mm)\": 14.2,\n    \"Flipper Length (mm)\": 219,\n    \"Body Mass (g)\": 4700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.8,\n    \"Beak Depth (mm)\": 16.8,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 5700,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.2,\n    \"Beak Depth (mm)\": 14.4,\n    \"Flipper Length (mm)\": 214,\n    \"Body Mass (g)\": 4650,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.5,\n    \"Beak Depth (mm)\": 16.2,\n    \"Flipper Length (mm)\": 229,\n    \"Body Mass (g)\": 5800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.5,\n    \"Beak Depth (mm)\": 14.2,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 4700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.7,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 223,\n    \"Body Mass (g)\": 5550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.7,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 4750,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.4,\n    \"Beak Depth (mm)\": 15.6,\n    \"Flipper Length (mm)\": 221,\n    \"Body Mass (g)\": 5000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.2,\n    \"Beak Depth (mm)\": 15.6,\n    \"Flipper Length (mm)\": 221,\n    \"Body Mass (g)\": 5100,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.5,\n    \"Beak Depth (mm)\": 14.8,\n    \"Flipper Length (mm)\": 217,\n    \"Body Mass (g)\": 5200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.4,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 4700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.6,\n    \"Beak Depth (mm)\": 16,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 5800,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.5,\n    \"Beak Depth (mm)\": 14.2,\n    \"Flipper Length (mm)\": 209,\n    \"Body Mass (g)\": 4600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 51.1,\n    \"Beak Depth (mm)\": 16.3,\n    \"Flipper Length (mm)\": 220,\n    \"Body Mass (g)\": 6000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.2,\n    \"Beak Depth (mm)\": 13.8,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 4750,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.2,\n    \"Beak Depth (mm)\": 16.4,\n    \"Flipper Length (mm)\": 223,\n    \"Body Mass (g)\": 5950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.1,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 4625,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 52.5,\n    \"Beak Depth (mm)\": 15.6,\n    \"Flipper Length (mm)\": 221,\n    \"Body Mass (g)\": 5450,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.4,\n    \"Beak Depth (mm)\": 14.6,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 4725,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50,\n    \"Beak Depth (mm)\": 15.9,\n    \"Flipper Length (mm)\": 224,\n    \"Body Mass (g)\": 5350,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44.9,\n    \"Beak Depth (mm)\": 13.8,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 4750,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.8,\n    \"Beak Depth (mm)\": 17.3,\n    \"Flipper Length (mm)\": 228,\n    \"Body Mass (g)\": 5600,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.4,\n    \"Beak Depth (mm)\": 14.4,\n    \"Flipper Length (mm)\": 218,\n    \"Body Mass (g)\": 4600,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 51.3,\n    \"Beak Depth (mm)\": 14.2,\n    \"Flipper Length (mm)\": 218,\n    \"Body Mass (g)\": 5300,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.5,\n    \"Beak Depth (mm)\": 14,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 4875,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 52.1,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 5550,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.5,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 218,\n    \"Body Mass (g)\": 4950,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 52.2,\n    \"Beak Depth (mm)\": 17.1,\n    \"Flipper Length (mm)\": 228,\n    \"Body Mass (g)\": 5400,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.5,\n    \"Beak Depth (mm)\": 14.5,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 4750,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.5,\n    \"Beak Depth (mm)\": 16.1,\n    \"Flipper Length (mm)\": 224,\n    \"Body Mass (g)\": 5650,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44.5,\n    \"Beak Depth (mm)\": 14.7,\n    \"Flipper Length (mm)\": 214,\n    \"Body Mass (g)\": 4850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.8,\n    \"Beak Depth (mm)\": 15.7,\n    \"Flipper Length (mm)\": 226,\n    \"Body Mass (g)\": 5200,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.4,\n    \"Beak Depth (mm)\": 15.8,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 4925,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.9,\n    \"Beak Depth (mm)\": 14.6,\n    \"Flipper Length (mm)\": 222,\n    \"Body Mass (g)\": 4875,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.4,\n    \"Beak Depth (mm)\": 14.4,\n    \"Flipper Length (mm)\": 203,\n    \"Body Mass (g)\": 4625,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 51.1,\n    \"Beak Depth (mm)\": 16.5,\n    \"Flipper Length (mm)\": 225,\n    \"Body Mass (g)\": 5250,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.5,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 219,\n    \"Body Mass (g)\": 4850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 55.9,\n    \"Beak Depth (mm)\": 17,\n    \"Flipper Length (mm)\": 228,\n    \"Body Mass (g)\": 5600,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.2,\n    \"Beak Depth (mm)\": 15.5,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 4975,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.1,\n    \"Beak Depth (mm)\": 15,\n    \"Flipper Length (mm)\": 228,\n    \"Body Mass (g)\": 5500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.3,\n    \"Beak Depth (mm)\": 13.8,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 4725,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.8,\n    \"Beak Depth (mm)\": 16.1,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 5500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 41.7,\n    \"Beak Depth (mm)\": 14.7,\n    \"Flipper Length (mm)\": 210,\n    \"Body Mass (g)\": 4700,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 53.4,\n    \"Beak Depth (mm)\": 15.8,\n    \"Flipper Length (mm)\": 219,\n    \"Body Mass (g)\": 5500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.3,\n    \"Beak Depth (mm)\": 14,\n    \"Flipper Length (mm)\": 208,\n    \"Body Mass (g)\": 4575,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.1,\n    \"Beak Depth (mm)\": 15.1,\n    \"Flipper Length (mm)\": 209,\n    \"Body Mass (g)\": 5500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.5,\n    \"Beak Depth (mm)\": 15.2,\n    \"Flipper Length (mm)\": 216,\n    \"Body Mass (g)\": 5000,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.8,\n    \"Beak Depth (mm)\": 15.9,\n    \"Flipper Length (mm)\": 229,\n    \"Body Mass (g)\": 5950,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 43.5,\n    \"Beak Depth (mm)\": 15.2,\n    \"Flipper Length (mm)\": 213,\n    \"Body Mass (g)\": 4650,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 51.5,\n    \"Beak Depth (mm)\": 16.3,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 5500,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.2,\n    \"Beak Depth (mm)\": 14.1,\n    \"Flipper Length (mm)\": 217,\n    \"Body Mass (g)\": 4375,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 55.1,\n    \"Beak Depth (mm)\": 16,\n    \"Flipper Length (mm)\": 230,\n    \"Body Mass (g)\": 5850,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 44.5,\n    \"Beak Depth (mm)\": 15.7,\n    \"Flipper Length (mm)\": 217,\n    \"Body Mass (g)\": 4875,\n    \"Sex\": \".\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 48.8,\n    \"Beak Depth (mm)\": 16.2,\n    \"Flipper Length (mm)\": 222,\n    \"Body Mass (g)\": 6000,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 47.2,\n    \"Beak Depth (mm)\": 13.7,\n    \"Flipper Length (mm)\": 214,\n    \"Body Mass (g)\": 4925,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": null,\n    \"Beak Depth (mm)\": null,\n    \"Flipper Length (mm)\": null,\n    \"Body Mass (g)\": null,\n    \"Sex\": null\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 46.8,\n    \"Beak Depth (mm)\": 14.3,\n    \"Flipper Length (mm)\": 215,\n    \"Body Mass (g)\": 4850,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 50.4,\n    \"Beak Depth (mm)\": 15.7,\n    \"Flipper Length (mm)\": 222,\n    \"Body Mass (g)\": 5750,\n    \"Sex\": \"MALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 45.2,\n    \"Beak Depth (mm)\": 14.8,\n    \"Flipper Length (mm)\": 212,\n    \"Body Mass (g)\": 5200,\n    \"Sex\": \"FEMALE\"\n  },\n  {\n    \"Species\": \"Gentoo\",\n    \"Island\": \"Biscoe\",\n    \"Beak Length (mm)\": 49.9,\n    \"Beak Depth (mm)\": 16.1,\n    \"Flipper Length (mm)\": 213,\n    \"Body Mass (g)\": 5400,\n    \"Sex\": \"MALE\"\n  }\n]"
  },
  {
    "path": "contrib/clickhouse/podman-config",
    "content": "NAME=clickhouse\nIMAGE=docker.io/clickhouse/clickhouse-server\nPUBLISH=9000:9000\nENV=\"CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 CLICKHOUSE_USER=clickhouse CLICKHOUSE_PASSWORD=P4ssw0rd\"\n"
  },
  {
    "path": "contrib/clickhouse/usql-config",
    "content": "DB=\"clickhouse://clickhouse:P4ssw0rd@localhost\"\nVSQL=\"select version() as version;\"\n"
  },
  {
    "path": "contrib/cockroach/podman-config",
    "content": "NAME=cockroach\nIMAGE=docker.io/cockroachdb/cockroach:latest\nPUBLISH=26257:26257\nENV=\"COCKROACH_DATABASE=cockroach COCKROACH_USER=cockroach COCKROACH_PASSWORD=P4ssw0rd\"\nCMD=start-single-node\n"
  },
  {
    "path": "contrib/config.yaml",
    "content": "---\n# named connections\nconnections:\n  my_couchbase_conn: couchbase://Administrator:P4ssw0rd@localhost\n  my_clickhouse_conn: clickhouse://clickhouse:P4ssw0rd@localhost\n  css: cassandra://cassandra:cassandra@localhost\n  fsl: flightsql://flight_username:P4ssw0rd@localhost\n  gdr:\n    protocol: godror\n    username: system\n    password: P4ssw0rd\n    hostname: localhost\n    port: 1521\n    database: free\n  ign: ignite://ignite:ignite@localhost\n  mss: sqlserver://sa:Adm1nP@ssw0rd@localhost\n  mym: mysql://root:P4ssw0rd@localhost\n  myz: mymysql://root:P4ssw0rd@localhost\n  ora: oracle://system:P4ssw0rd@localhost/free\n  ore: oracle://system:P4ssw0rd@localhost:1522/db1\n  pgs: postgres://postgres:P4ssw0rd@localhost\n  pgx: pgx://postgres:P4ssw0rd@localhost\n  vrt:\n    proto: vertica\n    user: vertica\n    pass: vertica\n    host: localhost\n  sll:\n    file: /path/to/mydb.sqlite3\n  mdc: modernsqlite:test.db\n  dkd: test.duckdb\n  zzz: [\"databricks\", \"token:dapi*****@adb-*************.azuredatabricks.net:443/sql/protocolv1/o/*********/*******\"]\n  zz2:\n    proto: mysql\n    user: \"my username\"\n    pass: \"my password!\"\n    host: localhost\n    opts:\n      opt1: \"😀\"\n# init script\ninit: |\n  \\echo welcome to the jungle `date`\n  \\set SYNTAX_HL_STYLE paraiso-dark\n  \\set PROMPT1 '\\033[32m%S%M%/%R%#\\033[0m '\n  \\set bar test\n  \\set foo test\n  -- \\set SHOW_HOST_INFORMATION false\n  -- \\set SYNTAX_HL false\n  \\set 型示師 '本門台初埼本門台初埼'\n# charts path\ncharts_path: charts\n# defined queries\nqueries:\n  q1:\n"
  },
  {
    "path": "contrib/couchbase/README.md",
    "content": "# Couchbase Notes\n\n```sh\n$ podman volume create couchbase-data\n```\n\nAfter running the docker image, browse to http://127.0.0.1:8091/ui/index.html\nand manually configure database.\n"
  },
  {
    "path": "contrib/couchbase/podman-config",
    "content": "NAME=couchbase\nIMAGE=docker.io/library/couchbase\nPUBLISH=8091-8094:8091-8094\nVOLUME=couchbase-data:/opt/couchbase/var\n"
  },
  {
    "path": "contrib/couchbase/usql-config",
    "content": "# NOTE: this will only work after setting up a database on http://localhost:8091/\nDB=\"couchbase://Administrator:P4ssw0rd@localhost\"\nVSQL=\"select raw ds_version() as version;\"\n"
  },
  {
    "path": "contrib/db2/README.md",
    "content": "# db2 Notes\n\n1. Install unixodbc:\n\n```sh\n$ sudo aptitude install unixodbc unixodbc-bin unixodbc-dev\n$ yay -S unixodbc\n```\n\n2. Download `dsdriver` and install:\n\n```sh\n$ ls ~/Downloads/ibm_data_server_driver_package_linuxx64_v11.5.tar.gz\n/home/ken/Downloads/ibm_data_server_driver_package_linuxx64_v11.5.tar.gz\n$ sudo ./install-dsdriver.sh\n```\n\n3. Copy ODBC and CLI configs:\n\n```sh\n$ cat odbcinst.ini | sudo tee -a /etc/odbcinst.ini\n$ sudo cp {db2cli.ini,db2dsdriver.cfg} /opt/db2/clidriver/cfg/\n```\n\n4. Run DB2 container:\n\n```sh\n$ ../podman-run.sh db2 -u\n```\n\n5. Verify DB2 working:\n\n```sh\n$ ./db2cli-validate.sh\n```\n"
  },
  {
    "path": "contrib/db2/db2cli-validate.sh",
    "content": "#!/bin/bash\n\n# see https://www.ibm.com/developerworks/community/blogs/ff78a96f-bf23-457e-befa-77f266844cbb/entry/db2cli_validate_command_line_tool_for_validating_and_testing_cli_environment_and_configuration?lang=en\n# see https://web.archive.org/web/20240715121603/https://blogs.sas.com/content/sgf/2017/11/16/connecting-sas-db2-database-via-odbc-without-tears/\n\nCLIDRIVER=${1:-/opt/db2/clidriver}\n\nexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CLIDRIVER/lib\nexport DB2CLIINIPATH=$CLIDRIVER/cfg\nexport DB2DSDRIVER_CFG_PATH=$CLIDRIVER/cfg\n\n$CLIDRIVER/bin/db2cli validate -dsn SAMPLE -connect -user db2inst1 -passwd P4ssw0rd\n"
  },
  {
    "path": "contrib/db2/db2dsdriver.cfg",
    "content": "<configuration>\n  <dsncollection>\n    <dsn alias=\"SAMPLE\" name=\"SAMPLE\" host=\"localhost\" port=\"50000\"> </dsn>\n  </dsncollection>\n  <databases>\n    <database name=\"SAMPLE\" host=\"localhost\" port=\"50000\"> </database>\n  </databases>\n</configuration>\n"
  },
  {
    "path": "contrib/db2/install-dsdriver.sh",
    "content": "#!/bin/bash\n\nDEST=${1:-/opt/db2}\nFILE=$2\n\nif [ ! -w $DEST ]; then\n  echo \"ERROR: not able to write to $DEST\"\n  exit 1\nfi\n\necho \"DEST: $DEST\"\n\nif [ ! -e \"$DEST\" ]; then\n  echo \"$DEST does not exist\"\n  exit 1\nfi\n\nif [ -z \"$FILE\" ]; then\n  FILE=$(ls $HOME/Downloads/ibm_data_server_driver_package_linuxx64_*.tar.gz||:)\nfi\n\nif [ -z \"$FILE\" ]; then\n  echo \"cannot find driver package to extract\"\n  exit 1\nfi\n\nset -e\n\nUSER=$(whoami)\n\n# extract\npushd $DEST &> /dev/null\necho \"EXTRACTING: $FILE\"\n\n# extract\ntar -zxf $FILE\ntar -zxf dsdriver/odbc_cli_driver/linuxamd64/ibm_data_server_driver_for_odbc_cli.tar.gz\n\n# fix permissions\nchown $USER:$USER -R .\nfind ./ -type d -exec chmod 0755 {} \\;\nfind ./ -type d -exec chmod -s {} \\;\n\npopd &> /dev/null\n"
  },
  {
    "path": "contrib/db2/podman-config",
    "content": "NAME=db2\nIMAGE=icr.io/db2_community/db2\nPUBLISH=\"50000:50000 55000:55000\"\nENV=\"LICENSE=accept DB2INSTANCE=db2inst1 DB2INST1_PASSWORD=P4ssw0rd DBNAME=testdb\"\nVOLUME=db2-data:/database\n"
  },
  {
    "path": "contrib/db2/test.sql",
    "content": "\\connect odbc+db2://db2inst1:P4ssw0rd@localhost/testdb\n\ncreate schema test;\n\ncreate table test.mytable (\n  COL1 INTEGER NOT NULL,\n  COL2 CHAR(25),\n  COL3 VARCHAR(25) NOT NULL,\n  COL4 DATE,\n  COL5 DECIMAL(10,2),\n  PRIMARY KEY (COL1),\n  UNIQUE (COL3)\n);\n\ninsert into test.mytable\n  (col1, col2, col3, col4, col5)\nvalues\n  (1, 'a', 'first', current date, 15.0),\n  (2, 'b', 'second', current date, 16.0),\n  (3, 'c', 'third', current date, 17.0)\n;\n\nselect * from test.mytable;\n"
  },
  {
    "path": "contrib/db2/usql-config",
    "content": "DB=\"odbc+db2://db2inst1:P4ssw0rd@localhost/testdb\"\nVSQL=\"SELECT service_level AS version FROM sysibmadm.env_inst_info;\"\n"
  },
  {
    "path": "contrib/duckdb/usql-config",
    "content": "DB=\"duckdb:test.duckdb\"\nVSQL=\"SELECT library_version AS version FROM pragma_version();\"\n"
  },
  {
    "path": "contrib/exasol/podman-config",
    "content": "NAME=exasol\nIMAGE=docker.io/exasol/docker-db\nPUBLISH=8563:8563\n"
  },
  {
    "path": "contrib/exasol/usql-config",
    "content": "DB=\"exasol://sys:exasol@localhost/?encryption=0\"\nVSQL=\"SELECT param_value AS version FROM exa_metadata WHERE param_name = 'databaseProductVersion';\"\n"
  },
  {
    "path": "contrib/firebird/podman-config",
    "content": "NAME=firebird\nIMAGE=docker.io/jacobalberty/firebird\nPUBLISH=3050:3050\nENV=\"FIREBIRD_DATABASE=booktest FIREBIRD_USER=booktest FIREBIRD_PASSWORD=booktest\"\n"
  },
  {
    "path": "contrib/firebird/usql-config",
    "content": "DB=\"firebird://booktest:booktest@localhost/booktest\"\nVSQL=\"SELECT rdb\\$get_context('SYSTEM', 'ENGINE_VERSION') AS version FROM rdb\\$database;\"\n"
  },
  {
    "path": "contrib/flightsql/podman-config",
    "content": "NAME=flightsql\nIMAGE=docker.io/voltrondata/flight-sql\nPUBLISH=31337:31337\nENV=\"FLIGHT_PASSWORD=P4ssw0rd\"\n"
  },
  {
    "path": "contrib/flightsql/usql-config",
    "content": "DB=\"flightsql://flight_username:P4ssw0rd@localhost:31337?tls=skip-verify\"\nVSQL=\"SELECT version() AS version;\"\n"
  },
  {
    "path": "contrib/go-setup.sh",
    "content": "#!/bin/bash\n\n# trimmed down version of:\n# https://github.com/kenshaw/shell-config/blob/master/scripts/go-setup.sh\n\nARCH=$(uname -m)\nPLATFORM=linux\n\ncase $ARCH in\n  aarch64) ARCH=arm64 ;;\n  x86_64)  ARCH=amd64 ;;\nesac\n\nREPO=https://go.googlesource.com/go\nDL=https://go.dev/dl/\nEXT=tar.gz\n\nDEST=/usr/local\n\nset -e\n\nLATEST=$(curl -4 -s \"$DL\"|sed -E -n \"/<a .+?>go1\\.[0-9]+(\\.[0-9]+)?\\.$PLATFORM-$ARCH\\.$EXT</p\"|head -1)\nARCHIVE=$(sed -E -e 's/.*<a .+?>(.+?)<\\/a.*/\\1/' <<< \"$LATEST\")\nSTABLE=$(sed -E -e 's/^go//' -e \"s/\\.$PLATFORM-$ARCH\\.$EXT$//\" <<< \"$ARCHIVE\")\n\nif ! [[ \"$STABLE\" =~ ^1\\.[0-9\\.]+$ ]]; then\n  echo \"ERROR: unable to retrieve latest Go version for $PLATFORM/$ARCH ($STABLE)\"\n  exit 1\nfi\n\nREMOTE=$(sed -E -e 's/.*<a .+?href=\"(.+?)\".*/\\1/' <<< \"$LATEST\")\nVERSION=\"go$STABLE\"\n\nOPTIND=1\nwhile getopts \"v:\" opt; do\ncase \"$opt\" in\n  v) VERSION=$OPTARG ;;\nesac\ndone\n\n# prefix passed version with go\nif [[ \"$VERSION\" =~ ^1\\.[0-9]+ ]]; then\n  VERSION=\"go$VERSION\"\nfi\n\nif ! [[ \"$VERSION\" =~ ^go1\\.[0-9]+\\.[0-9]+$ ]]; then\n  echo \"ERROR: invalid Go version $VERSION\"\n  exit 1\nfi\n\nif ! [[ \"$REMOTE\" =~ \"^https://\" ]]; then\n  REMOTE=\"https://go.dev$REMOTE\"\nfi\n\necho \"ARCH:       $PLATFORM/$ARCH\"\necho \"DEST:       $DEST\"\necho \"STABLE:     $STABLE ($REMOTE)\"\necho \"VERSION:    $VERSION\"\n\ngrab() {\n  echo \"RETRIEVING: $1 -> $2\"\n  curl -4 -L -# -o $2 $1\n}\n\n# extract\nWORKDIR=$(mktemp -d /tmp/go-setup.XXXX)\ngrab $REMOTE $WORKDIR/$ARCHIVE\necho \"USING:      $WORKDIR/$ARCHIVE\"\n\npushd $WORKDIR &> /dev/null\ncase $EXT in\n  tar.gz) tar -zxf $ARCHIVE ;;\n  zip)    unzip -q $ARCHIVE ;;\n  *)\n    echo \"ERROR:      unknown extension $EXT\"\n    exit\n  ;;\nesac\n\necho \"MOVING:     $WORKDIR/go -> $DEST/go\"\nmv go $DEST/go\n\nchown -R root:root $DEST/go\n\necho \"INSTALLED:  $($DEST/go/bin/go version)\"\n"
  },
  {
    "path": "contrib/godror/fix-oob-config.sh",
    "content": "#!/bin/bash\n\n# adds DISABLE_OOB=on to user's .sqlnet.ora config\n#\n# See:\n#   https://github.com/oracle/docker-images/issues/1352\n#   https://franckpachot.medium.com/19c-instant-client-and-docker-1566630ab20e\n\necho \"DISABLE_OOB=ON\" >> $HOME/.sqlnet.ora\n"
  },
  {
    "path": "contrib/godror/grab-instantclient.sh",
    "content": "#!/bin/bash\n\nDEST=${1:-/opt/oracle}\n\n# available versions:\n# 21.7.0.0.0\n# 21.6.0.0.0\n# 21.1.0.0.0\n# 19.9.0.0.0\n# 18.5.0.0.0\n# 12.2.0.1.0\n\nVERSION=\n\nOPTIND=1\nwhile getopts \"v:\" opt; do\ncase \"$opt\" in\n  v) VERSION=$OPTARG ;;\nesac\ndone\n\nif [ -z \"$VERSION\" ]; then\n  VERSION=$(\n    wget --quiet -O- https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html| \\\n      sed -n -e 's/.*\\/instantclient-basic-linux\\.x64-\\([^d]\\+\\)dbru\\.zip.*/\\1/p' | \\\n      head -1\n    )\nfi\n\nif [[ ! \"$VERSION\" =~ ^[0-9\\.]+$ ]]; then\n  echo \"error: invalid VERSION\"\n  exit 1\nfi\n\nBASE=https://download.oracle.com/otn_software/linux/instantclient/$(sed -e 's/[^0-9]//g' <<< \"$VERSION\")\n\n# build list of archives to retrieve\ndeclare -a ARCHIVES\nfor i in basic sdk sqlplus; do\n  ARCHIVES+=(\"$BASE/instantclient-$i-linux.x64-${VERSION}dbru.zip\")\ndone\n\ngrab() {\n  echo -n \"RETRIEVING: $1 -> $2     \"\n  wget --progress=dot -O $2 $1 2>&1 |\\\n    grep --line-buffered \"%\" | \\\n    sed -u -e \"s,\\.,,g\" | \\\n    awk '{printf(\"\\b\\b\\b\\b%4s\", $2)}'\n  echo -ne \"\\b\\b\\b\\b\"\n  echo \" DONE.\"\n}\n\ncache() {\n  FILE=$(basename $2)\n  if [ ! -f $1/$FILE ]; then\n    grab $2 $1/$FILE\n  fi\n}\n\nset -e\n\necho \"DEST:       $DEST\"\nif [ ! -w $DEST ]; then\n  echo \"$DEST is not writable!\"\n  exit 1\nfi\nif [ ! -e \"$DEST\" ]; then\n  echo \"$DEST does not exist!\"\n  exit 1\nfi\n\n# retrieve archives\nfor i in ${ARCHIVES[@]}; do\n  cache $DEST $i\ndone\n\n# remove existing directory, if any\nDVER=$(awk -F. '{print $1 \"_\" $2}' <<< \"$VERSION\")\nif [ -e $DEST/instantclient_$DVER ]; then\n  echo \"REMOVING:   $DEST/instantclient_$DVER\"\n  rm -rf $DEST/instantclient_$DVER\nfi\n\n# extract\npushd $DEST &> /dev/null\nfor i in ${ARCHIVES[@]}; do\n  unzip -qq $(basename $i)\ndone\npopd &> /dev/null\n\n# write pkg-config file\nDATA=$(cat <<ENDSTR\nprefix=\\${pcfiledir}\n\nversion=$VERSION\nbuild=client64\n\nlibdir=\\${prefix}/instantclient_${DVER}\nincludedir=\\${prefix}/instantclient_${DVER}/sdk/include\n\nName: OCI\nDescription: Oracle database engine\nVersion: ${VERSION}\nLibs: -L\\${libdir} -lclntsh\nLibs.private:\nCflags: -I\\${includedir}\nENDSTR\n)\necho \"$DATA\" > $DEST/oci8.pc\nrm -f /etc/ld.so.conf.d/oracle-instantclient.conf\necho \"$DEST/instantclient_$DVER\" | tee -a /etc/ld.so.conf.d/oracle-instantclient.conf\nldconfig -v\n\n# write sqlnet.ora\nDATA=$(cat <<ENDSTR\nDIAG_ADR_ENABLED = OFF\nTRACE_LEVEL_CLIENT = OFF\nTRACE_DIRECTORY_CLIENT = /dev/null\nLOG_DIRECTORY_CLIENT = /dev/null\nLOG_FILE_CLIENT = /dev/null\nLOG_LEVEL_CLIENT = OFF\nENDSTR\n)\necho \"$DATA\" > $DEST/instantclient_${DVER}/network/admin/sqlnet.ora\n"
  },
  {
    "path": "contrib/godror/usql-config",
    "content": "DB=\"godror://system:P4ssw0rd@localhost/orasid\"\nVSQL=\"SELECT version FROM v\\$instance\"\n"
  },
  {
    "path": "contrib/h2/podman-config",
    "content": "NAME=h2\nIMAGE=docker.io/buildo/h2database\nPUBLISH=\"8082:8082 9092:9092\"\n"
  },
  {
    "path": "contrib/hive/podman-config",
    "content": "NAME=hive\nIMAGE=docker.io/apache/hive:4.0.0-beta-1\nPUBLISH=\"10000:10000 10002:10002\"\nENV=\"SERVICE_NAME=hiveserver2\"\n"
  },
  {
    "path": "contrib/hive/usql-config",
    "content": "DB=\"hive://user:pass@localhost\"\nVSQL=\"SELECT version() AS version;\"\n"
  },
  {
    "path": "contrib/ignite/README.md",
    "content": "# Ignite Notes\n\nAfter starting the database, run `activate.sh`:\n\n```sh\n$ ./activate.sh\n```\n"
  },
  {
    "path": "contrib/ignite/activate.sh",
    "content": "#!/bin/bash\n\ndocker exec -it ignite \\\n  /opt/ignite/apache-ignite/bin/control.sh \\\n  --activate \\\n  --user ignite \\\n  --password ignite\n"
  },
  {
    "path": "contrib/ignite/podman-config",
    "content": "NAME=ignite\nIMAGE=docker.io/usql/ignite\nPUBLISH=10800:10800\nNETWORK=host\n"
  },
  {
    "path": "contrib/ignite/usql-config",
    "content": "DB=\"ignite://ignite:ignite@localhost/ExampleDB\"\n"
  },
  {
    "path": "contrib/mymysql/usql-config",
    "content": "DB=\"mymysql://root:P4ssw0rd@localhost/\"\nVSQL=\"SELECT version() AS version;\"\n"
  },
  {
    "path": "contrib/mysql/podman-config",
    "content": "NAME=mysql\nIMAGE=docker.io/library/mariadb\nPUBLISH=3306:3306\nENV=\"MYSQL_ROOT_PASSWORD=P4ssw0rd\"\n"
  },
  {
    "path": "contrib/mysql/test.sql",
    "content": "-- mysql test script\n\n\\set\n\n\\set SYNTAX_HL_FORMAT terminal16m\n\\set SYNTAX_HL true\n\n\\?\n\n\\copyright\n\n\\set SYNTAX_HL_STYLE dracula\n\nselect 'test''\n' \\g\n\n\\set NAME myname\n\ndrop database if exists testdb; create database testdb; use testdb;\n\nSET FOREIGN_KEY_CHECKS=0;\nDROP TABLE IF EXISTS authors;\nDROP TABLE IF EXISTS books;\nDROP FUNCTION IF EXISTS say_hello;\nSET FOREIGN_KEY_CHECKS=1;\n\nCREATE TABLE authors (\n  author_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,\n  name text NOT NULL DEFAULT ''\n) ENGINE=InnoDB;\n\nCREATE INDEX authors_name_idx ON authors(name(255));\n\n\\set SYNTAX_HL_STYLE paraiso-dark\n\nCREATE TABLE books (\n  /*\n    this is a multiline comment\n   */\n  book_id integer NOT NULL AUTO_INCREMENT PRIMARY KEY,\n  author_id integer NOT NULL,\n  isbn varchar(255) NOT NULL DEFAULT '' UNIQUE,\n  book_type ENUM('FICTION', 'NONFICTION') NOT NULL DEFAULT 'FICTION',\n  title text NOT NULL DEFAULT '',\n  year integer NOT NULL DEFAULT 2000,\n  available datetime NOT NULL DEFAULT NOW(),\n  tags text NOT NULL DEFAULT '',\n  CONSTRAINT FOREIGN KEY (author_id) REFERENCES authors(author_id)\n) ENGINE=InnoDB;\n\nCREATE INDEX books_title_idx ON books(title, year);\n\ninsert into authors (name) values\n  ('jk rowling'),\n  ('author amazing')\n\\g\n\n  select * from authors;\n\n\\set COLNAME name\n\\set NAME amaz\n\n\\echo `echo hello`\n\nselect :\"COLNAME\" from authors where :COLNAME like '%' || :'NAME' || '%'\n\n\\print \\raw\n\n\\g\n\n\\gset AUTHOR_\n\nselect :'AUTHOR_name';\n\n\\begin\ninsert into authors (name) values ('test');\n\\rollback\n\ninsert into authors (name) values ('hello');\nselect * from authors;\n\ninsert into books (author_id, isbn, title, year, available) values\n  (1, '1', 'one', 2018, '2018-06-01 00:00:00'),\n  (2, '2', 'two', 2019, '2019-06-01 00:00:00')\n;\n\nselect * from books b inner join authors a on a.author_id = b.author_id;\n\nCREATE FUNCTION say_hello(s text) RETURNS text\n  DETERMINISTIC\n  RETURN CONCAT('hello ', s);\n\nselect say_hello('a name!') \\G\n\n  /* exiting! */\n\\q\n"
  },
  {
    "path": "contrib/mysql/usql-config",
    "content": "DB=\"mysql://root:P4ssw0rd@localhost/\"\nVSQL=\"SELECT version() AS version;\"\n"
  },
  {
    "path": "contrib/oracle/init.sql",
    "content": "\\set ORACLE_USER system\n\\set ORACLE_PASS oracle\n\\set ORACLE_SVC xe\n\\set ORACLE_HOST `docker port oracle 1521`\n\n\\prompt NAME 'Create database user: '\n\\prompt -password PASS 'Password for \"':NAME'\": '\n\n\\connect 'oracle://':ORACLE_USER':':ORACLE_PASS'@':ORACLE_HOST'/':ORACLE_SVC\n\n\\set DATNAME :NAME.dat\n\nCREATE\n  TABLESPACE :NAME\n  NOLOGGING\n  DATAFILE :'DATNAME'\n  SIZE 100m\n  AUTOEXTEND ON;\n\nCREATE\n  USER :NAME\n  IDENTIFIED BY :NAME\n  DEFAULT TABLESPACE :NAME;\n\nGRANT\n  CREATE SESSION,\n  CREATE TABLE,\n  CREATE VIEW,\n  CREATE SEQUENCE,\n  CREATE PROCEDURE,\n  CREATE TRIGGER,\n  UNLIMITED TABLESPACE,\n  SELECT ANY DICTIONARY\nTO :NAME;\n\nALTER SYSTEM\n  SET OPEN_CURSORS=400\n  SCOPE=both;\n"
  },
  {
    "path": "contrib/oracle/podman-config",
    "content": "NAME=oracle\nIMAGE=container-registry.oracle.com/database/free\nPUBLISH=1521:1521\nENV=\"ORACLE_PDB=db1 ORACLE_PWD=P4ssw0rd\"\nVOLUME=oracle-free-data:/opt/oracle/oradata\n"
  },
  {
    "path": "contrib/oracle/usql-config",
    "content": "DB=\"oracle://system:P4ssw0rd@localhost/free\"\nVSQL=\"SELECT version FROM v\\$instance\"\n"
  },
  {
    "path": "contrib/oracle-enterprise/podman-config",
    "content": "NAME=oracle-enterprise\nIMAGE=container-registry.oracle.com/database/enterprise:21.3.0.0\nPUBLISH=1522:1521\nENV=\"ORACLE_PDB=db1 ORACLE_PWD=P4ssw0rd\"\nVOLUME=oracle-enterprise-data:/opt/oracle/oradata\n"
  },
  {
    "path": "contrib/oracle-enterprise/usql-config",
    "content": "DB=\"oracle://system:P4ssw0rd@localhost:1522/db1\"\nVSQL=\"SELECT version FROM v\\$instance\"\n"
  },
  {
    "path": "contrib/pgx/usql-config",
    "content": "DB=\"pgx://postgres:P4ssw0rd@localhost\"\nVSQL=\"SELECT setting AS version FROM pg_settings WHERE name='server_version';\"\n"
  },
  {
    "path": "contrib/podman-run.sh",
    "content": "#!/bin/bash\n\n# podman-run.sh: starts or restarts podman containers.\n#\n# Usage: podman-run.sh <TARGET> [-u]\n#\n# Where <target> is a name of a subdirectory containing podman-config,\n# 'all', or 'test'.\n#\n# all  -- starts all available database images.\n# test -- starts the primary testing images. The testing images are cassandra, mysql, postgres, sqlserver, and oracle\n# -u   -- perform podman pull for images prior to start.\n#\n# Will stop any running podman container prior to starting.\n\nDIR=$1\n\nSRC=$(realpath $(cd -P \"$(dirname \"${BASH_SOURCE[0]}\" )\" && pwd))\n\nif [ -z \"$DIR\" ]; then\n  echo \"usage: $0 <TARGET> [-u]\"\n  exit 1\nfi\n\nshift\n\nUPDATE=0\n\nOPTIND=1\nwhile getopts \"u\" opt; do\ncase \"$opt\" in\n  u) UPDATE=1 ;;\nesac\ndone\n\npodman_run() {\n  TARGET=$1\n  BASE=$SRC/$TARGET\n  if [ ! -e $BASE/podman-config ]; then\n    echo \"error: $BASE/podman-config doesn't exist\"\n    exit 1\n  fi\n\n  # load parameters from podman-config\n  unset IMAGE NAME PUBLISH ENV VOLUME NETWORK PRIVILEGED HOSTNAME PARAMS CMD\n  source $BASE/podman-config\n  if [[ \"$TARGET\" != \"$NAME\" ]]; then\n    echo \"error: $BASE/podman-config is invalid\"\n    exit 1\n  fi\n\n  # default network settings\n  if [ -z \"$NETWORK\" ]; then\n    NETWORK=slirp4netns\n  fi\n\n  # setup params\n  PARAMS=()\n  for k in NAME PUBLISH ENV VOLUME NETWORK PRIVILEGED HOSTNAME; do\n    n=$(tr 'A-Z' 'a-z' <<< \"$k\")\n    v=$(eval echo \"\\$$k\")\n    if [ ! -z \"$v\" ]; then\n      for p in $v; do\n        PARAMS+=(\"--$n=$p\")\n      done\n    fi\n  done\n\n  # determine if image exists\n  EXISTS=$(podman image ls -q $IMAGE)\n  if [[ \"$UPDATE\" == \"0\" && -z \"$EXISTS\" ]]; then\n    UPDATE=1\n  fi\n\n  # show parameters\n  echo \"-------------------------------------------\"\n  echo \"NAME:       $NAME\"\n  echo \"IMAGE:      $IMAGE (update: $UPDATE)\"\n  echo \"PUBLISH:    $PUBLISH\"\n  echo \"ENV:        $ENV\"\n  echo \"VOLUME:     $VOLUME\"\n  echo \"NETWORK:    $NETWORK\"\n  echo \"PRIVILEGED: $PRIVILEGED\"\n  echo \"HOSTNAME:   $HOSTNAME\"\n  echo \"CMD:        $CMD\"\n  echo\n\n  # update\n  if [[ \"$UPDATE\" == \"1\" ]]; then\n    if [ ! -f $BASE/Dockerfile ]; then\n      (set -ex;\n        podman pull $IMAGE\n      )\n    else\n      pushd $BASE &> /dev/null\n      (set -ex;\n        podman build --pull -t $IMAGE:latest .\n      )\n      popd &> /dev/null\n    fi\n    REF=$(awk -F: '{print $1}' <<< \"$IMAGE\")\n    REMOVE=$(podman image list --filter=dangling=true --filter=reference=$IMAGE -q)\n    if [ ! -z \"$REMOVE\" ]; then\n      (set -ex;\n        podman image rm -f $REMOVE\n      )\n    fi\n  fi\n\n  # stop and remove\n  if [ ! -z \"$(podman ps -q --filter \"name=$NAME\")\" ]; then\n    (set -x;\n      podman stop $NAME\n    )\n  fi\n  if [ ! -z \"$(podman ps -q -a --filter \"name=$NAME\")\" ]; then\n    (set -x;\n      podman rm -f $NAME\n    )\n  fi\n\n  # start\n  (set -ex;\n    podman run --detach --rm ${PARAMS[@]} $IMAGE $CMD\n  )\n  echo\n}\n\npushd $SRC &> /dev/null\nTARGETS=()\ncase $DIR in\n  all)\n    TARGETS+=($(find . -type f -name podman-config|awk -F'/' '{print $2}'))\n  ;;\n  test)\n    TARGETS+=(mysql postgres sqlserver oracle clickhouse cassandra)\n  ;;\n  *)\n    TARGETS+=($DIR)\n  ;;\nesac\n\nfor TARGET in ${TARGETS[@]}; do\n  podman_run $TARGET\ndone\npopd &> /dev/null\n"
  },
  {
    "path": "contrib/podman-stop.sh",
    "content": "#!/bin/bash\n\nSRC=$(realpath $(cd -P \"$(dirname \"${BASH_SOURCE[0]}\" )\" && pwd))\n\nfor TARGET in $SRC/*/podman-config; do\n  NAME=$(basename $(dirname $TARGET))\n  if [ ! -z \"$(podman ps -q --filter \"name=$NAME\")\" ]; then\n    (set -x;\n      podman stop $NAME\n    )\n  fi\n  if [ ! -z \"$(podman ps -q -a --filter \"name=$NAME\")\" ]; then\n    (set -x;\n      podman rm -f $NAME\n    )\n  fi\ndone\n"
  },
  {
    "path": "contrib/postgres/init.sql",
    "content": "\\set POSTGRES_USER postgres\n\\set POSTGRES_PASS P4ssw0rd\n\\set POSTGRES_DB   postgres\n\\set POSTGRES_HOST `docker port postgres 5432 | head -n1`\n\n\\prompt NAME 'Create database user: '\n\\prompt -password PASS 'Password for \"':NAME'\": '\n\n\\connect 'postgres://':POSTGRES_USER':':POSTGRES_PASS'@':POSTGRES_HOST'/':POSTGRES_DB'?sslmode=disable'\n\nDROP USER IF EXISTS :NAME;\n\nCREATE USER :NAME PASSWORD :'PASS';\n\nDROP DATABASE IF EXISTS :NAME;\n\nCREATE DATABASE :NAME OWNER :NAME;\n"
  },
  {
    "path": "contrib/postgres/podman-config",
    "content": "NAME=postgres\nIMAGE=docker.io/usql/postgres\nPUBLISH=5432:5432\nENV=\"POSTGRES_PASSWORD=P4ssw0rd\"\n"
  },
  {
    "path": "contrib/postgres/schema.sql",
    "content": "\\connect postgres://booktest:booktest@localhost/\n\nDROP TABLE IF EXISTS books CASCADE;\nDROP TYPE IF EXISTS book_type CASCADE;\nDROP TABLE IF EXISTS authors CASCADE;\nDROP FUNCTION IF EXISTS say_hello(text) CASCADE;\n\nCREATE TABLE authors (\n  author_id SERIAL PRIMARY KEY,\n  name text NOT NULL DEFAULT ''\n);\n\nCREATE INDEX authors_name_idx ON authors(name);\n\nCREATE TYPE book_type AS ENUM (\n  'FICTION',\n  'NONFICTION'\n);\n\nCREATE TABLE books (\n  book_id SERIAL PRIMARY KEY,\n  author_id integer NOT NULL REFERENCES authors(author_id),\n  isbn text NOT NULL DEFAULT '' UNIQUE,\n  booktype book_type NOT NULL DEFAULT 'FICTION',\n  title text NOT NULL DEFAULT '',\n  year integer NOT NULL DEFAULT 2000,\n  available timestamp with time zone NOT NULL DEFAULT 'NOW()',\n  tags varchar[] NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX books_title_idx ON books(title, year);\n\nCREATE FUNCTION say_hello(text) RETURNS text AS $$\nBEGIN\n  RETURN CONCAT('hello ', $1);\nEND;\n$$ LANGUAGE plpgsql;\n\nCREATE INDEX books_title_lower_idx ON books(title);\n"
  },
  {
    "path": "contrib/postgres/test.sql",
    "content": "-- postgres test script\n\n\\set\n\n\\set SYNTAX_HL_FORMAT terminal16m\n\\set SYNTAX_HL true\n\n\\?\n\n\\copyright\n\n\\set SYNTAX_HL_STYLE dracula\n\nselect 'test''\n' \\g\n\n\\set NAME myname\n\nDROP TABLE IF EXISTS books;\nDROP TABLE IF EXISTS authors;\n\nDROP TABLE IF EXISTS books CASCADE;\nDROP TYPE IF EXISTS book_type CASCADE;\nDROP TABLE IF EXISTS authors CASCADE;\nDROP FUNCTION IF EXISTS say_hello(text) CASCADE;\n\nCREATE TABLE authors (\n  author_id SERIAL PRIMARY KEY,\n  name text NOT NULL DEFAULT ''\n);\nCREATE INDEX authors_name_idx ON authors(name);\nCREATE TYPE book_type AS ENUM (\n  'FICTION',\n  'NONFICTION'\n);\n\nCREATE INDEX authors_name_idx ON authors(name);\n\n\\set SYNTAX_HL_STYLE paraiso-dark\n\nCREATE TABLE books (\n  /*\n    this is a multiline comment\n   */\n  book_id SERIAL PRIMARY KEY,\n  author_id integer NOT NULL REFERENCES authors(author_id),\n  isbn text NOT NULL DEFAULT '' UNIQUE,\n  booktype book_type NOT NULL DEFAULT 'FICTION',\n  title text NOT NULL DEFAULT '',\n  year integer NOT NULL DEFAULT 2000,\n  available timestamp with time zone NOT NULL DEFAULT 'NOW()',\n  tags varchar[] NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX books_title_idx ON books(title, year);\n\ninsert into authors (name) values\n  ('jk rowling'),\n  ('author amazing')\n\\g\n\n  select * from authors;\n\n\\set COLNAME name\n\\set NAME amaz\n\n\\echo `echo hello`\n\nselect :\"COLNAME\" from authors where :COLNAME like '%' || :'NAME' || '%'\n\n\\print \\raw\n\n\\g\n\n\\gset AUTHOR_\n\nselect :'AUTHOR_name';\n\n\\begin\ninsert into authors (name) values ('test');\n\\rollback\n\ninsert into authors (name) values ('hello');\nselect * from authors;\n\ninsert into books (author_id, isbn, title, year, available) values\n  (1, '1', 'one', 2018, '2018-06-01 00:00:00'),\n  (2, '2', 'two', 2019, '2019-06-01 00:00:00')\n;\n\nselect * from books b inner join authors a on a.author_id = b.author_id;\n\nCREATE FUNCTION say_hello(text) RETURNS text AS $$\nBEGIN\n  RETURN CONCAT('hello ', $1);\nEND;\n$$ LANGUAGE plpgsql;\n\nselect say_hello('a name!') \\G\n\n  /* exiting! */\n\\q\n"
  },
  {
    "path": "contrib/postgres/usql-config",
    "content": "DB=\"postgres://postgres:P4ssw0rd@localhost\"\nVSQL=\"SELECT setting AS version FROM pg_settings WHERE name='server_version';\"\n"
  },
  {
    "path": "contrib/presto/podman-config",
    "content": "NAME=presto\nIMAGE=docker.io/ahanaio/prestodb-sandbox\nPUBLISH=8080:8080\n"
  },
  {
    "path": "contrib/presto/usql-config",
    "content": "DB=\"presto://localhost\"\nVSQL=\"SELECT node_version AS version FROM system.runtime.nodes LIMIT 1;\"\n"
  },
  {
    "path": "contrib/sqlite3/build-windows-icu.sh",
    "content": "#!/bin/bash\n\n../source/runConfigureICU \\\n  MinGW \\\n  --host=x86_64-w64-mingw32 \\\n  --disable-release \\\n  --disable-debug \\\n  --enable-static \\\n  --prefix=/opt/local\n"
  },
  {
    "path": "contrib/sqlite3/icu-i18n-mingw64.pc",
    "content": "mingw64_prefix=C:\\msys64\\opt\\local\n\nincludedir=\"${mingw64_prefix}\\include\"\nlibdir=\"${mingw64_prefix}\\lib\"\n\nName: icu-i18n-mingw64\nVersion: dev\nDescription: icu-i18n\nCflags: -I${includedir}\nLibs: -L${libdir}\n"
  },
  {
    "path": "contrib/sqlite3/test.sql",
    "content": "-- sqlite3 test script\n\n\\set\n\n\\set SYNTAX_HL_FORMAT terminal16m\n\\set SYNTAX_HL true\n\nhelp\n\n\\?\n\n\\copyright\n\n\\set SYNTAX_HL_STYLE dracula\n\nselect 'test''\n' \\g\n\n\\set NAME myname\n\nPRAGMA foreign_keys = 1;\n\nDROP TABLE IF EXISTS books;\n\nDROP TABLE IF EXISTS authors;\n\nCREATE TABLE authors (\n  author_id integer NOT NULL PRIMARY KEY AUTOINCREMENT,\n  name text NOT NULL DEFAULT ''\n);\n\nCREATE INDEX authors_name_idx ON authors(name);\n\n\\set SYNTAX_HL_STYLE paraiso-dark\n\nCREATE TABLE books (\n  /*\n    this is a multiline comment\n   */\n  book_id integer NOT NULL PRIMARY KEY AUTOINCREMENT, -- the id of the author\n  author_id integer NOT NULL REFERENCES authors(author_id),\n  isbn text NOT NULL DEFAULT '' UNIQUE,\n  title text NOT NULL DEFAULT '',\n  year integer NOT NULL DEFAULT 2000,\n  available timestamp with time zone NOT NULL DEFAULT '',\n  tags text NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX books_title_idx ON books(title, year);\n\ninsert into authors (name) values\n  (\"jk rowling\"),\n  (\"author amazing\")\n\\g\n\n  select * from authors;\n\n\\set COLNAME name\n\\set NAME amaz\n\n\\echo `echo hello`\n\nselect :\"COLNAME\" from authors where :COLNAME like '%' || :'NAME' || '%'\n\n\\print \\raw\n\n\\g\n\n\\gset AUTHOR_\n\nselect :'AUTHOR_name';\n\n\\begin\ninsert into authors (name) values ('test');\n\\rollback\n\ninsert into authors (name) values ('hello');\nselect * from authors;\n\ninsert into books (author_id, isbn, title, year, available) values\n  (1, '1', 'one', 2018, '2018-06-01 00:00:00'),\n  (2, '2', 'two', 2019, '2019-06-01 00:00:00')\n;\n\nselect * from books b inner join authors a on a.author_id = b.author_id;\n\n  /* exiting! */\n\\q\n"
  },
  {
    "path": "contrib/sqlite3/usql-config",
    "content": "DB=\"sqlite3:test.sqlite3\"\nVSQL=\"SELECT sqlite_version() AS version;\"\n"
  },
  {
    "path": "contrib/sqlserver/init.sql",
    "content": "EXEC sp_configure\n  'contained database authentication', 1;\n\nRECONFIGURE;\n\nDROP LOGIN :NAME;\n\nDROP DATABASE :NAME;\n\nCREATE DATABASE :NAME\n  CONTAINMENT=PARTIAL;\n\n\\set QNAME \"''\":NAME\"''\"\n\n\\set SQL 'CREATE LOGIN ':NAME' WITH PASSWORD=':QNAME', CHECK_POLICY=OFF, DEFAULT_DATABASE=':NAME';'\nEXEC [:NAME].[dbo].[sp_executesql] N:'SQL'\n\n\\set SQL 'CREATE USER ':NAME' FOR LOGIN ':NAME' WITH DEFAULT_SCHEMA=':NAME';'\nEXEC [:NAME].[dbo].[sp_executesql] N:'SQL';\n\n\\set SQL 'CREATE SCHEMA ':NAME' AUTHORIZATION ':NAME';'\nEXEC [:NAME].[dbo].[sp_executesql] N:'SQL';\n\n\\set SQL 'EXEC sp_addrolemember db_owner, ':QNAME';'\nEXEC [:NAME].[dbo].[sp_executesql] N:'SQL';\n\n-- original reconnect version:\n--\n--\\connect 'sqlserver://localhost/':NAME\n--\n--CREATE LOGIN :NAME\n--  WITH\n--    PASSWORD=:'PASS',\n--    CHECK_POLICY=OFF,\n--    DEFAULT_DATABASE=:NAME;\n--\n--CREATE USER :NAME\n--  FOR LOGIN :NAME\n--  WITH DEFAULT_SCHEMA=:NAME;\n--\n--CREATE SCHEMA :NAME AUTHORIZATION :NAME;\n--\n--EXEC sp_addrolemember 'db_owner', :'NAME';\n"
  },
  {
    "path": "contrib/sqlserver/podman-config",
    "content": "NAME=sqlserver\nIMAGE=mcr.microsoft.com/mssql/server:2022-latest\nPUBLISH=1433:1433\nENV=\"ACCEPT_EULA=Y MSSQL_PID=Express SA_PASSWORD=Adm1nP@ssw0rd\"\n"
  },
  {
    "path": "contrib/sqlserver/test.sql",
    "content": "-- sqlserver test script\n\n\\set\n\n\\set SYNTAX_HL_FORMAT terminal16m\n\\set SYNTAX_HL true\n\n\\?\n\n\\copyright\n\n\\set SYNTAX_HL_STYLE dracula\n\nselect 'test''\n' \\g\n\n\\set NAME myname\n\nDROP TABLE IF EXISTS books;\nDROP TABLE IF EXISTS authors;\n\nCREATE TABLE authors (\n  author_id integer NOT NULL IDENTITY(1,1) PRIMARY KEY,\n  name varchar(255) NOT NULL DEFAULT ''\n);\n\nCREATE INDEX authors_name_idx ON authors(name);\n\nCREATE TABLE books (\n  book_id integer NOT NULL IDENTITY(1,1) PRIMARY KEY,\n  author_id integer NOT NULL FOREIGN KEY REFERENCES authors(author_id),\n  isbn varchar(255) NOT NULL DEFAULT '' UNIQUE,\n  title varchar(255) NOT NULL DEFAULT '',\n  year integer NOT NULL DEFAULT 2000,\n  available datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  tags varchar(255) NOT NULL DEFAULT ''\n);\n\nCREATE INDEX books_title_idx ON books(title, year);\n\n\\set SYNTAX_HL_STYLE paraiso-dark\n\ninsert into authors (name) values\n  ('jk rowling'),\n  ('author amazing')\n\\g\n\n  select * from authors;\n\n\\set COLNAME name\n\\set NAME amaz\n\n\\echo `echo hello`\n\nselect :\"COLNAME\" from authors where :COLNAME like '%' || :'NAME' || '%'\n\n\\print \\raw\n\n\\g\n\n\\gset AUTHOR_\n\nselect :'AUTHOR_name';\n\n\\begin\ninsert into authors (name) values ('test');\n\\rollback\n\ninsert into authors (name) values ('hello');\nselect * from authors;\n\ninsert into books (author_id, isbn, title, year, available) values\n  (1, '1', 'one', 2018, '2018-06-01 00:00:00'),\n  (2, '2', 'two', 2019, '2019-06-01 00:00:00')\n;\n\nselect * from books b inner join authors a on a.author_id = b.author_id;\n\n  /* exiting! */\n\\q\n"
  },
  {
    "path": "contrib/sqlserver/usql-config",
    "content": "DB=\"sqlserver://sa:Adm1nP@ssw0rd@localhost/\"\nVSQL=\"SELECT SERVERPROPERTY('productversion') AS version;\"\n"
  },
  {
    "path": "contrib/trino/podman-config",
    "content": "NAME=trino\nIMAGE=docker.io/trinodb/trino\nPUBLISH=8080:8080\n"
  },
  {
    "path": "contrib/trino/usql-config",
    "content": "DB=\"trino://localhost\"\nVSQL=\"SELECT node_version AS version FROM system.runtime.nodes LIMIT 1;\"\n"
  },
  {
    "path": "contrib/usql-test.sh",
    "content": "#!/bin/bash\n\nSRC=$(realpath $(cd -P \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd))\n\nUSQL=$(which usql)\nif [ -f $SRC/../usql ]; then\n  USQL=$(realpath $SRC/../usql)\nfi\n\nexport USQL_SHOW_HOST_INFORMATION=false\nfor TARGET in $SRC/*/usql-config; do\n  NAME=$(basename $(dirname $TARGET))\n  if [[ ! -z \"$(podman ps -q --filter \"name=$NAME\")\" || \"$NAME\" == \"duckdb\" || \"$NAME\" == \"sqlite3\" ]]; then\n    unset DB VSQL\n    source $TARGET\n    if [[ -z \"$DB\" || -z \"$VSQL\" ]]; then\n      echo -e \"ERROR: DB or VSQL not defined in $TARGET!\\n\"\n      continue\n    fi\n    (set -x;\n      $USQL \"$DB\" -X -J -c \"$VSQL\"\n    )\n    echo\n  fi\ndone\n"
  },
  {
    "path": "contrib/usqlpass",
    "content": "# sample ~/.usqlpass file\n# \n# format is:\n# protocol:host:port:dbname:user:pass\npostgres:*:*:*:postgres:P4ssw0rd\n\ncql:*:*:*:cassandra:cassandra\nclickhouse:*:*:*:clickhouse:P4ssw0rd\ncouchbase:*:*:*:Administrator:P4ssw0rd\ngodror:*:*:*:system:P4ssw0rd\nignite:*:*:*:ignite:ignite\nmymysql:*:*:*:root:P4ssw0rd\nmysql:*:*:*:root:P4ssw0rd\noracle:*:*:*:system:P4ssw0rd\npgx:*:*:*:postgres:P4ssw0rd\nsqlserver:*:*:*:sa:Adm1nP@ssw0rd\nvertica:*:*:*:vertica:P4ssw0rd\nflightsql:*:*:*:flight_username:P4ssw0rd\n"
  },
  {
    "path": "contrib/usqlrc",
    "content": "-- example usqlrc file\n-- put in $HOME/.usqlrc\n\\echo welcome `echo $USER`, today is:`date`\n\\set SYNTAX_HL_STYLE paraiso-dark\n"
  },
  {
    "path": "contrib/vertica/podman-config",
    "content": "NAME=vertica\nIMAGE=docker.io/vertica/vertica-ce:latest\nPUBLISH=5433:5433\nENV=\"APP_DB_USER=vertica APP_DB_PASSWORD=P4ssw0rd\"\n"
  },
  {
    "path": "contrib/vertica/usql-config",
    "content": "DB=\"vertica://vertica:P4ssw0rd@localhost/vertica\"\nVSQL=\"SELECT version() AS version;\"\n"
  },
  {
    "path": "contrib/ydb/podman-config",
    "content": "NAME=ydb\nIMAGE=cr.yandex/yc/yandex-docker-local-ydb\nPUBLISH=\"2135:2135 2136:2136 8765:8765\"\nENV=\"YDB_DEFAULT_LOG_LEVEL=NOTICE GRPC_TLS_PORT=2135 GRPC_PORT=2136 MON_PORT=8765\"\nVOLUME=\"ydb-certs:/ydb_certs ydb-data:/ydb_data\"\nHOSTNAME=localhost\n"
  },
  {
    "path": "contrib/ydb/usql-config",
    "content": "DB=\"ydb://localhost/local\"\nVSQL=\"SELECT 'unk' as version;\"\n"
  },
  {
    "path": "drivers/adodb/adodb.go",
    "content": "// Package adodb defines and registers usql's Microsoft ADODB driver. Requires\n// CGO. Windows only.\n//\n// Alias: oleodbc, OLE ODBC\n//\n// See: https://github.com/mattn/go-adodb\npackage adodb\n\nimport (\n\t\"database/sql\"\n\t\"regexp\"\n\t\"strings\"\n\n\t_ \"github.com/mattn/go-adodb\" // DRIVER\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tendRE := regexp.MustCompile(`;?\\s*$`)\n\tendAnchorRE := regexp.MustCompile(`(?i)\\send\\s*;\\s*$`)\n\tdrivers.Register(\"adodb\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tAllowCComments:         true,\n\t\tProcess: func(u *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) {\n\t\t\t// trim last ; but only when not END;\n\t\t\tif s := strings.ToLower(u.Query().Get(\"usql_trim\")); s != \"\" && s != \"off\" && s != \"0\" && s != \"false\" {\n\t\t\t\tif !endAnchorRE.MatchString(sqlstr) {\n\t\t\t\t\tsqlstr = endRE.ReplaceAllString(sqlstr, \"\")\n\t\t\t\t}\n\t\t\t}\n\t\t\ttyp, q := drivers.QueryExecType(prefix, sqlstr)\n\t\t\treturn typ, sqlstr, q, nil\n\t\t},\n\t\tRowsAffected: func(res sql.Result) (int64, error) {\n\t\t\treturn 0, nil\n\t\t},\n\t}, \"oleodbc\")\n}\n"
  },
  {
    "path": "drivers/athena/athena.go",
    "content": "// Package athena defines and registers usql's AWS Athena driver.\n//\n// See: https://github.com/uber/athenadriver\npackage athena\n\nimport (\n\t\"context\"\n\n\t_ \"github.com/uber/athenadriver/go\" // DRIVER: awsathena\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"awsathena\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tProcess:                drivers.StripTrailingSemicolon,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(\n\t\t\t\tctx,\n\t\t\t\t`SELECT node_version FROM system.runtime.nodes LIMIT 1`,\n\t\t\t).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Athena \" + ver, nil\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/avatica/avatica.go",
    "content": "// Package avatica defines and registers usql's Apache Avatica driver.\n//\n// See: https://github.com/apache/calcite-avatica-go\npackage avatica\n\nimport (\n\t\"strconv\"\n\n\t_ \"github.com/apache/calcite-avatica-go/v5\" // DRIVER\n\tavaticaerrors \"github.com/apache/calcite-avatica-go/v5/errors\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"avatica\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tAllowCComments:         true,\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(avaticaerrors.ResponseError); ok {\n\t\t\t\treturn strconv.Itoa(int(e.ErrorCode)), e.ErrorMessage\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/bigquery/bigquery.go",
    "content": "// Package bigquery defines and registers usql's Google BigQuery driver.\n//\n// See: https://github.com/go-gorm/bigquery\npackage bigquery\n\nimport (\n\t\"github.com/xo/usql/drivers\"\n\t_ \"gorm.io/driver/bigquery/driver\" // DRIVER\n)\n\nfunc init() {\n\tdrivers.Register(\"bigquery\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/cassandra/cassandra.go",
    "content": "// Package cassandra defines and registers usql's Cassandra driver.\n//\n// See: https://github.com/MichaelS11/go-cql-driver\npackage cassandra\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\n\tcql \"github.com/MichaelS11/go-cql-driver\" // DRIVER: cql\n\t\"github.com/gocql/gocql\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tvar debug bool\n\tif s := os.Getenv(\"CQL_DEBUG\"); s != \"\" {\n\t\tlog.Printf(\"ENABLING DEBUGGING FOR CQL\")\n\t\tdebug = true\n\t}\n\t// error regexp's\n\tauthReqRE := regexp.MustCompile(`authentication required`)\n\tpasswordErrRE := regexp.MustCompile(`Provided username (.*)and/or password are incorrect`)\n\tvar l *logger\n\tdrivers.Register(\"cql\", drivers.Driver{\n\t\tAllowDollar:            true,\n\t\tAllowMultilineComments: true,\n\t\tAllowCComments:         true,\n\t\tLexerName:              \"cql\",\n\t\tForceParams: func(u *dburl.URL) {\n\t\t\tif q := u.Query(); q.Get(\"timeout\") == \"\" {\n\t\t\t\tq.Set(\"timeout\", \"300s\")\n\t\t\t\tu.RawQuery = q.Encode()\n\t\t\t}\n\t\t},\n\t\tOpen: func(_ context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) {\n\t\t\t// override cql and gocql loggers\n\t\t\tl = &logger{debug: debug}\n\t\t\tgocql.Logger, cql.CqlDriver.Logger = l, log.New(l, \"\", 0)\n\t\t\treturn sql.Open, nil\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar release, protocol, cql string\n\t\t\terr := db.QueryRowContext(\n\t\t\t\tctx,\n\t\t\t\t`SELECT release_version, cql_version, native_protocol_version FROM system.local WHERE key = 'local'`,\n\t\t\t).Scan(&release, &cql, &protocol)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Cassandra \" + release + \", CQL \" + cql + \", Protocol v\" + protocol, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, _ string) error {\n\t\t\t_, err := db.Exec(`ALTER ROLE ` + user + ` WITH PASSWORD = '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\treturn passwordErrRE.MatchString(l.last)\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif authReqRE.MatchString(l.last) {\n\t\t\t\treturn \"\", \"authentication required\"\n\t\t\t}\n\t\t\tif m := passwordErrRE.FindStringSubmatch(l.last); m != nil {\n\t\t\t\treturn \"\", fmt.Sprintf(\"invalid username %sor password\", m[1])\n\t\t\t}\n\t\t\treturn \"\", strings.TrimPrefix(strings.TrimPrefix(err.Error(), \"driver: \"), \"gocql: \")\n\t\t},\n\t\tRowsAffected: func(sql.Result) (int64, error) {\n\t\t\treturn 0, nil\n\t\t},\n\t\tConvertDefault: func(v interface{}) (string, error) {\n\t\t\tbuf, err := json.Marshal(v)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn string(buf), nil\n\t\t},\n\t\tBatchQueryPrefixes: map[string]string{\n\t\t\t\"BEGIN BATCH\": \"APPLY BATCH\",\n\t\t},\n\t})\n}\n\n// logger is a null logger that satisfies the gocql.StdLogger and the io.Writer\n// interfaces in order to capture the last error issued by the cql/gocql\n// packages, since the cql package does not (at this time) return any error\n// other than sql.ErrBadConn.\ntype logger struct {\n\tdebug bool\n\tlast  string\n}\n\nfunc (l *logger) Print(v ...interface{}) {\n\tif l.debug {\n\t\tlog.Print(v...)\n\t}\n}\n\nfunc (l *logger) Printf(s string, v ...interface{}) {\n\tif l.debug {\n\t\tlog.Printf(s, v...)\n\t}\n}\n\nfunc (l *logger) Println(v ...interface{}) {\n\tif l.debug {\n\t\tlog.Println(v...)\n\t}\n}\n\nfunc (l *logger) Write(buf []byte) (int, error) {\n\tif l.debug {\n\t\tlog.Printf(\"WRITE: %s\", string(buf))\n\t}\n\tl.last = string(buf)\n\treturn len(buf), nil\n}\n"
  },
  {
    "path": "drivers/chai/chai.go",
    "content": "// Package chai defines and registers usql's ChaiSQL driver.\n//\n// See: https://github.com/chaisql/chai\npackage chai\n\nimport (\n\t_ \"github.com/chaisql/chai\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"chai\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/clickhouse/clickhouse.go",
    "content": "// Package clickhouse defines and registers usql's ClickHouse driver.\n//\n// Group: base\n// See: https://github.com/ClickHouse/clickhouse-go\npackage clickhouse\n\nimport (\n\t\"database/sql\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/ClickHouse/clickhouse-go/v2\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"clickhouse\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tRowsAffected: func(sql.Result) (int64, error) {\n\t\t\treturn 0, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, oldpw string) error {\n\t\t\t_, err := db.Exec(`ALTER USER ` + user + ` IDENTIFIED BY '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*clickhouse.Exception); ok {\n\t\t\t\treturn strconv.Itoa(int(e.Code)), strings.TrimPrefix(e.Message, \"clickhouse: \")\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tif e, ok := err.(*clickhouse.Exception); ok {\n\t\t\t\treturn e.Code == 516\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tCopy:              drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t\tNewMetadataReader: NewMetadataReader,\n\t})\n}\n"
  },
  {
    "path": "drivers/clickhouse/clickhouse_test.go",
    "content": "package clickhouse_test\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\tdt \"github.com/ory/dockertest/v3\"\n\t\"github.com/xo/usql/drivers/clickhouse\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/yookoala/realpath\"\n\n\t_ \"github.com/xo/usql/drivers/csvq\"\n\t_ \"github.com/xo/usql/drivers/moderncsqlite\"\n)\n\n// db is the database connection.\nvar db struct {\n\tdb  *sql.DB\n\tres *dt.Resource\n\tr   metadata.BasicReader\n}\n\nfunc TestMain(m *testing.M) {\n\tcleanup := flag.Bool(\"cleanup\", true, \"cleanup when finished\")\n\tflag.Parse()\n\tcode, err := doMain(m, *cleanup)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tif code == 0 {\n\t\t\tcode = 1\n\t\t}\n\t}\n\tos.Exit(code)\n}\n\nfunc doMain(m *testing.M, cleanup bool) (int, error) {\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdir, err = realpath.Realpath(dir)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tpool, err := dt.NewPool(\"\")\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"could not connect to docker: %w\", err)\n\t}\n\tdb.res, err = pool.RunWithOptions(&dt.RunOptions{\n\t\tRepository: \"clickhouse/clickhouse-server\",\n\t\tTag:        \"22.7\",\n\t\tMounts:     []string{filepath.Join(dir, \"testdata\") + \":/docker-entrypoint-initdb.d\"},\n\t})\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"unable to run: %w\", err)\n\t}\n\tif cleanup {\n\t\tdefer func() {\n\t\t\tif err := pool.Purge(db.res); err != nil {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"error: could not purge resource: %v\\n\", err)\n\t\t\t}\n\t\t}()\n\t}\n\t// exponential backoff-retry, because the application in the container\n\t// might not be ready to accept connections yet\n\tif err := pool.Retry(func() error {\n\t\tport := db.res.GetPort(\"9000/tcp\")\n\t\tvar err error\n\t\tif db.db, err = sql.Open(\"clickhouse\", fmt.Sprintf(\"clickhouse://127.0.0.1:%s\", port)); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn db.db.Ping()\n\t}); err != nil {\n\t\treturn 0, fmt.Errorf(\"unable to open database: %w\", err)\n\t}\n\tdb.r = clickhouse.NewMetadataReader(db.db).(metadata.BasicReader)\n\tcode := m.Run()\n\treturn code, nil\n}\n\nfunc TestSchemas(t *testing.T) {\n\tres, err := db.r.Schemas(metadata.Filter{WithSystem: true})\n\tif err != nil {\n\t\tt.Fatalf(\"could not read schemas: %v\", err)\n\t}\n\tcheckNames(t, \"schema\", res, \"default\", \"system\", \"tutorial\", \"tutorial_unexpected\", \"INFORMATION_SCHEMA\", \"information_schema\", \"copy_test\")\n}\n\nfunc TestTables(t *testing.T) {\n\tres, err := db.r.Tables(metadata.Filter{\n\t\tSchema: \"tutorial\",\n\t\tTypes:  []string{\"BASE TABLE\", \"TABLE\", \"VIEW\"},\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"could not read tables: %v\", err)\n\t}\n\tcheckNames(t, \"table\", res, \"hits_v1\", \"visits_v1\")\n}\n\nfunc TestFunctions(t *testing.T) {\n\tr := clickhouse.NewMetadataReader(db.db).(metadata.FunctionReader)\n\tres, err := r.Functions(metadata.Filter{Schema: \"tutorial\"})\n\tif err != nil {\n\t\tt.Fatalf(\"could not read functions: %v\", err)\n\t}\n\tcheckNames(t, \"function\", res, funcNames()...)\n}\n\nfunc TestColumns(t *testing.T) {\n\tres, err := db.r.Columns(metadata.Filter{\n\t\tSchema: \"tutorial\",\n\t\tParent: \"hits_v1\",\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"could not read columns: %v\", err)\n\t}\n\tcheckNames(t, \"column\", res, colNames()...)\n}\n\nfunc TestCopy(t *testing.T) {\n\t// Tests with csvq source DB. That driver doesn't support ScanType()\n\tfor _, destTableSpec := range []string{\n\t\t\"copy_test.dest\",\n\t\t\"copy_test.dest(StringCol, NumCol)\",\n\t\t\"insert into copy_test.dest values(?, ?)\",\n\t} {\n\t\tt.Run(\"csvq_\"+destTableSpec, func(t *testing.T) {\n\t\t\ttestCopy(t, destTableSpec, \"csvq:.\")\n\t\t})\n\t}\n\t// Test with a driver that supports ScanType()\n\tt.Run(\"sqlite\", func(t *testing.T) {\n\t\ttestCopy(t, \"copy_test.dest\", \"moderncsqlite://:memory:\")\n\t})\n}\n\nfunc testCopy(t *testing.T, destTableSpec string, sourceDbUrlStr string) {\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\t_, err := db.db.ExecContext(ctx, \"truncate table copy_test.dest\")\n\tif err != nil {\n\t\tt.Fatalf(\"could not truncate copy_test table: %v\", err)\n\t}\n\t// Prepare copy destination URL\n\tport := db.res.GetPort(\"9000/tcp\")\n\tdbUrlStr := fmt.Sprintf(\"clickhouse://127.0.0.1:%s\", port)\n\tdbUrl, err := dburl.Parse(dbUrlStr)\n\tif err != nil {\n\t\tt.Fatalf(\"could not parse clickhouse url %s: %v\", dbUrlStr, err)\n\t}\n\t// Prepare source data\n\tsourceDbUrl, err := dburl.Parse(sourceDbUrlStr)\n\tif err != nil {\n\t\tt.Fatalf(\"could not parse source DB url %s: %v\", sourceDbUrlStr, err)\n\t}\n\tsourceDb, err := drivers.Open(ctx, sourceDbUrl, nil, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"could not open sourceDb: %v\", err)\n\t}\n\tdefer sourceDb.Close()\n\trows, err := sourceDb.QueryContext(ctx, \"select 'string', 1\")\n\tif err != nil {\n\t\tt.Fatalf(\"could not retrieve source rows: %v\", err)\n\t}\n\t// Do Copy, ignoring copied rows count because clickhouse driver doesn't report RowsAffected\n\t_, err = drivers.Copy(ctx, dbUrl, nil, nil, rows, destTableSpec)\n\tif err != nil {\n\t\tt.Fatalf(\"copy failed: %v\", err)\n\t}\n\trows, err = db.db.QueryContext(ctx, \"select StringCol, NumCol from copy_test.dest\")\n\tif err != nil {\n\t\tt.Fatalf(\"failed to query: %v\", err)\n\t}\n\tdefer rows.Close()\n\tvar copiedString string\n\tvar copiedNum int\n\tif !rows.Next() {\n\t\tt.Fatalf(\"nothing copied\")\n\t}\n\terr = rows.Scan(&copiedString, &copiedNum)\n\tif err != nil {\n\t\tt.Fatalf(\"could not read copied data: %v\", err)\n\t}\n\tif copiedString != \"string\" || copiedNum != 1 {\n\t\tt.Fatalf(\"copied data differs: %s != string, %d != 1\", copiedString, copiedNum)\n\t}\n}\n\nfunc checkNames(t *testing.T, typ string, res interface{ Next() bool }, exp ...string) {\n\tn := make(map[string]bool)\n\tfor _, s := range exp {\n\t\tn[s] = true\n\t}\n\tnames := make(map[string]bool)\n\tfor res.Next() {\n\t\tname := getName(res)\n\t\tif _, ok := names[name]; ok {\n\t\t\tt.Errorf(\"already declared %s %q\", typ, name)\n\t\t}\n\t\tnames[name] = true\n\t}\n\tfor name := range n {\n\t\tif _, ok := names[name]; !ok {\n\t\t\tt.Errorf(\"missing %s %q\", typ, name)\n\t\t}\n\t}\n\tfor name := range names {\n\t\tif _, ok := n[name]; !ok {\n\t\t\tt.Errorf(\"unexpected %s %q\", typ, name)\n\t\t}\n\t}\n}\n\nfunc getName(res interface{}) string {\n\tswitch x := res.(type) {\n\tcase *metadata.SchemaSet:\n\t\treturn x.Get().Schema\n\tcase *metadata.TableSet:\n\t\treturn x.Get().Name\n\tcase *metadata.FunctionSet:\n\t\treturn x.Get().Name\n\tcase *metadata.ColumnSet:\n\t\treturn x.Get().Name\n\t}\n\tpanic(fmt.Sprintf(\"unknown type %T\", res))\n}\n\nfunc funcNames() []string {\n\treturn []string{\n\t\t\"BIT_AND\",\n\t\t\"BIT_OR\",\n\t\t\"BIT_XOR\",\n\t\t\"CAST\",\n\t\t\"CHARACTER_LENGTH\",\n\t\t\"CHAR_LENGTH\",\n\t\t\"COVAR_POP\",\n\t\t\"COVAR_SAMP\",\n\t\t\"CRC32\",\n\t\t\"CRC32IEEE\",\n\t\t\"CRC64\",\n\t\t\"DATABASE\",\n\t\t\"DATE\",\n\t\t\"DAY\",\n\t\t\"DAYOFMONTH\",\n\t\t\"DAYOFWEEK\",\n\t\t\"DAYOFYEAR\",\n\t\t\"FQDN\",\n\t\t\"FROM_BASE64\",\n\t\t\"FROM_UNIXTIME\",\n\t\t\"HOUR\",\n\t\t\"INET6_ATON\",\n\t\t\"INET6_NTOA\",\n\t\t\"INET_ATON\",\n\t\t\"INET_NTOA\",\n\t\t\"IPv4CIDRToRange\",\n\t\t\"IPv4NumToString\",\n\t\t\"IPv4NumToStringClassC\",\n\t\t\"IPv4StringToNum\",\n\t\t\"IPv4StringToNumOrDefault\",\n\t\t\"IPv4StringToNumOrNull\",\n\t\t\"IPv4ToIPv6\",\n\t\t\"IPv6CIDRToRange\",\n\t\t\"IPv6NumToString\",\n\t\t\"IPv6StringToNum\",\n\t\t\"IPv6StringToNumOrDefault\",\n\t\t\"IPv6StringToNumOrNull\",\n\t\t\"JSONExtract\",\n\t\t\"JSONExtractArrayRaw\",\n\t\t\"JSONExtractBool\",\n\t\t\"JSONExtractFloat\",\n\t\t\"JSONExtractInt\",\n\t\t\"JSONExtractKeys\",\n\t\t\"JSONExtractKeysAndValues\",\n\t\t\"JSONExtractKeysAndValuesRaw\",\n\t\t\"JSONExtractRaw\",\n\t\t\"JSONExtractString\",\n\t\t\"JSONExtractUInt\",\n\t\t\"JSONHas\",\n\t\t\"JSONKey\",\n\t\t\"JSONLength\",\n\t\t\"JSONType\",\n\t\t\"JSON_EXISTS\",\n\t\t\"JSON_QUERY\",\n\t\t\"JSON_VALUE\",\n\t\t\"L1Distance\",\n\t\t\"L1Norm\",\n\t\t\"L1Normalize\",\n\t\t\"L2Distance\",\n\t\t\"L2Norm\",\n\t\t\"L2Normalize\",\n\t\t\"L2SquaredDistance\",\n\t\t\"L2SquaredNorm\",\n\t\t\"LAST_DAY\",\n\t\t\"LinfDistance\",\n\t\t\"LinfNorm\",\n\t\t\"LinfNormalize\",\n\t\t\"LpDistance\",\n\t\t\"LpNorm\",\n\t\t\"LpNormalize\",\n\t\t\"MACNumToString\",\n\t\t\"MACStringToNum\",\n\t\t\"MACStringToOUI\",\n\t\t\"MD4\",\n\t\t\"MD5\",\n\t\t\"MINUTE\",\n\t\t\"MONTH\",\n\t\t\"QUARTER\",\n\t\t\"REGEXP_MATCHES\",\n\t\t\"REGEXP_REPLACE\",\n\t\t\"SECOND\",\n\t\t\"SHA1\",\n\t\t\"SHA224\",\n\t\t\"SHA256\",\n\t\t\"SHA384\",\n\t\t\"SHA512\",\n\t\t\"STDDEV_POP\",\n\t\t\"STDDEV_SAMP\",\n\t\t\"SVG\",\n\t\t\"TO_BASE64\",\n\t\t\"URLHash\",\n\t\t\"URLHierarchy\",\n\t\t\"URLPathHierarchy\",\n\t\t\"UUIDNumToString\",\n\t\t\"UUIDStringToNum\",\n\t\t\"VAR_POP\",\n\t\t\"VAR_SAMP\",\n\t\t\"YEAR\",\n\t\t\"_CAST\",\n\t\t\"__bitBoolMaskAnd\",\n\t\t\"__bitBoolMaskOr\",\n\t\t\"__bitSwapLastTwo\",\n\t\t\"__bitWrapperFunc\",\n\t\t\"__getScalar\",\n\t\t\"abs\",\n\t\t\"accurateCast\",\n\t\t\"accurateCastOrDefault\",\n\t\t\"accurateCastOrNull\",\n\t\t\"accurate_Cast\",\n\t\t\"accurate_CastOrNull\",\n\t\t\"acos\",\n\t\t\"acosh\",\n\t\t\"addDays\",\n\t\t\"addHours\",\n\t\t\"addMicroseconds\",\n\t\t\"addMilliseconds\",\n\t\t\"addMinutes\",\n\t\t\"addMonths\",\n\t\t\"addNanoseconds\",\n\t\t\"addQuarters\",\n\t\t\"addSeconds\",\n\t\t\"addWeeks\",\n\t\t\"addYears\",\n\t\t\"addressToLine\",\n\t\t\"addressToLineWithInlines\",\n\t\t\"addressToSymbol\",\n\t\t\"aes_decrypt_mysql\",\n\t\t\"aes_encrypt_mysql\",\n\t\t\"aggThrow\",\n\t\t\"alphaTokens\",\n\t\t\"and\",\n\t\t\"any\",\n\t\t\"anyHeavy\",\n\t\t\"anyLast\",\n\t\t\"appendTrailingCharIfAbsent\",\n\t\t\"argMax\",\n\t\t\"argMin\",\n\t\t\"array\",\n\t\t\"arrayAUC\",\n\t\t\"arrayAll\",\n\t\t\"arrayAvg\",\n\t\t\"arrayCompact\",\n\t\t\"arrayConcat\",\n\t\t\"arrayCount\",\n\t\t\"arrayCumSum\",\n\t\t\"arrayCumSumNonNegative\",\n\t\t\"arrayDifference\",\n\t\t\"arrayDistinct\",\n\t\t\"arrayElement\",\n\t\t\"arrayEnumerate\",\n\t\t\"arrayEnumerateDense\",\n\t\t\"arrayEnumerateDenseRanked\",\n\t\t\"arrayEnumerateUniq\",\n\t\t\"arrayEnumerateUniqRanked\",\n\t\t\"arrayExists\",\n\t\t\"arrayFill\",\n\t\t\"arrayFilter\",\n\t\t\"arrayFirst\",\n\t\t\"arrayFirstIndex\",\n\t\t\"arrayFirstOrNull\",\n\t\t\"arrayFlatten\",\n\t\t\"arrayIntersect\",\n\t\t\"arrayJoin\",\n\t\t\"arrayLast\",\n\t\t\"arrayLastIndex\",\n\t\t\"arrayLastOrNull\",\n\t\t\"arrayMap\",\n\t\t\"arrayMax\",\n\t\t\"arrayMin\",\n\t\t\"arrayPopBack\",\n\t\t\"arrayPopFront\",\n\t\t\"arrayProduct\",\n\t\t\"arrayPushBack\",\n\t\t\"arrayPushFront\",\n\t\t\"arrayReduce\",\n\t\t\"arrayReduceInRanges\",\n\t\t\"arrayResize\",\n\t\t\"arrayReverse\",\n\t\t\"arrayReverseFill\",\n\t\t\"arrayReverseSort\",\n\t\t\"arrayReverseSplit\",\n\t\t\"arraySlice\",\n\t\t\"arraySort\",\n\t\t\"arraySplit\",\n\t\t\"arrayStringConcat\",\n\t\t\"arraySum\",\n\t\t\"arrayUniq\",\n\t\t\"arrayWithConstant\",\n\t\t\"arrayZip\",\n\t\t\"asin\",\n\t\t\"asinh\",\n\t\t\"assumeNotNull\",\n\t\t\"atan\",\n\t\t\"atan2\",\n\t\t\"atanh\",\n\t\t\"avg\",\n\t\t\"avgWeighted\",\n\t\t\"bar\",\n\t\t\"base58Decode\",\n\t\t\"base58Encode\",\n\t\t\"base64Decode\",\n\t\t\"base64Encode\",\n\t\t\"basename\",\n\t\t\"bin\",\n\t\t\"bitAnd\",\n\t\t\"bitCount\",\n\t\t\"bitHammingDistance\",\n\t\t\"bitNot\",\n\t\t\"bitOr\",\n\t\t\"bitPositionsToArray\",\n\t\t\"bitRotateLeft\",\n\t\t\"bitRotateRight\",\n\t\t\"bitShiftLeft\",\n\t\t\"bitShiftRight\",\n\t\t\"bitSlice\",\n\t\t\"bitTest\",\n\t\t\"bitTestAll\",\n\t\t\"bitTestAny\",\n\t\t\"bitXor\",\n\t\t\"bitmapAnd\",\n\t\t\"bitmapAndCardinality\",\n\t\t\"bitmapAndnot\",\n\t\t\"bitmapAndnotCardinality\",\n\t\t\"bitmapBuild\",\n\t\t\"bitmapCardinality\",\n\t\t\"bitmapContains\",\n\t\t\"bitmapHasAll\",\n\t\t\"bitmapHasAny\",\n\t\t\"bitmapMax\",\n\t\t\"bitmapMin\",\n\t\t\"bitmapOr\",\n\t\t\"bitmapOrCardinality\",\n\t\t\"bitmapSubsetInRange\",\n\t\t\"bitmapSubsetLimit\",\n\t\t\"bitmapToArray\",\n\t\t\"bitmapTransform\",\n\t\t\"bitmapXor\",\n\t\t\"bitmapXorCardinality\",\n\t\t\"bitmaskToArray\",\n\t\t\"bitmaskToList\",\n\t\t\"blockNumber\",\n\t\t\"blockSerializedSize\",\n\t\t\"blockSize\",\n\t\t\"boundingRatio\",\n\t\t\"buildId\",\n\t\t\"byteSize\",\n\t\t\"caseWithExpr\",\n\t\t\"caseWithExpression\",\n\t\t\"caseWithoutExpr\",\n\t\t\"caseWithoutExpression\",\n\t\t\"categoricalInformationValue\",\n\t\t\"cbrt\",\n\t\t\"ceil\",\n\t\t\"ceiling\",\n\t\t\"char\",\n\t\t\"cityHash64\",\n\t\t\"coalesce\",\n\t\t\"concat\",\n\t\t\"concatAssumeInjective\",\n\t\t\"connectionId\",\n\t\t\"connection_id\",\n\t\t\"contingency\",\n\t\t\"convertCharset\",\n\t\t\"corr\",\n\t\t\"corrStable\",\n\t\t\"cos\",\n\t\t\"cosh\",\n\t\t\"cosineDistance\",\n\t\t\"count\",\n\t\t\"countDigits\",\n\t\t\"countEqual\",\n\t\t\"countMatches\",\n\t\t\"countMatchesCaseInsensitive\",\n\t\t\"countSubstrings\",\n\t\t\"countSubstringsCaseInsensitive\",\n\t\t\"countSubstringsCaseInsensitiveUTF8\",\n\t\t\"covarPop\",\n\t\t\"covarPopStable\",\n\t\t\"covarSamp\",\n\t\t\"covarSampStable\",\n\t\t\"cramersV\",\n\t\t\"cramersVBiasCorrected\",\n\t\t\"currentDatabase\",\n\t\t\"currentProfiles\",\n\t\t\"currentRoles\",\n\t\t\"currentUser\",\n\t\t\"cutFragment\",\n\t\t\"cutIPv6\",\n\t\t\"cutQueryString\",\n\t\t\"cutQueryStringAndFragment\",\n\t\t\"cutToFirstSignificantSubdomain\",\n\t\t\"cutToFirstSignificantSubdomainCustom\",\n\t\t\"cutToFirstSignificantSubdomainCustomWithWWW\",\n\t\t\"cutToFirstSignificantSubdomainWithWWW\",\n\t\t\"cutURLParameter\",\n\t\t\"cutWWW\",\n\t\t\"dateDiff\",\n\t\t\"dateName\",\n\t\t\"dateTime64ToSnowflake\",\n\t\t\"dateTimeToSnowflake\",\n\t\t\"dateTrunc\",\n\t\t\"date_trunc\",\n\t\t\"decodeURLComponent\",\n\t\t\"decodeURLFormComponent\",\n\t\t\"decodeXMLComponent\",\n\t\t\"decrypt\",\n\t\t\"defaultProfiles\",\n\t\t\"defaultRoles\",\n\t\t\"defaultValueOfArgumentType\",\n\t\t\"defaultValueOfTypeName\",\n\t\t\"degrees\",\n\t\t\"deltaSum\",\n\t\t\"deltaSumTimestamp\",\n\t\t\"demangle\",\n\t\t\"dense_rank\",\n\t\t\"detectCharset\",\n\t\t\"detectLanguage\",\n\t\t\"detectLanguageMixed\",\n\t\t\"detectLanguageUnknown\",\n\t\t\"detectProgrammingLanguage\",\n\t\t\"detectTonality\",\n\t\t\"dictGet\",\n\t\t\"dictGetChildren\",\n\t\t\"dictGetDate\",\n\t\t\"dictGetDateOrDefault\",\n\t\t\"dictGetDateTime\",\n\t\t\"dictGetDateTimeOrDefault\",\n\t\t\"dictGetDescendants\",\n\t\t\"dictGetFloat32\",\n\t\t\"dictGetFloat32OrDefault\",\n\t\t\"dictGetFloat64\",\n\t\t\"dictGetFloat64OrDefault\",\n\t\t\"dictGetHierarchy\",\n\t\t\"dictGetInt16\",\n\t\t\"dictGetInt16OrDefault\",\n\t\t\"dictGetInt32\",\n\t\t\"dictGetInt32OrDefault\",\n\t\t\"dictGetInt64\",\n\t\t\"dictGetInt64OrDefault\",\n\t\t\"dictGetInt8\",\n\t\t\"dictGetInt8OrDefault\",\n\t\t\"dictGetOrDefault\",\n\t\t\"dictGetOrNull\",\n\t\t\"dictGetString\",\n\t\t\"dictGetStringOrDefault\",\n\t\t\"dictGetUInt16\",\n\t\t\"dictGetUInt16OrDefault\",\n\t\t\"dictGetUInt32\",\n\t\t\"dictGetUInt32OrDefault\",\n\t\t\"dictGetUInt64\",\n\t\t\"dictGetUInt64OrDefault\",\n\t\t\"dictGetUInt8\",\n\t\t\"dictGetUInt8OrDefault\",\n\t\t\"dictGetUUID\",\n\t\t\"dictGetUUIDOrDefault\",\n\t\t\"dictHas\",\n\t\t\"dictIsIn\",\n\t\t\"distanceL1\",\n\t\t\"distanceL2\",\n\t\t\"distanceL2Squared\",\n\t\t\"distanceLinf\",\n\t\t\"distanceLp\",\n\t\t\"divide\",\n\t\t\"domain\",\n\t\t\"domainWithoutWWW\",\n\t\t\"dotProduct\",\n\t\t\"dumpColumnStructure\",\n\t\t\"e\",\n\t\t\"empty\",\n\t\t\"emptyArrayDate\",\n\t\t\"emptyArrayDateTime\",\n\t\t\"emptyArrayFloat32\",\n\t\t\"emptyArrayFloat64\",\n\t\t\"emptyArrayInt16\",\n\t\t\"emptyArrayInt32\",\n\t\t\"emptyArrayInt64\",\n\t\t\"emptyArrayInt8\",\n\t\t\"emptyArrayString\",\n\t\t\"emptyArrayToSingle\",\n\t\t\"emptyArrayUInt16\",\n\t\t\"emptyArrayUInt32\",\n\t\t\"emptyArrayUInt64\",\n\t\t\"emptyArrayUInt8\",\n\t\t\"enabledProfiles\",\n\t\t\"enabledRoles\",\n\t\t\"encodeURLComponent\",\n\t\t\"encodeURLFormComponent\",\n\t\t\"encodeXMLComponent\",\n\t\t\"encrypt\",\n\t\t\"endsWith\",\n\t\t\"entropy\",\n\t\t\"equals\",\n\t\t\"erf\",\n\t\t\"erfc\",\n\t\t\"errorCodeToName\",\n\t\t\"evalMLMethod\",\n\t\t\"exp\",\n\t\t\"exp10\",\n\t\t\"exp2\",\n\t\t\"exponentialMovingAverage\",\n\t\t\"exponentialTimeDecayedAvg\",\n\t\t\"exponentialTimeDecayedCount\",\n\t\t\"exponentialTimeDecayedMax\",\n\t\t\"exponentialTimeDecayedSum\",\n\t\t\"extract\",\n\t\t\"extractAll\",\n\t\t\"extractAllGroups\",\n\t\t\"extractAllGroupsHorizontal\",\n\t\t\"extractAllGroupsVertical\",\n\t\t\"extractGroups\",\n\t\t\"extractTextFromHTML\",\n\t\t\"extractURLParameter\",\n\t\t\"extractURLParameterNames\",\n\t\t\"extractURLParameters\",\n\t\t\"farmFingerprint64\",\n\t\t\"farmHash64\",\n\t\t\"file\",\n\t\t\"filesystemAvailable\",\n\t\t\"filesystemCapacity\",\n\t\t\"filesystemFree\",\n\t\t\"finalizeAggregation\",\n\t\t\"firstSignificantSubdomain\",\n\t\t\"firstSignificantSubdomainCustom\",\n\t\t\"first_value\",\n\t\t\"flatten\",\n\t\t\"flattenTuple\",\n\t\t\"floor\",\n\t\t\"format\",\n\t\t\"formatDateTime\",\n\t\t\"formatReadableQuantity\",\n\t\t\"formatReadableSize\",\n\t\t\"formatReadableTimeDelta\",\n\t\t\"formatRow\",\n\t\t\"formatRowNoNewline\",\n\t\t\"fragment\",\n\t\t\"fromModifiedJulianDay\",\n\t\t\"fromModifiedJulianDayOrNull\",\n\t\t\"fromUnixTimestamp\",\n\t\t\"fromUnixTimestamp64Micro\",\n\t\t\"fromUnixTimestamp64Milli\",\n\t\t\"fromUnixTimestamp64Nano\",\n\t\t\"fullHostName\",\n\t\t\"fuzzBits\",\n\t\t\"gccMurmurHash\",\n\t\t\"gcd\",\n\t\t\"generateUUIDv4\",\n\t\t\"geoDistance\",\n\t\t\"geoToH3\",\n\t\t\"geoToS2\",\n\t\t\"geohashDecode\",\n\t\t\"geohashEncode\",\n\t\t\"geohashesInBox\",\n\t\t\"getMacro\",\n\t\t\"getOSKernelVersion\",\n\t\t\"getServerPort\",\n\t\t\"getSetting\",\n\t\t\"getSizeOfEnumType\",\n\t\t\"getTypeSerializationStreams\",\n\t\t\"globalIn\",\n\t\t\"globalInIgnoreSet\",\n\t\t\"globalNotIn\",\n\t\t\"globalNotInIgnoreSet\",\n\t\t\"globalNotNullIn\",\n\t\t\"globalNotNullInIgnoreSet\",\n\t\t\"globalNullIn\",\n\t\t\"globalNullInIgnoreSet\",\n\t\t\"globalVariable\",\n\t\t\"greatCircleAngle\",\n\t\t\"greatCircleDistance\",\n\t\t\"greater\",\n\t\t\"greaterOrEquals\",\n\t\t\"greatest\",\n\t\t\"groupArray\",\n\t\t\"groupArrayInsertAt\",\n\t\t\"groupArrayMovingAvg\",\n\t\t\"groupArrayMovingSum\",\n\t\t\"groupArraySample\",\n\t\t\"groupBitAnd\",\n\t\t\"groupBitOr\",\n\t\t\"groupBitXor\",\n\t\t\"groupBitmap\",\n\t\t\"groupBitmapAnd\",\n\t\t\"groupBitmapOr\",\n\t\t\"groupBitmapXor\",\n\t\t\"groupUniqArray\",\n\t\t\"h3CellAreaM2\",\n\t\t\"h3CellAreaRads2\",\n\t\t\"h3Distance\",\n\t\t\"h3EdgeAngle\",\n\t\t\"h3EdgeLengthKm\",\n\t\t\"h3EdgeLengthM\",\n\t\t\"h3ExactEdgeLengthKm\",\n\t\t\"h3ExactEdgeLengthM\",\n\t\t\"h3ExactEdgeLengthRads\",\n\t\t\"h3GetBaseCell\",\n\t\t\"h3GetDestinationIndexFromUnidirectionalEdge\",\n\t\t\"h3GetFaces\",\n\t\t\"h3GetIndexesFromUnidirectionalEdge\",\n\t\t\"h3GetOriginIndexFromUnidirectionalEdge\",\n\t\t\"h3GetPentagonIndexes\",\n\t\t\"h3GetRes0Indexes\",\n\t\t\"h3GetResolution\",\n\t\t\"h3GetUnidirectionalEdge\",\n\t\t\"h3GetUnidirectionalEdgeBoundary\",\n\t\t\"h3GetUnidirectionalEdgesFromHexagon\",\n\t\t\"h3HexAreaKm2\",\n\t\t\"h3HexAreaM2\",\n\t\t\"h3HexRing\",\n\t\t\"h3IndexesAreNeighbors\",\n\t\t\"h3IsPentagon\",\n\t\t\"h3IsResClassIII\",\n\t\t\"h3IsValid\",\n\t\t\"h3Line\",\n\t\t\"h3NumHexagons\",\n\t\t\"h3PointDistKm\",\n\t\t\"h3PointDistM\",\n\t\t\"h3PointDistRads\",\n\t\t\"h3ToCenterChild\",\n\t\t\"h3ToChildren\",\n\t\t\"h3ToGeo\",\n\t\t\"h3ToGeoBoundary\",\n\t\t\"h3ToParent\",\n\t\t\"h3ToString\",\n\t\t\"h3UnidirectionalEdgeIsValid\",\n\t\t\"h3kRing\",\n\t\t\"halfMD5\",\n\t\t\"has\",\n\t\t\"hasAll\",\n\t\t\"hasAny\",\n\t\t\"hasColumnInTable\",\n\t\t\"hasSubstr\",\n\t\t\"hasThreadFuzzer\",\n\t\t\"hasToken\",\n\t\t\"hasTokenCaseInsensitive\",\n\t\t\"hashid\",\n\t\t\"hex\",\n\t\t\"histogram\",\n\t\t\"hiveHash\",\n\t\t\"hop\",\n\t\t\"hopEnd\",\n\t\t\"hopStart\",\n\t\t\"hostName\",\n\t\t\"hostname\",\n\t\t\"hypot\",\n\t\t\"identity\",\n\t\t\"if\",\n\t\t\"ifNotFinite\",\n\t\t\"ifNull\",\n\t\t\"ignore\",\n\t\t\"ilike\",\n\t\t\"in\",\n\t\t\"inIgnoreSet\",\n\t\t\"indexHint\",\n\t\t\"indexOf\",\n\t\t\"initialQueryID\",\n\t\t\"initial_query_id\",\n\t\t\"initializeAggregation\",\n\t\t\"intDiv\",\n\t\t\"intDivOrZero\",\n\t\t\"intExp10\",\n\t\t\"intExp2\",\n\t\t\"intHash32\",\n\t\t\"intHash64\",\n\t\t\"intervalLengthSum\",\n\t\t\"isConstant\",\n\t\t\"isDecimalOverflow\",\n\t\t\"isFinite\",\n\t\t\"isIPAddressInRange\",\n\t\t\"isIPv4String\",\n\t\t\"isIPv6String\",\n\t\t\"isInfinite\",\n\t\t\"isNaN\",\n\t\t\"isNotNull\",\n\t\t\"isNull\",\n\t\t\"isNullable\",\n\t\t\"isValidJSON\",\n\t\t\"isValidUTF8\",\n\t\t\"isZeroOrNull\",\n\t\t\"javaHash\",\n\t\t\"javaHashUTF16LE\",\n\t\t\"joinGet\",\n\t\t\"joinGetOrNull\",\n\t\t\"jumpConsistentHash\",\n\t\t\"kostikConsistentHash\",\n\t\t\"kurtPop\",\n\t\t\"kurtSamp\",\n\t\t\"lagInFrame\",\n\t\t\"last_value\",\n\t\t\"lcase\",\n\t\t\"lcm\",\n\t\t\"leadInFrame\",\n\t\t\"least\",\n\t\t\"left\",\n\t\t\"leftPad\",\n\t\t\"leftPadUTF8\",\n\t\t\"leftUTF8\",\n\t\t\"lemmatize\",\n\t\t\"length\",\n\t\t\"lengthUTF8\",\n\t\t\"less\",\n\t\t\"lessOrEquals\",\n\t\t\"lgamma\",\n\t\t\"like\",\n\t\t\"ln\",\n\t\t\"locate\",\n\t\t\"log\",\n\t\t\"log10\",\n\t\t\"log1p\",\n\t\t\"log2\",\n\t\t\"logTrace\",\n\t\t\"lowCardinalityIndices\",\n\t\t\"lowCardinalityKeys\",\n\t\t\"lower\",\n\t\t\"lowerUTF8\",\n\t\t\"lpad\",\n\t\t\"makeDate\",\n\t\t\"makeDate32\",\n\t\t\"makeDateTime\",\n\t\t\"makeDateTime64\",\n\t\t\"mannWhitneyUTest\",\n\t\t\"map\",\n\t\t\"mapAdd\",\n\t\t\"mapApply\",\n\t\t\"mapContains\",\n\t\t\"mapContainsKeyLike\",\n\t\t\"mapExtractKeyLike\",\n\t\t\"mapFilter\",\n\t\t\"mapKeys\",\n\t\t\"mapPopulateSeries\",\n\t\t\"mapSubtract\",\n\t\t\"mapUpdate\",\n\t\t\"mapValues\",\n\t\t\"match\",\n\t\t\"materialize\",\n\t\t\"max\",\n\t\t\"max2\",\n\t\t\"maxIntersections\",\n\t\t\"maxIntersectionsPosition\",\n\t\t\"maxMappedArrays\",\n\t\t\"meanZTest\",\n\t\t\"median\",\n\t\t\"medianBFloat16\",\n\t\t\"medianBFloat16Weighted\",\n\t\t\"medianDeterministic\",\n\t\t\"medianExact\",\n\t\t\"medianExactHigh\",\n\t\t\"medianExactLow\",\n\t\t\"medianExactWeighted\",\n\t\t\"medianTDigest\",\n\t\t\"medianTDigestWeighted\",\n\t\t\"medianTiming\",\n\t\t\"medianTimingWeighted\",\n\t\t\"meiliMatch\",\n\t\t\"metroHash64\",\n\t\t\"mid\",\n\t\t\"min\",\n\t\t\"min2\",\n\t\t\"minMappedArrays\",\n\t\t\"minSampleSizeContinous\",\n\t\t\"minSampleSizeConversion\",\n\t\t\"minus\",\n\t\t\"mod\",\n\t\t\"modelEvaluate\",\n\t\t\"modulo\",\n\t\t\"moduloLegacy\",\n\t\t\"moduloOrZero\",\n\t\t\"monthName\",\n\t\t\"multiFuzzyMatchAllIndices\",\n\t\t\"multiFuzzyMatchAny\",\n\t\t\"multiFuzzyMatchAnyIndex\",\n\t\t\"multiIf\",\n\t\t\"multiMatchAllIndices\",\n\t\t\"multiMatchAny\",\n\t\t\"multiMatchAnyIndex\",\n\t\t\"multiSearchAllPositions\",\n\t\t\"multiSearchAllPositionsCaseInsensitive\",\n\t\t\"multiSearchAllPositionsCaseInsensitiveUTF8\",\n\t\t\"multiSearchAllPositionsUTF8\",\n\t\t\"multiSearchAny\",\n\t\t\"multiSearchAnyCaseInsensitive\",\n\t\t\"multiSearchAnyCaseInsensitiveUTF8\",\n\t\t\"multiSearchAnyUTF8\",\n\t\t\"multiSearchFirstIndex\",\n\t\t\"multiSearchFirstIndexCaseInsensitive\",\n\t\t\"multiSearchFirstIndexCaseInsensitiveUTF8\",\n\t\t\"multiSearchFirstIndexUTF8\",\n\t\t\"multiSearchFirstPosition\",\n\t\t\"multiSearchFirstPositionCaseInsensitive\",\n\t\t\"multiSearchFirstPositionCaseInsensitiveUTF8\",\n\t\t\"multiSearchFirstPositionUTF8\",\n\t\t\"multiply\",\n\t\t\"murmurHash2_32\",\n\t\t\"murmurHash2_64\",\n\t\t\"murmurHash3_128\",\n\t\t\"murmurHash3_32\",\n\t\t\"murmurHash3_64\",\n\t\t\"negate\",\n\t\t\"neighbor\",\n\t\t\"netloc\",\n\t\t\"ngramDistance\",\n\t\t\"ngramDistanceCaseInsensitive\",\n\t\t\"ngramDistanceCaseInsensitiveUTF8\",\n\t\t\"ngramDistanceUTF8\",\n\t\t\"ngramMinHash\",\n\t\t\"ngramMinHashArg\",\n\t\t\"ngramMinHashArgCaseInsensitive\",\n\t\t\"ngramMinHashArgCaseInsensitiveUTF8\",\n\t\t\"ngramMinHashArgUTF8\",\n\t\t\"ngramMinHashCaseInsensitive\",\n\t\t\"ngramMinHashCaseInsensitiveUTF8\",\n\t\t\"ngramMinHashUTF8\",\n\t\t\"ngramSearch\",\n\t\t\"ngramSearchCaseInsensitive\",\n\t\t\"ngramSearchCaseInsensitiveUTF8\",\n\t\t\"ngramSearchUTF8\",\n\t\t\"ngramSimHash\",\n\t\t\"ngramSimHashCaseInsensitive\",\n\t\t\"ngramSimHashCaseInsensitiveUTF8\",\n\t\t\"ngramSimHashUTF8\",\n\t\t\"ngrams\",\n\t\t\"nonNegativeDerivative\",\n\t\t\"normL1\",\n\t\t\"normL2\",\n\t\t\"normL2Squared\",\n\t\t\"normLinf\",\n\t\t\"normLp\",\n\t\t\"normalizeL1\",\n\t\t\"normalizeL2\",\n\t\t\"normalizeLinf\",\n\t\t\"normalizeLp\",\n\t\t\"normalizeQuery\",\n\t\t\"normalizeQueryKeepNames\",\n\t\t\"normalizeUTF8NFC\",\n\t\t\"normalizeUTF8NFD\",\n\t\t\"normalizeUTF8NFKC\",\n\t\t\"normalizeUTF8NFKD\",\n\t\t\"normalizedQueryHash\",\n\t\t\"normalizedQueryHashKeepNames\",\n\t\t\"not\",\n\t\t\"notEmpty\",\n\t\t\"notEquals\",\n\t\t\"notILike\",\n\t\t\"notIn\",\n\t\t\"notInIgnoreSet\",\n\t\t\"notLike\",\n\t\t\"notNullIn\",\n\t\t\"notNullInIgnoreSet\",\n\t\t\"nothing\",\n\t\t\"now\",\n\t\t\"now64\",\n\t\t\"nth_value\",\n\t\t\"nullIf\",\n\t\t\"nullIn\",\n\t\t\"nullInIgnoreSet\",\n\t\t\"or\",\n\t\t\"parseDateTime32BestEffort\",\n\t\t\"parseDateTime32BestEffortOrNull\",\n\t\t\"parseDateTime32BestEffortOrZero\",\n\t\t\"parseDateTime64BestEffort\",\n\t\t\"parseDateTime64BestEffortOrNull\",\n\t\t\"parseDateTime64BestEffortOrZero\",\n\t\t\"parseDateTimeBestEffort\",\n\t\t\"parseDateTimeBestEffortOrNull\",\n\t\t\"parseDateTimeBestEffortOrZero\",\n\t\t\"parseDateTimeBestEffortUS\",\n\t\t\"parseDateTimeBestEffortUSOrNull\",\n\t\t\"parseDateTimeBestEffortUSOrZero\",\n\t\t\"parseTimeDelta\",\n\t\t\"partitionId\",\n\t\t\"path\",\n\t\t\"pathFull\",\n\t\t\"pi\",\n\t\t\"plus\",\n\t\t\"pointInEllipses\",\n\t\t\"pointInPolygon\",\n\t\t\"polygonAreaCartesian\",\n\t\t\"polygonAreaSpherical\",\n\t\t\"polygonConvexHullCartesian\",\n\t\t\"polygonPerimeterCartesian\",\n\t\t\"polygonPerimeterSpherical\",\n\t\t\"polygonsDistanceCartesian\",\n\t\t\"polygonsDistanceSpherical\",\n\t\t\"polygonsEqualsCartesian\",\n\t\t\"polygonsIntersectionCartesian\",\n\t\t\"polygonsIntersectionSpherical\",\n\t\t\"polygonsSymDifferenceCartesian\",\n\t\t\"polygonsSymDifferenceSpherical\",\n\t\t\"polygonsUnionCartesian\",\n\t\t\"polygonsUnionSpherical\",\n\t\t\"polygonsWithinCartesian\",\n\t\t\"polygonsWithinSpherical\",\n\t\t\"port\",\n\t\t\"position\",\n\t\t\"positionCaseInsensitive\",\n\t\t\"positionCaseInsensitiveUTF8\",\n\t\t\"positionUTF8\",\n\t\t\"pow\",\n\t\t\"power\",\n\t\t\"proportionsZTest\",\n\t\t\"protocol\",\n\t\t\"quantile\",\n\t\t\"quantileBFloat16\",\n\t\t\"quantileBFloat16Weighted\",\n\t\t\"quantileDeterministic\",\n\t\t\"quantileExact\",\n\t\t\"quantileExactExclusive\",\n\t\t\"quantileExactHigh\",\n\t\t\"quantileExactInclusive\",\n\t\t\"quantileExactLow\",\n\t\t\"quantileExactWeighted\",\n\t\t\"quantileTDigest\",\n\t\t\"quantileTDigestWeighted\",\n\t\t\"quantileTiming\",\n\t\t\"quantileTimingWeighted\",\n\t\t\"quantiles\",\n\t\t\"quantilesBFloat16\",\n\t\t\"quantilesBFloat16Weighted\",\n\t\t\"quantilesDeterministic\",\n\t\t\"quantilesExact\",\n\t\t\"quantilesExactExclusive\",\n\t\t\"quantilesExactHigh\",\n\t\t\"quantilesExactInclusive\",\n\t\t\"quantilesExactLow\",\n\t\t\"quantilesExactWeighted\",\n\t\t\"quantilesTDigest\",\n\t\t\"quantilesTDigestWeighted\",\n\t\t\"quantilesTiming\",\n\t\t\"quantilesTimingWeighted\",\n\t\t\"queryID\",\n\t\t\"queryString\",\n\t\t\"queryStringAndFragment\",\n\t\t\"query_id\",\n\t\t\"radians\",\n\t\t\"rand\",\n\t\t\"rand32\",\n\t\t\"rand64\",\n\t\t\"randConstant\",\n\t\t\"randomFixedString\",\n\t\t\"randomPrintableASCII\",\n\t\t\"randomString\",\n\t\t\"randomStringUTF8\",\n\t\t\"range\",\n\t\t\"rank\",\n\t\t\"rankCorr\",\n\t\t\"readWKTMultiPolygon\",\n\t\t\"readWKTPoint\",\n\t\t\"readWKTPolygon\",\n\t\t\"readWKTRing\",\n\t\t\"regexpQuoteMeta\",\n\t\t\"regionHierarchy\",\n\t\t\"regionIn\",\n\t\t\"regionToArea\",\n\t\t\"regionToCity\",\n\t\t\"regionToContinent\",\n\t\t\"regionToCountry\",\n\t\t\"regionToDistrict\",\n\t\t\"regionToName\",\n\t\t\"regionToPopulation\",\n\t\t\"regionToTopContinent\",\n\t\t\"reinterpret\",\n\t\t\"reinterpretAsDate\",\n\t\t\"reinterpretAsDateTime\",\n\t\t\"reinterpretAsFixedString\",\n\t\t\"reinterpretAsFloat32\",\n\t\t\"reinterpretAsFloat64\",\n\t\t\"reinterpretAsInt128\",\n\t\t\"reinterpretAsInt16\",\n\t\t\"reinterpretAsInt256\",\n\t\t\"reinterpretAsInt32\",\n\t\t\"reinterpretAsInt64\",\n\t\t\"reinterpretAsInt8\",\n\t\t\"reinterpretAsString\",\n\t\t\"reinterpretAsUInt128\",\n\t\t\"reinterpretAsUInt16\",\n\t\t\"reinterpretAsUInt256\",\n\t\t\"reinterpretAsUInt32\",\n\t\t\"reinterpretAsUInt64\",\n\t\t\"reinterpretAsUInt8\",\n\t\t\"reinterpretAsUUID\",\n\t\t\"repeat\",\n\t\t\"replace\",\n\t\t\"replaceAll\",\n\t\t\"replaceOne\",\n\t\t\"replaceRegexpAll\",\n\t\t\"replaceRegexpOne\",\n\t\t\"replicate\",\n\t\t\"retention\",\n\t\t\"reverse\",\n\t\t\"reverseUTF8\",\n\t\t\"revision\",\n\t\t\"right\",\n\t\t\"rightPad\",\n\t\t\"rightPadUTF8\",\n\t\t\"rightUTF8\",\n\t\t\"round\",\n\t\t\"roundAge\",\n\t\t\"roundBankers\",\n\t\t\"roundDown\",\n\t\t\"roundDuration\",\n\t\t\"roundToExp2\",\n\t\t\"rowNumberInAllBlocks\",\n\t\t\"rowNumberInBlock\",\n\t\t\"row_number\",\n\t\t\"rpad\",\n\t\t\"runningAccumulate\",\n\t\t\"runningConcurrency\",\n\t\t\"runningDifference\",\n\t\t\"runningDifferenceStartingWithFirstValue\",\n\t\t\"s2CapContains\",\n\t\t\"s2CapUnion\",\n\t\t\"s2CellsIntersect\",\n\t\t\"s2GetNeighbors\",\n\t\t\"s2RectAdd\",\n\t\t\"s2RectContains\",\n\t\t\"s2RectIntersection\",\n\t\t\"s2RectUnion\",\n\t\t\"s2ToGeo\",\n\t\t\"scalarProduct\",\n\t\t\"sequenceCount\",\n\t\t\"sequenceMatch\",\n\t\t\"sequenceNextNode\",\n\t\t\"serverUUID\",\n\t\t\"shardCount\",\n\t\t\"shardNum\",\n\t\t\"showCertificate\",\n\t\t\"sigmoid\",\n\t\t\"sign\",\n\t\t\"simpleJSONExtractBool\",\n\t\t\"simpleJSONExtractFloat\",\n\t\t\"simpleJSONExtractInt\",\n\t\t\"simpleJSONExtractRaw\",\n\t\t\"simpleJSONExtractString\",\n\t\t\"simpleJSONExtractUInt\",\n\t\t\"simpleJSONHas\",\n\t\t\"simpleLinearRegression\",\n\t\t\"sin\",\n\t\t\"singleValueOrNull\",\n\t\t\"sinh\",\n\t\t\"sipHash128\",\n\t\t\"sipHash64\",\n\t\t\"skewPop\",\n\t\t\"skewSamp\",\n\t\t\"sleep\",\n\t\t\"sleepEachRow\",\n\t\t\"snowflakeToDateTime\",\n\t\t\"snowflakeToDateTime64\",\n\t\t\"sparkbar\",\n\t\t\"splitByChar\",\n\t\t\"splitByNonAlpha\",\n\t\t\"splitByRegexp\",\n\t\t\"splitByString\",\n\t\t\"splitByWhitespace\",\n\t\t\"sqrt\",\n\t\t\"startsWith\",\n\t\t\"stddevPop\",\n\t\t\"stddevPopStable\",\n\t\t\"stddevSamp\",\n\t\t\"stddevSampStable\",\n\t\t\"stem\",\n\t\t\"stochasticLinearRegression\",\n\t\t\"stochasticLogisticRegression\",\n\t\t\"stringToH3\",\n\t\t\"studentTTest\",\n\t\t\"subBitmap\",\n\t\t\"substr\",\n\t\t\"substring\",\n\t\t\"substringUTF8\",\n\t\t\"subtractDays\",\n\t\t\"subtractHours\",\n\t\t\"subtractMicroseconds\",\n\t\t\"subtractMilliseconds\",\n\t\t\"subtractMinutes\",\n\t\t\"subtractMonths\",\n\t\t\"subtractNanoseconds\",\n\t\t\"subtractQuarters\",\n\t\t\"subtractSeconds\",\n\t\t\"subtractWeeks\",\n\t\t\"subtractYears\",\n\t\t\"sum\",\n\t\t\"sumCount\",\n\t\t\"sumKahan\",\n\t\t\"sumMapFiltered\",\n\t\t\"sumMapFilteredWithOverflow\",\n\t\t\"sumMapWithOverflow\",\n\t\t\"sumMappedArrays\",\n\t\t\"sumWithOverflow\",\n\t\t\"svg\",\n\t\t\"synonyms\",\n\t\t\"tan\",\n\t\t\"tanh\",\n\t\t\"tcpPort\",\n\t\t\"tgamma\",\n\t\t\"theilsU\",\n\t\t\"throwIf\",\n\t\t\"tid\",\n\t\t\"timeSlot\",\n\t\t\"timeSlots\",\n\t\t\"timeZone\",\n\t\t\"timeZoneOf\",\n\t\t\"timeZoneOffset\",\n\t\t\"timezone\",\n\t\t\"timezoneOf\",\n\t\t\"timezoneOffset\",\n\t\t\"toBool\",\n\t\t\"toColumnTypeName\",\n\t\t\"toDate\",\n\t\t\"toDate32\",\n\t\t\"toDate32OrDefault\",\n\t\t\"toDate32OrNull\",\n\t\t\"toDate32OrZero\",\n\t\t\"toDateOrDefault\",\n\t\t\"toDateOrNull\",\n\t\t\"toDateOrZero\",\n\t\t\"toDateTime\",\n\t\t\"toDateTime32\",\n\t\t\"toDateTime64\",\n\t\t\"toDateTime64OrDefault\",\n\t\t\"toDateTime64OrNull\",\n\t\t\"toDateTime64OrZero\",\n\t\t\"toDateTimeOrDefault\",\n\t\t\"toDateTimeOrNull\",\n\t\t\"toDateTimeOrZero\",\n\t\t\"toDayOfMonth\",\n\t\t\"toDayOfWeek\",\n\t\t\"toDayOfYear\",\n\t\t\"toDecimal128\",\n\t\t\"toDecimal128OrDefault\",\n\t\t\"toDecimal128OrNull\",\n\t\t\"toDecimal128OrZero\",\n\t\t\"toDecimal256\",\n\t\t\"toDecimal256OrDefault\",\n\t\t\"toDecimal256OrNull\",\n\t\t\"toDecimal256OrZero\",\n\t\t\"toDecimal32\",\n\t\t\"toDecimal32OrDefault\",\n\t\t\"toDecimal32OrNull\",\n\t\t\"toDecimal32OrZero\",\n\t\t\"toDecimal64\",\n\t\t\"toDecimal64OrDefault\",\n\t\t\"toDecimal64OrNull\",\n\t\t\"toDecimal64OrZero\",\n\t\t\"toFixedString\",\n\t\t\"toFloat32\",\n\t\t\"toFloat32OrDefault\",\n\t\t\"toFloat32OrNull\",\n\t\t\"toFloat32OrZero\",\n\t\t\"toFloat64\",\n\t\t\"toFloat64OrDefault\",\n\t\t\"toFloat64OrNull\",\n\t\t\"toFloat64OrZero\",\n\t\t\"toHour\",\n\t\t\"toIPv4\",\n\t\t\"toIPv4OrDefault\",\n\t\t\"toIPv4OrNull\",\n\t\t\"toIPv6\",\n\t\t\"toIPv6OrDefault\",\n\t\t\"toIPv6OrNull\",\n\t\t\"toISOWeek\",\n\t\t\"toISOYear\",\n\t\t\"toInt128\",\n\t\t\"toInt128OrDefault\",\n\t\t\"toInt128OrNull\",\n\t\t\"toInt128OrZero\",\n\t\t\"toInt16\",\n\t\t\"toInt16OrDefault\",\n\t\t\"toInt16OrNull\",\n\t\t\"toInt16OrZero\",\n\t\t\"toInt256\",\n\t\t\"toInt256OrDefault\",\n\t\t\"toInt256OrNull\",\n\t\t\"toInt256OrZero\",\n\t\t\"toInt32\",\n\t\t\"toInt32OrDefault\",\n\t\t\"toInt32OrNull\",\n\t\t\"toInt32OrZero\",\n\t\t\"toInt64\",\n\t\t\"toInt64OrDefault\",\n\t\t\"toInt64OrNull\",\n\t\t\"toInt64OrZero\",\n\t\t\"toInt8\",\n\t\t\"toInt8OrDefault\",\n\t\t\"toInt8OrNull\",\n\t\t\"toInt8OrZero\",\n\t\t\"toIntervalDay\",\n\t\t\"toIntervalHour\",\n\t\t\"toIntervalMicrosecond\",\n\t\t\"toIntervalMillisecond\",\n\t\t\"toIntervalMinute\",\n\t\t\"toIntervalMonth\",\n\t\t\"toIntervalNanosecond\",\n\t\t\"toIntervalQuarter\",\n\t\t\"toIntervalSecond\",\n\t\t\"toIntervalWeek\",\n\t\t\"toIntervalYear\",\n\t\t\"toJSONString\",\n\t\t\"toLastDayOfMonth\",\n\t\t\"toLowCardinality\",\n\t\t\"toMinute\",\n\t\t\"toModifiedJulianDay\",\n\t\t\"toModifiedJulianDayOrNull\",\n\t\t\"toMonday\",\n\t\t\"toMonth\",\n\t\t\"toNullable\",\n\t\t\"toQuarter\",\n\t\t\"toRelativeDayNum\",\n\t\t\"toRelativeHourNum\",\n\t\t\"toRelativeMinuteNum\",\n\t\t\"toRelativeMonthNum\",\n\t\t\"toRelativeQuarterNum\",\n\t\t\"toRelativeSecondNum\",\n\t\t\"toRelativeWeekNum\",\n\t\t\"toRelativeYearNum\",\n\t\t\"toSecond\",\n\t\t\"toStartOfDay\",\n\t\t\"toStartOfFifteenMinutes\",\n\t\t\"toStartOfFiveMinute\",\n\t\t\"toStartOfFiveMinutes\",\n\t\t\"toStartOfHour\",\n\t\t\"toStartOfISOYear\",\n\t\t\"toStartOfInterval\",\n\t\t\"toStartOfMicrosecond\",\n\t\t\"toStartOfMillisecond\",\n\t\t\"toStartOfMinute\",\n\t\t\"toStartOfMonth\",\n\t\t\"toStartOfNanosecond\",\n\t\t\"toStartOfQuarter\",\n\t\t\"toStartOfSecond\",\n\t\t\"toStartOfTenMinutes\",\n\t\t\"toStartOfWeek\",\n\t\t\"toStartOfYear\",\n\t\t\"toString\",\n\t\t\"toStringCutToZero\",\n\t\t\"toTime\",\n\t\t\"toTimeZone\",\n\t\t\"toTimezone\",\n\t\t\"toTypeName\",\n\t\t\"toUInt128\",\n\t\t\"toUInt128OrNull\",\n\t\t\"toUInt128OrZero\",\n\t\t\"toUInt16\",\n\t\t\"toUInt16OrDefault\",\n\t\t\"toUInt16OrNull\",\n\t\t\"toUInt16OrZero\",\n\t\t\"toUInt256\",\n\t\t\"toUInt256OrDefault\",\n\t\t\"toUInt256OrNull\",\n\t\t\"toUInt256OrZero\",\n\t\t\"toUInt32\",\n\t\t\"toUInt32OrDefault\",\n\t\t\"toUInt32OrNull\",\n\t\t\"toUInt32OrZero\",\n\t\t\"toUInt64\",\n\t\t\"toUInt64OrDefault\",\n\t\t\"toUInt64OrNull\",\n\t\t\"toUInt64OrZero\",\n\t\t\"toUInt8\",\n\t\t\"toUInt8OrDefault\",\n\t\t\"toUInt8OrNull\",\n\t\t\"toUInt8OrZero\",\n\t\t\"toUUID\",\n\t\t\"toUUIDOrDefault\",\n\t\t\"toUUIDOrNull\",\n\t\t\"toUUIDOrZero\",\n\t\t\"toUnixTimestamp\",\n\t\t\"toUnixTimestamp64Micro\",\n\t\t\"toUnixTimestamp64Milli\",\n\t\t\"toUnixTimestamp64Nano\",\n\t\t\"toValidUTF8\",\n\t\t\"toWeek\",\n\t\t\"toYYYYMM\",\n\t\t\"toYYYYMMDD\",\n\t\t\"toYYYYMMDDhhmmss\",\n\t\t\"toYear\",\n\t\t\"toYearWeek\",\n\t\t\"today\",\n\t\t\"tokens\",\n\t\t\"topK\",\n\t\t\"topKWeighted\",\n\t\t\"topLevelDomain\",\n\t\t\"transactionID\",\n\t\t\"transactionLatestSnapshot\",\n\t\t\"transactionOldestSnapshot\",\n\t\t\"transform\",\n\t\t\"translate\",\n\t\t\"translateUTF8\",\n\t\t\"trimBoth\",\n\t\t\"trimLeft\",\n\t\t\"trimRight\",\n\t\t\"trunc\",\n\t\t\"truncate\",\n\t\t\"tryBase64Decode\",\n\t\t\"tumble\",\n\t\t\"tumbleEnd\",\n\t\t\"tumbleStart\",\n\t\t\"tuple\",\n\t\t\"tupleDivide\",\n\t\t\"tupleDivideByNumber\",\n\t\t\"tupleElement\",\n\t\t\"tupleHammingDistance\",\n\t\t\"tupleMinus\",\n\t\t\"tupleMultiply\",\n\t\t\"tupleMultiplyByNumber\",\n\t\t\"tupleNegate\",\n\t\t\"tuplePlus\",\n\t\t\"tupleToNameValuePairs\",\n\t\t\"ucase\",\n\t\t\"unbin\",\n\t\t\"unhex\",\n\t\t\"uniq\",\n\t\t\"uniqCombined\",\n\t\t\"uniqCombined64\",\n\t\t\"uniqExact\",\n\t\t\"uniqHLL12\",\n\t\t\"uniqTheta\",\n\t\t\"uniqUpTo\",\n\t\t\"upper\",\n\t\t\"upperUTF8\",\n\t\t\"uptime\",\n\t\t\"user\",\n\t\t\"validateNestedArraySizes\",\n\t\t\"varPop\",\n\t\t\"varPopStable\",\n\t\t\"varSamp\",\n\t\t\"varSampStable\",\n\t\t\"vectorDifference\",\n\t\t\"vectorSum\",\n\t\t\"version\",\n\t\t\"visibleWidth\",\n\t\t\"visitParamExtractBool\",\n\t\t\"visitParamExtractFloat\",\n\t\t\"visitParamExtractInt\",\n\t\t\"visitParamExtractRaw\",\n\t\t\"visitParamExtractString\",\n\t\t\"visitParamExtractUInt\",\n\t\t\"visitParamHas\",\n\t\t\"week\",\n\t\t\"welchTTest\",\n\t\t\"windowFunnel\",\n\t\t\"windowID\",\n\t\t\"wkt\",\n\t\t\"wordShingleMinHash\",\n\t\t\"wordShingleMinHashArg\",\n\t\t\"wordShingleMinHashArgCaseInsensitive\",\n\t\t\"wordShingleMinHashArgCaseInsensitiveUTF8\",\n\t\t\"wordShingleMinHashArgUTF8\",\n\t\t\"wordShingleMinHashCaseInsensitive\",\n\t\t\"wordShingleMinHashCaseInsensitiveUTF8\",\n\t\t\"wordShingleMinHashUTF8\",\n\t\t\"wordShingleSimHash\",\n\t\t\"wordShingleSimHashCaseInsensitive\",\n\t\t\"wordShingleSimHashCaseInsensitiveUTF8\",\n\t\t\"wordShingleSimHashUTF8\",\n\t\t\"wyHash64\",\n\t\t\"xor\",\n\t\t\"xxHash32\",\n\t\t\"xxHash64\",\n\t\t\"yandexConsistentHash\",\n\t\t\"yearweek\",\n\t\t\"yesterday\",\n\t\t\"zookeeperSessionUptime\",\n\t}\n}\n\nfunc colNames() []string {\n\treturn []string{\n\t\t\"AdvEngineID\",\n\t\t\"Age\",\n\t\t\"BrowserCountry\",\n\t\t\"BrowserLanguage\",\n\t\t\"CLID\",\n\t\t\"ClientEventTime\",\n\t\t\"ClientIP\",\n\t\t\"ClientIP\",\n\t\t\"ClientIP6\",\n\t\t\"ClientTimeZone\",\n\t\t\"CodeVersion\",\n\t\t\"ConnectTiming\",\n\t\t\"CookieEnable\",\n\t\t\"CounterClass\",\n\t\t\"CounterID\",\n\t\t\"DNSTiming\",\n\t\t\"DOMCompleteTiming\",\n\t\t\"DOMContentLoadedTiming\",\n\t\t\"DOMInteractiveTiming\",\n\t\t\"DontCountHits\",\n\t\t\"EventDate\",\n\t\t\"EventTime\",\n\t\t\"FUniqID\",\n\t\t\"FetchTiming\",\n\t\t\"FirstPaintTiming\",\n\t\t\"FlashMajor\",\n\t\t\"FlashMinor\",\n\t\t\"FlashMinor2\",\n\t\t\"FromTag\",\n\t\t\"GeneralInterests\",\n\t\t\"GoalsReached\",\n\t\t\"GoodEvent\",\n\t\t\"HID\",\n\t\t\"HTTPError\",\n\t\t\"HasGCLID\",\n\t\t\"HistoryLength\",\n\t\t\"HitColor\",\n\t\t\"IPNetworkID\",\n\t\t\"Income\",\n\t\t\"Interests\",\n\t\t\"IsArtifical\",\n\t\t\"IsDownload\",\n\t\t\"IsEvent\",\n\t\t\"IsLink\",\n\t\t\"IsMobile\",\n\t\t\"IsNotBounce\",\n\t\t\"IsOldCounter\",\n\t\t\"IsParameter\",\n\t\t\"IsRobot\",\n\t\t\"IslandID\",\n\t\t\"JavaEnable\",\n\t\t\"JavascriptEnable\",\n\t\t\"LoadEventEndTiming\",\n\t\t\"LoadEventStartTiming\",\n\t\t\"MobilePhone\",\n\t\t\"MobilePhoneModel\",\n\t\t\"NSToDOMContentLoadedTiming\",\n\t\t\"NetMajor\",\n\t\t\"NetMinor\",\n\t\t\"OS\",\n\t\t\"OpenerName\",\n\t\t\"OpenstatAdID\",\n\t\t\"OpenstatCampaignID\",\n\t\t\"OpenstatServiceName\",\n\t\t\"OpenstatSourceID\",\n\t\t\"PageCharset\",\n\t\t\"ParamCurrency\",\n\t\t\"ParamCurrencyID\",\n\t\t\"ParamOrderID\",\n\t\t\"ParamPrice\",\n\t\t\"Params\",\n\t\t\"ParsedParams.Key1\",\n\t\t\"ParsedParams.Key2\",\n\t\t\"ParsedParams.Key3\",\n\t\t\"ParsedParams.Key4\",\n\t\t\"ParsedParams.Key5\",\n\t\t\"ParsedParams.ValueDouble\",\n\t\t\"RedirectCount\",\n\t\t\"RedirectTiming\",\n\t\t\"Referer\",\n\t\t\"RefererCategories\",\n\t\t\"RefererDomain\",\n\t\t\"RefererHash\",\n\t\t\"RefererRegions\",\n\t\t\"Refresh\",\n\t\t\"RegionID\",\n\t\t\"RemoteIP\",\n\t\t\"RemoteIP6\",\n\t\t\"RequestNum\",\n\t\t\"RequestTry\",\n\t\t\"ResolutionDepth\",\n\t\t\"ResolutionHeight\",\n\t\t\"ResolutionWidth\",\n\t\t\"ResponseEndTiming\",\n\t\t\"ResponseStartTiming\",\n\t\t\"Robotness\",\n\t\t\"SearchEngineID\",\n\t\t\"SearchPhrase\",\n\t\t\"SendTiming\",\n\t\t\"Sex\",\n\t\t\"ShareService\",\n\t\t\"ShareTitle\",\n\t\t\"ShareURL\",\n\t\t\"SilverlightVersion1\",\n\t\t\"SilverlightVersion2\",\n\t\t\"SilverlightVersion3\",\n\t\t\"SilverlightVersion4\",\n\t\t\"SocialAction\",\n\t\t\"SocialNetwork\",\n\t\t\"SocialSourceNetworkID\",\n\t\t\"SocialSourcePage\",\n\t\t\"Title\",\n\t\t\"TraficSourceID\",\n\t\t\"URL\",\n\t\t\"URLCategories\",\n\t\t\"URLDomain\",\n\t\t\"URLHash\",\n\t\t\"URLRegions\",\n\t\t\"UTCEventTime\",\n\t\t\"UTMCampaign\",\n\t\t\"UTMContent\",\n\t\t\"UTMMedium\",\n\t\t\"UTMSource\",\n\t\t\"UTMTerm\",\n\t\t\"UserAgent\",\n\t\t\"UserAgentMajor\",\n\t\t\"UserAgentMinor\",\n\t\t\"UserID\",\n\t\t\"WatchID\",\n\t\t\"WindowClientHeight\",\n\t\t\"WindowClientWidth\",\n\t\t\"WindowName\",\n\t\t\"WithHash\",\n\t\t\"YCLID\",\n\t}\n}\n"
  },
  {
    "path": "drivers/clickhouse/reader.go",
    "content": "package clickhouse\n\nimport (\n\t\"database/sql\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n)\n\ntype MetadataReader struct {\n\tmetadata.LoggingReader\n}\n\n// NewMetadataReader creates the metadata reader for clickhouse databases.\nfunc NewMetadataReader(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\treturn &MetadataReader{\n\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t}\n}\n\nfunc (r MetadataReader) Tables(f metadata.Filter) (*metadata.TableSet, error) {\n\tqstr := `SELECT\n  database AS Schema,\n  name AS Name,\n  COALESCE(\n    IF(database LIKE 'system', 'SYSTEM TABLE', null),\n    IF(is_temporary,'LOCAL TEMPORARY', null),\n    IF(engine LIKE 'View', 'VIEW', null),\n    'TABLE'\n  ) AS Type,\n  COALESCE(total_bytes, 0) AS Size,\n  comment as Comment\nFROM\n  system.tables`\n\tvar conds []string\n\tvar vals []interface{}\n\tif !f.WithSystem {\n\t\tconds = append(conds, \"database NOT LIKE 'system'\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, \"database LIKE ?\")\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"name LIKE ?\")\n\t}\n\tif len(f.Types) != 0 {\n\t\tvar pholders []string\n\t\tfor _, t := range f.Types {\n\t\t\tvals = append(vals, t)\n\t\t\tpholders = append(pholders, \"?\")\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, \"Type IN (\"+strings.Join(pholders, \", \")+\")\")\n\t\t}\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"Schema, Name\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\tvar results []metadata.Table\n\tfor rows.Next() {\n\t\tvar rec metadata.Table\n\t\tif err := rows.Scan(&rec.Schema, &rec.Name, &rec.Type, &rec.Size, &rec.Comment); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewTableSet(results), nil\n}\n\nfunc (r MetadataReader) Columns(f metadata.Filter) (*metadata.ColumnSet, error) {\n\tqstr := `SELECT\n  position,\n  database as schema,\n  name,\n  type,\n  COALESCE(default_expression, '')\nFROM\n  system.columns`\n\tvals := []interface{}{f.Parent}\n\tconds := []string{\"table LIKE ?\"}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, \"database LIKE ?\")\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"name LIKE ?\")\n\t}\n\tif len(f.Types) != 0 {\n\t\tvar pholders []string\n\t\tfor _, t := range f.Types {\n\t\t\tvals = append(vals, t)\n\t\t\tpholders = append(pholders, \"?\")\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, \"Type IN (\"+strings.Join(pholders, \", \")+\")\")\n\t\t}\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"name\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\tvar results []metadata.Column\n\tfor rows.Next() {\n\t\trec := metadata.Column{\n\t\t\tCatalog: f.Catalog,\n\t\t\tTable:   f.Parent,\n\t\t}\n\t\tif err := rows.Scan(\n\t\t\t&rec.OrdinalPosition,\n\t\t\t&rec.Schema,\n\t\t\t&rec.Name,\n\t\t\t&rec.DataType,\n\t\t\t&rec.Default,\n\t\t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewColumnSet(results), nil\n}\n\nfunc (r MetadataReader) Schemas(f metadata.Filter) (*metadata.SchemaSet, error) {\n\tqstr := `SELECT\n  name\nFROM\n  system.databases`\n\tvar conds []string\n\tvar vals []interface{}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"name LIKE ?\")\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"name\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\tvar results []metadata.Schema\n\tfor rows.Next() {\n\t\tvar rec metadata.Schema\n\t\tif err := rows.Scan(&rec.Schema); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewSchemaSet(results), nil\n}\n\nfunc (r MetadataReader) Functions(f metadata.Filter) (*metadata.FunctionSet, error) {\n\tqstr := `SELECT\n  name AS specific_name,\n  name AS routine_name,\n  (IF(is_aggregate = 1,'AGGREGATE','FUNCTION')) AS type\nFROM\n  system.functions`\n\tvar conds []string\n\tvar vals []interface{}\n\tif f.Name != \"\" {\n\t\tconds = append(conds, \"name LIKE ?\")\n\t\tvals = append(vals, f.Name)\n\t}\n\tif len(f.Types) != 0 {\n\t\tvar pholders []string\n\t\tfor _, t := range f.Types {\n\t\t\tvals = append(vals, t)\n\t\t\tpholders = append(pholders, \"?\")\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, \"type IN (\"+strings.Join(pholders, \", \")+\")\")\n\t\t}\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"name, type\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\tvar results []metadata.Function\n\tfor rows.Next() {\n\t\tvar rec metadata.Function\n\t\tif err := rows.Scan(\n\t\t\t&rec.SpecificName,\n\t\t\t&rec.Name,\n\t\t\t&rec.Type,\n\t\t); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewFunctionSet(results), nil\n}\n\nfunc (r MetadataReader) query(qstr string, conds []string, order string, vals ...interface{}) (*sql.Rows, func(), error) {\n\tif len(conds) != 0 {\n\t\tqstr += \"\\nWHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tif order != \"\" {\n\t\tqstr += \"\\nORDER BY \" + order\n\t}\n\treturn r.Query(qstr, vals...)\n}\n"
  },
  {
    "path": "drivers/clickhouse/testdata/clickhouse.sql",
    "content": "-- https://clickhouse.tech/docs/en/getting-started/tutorial/#create-tables\nCREATE DATABASE tutorial;\n\nCREATE TABLE tutorial.hits_v1 (\n    `WatchID` UInt64,\n    `JavaEnable` UInt8,\n    `Title` String,\n    `GoodEvent` Int16,\n    `EventTime` DateTime,\n    `EventDate` Date,\n    `CounterID` UInt32,\n    `ClientIP` UInt32,\n    `ClientIP6` FixedString(16),\n    `RegionID` UInt32,\n    `UserID` UInt64,\n    `CounterClass` Int8,\n    `OS` UInt8,\n    `UserAgent` UInt8,\n    `URL` String,\n    `Referer` String,\n    `URLDomain` String,\n    `RefererDomain` String,\n    `Refresh` UInt8,\n    `IsRobot` UInt8,\n    `RefererCategories` Array(UInt16),\n    `URLCategories` Array(UInt16),\n    `URLRegions` Array(UInt32),\n    `RefererRegions` Array(UInt32),\n    `ResolutionWidth` UInt16,\n    `ResolutionHeight` UInt16,\n    `ResolutionDepth` UInt8,\n    `FlashMajor` UInt8,\n    `FlashMinor` UInt8,\n    `FlashMinor2` String,\n    `NetMajor` UInt8,\n    `NetMinor` UInt8,\n    `UserAgentMajor` UInt16,\n    `UserAgentMinor` FixedString(2),\n    `CookieEnable` UInt8,\n    `JavascriptEnable` UInt8,\n    `IsMobile` UInt8,\n    `MobilePhone` UInt8,\n    `MobilePhoneModel` String,\n    `Params` String,\n    `IPNetworkID` UInt32,\n    `TraficSourceID` Int8,\n    `SearchEngineID` UInt16,\n    `SearchPhrase` String,\n    `AdvEngineID` UInt8,\n    `IsArtifical` UInt8,\n    `WindowClientWidth` UInt16,\n    `WindowClientHeight` UInt16,\n    `ClientTimeZone` Int16,\n    `ClientEventTime` DateTime,\n    `SilverlightVersion1` UInt8,\n    `SilverlightVersion2` UInt8,\n    `SilverlightVersion3` UInt32,\n    `SilverlightVersion4` UInt16,\n    `PageCharset` String,\n    `CodeVersion` UInt32,\n    `IsLink` UInt8,\n    `IsDownload` UInt8,\n    `IsNotBounce` UInt8,\n    `FUniqID` UInt64,\n    `HID` UInt32,\n    `IsOldCounter` UInt8,\n    `IsEvent` UInt8,\n    `IsParameter` UInt8,\n    `DontCountHits` UInt8,\n    `WithHash` UInt8,\n    `HitColor` FixedString(1),\n    `UTCEventTime` DateTime,\n    `Age` UInt8,\n    `Sex` UInt8,\n    `Income` UInt8,\n    `Interests` UInt16,\n    `Robotness` UInt8,\n    `GeneralInterests` Array(UInt16),\n    `RemoteIP` UInt32,\n    `RemoteIP6` FixedString(16),\n    `WindowName` Int32,\n    `OpenerName` Int32,\n    `HistoryLength` Int16,\n    `BrowserLanguage` FixedString(2),\n    `BrowserCountry` FixedString(2),\n    `SocialNetwork` String,\n    `SocialAction` String,\n    `HTTPError` UInt16,\n    `SendTiming` Int32,\n    `DNSTiming` Int32,\n    `ConnectTiming` Int32,\n    `ResponseStartTiming` Int32,\n    `ResponseEndTiming` Int32,\n    `FetchTiming` Int32,\n    `RedirectTiming` Int32,\n    `DOMInteractiveTiming` Int32,\n    `DOMContentLoadedTiming` Int32,\n    `DOMCompleteTiming` Int32,\n    `LoadEventStartTiming` Int32,\n    `LoadEventEndTiming` Int32,\n    `NSToDOMContentLoadedTiming` Int32,\n    `FirstPaintTiming` Int32,\n    `RedirectCount` Int8,\n    `SocialSourceNetworkID` UInt8,\n    `SocialSourcePage` String,\n    `ParamPrice` Int64,\n    `ParamOrderID` String,\n    `ParamCurrency` FixedString(3),\n    `ParamCurrencyID` UInt16,\n    `GoalsReached` Array(UInt32),\n    `OpenstatServiceName` String,\n    `OpenstatCampaignID` String,\n    `OpenstatAdID` String,\n    `OpenstatSourceID` String,\n    `UTMSource` String,\n    `UTMMedium` String,\n    `UTMCampaign` String,\n    `UTMContent` String,\n    `UTMTerm` String,\n    `FromTag` String,\n    `HasGCLID` UInt8,\n    `RefererHash` UInt64,\n    `URLHash` UInt64,\n    `CLID` UInt32,\n    `YCLID` UInt64,\n    `ShareService` String,\n    `ShareURL` String,\n    `ShareTitle` String,\n    `ParsedParams` Nested(\n        Key1 String,\n        Key2 String,\n        Key3 String,\n        Key4 String,\n        Key5 String,\n        ValueDouble Float64),\n    `IslandID` FixedString(16),\n    `RequestNum` UInt32,\n    `RequestTry` UInt8\n)\nENGINE = MergeTree()\nPARTITION BY toYYYYMM(EventDate)\nORDER BY (CounterID, EventDate, intHash32(UserID))\nSAMPLE BY intHash32(UserID);\n\nCREATE TABLE tutorial.visits_v1 (\n    `CounterID` UInt32,\n    `StartDate` Date,\n    `Sign` Int8,\n    `IsNew` UInt8,\n    `VisitID` UInt64,\n    `UserID` UInt64,\n    `StartTime` DateTime,\n    `Duration` UInt32,\n    `UTCStartTime` DateTime,\n    `PageViews` Int32,\n    `Hits` Int32,\n    `IsBounce` UInt8,\n    `Referer` String,\n    `StartURL` String,\n    `RefererDomain` String,\n    `StartURLDomain` String,\n    `EndURL` String,\n    `LinkURL` String,\n    `IsDownload` UInt8,\n    `TraficSourceID` Int8,\n    `SearchEngineID` UInt16,\n    `SearchPhrase` String,\n    `AdvEngineID` UInt8,\n    `PlaceID` Int32,\n    `RefererCategories` Array(UInt16),\n    `URLCategories` Array(UInt16),\n    `URLRegions` Array(UInt32),\n    `RefererRegions` Array(UInt32),\n    `IsYandex` UInt8,\n    `GoalReachesDepth` Int32,\n    `GoalReachesURL` Int32,\n    `GoalReachesAny` Int32,\n    `SocialSourceNetworkID` UInt8,\n    `SocialSourcePage` String,\n    `MobilePhoneModel` String,\n    `ClientEventTime` DateTime,\n    `RegionID` UInt32,\n    `ClientIP` UInt32,\n    `ClientIP6` FixedString(16),\n    `RemoteIP` UInt32,\n    `RemoteIP6` FixedString(16),\n    `IPNetworkID` UInt32,\n    `SilverlightVersion3` UInt32,\n    `CodeVersion` UInt32,\n    `ResolutionWidth` UInt16,\n    `ResolutionHeight` UInt16,\n    `UserAgentMajor` UInt16,\n    `UserAgentMinor` UInt16,\n    `WindowClientWidth` UInt16,\n    `WindowClientHeight` UInt16,\n    `SilverlightVersion2` UInt8,\n    `SilverlightVersion4` UInt16,\n    `FlashVersion3` UInt16,\n    `FlashVersion4` UInt16,\n    `ClientTimeZone` Int16,\n    `OS` UInt8,\n    `UserAgent` UInt8,\n    `ResolutionDepth` UInt8,\n    `FlashMajor` UInt8,\n    `FlashMinor` UInt8,\n    `NetMajor` UInt8,\n    `NetMinor` UInt8,\n    `MobilePhone` UInt8,\n    `SilverlightVersion1` UInt8,\n    `Age` UInt8,\n    `Sex` UInt8,\n    `Income` UInt8,\n    `JavaEnable` UInt8,\n    `CookieEnable` UInt8,\n    `JavascriptEnable` UInt8,\n    `IsMobile` UInt8,\n    `BrowserLanguage` UInt16,\n    `BrowserCountry` UInt16,\n    `Interests` UInt16,\n    `Robotness` UInt8,\n    `GeneralInterests` Array(UInt16),\n    `Params` Array(String),\n    `Goals` Nested(\n        ID UInt32,\n        Serial UInt32,\n        EventTime DateTime,\n        Price Int64,\n        OrderID String,\n        CurrencyID UInt32),\n    `WatchIDs` Array(UInt64),\n    `ParamSumPrice` Int64,\n    `ParamCurrency` FixedString(3),\n    `ParamCurrencyID` UInt16,\n    `ClickLogID` UInt64,\n    `ClickEventID` Int32,\n    `ClickGoodEvent` Int32,\n    `ClickEventTime` DateTime,\n    `ClickPriorityID` Int32,\n    `ClickPhraseID` Int32,\n    `ClickPageID` Int32,\n    `ClickPlaceID` Int32,\n    `ClickTypeID` Int32,\n    `ClickResourceID` Int32,\n    `ClickCost` UInt32,\n    `ClickClientIP` UInt32,\n    `ClickDomainID` UInt32,\n    `ClickURL` String,\n    `ClickAttempt` UInt8,\n    `ClickOrderID` UInt32,\n    `ClickBannerID` UInt32,\n    `ClickMarketCategoryID` UInt32,\n    `ClickMarketPP` UInt32,\n    `ClickMarketCategoryName` String,\n    `ClickMarketPPName` String,\n    `ClickAWAPSCampaignName` String,\n    `ClickPageName` String,\n    `ClickTargetType` UInt16,\n    `ClickTargetPhraseID` UInt64,\n    `ClickContextType` UInt8,\n    `ClickSelectType` Int8,\n    `ClickOptions` String,\n    `ClickGroupBannerID` Int32,\n    `OpenstatServiceName` String,\n    `OpenstatCampaignID` String,\n    `OpenstatAdID` String,\n    `OpenstatSourceID` String,\n    `UTMSource` String,\n    `UTMMedium` String,\n    `UTMCampaign` String,\n    `UTMContent` String,\n    `UTMTerm` String,\n    `FromTag` String,\n    `HasGCLID` UInt8,\n    `FirstVisit` DateTime,\n    `PredLastVisit` Date,\n    `LastVisit` Date,\n    `TotalVisits` UInt32,\n    `TraficSource` Nested(\n        ID Int8,\n        SearchEngineID UInt16,\n        AdvEngineID UInt8,\n        PlaceID UInt16,\n        SocialSourceNetworkID UInt8,\n        Domain String,\n        SearchPhrase String,\n        SocialSourcePage String),\n    `Attendance` FixedString(16),\n    `CLID` UInt32,\n    `YCLID` UInt64,\n    `NormalizedRefererHash` UInt64,\n    `SearchPhraseHash` UInt64,\n    `RefererDomainHash` UInt64,\n    `NormalizedStartURLHash` UInt64,\n    `StartURLDomainHash` UInt64,\n    `NormalizedEndURLHash` UInt64,\n    `TopLevelDomain` UInt64,\n    `URLScheme` UInt64,\n    `OpenstatServiceNameHash` UInt64,\n    `OpenstatCampaignIDHash` UInt64,\n    `OpenstatAdIDHash` UInt64,\n    `OpenstatSourceIDHash` UInt64,\n    `UTMSourceHash` UInt64,\n    `UTMMediumHash` UInt64,\n    `UTMCampaignHash` UInt64,\n    `UTMContentHash` UInt64,\n    `UTMTermHash` UInt64,\n    `FromHash` UInt64,\n    `WebVisorEnabled` UInt8,\n    `WebVisorActivity` UInt32,\n    `ParsedParams` Nested(\n        Key1 String,\n        Key2 String,\n        Key3 String,\n        Key4 String,\n        Key5 String,\n        ValueDouble Float64),\n    `Market` Nested(\n        Type UInt8,\n        GoalID UInt32,\n        OrderID String,\n        OrderPrice Int64,\n        PP UInt32,\n        DirectPlaceID UInt32,\n        DirectOrderID UInt32,\n        DirectBannerID UInt32,\n        GoodID String,\n        GoodName String,\n        GoodQuantity Int32,\n        GoodPrice Int64),\n    `IslandID` FixedString(16)\n)\nENGINE = CollapsingMergeTree(Sign)\nPARTITION BY toYYYYMM(StartDate)\nORDER BY (CounterID, StartDate, intHash32(UserID), VisitID)\nSAMPLE BY intHash32(UserID);\n\nCREATE DATABASE tutorial_unexpected;\nCREATE TABLE tutorial_unexpected.hits_v1 (\n    `Unexpected` String\n)\nENGINE = MergeTree()\nORDER BY (Unexpected);\n\nCREATE DATABASE copy_test;\nCREATE TABLE copy_test.dest (\n    StringCol String,\n    NumCol UInt32\n)\nENGINE = MergeTree()\nORDER BY (StringCol);"
  },
  {
    "path": "drivers/completer/completer.go",
    "content": "// completer package provides a generic SQL command line completer\npackage completer\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/gohxs/readline\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/text\"\n)\n\nconst (\n\tWORD_BREAKS = \"\\t\\n$><=;|&{() \"\n)\n\ntype caseType bool\n\nvar (\n\tIGNORE_CASE            = caseType(true)\n\tMATCH_CASE             = caseType(false)\n\tCommonSqlStartCommands = []string{\n\t\t\"ABORT\",\n\t\t\"ALTER\",\n\t\t\"ANALYZE\",\n\t\t\"BEGIN\",\n\t\t\"CALL\",\n\t\t\"CHECKPOINT\",\n\t\t\"CLOSE\",\n\t\t\"CLUSTER\",\n\t\t\"COMMENT\",\n\t\t\"COMMIT\",\n\t\t\"COPY\",\n\t\t\"CREATE\",\n\t\t\"DEALLOCATE\",\n\t\t\"DECLARE\",\n\t\t\"DELETE FROM\",\n\t\t\"DESC\",\n\t\t\"DESCRIBE\",\n\t\t\"DISCARD\",\n\t\t\"DO\",\n\t\t\"DROP\",\n\t\t\"END\",\n\t\t\"EXEC\",\n\t\t\"EXECUTE\",\n\t\t\"EXPLAIN\",\n\t\t\"FETCH\",\n\t\t\"GRANT\",\n\t\t\"IMPORT\",\n\t\t\"INSERT\",\n\t\t\"LIST\",\n\t\t\"LISTEN\",\n\t\t\"LOAD\",\n\t\t\"LOCK\",\n\t\t\"MOVE\",\n\t\t\"NOTIFY\",\n\t\t\"PRAGMA\",\n\t\t\"PREPARE\",\n\t\t\"REASSIGN\",\n\t\t\"REFRESH MATERIALIZED VIEW\",\n\t\t\"REINDEX\",\n\t\t\"RELEASE\",\n\t\t\"RESET\",\n\t\t\"REVOKE\",\n\t\t\"ROLLBACK\",\n\t\t\"SAVEPOINT\",\n\t\t\"SECURITY LABEL\",\n\t\t\"SELECT\",\n\t\t\"SET\",\n\t\t\"SHOW\",\n\t\t\"START\",\n\t\t\"TABLE\",\n\t\t\"TRUNCATE\",\n\t\t\"UNLISTEN\",\n\t\t\"UPDATE\",\n\t\t\"VACUUM\",\n\t\t\"VALUES\",\n\t\t\"WITH\",\n\t}\n\tCommonSqlCommands = []string{\n\t\t\"AND\",\n\t\t\"CASE\",\n\t\t\"CROSS JOIN\",\n\t\t\"ELSE\",\n\t\t\"END\",\n\t\t\"FETCH\",\n\t\t\"FROM\",\n\t\t\"FULL OUTER JOIN\",\n\t\t\"GROUP BY\",\n\t\t\"HAVING\",\n\t\t\"IN\",\n\t\t\"INNER JOIN\",\n\t\t\"IS NOT NULL\",\n\t\t\"IS NULL\",\n\t\t\"JOIN\",\n\t\t\"LEFT JOIN\",\n\t\t\"LIMIT\",\n\t\t\"NOT\",\n\t\t\"ON\",\n\t\t\"OR\",\n\t\t\"ORDER BY\",\n\t\t\"THEN\",\n\t\t\"WHEN\",\n\t\t\"WHERE\",\n\t}\n)\n\nfunc NewDefaultCompleter(opts ...Option) readline.AutoCompleter {\n\tc := completer{\n\t\t// an empty struct satisfies the metadata.Reader interface, because it is actually empty\n\t\treader:           struct{}{},\n\t\tlogger:           log.New(os.Stdout, \"ERROR: \", log.LstdFlags),\n\t\tsqlStartCommands: CommonSqlStartCommands,\n\t\t// TODO do we need to add built-in functions like, COALESCE, CAST, NULLIF, CONCAT etc?\n\t\tsqlCommands: CommonSqlCommands,\n\t\tbackslashCommands: []string{\n\t\t\t`\\!`,\n\t\t\t`\\?`,\n\t\t\t`\\C`,\n\t\t\t`\\H`,\n\t\t\t`\\T`,\n\t\t\t`\\Z`,\n\t\t\t`\\a`,\n\t\t\t`\\begin`,\n\t\t\t`\\bind`,\n\t\t\t`\\c`,\n\t\t\t`\\cd`,\n\t\t\t`\\commit`,\n\t\t\t`\\connect`,\n\t\t\t`\\conninfo`,\n\t\t\t`\\copy`,\n\t\t\t`\\copyright`,\n\t\t\t`\\cset`,\n\t\t\t`\\d+`,\n\t\t\t`\\dS+`,\n\t\t\t`\\dS`,\n\t\t\t`\\da+`,\n\t\t\t`\\daS+`,\n\t\t\t`\\daS`,\n\t\t\t`\\da`,\n\t\t\t`\\df+`,\n\t\t\t`\\dfS+`,\n\t\t\t`\\dfS`,\n\t\t\t`\\df`,\n\t\t\t`\\di+`,\n\t\t\t`\\diS+`,\n\t\t\t`\\diS`,\n\t\t\t`\\di`,\n\t\t\t`\\dm+`,\n\t\t\t`\\dmS+`,\n\t\t\t`\\dmS`,\n\t\t\t`\\dm`,\n\t\t\t`\\dn+`,\n\t\t\t`\\dnS+`,\n\t\t\t`\\dnS`,\n\t\t\t`\\dn`,\n\t\t\t`\\drivers`,\n\t\t\t`\\ds+`,\n\t\t\t`\\dsS+`,\n\t\t\t`\\dsS`,\n\t\t\t`\\ds`,\n\t\t\t`\\dt+`,\n\t\t\t`\\dtS+`,\n\t\t\t`\\dtS`,\n\t\t\t`\\dt`,\n\t\t\t`\\dv+`,\n\t\t\t`\\dvS+`,\n\t\t\t`\\dvS`,\n\t\t\t`\\dv`,\n\t\t\t`\\e`,\n\t\t\t`\\echo`,\n\t\t\t`\\f`,\n\t\t\t`\\g`,\n\t\t\t`\\getenv`,\n\t\t\t`\\gexec`,\n\t\t\t`\\gset`,\n\t\t\t`\\gx`,\n\t\t\t`\\i`,\n\t\t\t`\\ir`,\n\t\t\t`\\l+`,\n\t\t\t`\\l`,\n\t\t\t`\\p`,\n\t\t\t`\\password`,\n\t\t\t`\\prompt`,\n\t\t\t`\\pset`,\n\t\t\t`\\q`,\n\t\t\t`\\r`,\n\t\t\t`\\raw`,\n\t\t\t`\\rollback`,\n\t\t\t`\\set`,\n\t\t\t`\\setenv`,\n\t\t\t`\\t`,\n\t\t\t`\\timing`,\n\t\t\t`\\unset`,\n\t\t\t`\\w`,\n\t\t\t`\\watch`,\n\t\t\t`\\x`,\n\t\t},\n\t}\n\tfor _, o := range opts {\n\t\to(&c)\n\t}\n\treturn c\n}\n\n// Option to configure the reader\ntype Option func(*completer)\n\n// WithDB option\nfunc WithDB(db metadata.DB) Option {\n\treturn func(c *completer) {\n\t\tc.db = db\n\t}\n}\n\n// WithReader option\nfunc WithReader(r metadata.Reader) Option {\n\treturn func(c *completer) {\n\t\tc.reader = r\n\t}\n}\n\n// WithLogger option\nfunc WithLogger(l logger) Option {\n\treturn func(c *completer) {\n\t\tc.logger = l\n\t}\n}\n\n// WithSQLStartCommands that can begin a query\nfunc WithSQLStartCommands(commands []string) Option {\n\treturn func(c *completer) {\n\t\tc.sqlStartCommands = commands\n\t}\n}\n\n// WithSQLCommands that can be any part of a query\nfunc WithSQLCommands(commands []string) Option {\n\treturn func(c *completer) {\n\t\tc.sqlCommands = commands\n\t}\n}\n\n// WithConnStrings option\nfunc WithConnStrings(connStrings []string) Option {\n\treturn func(c *completer) {\n\t\tc.connStrings = connStrings\n\t}\n}\n\n// WithBeforeComplete option\nfunc WithBeforeComplete(f CompleteFunc) Option {\n\treturn func(c *completer) {\n\t\tc.beforeComplete = f\n\t}\n}\n\n// completer based on https://github.com/postgres/postgres/blob/9f3665fbfc34b963933e51778c7feaa8134ac885/src/bin/psql/tab-complete.c\ntype completer struct {\n\tdb                metadata.DB\n\treader            metadata.Reader\n\tlogger            logger\n\tsqlStartCommands  []string\n\tsqlCommands       []string\n\tbackslashCommands []string\n\tconnStrings       []string\n\tbeforeComplete    CompleteFunc\n}\n\n// CompleteFunc returns patterns completing current text, using previous words as context\ntype CompleteFunc func(previousWords []string, text []rune) [][]rune\n\ntype logger interface {\n\tPrintln(...interface{})\n}\n\nfunc (c completer) Do(line []rune, start int) (newLine [][]rune, length int) {\n\tvar i int\n\tfor i = start - 1; i > 0; i-- {\n\t\tif strings.ContainsRune(WORD_BREAKS, line[i]) {\n\t\t\ti++\n\t\t\tbreak\n\t\t}\n\t}\n\tif i == -1 {\n\t\ti = 0\n\t}\n\tpreviousWords := getPreviousWords(start, line)\n\ttext := line[i:start]\n\n\tif c.beforeComplete != nil {\n\t\tresult := c.beforeComplete(previousWords, text)\n\t\tif result != nil {\n\t\t\treturn result, len(text)\n\t\t}\n\t}\n\tresult := c.complete(previousWords, text)\n\tif result != nil {\n\t\treturn result, len(text)\n\t}\n\treturn nil, 0\n}\n\nfunc (c completer) complete(previousWords []string, text []rune) [][]rune {\n\tif len(text) > 0 {\n\t\tif len(previousWords) == 0 && text[0] == '\\\\' {\n\t\t\t/* If current word is a backslash command, offer completions for that */\n\t\t\treturn CompleteFromListCase(MATCH_CASE, text, c.backslashCommands...)\n\t\t}\n\t\tif text[0] == ':' {\n\t\t\tif len(text) == 1 || text[1] == ':' {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\t/* If current word is a variable interpolation, handle that case */\n\t\t\tif text[1] == '\\'' {\n\t\t\t\treturn completeFromVariables(text, \":'\", \"'\", true)\n\t\t\t}\n\t\t\tif text[1] == '\"' {\n\t\t\t\treturn completeFromVariables(text, \":\\\"\", \"\\\"\", true)\n\t\t\t}\n\t\t\treturn completeFromVariables(text, \":\", \"\", true)\n\t\t}\n\t}\n\tif len(previousWords) == 0 {\n\t\t/* If no previous word, suggest one of the basic sql commands */\n\t\treturn CompleteFromList(text, c.sqlStartCommands...)\n\t}\n\t/* DELETE --- can be inside EXPLAIN, RULE, etc */\n\t/* ... despite which, only complete DELETE with FROM at start of line */\n\tif matches(IGNORE_CASE, previousWords, \"DELETE\") {\n\t\treturn CompleteFromList(text, \"FROM\")\n\t}\n\t/* Complete DELETE FROM with a list of tables */\n\tif TailMatches(IGNORE_CASE, previousWords, \"DELETE\", \"FROM\") {\n\t\treturn c.completeWithUpdatables(text)\n\t}\n\t/* Complete DELETE FROM <table> */\n\tif TailMatches(IGNORE_CASE, previousWords, \"DELETE\", \"FROM\", \"*\") {\n\t\treturn CompleteFromList(text, \"USING\", \"WHERE\")\n\t}\n\t/* XXX: implement tab completion for DELETE ... USING */\n\n\t/* Complete CREATE */\n\tif TailMatches(IGNORE_CASE, previousWords, \"CREATE\") {\n\t\treturn CompleteFromList(text, \"DATABASE\", \"SCHEMA\", \"SEQUENCE\", \"TABLE\", \"VIEW\", \"TEMPORARY\")\n\t}\n\tif TailMatches(IGNORE_CASE, previousWords, \"CREATE\", \"TEMP|TEMPORARY\") {\n\t\treturn CompleteFromList(text, \"TABLE\", \"VIEW\")\n\t}\n\tif TailMatches(IGNORE_CASE, previousWords, \"CREATE\", \"TABLE\", \"*\") || TailMatches(IGNORE_CASE, previousWords, \"CREATE\", \"TEMP|TEMPORARY\", \"TABLE\", \"*\") {\n\t\treturn CompleteFromList(text, \"(\")\n\t}\n\t/* INSERT --- can be inside EXPLAIN, RULE, etc */\n\t/* Complete INSERT with \"INTO\" */\n\tif TailMatches(IGNORE_CASE, previousWords, \"INSERT\") {\n\t\treturn CompleteFromList(text, \"INTO\")\n\t}\n\t/* Complete INSERT INTO with table names */\n\tif TailMatches(IGNORE_CASE, previousWords, \"INSERT\", \"INTO\") {\n\t\treturn c.completeWithUpdatables(text)\n\t}\n\t/* Complete \"INSERT INTO <table> (\" with attribute names */\n\tif TailMatches(IGNORE_CASE, previousWords, \"INSERT\", \"INTO\", \"*\", \"(\") {\n\t\treturn c.completeWithAttributes(IGNORE_CASE, previousWords[1], text)\n\t}\n\n\t/*\n\t * Complete INSERT INTO <table> with \"(\" or \"VALUES\" or \"SELECT\" or\n\t * \"TABLE\" or \"DEFAULT VALUES\" or \"OVERRIDING\"\n\t */\n\tif TailMatches(IGNORE_CASE, previousWords, \"INSERT\", \"INTO\", \"*\") {\n\t\treturn CompleteFromList(text, \"(\", \"DEFAULT VALUES\", \"SELECT\", \"TABLE\", \"VALUES\", \"OVERRIDING\")\n\t}\n\n\t/*\n\t * Complete INSERT INTO <table> (attribs) with \"VALUES\" or \"SELECT\" or\n\t * \"TABLE\" or \"OVERRIDING\"\n\t */\n\tif TailMatches(IGNORE_CASE, previousWords, \"INSERT\", \"INTO\", \"*\", \"*\") &&\n\t\tstrings.HasSuffix(previousWords[0], \")\") {\n\t\treturn CompleteFromList(text, \"SELECT\", \"TABLE\", \"VALUES\", \"OVERRIDING\")\n\t}\n\n\t/* Complete OVERRIDING */\n\tif TailMatches(IGNORE_CASE, previousWords, \"OVERRIDING\") {\n\t\treturn CompleteFromList(text, \"SYSTEM VALUE\", \"USER VALUE\")\n\t}\n\n\t/* Complete after OVERRIDING clause */\n\tif TailMatches(IGNORE_CASE, previousWords, \"OVERRIDING\", \"*\", \"VALUE\") {\n\t\treturn CompleteFromList(text, \"SELECT\", \"TABLE\", \"VALUES\")\n\t}\n\n\t/* Insert an open parenthesis after \"VALUES\" */\n\tif TailMatches(IGNORE_CASE, previousWords, \"VALUES\") && !TailMatches(IGNORE_CASE, previousWords, \"DEFAULT\", \"VALUES\") {\n\t\treturn CompleteFromList(text, \"(\")\n\t}\n\t/* UPDATE --- can be inside EXPLAIN, RULE, etc */\n\t/* If prev. word is UPDATE suggest a list of tables */\n\tif TailMatches(IGNORE_CASE, previousWords, \"UPDATE\") {\n\t\treturn c.completeWithUpdatables(text)\n\t}\n\t/* Complete UPDATE <table> with \"SET\" */\n\tif TailMatches(IGNORE_CASE, previousWords, \"UPDATE\", \"*\") {\n\t\treturn CompleteFromList(text, \"SET\")\n\t}\n\t/* Complete UPDATE <table> SET with list of attributes */\n\tif TailMatches(IGNORE_CASE, previousWords, \"UPDATE\", \"*\", \"SET\") {\n\t\treturn c.completeWithAttributes(IGNORE_CASE, previousWords[1], text)\n\t}\n\t/* UPDATE <table> SET <attr> = */\n\tif TailMatches(IGNORE_CASE, previousWords, \"UPDATE\", \"*\", \"SET\", \"!*=\") {\n\t\treturn CompleteFromList(text, \"=\")\n\t}\n\t/* WHERE */\n\t/* Simple case of the word before the where being the table name */\n\tif TailMatches(IGNORE_CASE, previousWords, \"*\", \"WHERE\") {\n\t\t// TODO would be great to _try_ to parse the (incomplete) query\n\t\t// and get a list of possible selectables to filter by\n\t\treturn c.completeWithAttributes(IGNORE_CASE, previousWords[1], text,\n\t\t\t\"AND\",\n\t\t\t\"OR\",\n\t\t\t\"CASE\",\n\t\t\t\"WHEN\",\n\t\t\t\"THEN\",\n\t\t\t\"ELSE\",\n\t\t\t\"END\",\n\t\t)\n\t}\n\n\t/* ... FROM | JOIN ... */\n\tif TailMatches(IGNORE_CASE, previousWords, \"FROM|JOIN\") {\n\t\treturn c.completeWithSelectables(text)\n\t}\n\t/* TABLE, but not TABLE embedded in other commands */\n\tif matches(IGNORE_CASE, previousWords, \"TABLE\") {\n\t\treturn c.completeWithUpdatables(text)\n\t}\n\t/* Backslash commands */\n\tif TailMatches(MATCH_CASE, previousWords, `\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|\\ir|\\include_relative|\\o|\\out|\\s|\\w|\\write`) {\n\t\treturn completeFromFiles(text)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\c|\\connect|\\copy`) ||\n\t\tTailMatches(MATCH_CASE, previousWords, `\\copy`, `*`) {\n\t\treturn CompleteFromList(text, c.connStrings...)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\copy`, `*`, `*`) {\n\t\treturn nil\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\da*`) {\n\t\treturn c.completeWithFunctions(text, []string{\"AGGREGATE\"})\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\df*`) {\n\t\treturn c.completeWithFunctions(text, []string{})\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\di*`) {\n\t\treturn c.completeWithIndexes(text)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\dn*`) {\n\t\treturn c.completeWithSchemas(text)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\ds*`) {\n\t\treturn c.completeWithSequences(text)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\dt*`) {\n\t\treturn c.completeWithTables(text, []string{\"TABLE\", \"BASE TABLE\", \"SYSTEM TABLE\", \"SYNONYM\", \"LOCAL TEMPORARY\", \"GLOBAL TEMPORARY\"})\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\dv*`) {\n\t\treturn c.completeWithTables(text, []string{\"VIEW\", \"SYSTEM VIEW\"})\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\dm*`) {\n\t\treturn c.completeWithTables(text, []string{\"MATERIALIZED VIEW\"})\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\d*`) {\n\t\treturn c.completeWithSelectables(text)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\l*`) ||\n\t\tTailMatches(MATCH_CASE, previousWords, `\\lo*`) {\n\t\treturn c.completeWithCatalogs(text)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`) {\n\t\treturn CompleteFromList(text, `border`, `columns`, `expanded`, `fieldsep`, `fieldsep_zero`,\n\t\t\t`footer`, `format`, `linestyle`, `null`, `numericlocale`, `pager`, `pager_min_lines`,\n\t\t\t`recordsep`, `recordsep_zero`, `tableattr`, `title`, `title`, `tuples_only`,\n\t\t\t`unicode_border_linestyle`, `unicode_column_linestyle`, `unicode_header_linestyle`)\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `expanded`) {\n\t\treturn CompleteFromList(text, \"auto\", \"on\", \"off\")\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `pager`) {\n\t\treturn CompleteFromList(text, \"always\", \"on\", \"off\")\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `fieldsep_zero|footer|numericlocale|pager|recordsep_zero|tuples_only`) {\n\t\treturn CompleteFromList(text, \"on\", \"off\")\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `format`) {\n\t\treturn CompleteFromList(text, \"unaligned\", \"aligned\", \"wrapped\", \"html\", \"asciidoc\", \"latex\", \"latex-longtable\", \"troff-ms\", \"csv\", \"json\", \"vertical\")\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `linestyle`) {\n\t\treturn CompleteFromList(text, \"ascii\", \"old-ascii\", \"unicode\")\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `unicode_border_linestyle|unicode_column_linestyle|unicode_header_linestyle`) {\n\t\treturn CompleteFromList(text, \"single\", \"double\")\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\pset`, `*`) ||\n\t\tTailMatches(MATCH_CASE, previousWords, `\\pset`, `*`, `*`) {\n\t\treturn nil\n\t}\n\tif TailMatches(MATCH_CASE, previousWords, `\\?`) {\n\t\treturn CompleteFromList(text, \"commands\", \"options\", \"variables\")\n\t}\n\t// is suggesting basic sql commands better than nothing?\n\treturn CompleteFromList(text, c.sqlCommands...)\n}\n\nfunc getPreviousWords(point int, buf []rune) []string {\n\tvar i int\n\n\t/*\n\t * Allocate a slice of strings (rune slices). The worst case is that the line contains only\n\t * non-whitespace WORD_BREAKS characters, making each one a separate word.\n\t * This is usually much more space than we need, but it's cheaper than\n\t * doing a separate malloc() for each word.\n\t */\n\tpreviousWords := make([]string, 0, point*2)\n\n\t/*\n\t * First we look for a non-word char before the current point.  (This is\n\t * probably useless, if readline is on the same page as we are about what\n\t * is a word, but if so it's cheap.)\n\t */\n\tfor i = point - 1; i >= 0; i-- {\n\t\tif strings.ContainsRune(WORD_BREAKS, buf[i]) {\n\t\t\tbreak\n\t\t}\n\t}\n\tpoint = i\n\n\t/*\n\t * Now parse words, working backwards, until we hit start of line.  The\n\t * backwards scan has some interesting but intentional properties\n\t * concerning parenthesis handling.\n\t */\n\tfor point >= 0 {\n\t\tvar start, end int\n\t\tinquotes := false\n\t\tparentheses := 0\n\n\t\t/* now find the first non-space which then constitutes the end */\n\t\tend = -1\n\t\tfor i = point; i >= 0; i-- {\n\t\t\tif !unicode.IsSpace(buf[i]) {\n\t\t\t\tend = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\t/* if no end found, we're done */\n\t\tif end < 0 {\n\t\t\tbreak\n\t\t}\n\n\t\t/*\n\t\t * Otherwise we now look for the start.  The start is either the last\n\t\t * character before any word-break character going backwards from the\n\t\t * end, or it's simply character 0.  We also handle open quotes and\n\t\t * parentheses.\n\t\t */\n\t\tfor start = end; start > 0; start-- {\n\t\t\tif buf[start] == '\"' {\n\t\t\t\tinquotes = !inquotes\n\t\t\t}\n\t\t\tif inquotes {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif buf[start] == ')' {\n\t\t\t\tparentheses++\n\t\t\t} else if buf[start] == '(' {\n\t\t\t\tparentheses -= 1\n\t\t\t\tif parentheses <= 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t} else if parentheses == 0 && strings.ContainsRune(WORD_BREAKS, buf[start-1]) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t/* Return the word located at start to end inclusive */\n\t\ti = end - start + 1\n\t\tpreviousWords = append(previousWords, string(buf[start:start+i]))\n\n\t\t/* Continue searching */\n\t\tpoint = start - 1\n\t}\n\n\treturn previousWords\n}\n\n// TailMatches when last words match all patterns\nfunc TailMatches(ct caseType, words []string, patterns ...string) bool {\n\tif len(words) < len(patterns) {\n\t\treturn false\n\t}\n\tfor i, p := range patterns {\n\t\tif !wordMatches(ct, p, words[len(patterns)-i-1]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc matches(ct caseType, words []string, patterns ...string) bool {\n\tif len(words) != len(patterns) {\n\t\treturn false\n\t}\n\tfor i, p := range patterns {\n\t\tif !wordMatches(ct, p, words[len(patterns)-i-1]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc wordMatches(ct caseType, pattern, word string) bool {\n\tif pattern == \"*\" {\n\t\treturn true\n\t}\n\n\tif pattern[0] == '!' {\n\t\treturn !wordMatches(ct, pattern[1:], word)\n\t}\n\n\tcmp := func(a, b string) bool { return a == b }\n\tif ct == IGNORE_CASE {\n\t\tcmp = strings.EqualFold\n\t}\n\n\tfor _, p := range strings.Split(pattern, \"|\") {\n\t\tstar := strings.IndexByte(p, '*')\n\t\tif star == -1 {\n\t\t\tif cmp(p, word) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\tif len(word) >= len(p)-1 && cmp(p[0:star], word[0:star]) && (star >= len(p) || cmp(p[star+1:], word[len(word)-len(p)+star+1:])) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\n// CompleteFromList where items starts with text, ignoring case\nfunc CompleteFromList(text []rune, options ...string) [][]rune {\n\treturn CompleteFromListCase(IGNORE_CASE, text, options...)\n}\n\n// CompleteFromList where items starts with text\nfunc CompleteFromListCase(ct caseType, text []rune, options ...string) [][]rune {\n\tif len(options) == 0 {\n\t\treturn nil\n\t}\n\tisLower := false\n\tif len(text) > 0 {\n\t\tisLower = unicode.IsLower(text[0])\n\t}\n\tprefix := string(text)\n\tif ct == IGNORE_CASE {\n\t\tprefix = strings.ToUpper(prefix)\n\t}\n\tresult := make([][]rune, 0, len(options))\n\tfor _, o := range options {\n\t\tif (ct == IGNORE_CASE && !strings.HasPrefix(strings.ToUpper(o), prefix)) ||\n\t\t\t(ct == MATCH_CASE && !strings.HasPrefix(o, prefix)) {\n\t\t\tcontinue\n\t\t}\n\t\tmatch := o[len(text):]\n\t\tif ct == IGNORE_CASE && isLower {\n\t\t\tmatch = strings.ToLower(match)\n\t\t}\n\t\tresult = append(result, []rune(match))\n\t}\n\treturn result\n}\n\nfunc completeFromVariables(text []rune, prefix, suffix string, needValue bool) [][]rune {\n\tvars := env.Vars().Vars()\n\tnames := make([]string, 0, len(vars))\n\tfor name, value := range vars {\n\t\tif needValue && value == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tnames = append(names, prefix+name+suffix)\n\t}\n\treturn CompleteFromListCase(MATCH_CASE, text, names...)\n}\n\nfunc (c completer) completeWithSelectables(text []rune) [][]rune {\n\tfilter := parseIdentifier(string(text))\n\tnames := c.getNamespaces(filter)\n\tif r, ok := c.reader.(metadata.TableReader); ok {\n\t\ttables := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\treturn r.Tables(filter)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\tt := res.(*metadata.TableSet).Get()\n\t\t\t\treturn qualifiedIdentifier(filter, t.Catalog, t.Schema, t.Name)\n\t\t\t},\n\t\t)\n\t\tnames = append(names, tables...)\n\t}\n\tif r, ok := c.reader.(metadata.FunctionReader); ok {\n\t\tfunctions := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\treturn r.Functions(filter)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\tf := res.(*metadata.FunctionSet).Get()\n\t\t\t\treturn qualifiedIdentifier(filter, f.Catalog, f.Schema, f.Name)\n\t\t\t},\n\t\t)\n\t\tnames = append(names, functions...)\n\t}\n\tif r, ok := c.reader.(metadata.SequenceReader); ok {\n\t\tsequences := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\treturn r.Sequences(filter)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\ts := res.(*metadata.SequenceSet).Get()\n\t\t\t\treturn qualifiedIdentifier(filter, s.Catalog, s.Schema, s.Name)\n\t\t\t},\n\t\t)\n\t\tnames = append(names, sequences...)\n\t}\n\tsort.Strings(names)\n\t// TODO make sure CompleteFromList would properly handle quoted identifiers\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithTables(text []rune, types []string) [][]rune {\n\tr, ok := c.reader.(metadata.TableReader)\n\tif !ok {\n\t\treturn [][]rune{}\n\t}\n\n\tfilter := parseIdentifier(string(text))\n\tfilter.Types = types\n\tnames := c.getNamespaces(filter)\n\ttables := c.getNames(\n\t\tfunc() (iterator, error) {\n\t\t\treturn r.Tables(filter)\n\t\t},\n\t\tfunc(res interface{}) string {\n\t\t\tt := res.(*metadata.TableSet).Get()\n\t\t\treturn qualifiedIdentifier(filter, t.Catalog, t.Schema, t.Name)\n\t\t},\n\t)\n\tnames = append(names, tables...)\n\tsort.Strings(names)\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithFunctions(text []rune, types []string) [][]rune {\n\tr, ok := c.reader.(metadata.FunctionReader)\n\tif !ok {\n\t\treturn [][]rune{}\n\t}\n\tfilter := parseIdentifier(string(text))\n\tfilter.Types = types\n\tnames := c.getNamespaces(filter)\n\tfunctions := c.getNames(\n\t\tfunc() (iterator, error) {\n\t\t\treturn r.Functions(filter)\n\t\t},\n\t\tfunc(res interface{}) string {\n\t\t\tf := res.(*metadata.FunctionSet).Get()\n\t\t\treturn qualifiedIdentifier(filter, f.Catalog, f.Schema, f.Name)\n\t\t},\n\t)\n\tnames = append(names, functions...)\n\tsort.Strings(names)\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithIndexes(text []rune) [][]rune {\n\tr, ok := c.reader.(metadata.IndexReader)\n\tif !ok {\n\t\treturn [][]rune{}\n\t}\n\tfilter := parseIdentifier(string(text))\n\tnames := c.getNamespaces(filter)\n\tindexes := c.getNames(\n\t\tfunc() (iterator, error) {\n\t\t\treturn r.Indexes(filter)\n\t\t},\n\t\tfunc(res interface{}) string {\n\t\t\tf := res.(*metadata.IndexSet).Get()\n\t\t\treturn qualifiedIdentifier(filter, f.Catalog, f.Schema, f.Name)\n\t\t},\n\t)\n\tnames = append(names, indexes...)\n\tsort.Strings(names)\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithSequences(text []rune) [][]rune {\n\tr, ok := c.reader.(metadata.SequenceReader)\n\tif !ok {\n\t\treturn [][]rune{}\n\t}\n\tfilter := parseIdentifier(string(text))\n\tnames := c.getNamespaces(filter)\n\tsequences := c.getNames(\n\t\tfunc() (iterator, error) {\n\t\t\treturn r.Sequences(filter)\n\t\t},\n\t\tfunc(res interface{}) string {\n\t\t\ts := res.(*metadata.SequenceSet).Get()\n\t\t\treturn qualifiedIdentifier(filter, s.Catalog, s.Schema, s.Name)\n\t\t},\n\t)\n\tnames = append(names, sequences...)\n\tsort.Strings(names)\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithSchemas(text []rune) [][]rune {\n\tr, ok := c.reader.(metadata.SchemaReader)\n\tif !ok {\n\t\treturn [][]rune{}\n\t}\n\tfilter := parseIdentifier(string(text))\n\tnames := c.getNames(\n\t\tfunc() (iterator, error) {\n\t\t\tif filter.Schema != \"\" {\n\t\t\t\t// name should already have a wildcard appended\n\t\t\t\treturn r.Schemas(metadata.Filter{Catalog: filter.Schema, Name: filter.Name, WithSystem: true})\n\t\t\t}\n\t\t\treturn r.Schemas(filter)\n\t\t},\n\t\tfunc(res interface{}) string {\n\t\t\ts := res.(*metadata.SchemaSet).Get()\n\t\t\treturn qualifiedIdentifier(filter, \"\", s.Catalog, s.Schema)\n\t\t},\n\t)\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithCatalogs(text []rune) [][]rune {\n\tr, ok := c.reader.(metadata.CatalogReader)\n\tif !ok {\n\t\treturn [][]rune{}\n\t}\n\tfilter := parseIdentifier(string(text))\n\tnames := c.getNames(\n\t\tfunc() (iterator, error) {\n\t\t\treturn r.Catalogs(filter)\n\t\t},\n\t\tfunc(res interface{}) string {\n\t\t\ts := res.(*metadata.CatalogSet).Get()\n\t\t\treturn s.Catalog\n\t\t},\n\t)\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) completeWithUpdatables(text []rune) [][]rune {\n\tfilter := parseIdentifier(string(text))\n\tnames := c.getNamespaces(filter)\n\tif r, ok := c.reader.(metadata.TableReader); ok {\n\t\t// exclude materialized views, sequences, system tables, synonyms\n\t\tfilter.Types = []string{\"TABLE\", \"BASE TABLE\", \"LOCAL TEMPORARY\", \"GLOBAL TEMPORARY\", \"VIEW\"}\n\t\ttables := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\treturn r.Tables(filter)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\tt := res.(*metadata.TableSet).Get()\n\t\t\t\treturn qualifiedIdentifier(filter, t.Catalog, t.Schema, t.Name)\n\t\t\t},\n\t\t)\n\t\tnames = append(names, tables...)\n\t}\n\tsort.Strings(names)\n\t// TODO make sure CompleteFromList would properly handle quoted identifiers\n\treturn CompleteFromList(text, names...)\n}\n\nfunc (c completer) getNamespaces(f metadata.Filter) []string {\n\tnames := make([]string, 0, 10)\n\tif f.Catalog == \"\" && f.Schema == \"\" {\n\t\tif r, ok := c.reader.(metadata.CatalogReader); ok {\n\t\t\tcatalogs := c.getNames(\n\t\t\t\tfunc() (iterator, error) { return r.Catalogs(metadata.Filter{}) },\n\t\t\t\tfunc(res interface{}) string {\n\t\t\t\t\treturn res.(*metadata.CatalogSet).Get().Catalog\n\t\t\t\t},\n\t\t\t)\n\t\t\tnames = append(names, catalogs...)\n\t\t}\n\t}\n\tif f.Catalog != \"\" {\n\t\t// filter is already fully qualified, so don't return any namespaces\n\t\treturn names\n\t}\n\tif r, ok := c.reader.(metadata.SchemaReader); ok {\n\t\tschemas := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\tif f.Schema != \"\" {\n\t\t\t\t\t// name should already have a wildcard appended\n\t\t\t\t\treturn r.Schemas(metadata.Filter{Catalog: f.Schema, Name: f.Name, WithSystem: true})\n\t\t\t\t}\n\t\t\t\treturn r.Schemas(f)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\ts := res.(*metadata.SchemaSet).Get()\n\t\t\t\treturn qualifiedIdentifier(f, \"\", s.Catalog, s.Schema)\n\t\t\t},\n\t\t)\n\t\tnames = append(names, schemas...)\n\t}\n\treturn names\n}\n\nfunc (c completer) completeWithAttributes(_ caseType, selectable string, text []rune, options ...string) [][]rune {\n\tnames := make([]string, 0, 10)\n\tif r, ok := c.reader.(metadata.ColumnReader); ok {\n\t\tparent := parseParentIdentifier(selectable)\n\t\tcolumns := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\treturn r.Columns(parent)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\treturn res.(*metadata.ColumnSet).Get().Name\n\t\t\t},\n\t\t)\n\t\tnames = append(names, columns...)\n\t}\n\tif r, ok := c.reader.(metadata.FunctionReader); ok {\n\t\tfilter := parseIdentifier(string(text))\n\t\t// functions don't have to be fully qualified to be callable\n\t\tfilter.OnlyVisible = false\n\t\tfunctions := c.getNames(\n\t\t\tfunc() (iterator, error) {\n\t\t\t\treturn r.Functions(filter)\n\t\t\t},\n\t\t\tfunc(res interface{}) string {\n\t\t\t\treturn res.(*metadata.FunctionSet).Get().Name\n\t\t\t},\n\t\t)\n\t\tnames = append(names, functions...)\n\t}\n\tnames = append(names, options...)\n\treturn CompleteFromList(text, names...)\n}\n\n// parseIdentifier into catalog, schema and name\nfunc parseIdentifier(name string) metadata.Filter {\n\t// TODO handle quoted identifiers\n\tresult := metadata.Filter{}\n\tif !strings.ContainsRune(name, '.') {\n\t\tresult.Name = name + \"%\"\n\t\tresult.OnlyVisible = true\n\t} else {\n\t\tparts := strings.SplitN(name, \".\", 3)\n\t\tif len(parts) == 2 {\n\t\t\tresult.Schema = parts[0]\n\t\t\tresult.Name = parts[1] + \"%\"\n\t\t} else {\n\t\t\tresult.Catalog = parts[0]\n\t\t\tresult.Schema = parts[1]\n\t\t\tresult.Name = parts[2] + \"%\"\n\t\t}\n\t}\n\n\tif result.Schema != \"\" || len(result.Name) > 3 {\n\t\tresult.WithSystem = true\n\t}\n\treturn result\n}\n\n// parseParentIdentifier into catalog, schema and parent\nfunc parseParentIdentifier(name string) metadata.Filter {\n\t// TODO handle quoted identifiers\n\tresult := metadata.Filter{}\n\tif !strings.ContainsRune(name, '.') {\n\t\tresult.Parent = name\n\t\tresult.OnlyVisible = true\n\t} else {\n\t\tparts := strings.SplitN(name, \".\", 3)\n\t\tif len(parts) == 2 {\n\t\t\tresult.Schema = parts[0]\n\t\t\tresult.Parent = parts[1]\n\t\t} else {\n\t\t\tresult.Catalog = parts[0]\n\t\t\tresult.Schema = parts[1]\n\t\t\tresult.Parent = parts[2]\n\t\t}\n\t}\n\n\tif result.Schema != \"\" {\n\t\tresult.WithSystem = true\n\t}\n\treturn result\n}\n\nfunc qualifiedIdentifier(filter metadata.Filter, catalog, schema, name string) string {\n\t// TODO handle quoted identifiers\n\tif filter.Catalog != \"\" && filter.Schema != \"\" {\n\t\treturn catalog + \".\" + schema + \".\" + name\n\t}\n\tif filter.Schema != \"\" {\n\t\treturn schema + \".\" + name\n\t}\n\treturn name\n}\n\nfunc (c completer) getNames(query func() (iterator, error), mapper func(interface{}) string) []string {\n\tres, err := query()\n\tif err != nil {\n\t\tif err != text.ErrNotSupported {\n\t\t\tc.logger.Println(\"Error getting selectables\", err)\n\t\t}\n\t\treturn nil\n\t}\n\tdefer res.Close()\n\n\t// there can be duplicates if names are not qualified\n\tvalues := make(map[string]struct{}, 10)\n\tfor res.Next() {\n\t\tvalues[mapper(res)] = struct{}{}\n\t}\n\tresult := make([]string, 0, len(values))\n\tfor v := range values {\n\t\tresult = append(result, v)\n\t}\n\treturn result\n}\n\ntype iterator interface {\n\tNext() bool\n\tClose() error\n}\n\nfunc completeFromFiles(text []rune) [][]rune {\n\t// TODO handle quotes properly\n\tdir := filepath.Dir(string(text))\n\tdirs, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tmatches := make([]string, 0, len(dirs))\n\tswitch dir {\n\tcase \".\":\n\t\tdir = \"\"\n\tcase \"/\":\n\t\t// pass\n\tdefault:\n\t\tdir += \"/\"\n\t}\n\tfor _, entry := range dirs {\n\t\tname := entry.Name()\n\t\tif entry.IsDir() {\n\t\t\tname += \"/\"\n\t\t}\n\t\tmatches = append(matches, dir+name)\n\t}\n\treturn CompleteFromList(text, matches...)\n}\n"
  },
  {
    "path": "drivers/completer/completer_test.go",
    "content": "package completer\n\nimport (\n\t\"testing\"\n\n\t\"github.com/xo/usql/drivers/metadata\"\n)\n\nfunc TestCompleter(t *testing.T) {\n\tcases := []struct {\n\t\tname           string\n\t\tline           string\n\t\tstart          int\n\t\texpSuggestions []string\n\t\texpLength      int\n\t}{\n\t\t{\n\t\t\t\"Single SQL keyword, uppercase\",\n\t\t\t\"SEL\",\n\t\t\t3,\n\t\t\t[]string{\n\t\t\t\t\"ECT\",\n\t\t\t},\n\t\t\t3,\n\t\t},\n\t\t{\n\t\t\t\"Single SQL keyword, lowercase\",\n\t\t\t\"ex\",\n\t\t\t2,\n\t\t\t[]string{\n\t\t\t\t\"ec\",\n\t\t\t\t\"ecute\",\n\t\t\t\t\"plain\",\n\t\t\t},\n\t\t\t2,\n\t\t},\n\t\t{\n\t\t\t\"usql command\",\n\t\t\t`\\dt`,\n\t\t\t3,\n\t\t\t[]string{\n\t\t\t\t`+`,\n\t\t\t\t``,\n\t\t\t\t`S+`,\n\t\t\t\t`S`,\n\t\t\t},\n\t\t\t3,\n\t\t},\n\t\t{\n\t\t\t\"files\",\n\t\t\t`\\i comp`,\n\t\t\t7,\n\t\t\t[]string{\n\t\t\t\t\"leter.go\",\n\t\t\t\t\"leter_test.go\",\n\t\t\t},\n\t\t\t4,\n\t\t},\n\t\t{\n\t\t\t\"connections\",\n\t\t\t`\\c p`,\n\t\t\t4,\n\t\t\t[]string{\n\t\t\t\t\"g://\",\n\t\t\t},\n\t\t\t1,\n\t\t},\n\t\t{\n\t\t\t\"3rd word\",\n\t\t\t\"SELECT * F\",\n\t\t\t10,\n\t\t\t[]string{\n\t\t\t\t\"ULL OUTER JOIN\",\n\t\t\t\t\"ROM\",\n\t\t\t\t\"ETCH\",\n\t\t\t},\n\t\t\t1,\n\t\t},\n\t\t{\n\t\t\t\"Selectables\",\n\t\t\t\"SELECT * FROM \",\n\t\t\t14,\n\t\t\t[]string{\n\t\t\t\t\"main\",\n\t\t\t\t\"remote\",\n\t\t\t\t\"default\",\n\t\t\t\t\"system\",\n\t\t\t\t\"film\",\n\t\t\t\t\"factory\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"Namespaced with catalog\",\n\t\t\t\"SELECT * FROM remote.\",\n\t\t\t21,\n\t\t\t[]string{\n\t\t\t\t\"film\",\n\t\t\t\t\"factory\",\n\t\t\t},\n\t\t\t7,\n\t\t},\n\t\t{\n\t\t\t\"Namespaced with schema\",\n\t\t\t\"SELECT * FROM system.\",\n\t\t\t21,\n\t\t\t[]string{\n\t\t\t\t\"film\",\n\t\t\t\t\"factory\",\n\t\t\t},\n\t\t\t7,\n\t\t},\n\t\t{\n\t\t\t\"Namespaced with catalog.schema\",\n\t\t\t\"SELECT * FROM remote.default.f\",\n\t\t\t30,\n\t\t\t[]string{\n\t\t\t\t\"ilm\",\n\t\t\t\t\"actory\",\n\t\t\t},\n\t\t\t16,\n\t\t},\n\t\t{\n\t\t\t\"Attributes\",\n\t\t\t\"SELECT * FROM film WHERE \",\n\t\t\t25,\n\t\t\t[]string{\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"CASE\",\n\t\t\t\t\"AND\",\n\t\t\t\t\"OR\",\n\t\t\t\t\"WHEN\",\n\t\t\t\t\"THEN\",\n\t\t\t\t\"ELSE\",\n\t\t\t\t\"END\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"insert\",\n\t\t\t\"INS\",\n\t\t\t3,\n\t\t\t[]string{\n\t\t\t\t\"ERT\",\n\t\t\t},\n\t\t\t3,\n\t\t},\n\t\t{\n\t\t\t\"insert into\",\n\t\t\t\"INSERT IN\",\n\t\t\t9,\n\t\t\t[]string{\n\t\t\t\t\"TO\",\n\t\t\t},\n\t\t\t2,\n\t\t},\n\t\t{\n\t\t\t\"insert into table\",\n\t\t\t\"INSERT INTO fi\",\n\t\t\t14,\n\t\t\t[]string{\n\t\t\t\t\"lm\",\n\t\t\t},\n\t\t\t2,\n\t\t},\n\t\t{\n\t\t\t\"insert into table select from\",\n\t\t\t\"INSERT INTO film SE\",\n\t\t\t19,\n\t\t\t[]string{\n\t\t\t\t\"LECT\",\n\t\t\t},\n\t\t\t2,\n\t\t},\n\t\t{\n\t\t\t\"insert into table attrs\",\n\t\t\t\"INSERT INTO film (\",\n\t\t\t18,\n\t\t\t[]string{\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"insert into table values\",\n\t\t\t\"INSERT INTO film (a)\",\n\t\t\t20,\n\t\t\t[]string{\n\t\t\t\t\"SELECT\",\n\t\t\t\t\"TABLE\",\n\t\t\t\t\"VALUES\",\n\t\t\t\t\"OVERRIDING\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"update table set attrs\",\n\t\t\t\"UPDATE film SET \",\n\t\t\t16,\n\t\t\t[]string{\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"update table set\",\n\t\t\t\"update film set name \",\n\t\t\t21,\n\t\t\t[]string{\n\t\t\t\t\"=\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"variables\",\n\t\t\t\":a\",\n\t\t\t2,\n\t\t\t[]string{},\n\t\t\t2,\n\t\t},\n\t\t{\n\t\t\t\"type on create\",\n\t\t\t\"CREATE \",\n\t\t\t7,\n\t\t\t[]string{\n\t\t\t\t\"SCHEMA\",\n\t\t\t\t\"DATABASE\",\n\t\t\t\t\"TABLE\",\n\t\t\t\t\"SEQUENCE\",\n\t\t\t\t\"VIEW\",\n\t\t\t\t\"TEMPORARY\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"brackets on create table\",\n\t\t\t\"CREATE TABLE p \",\n\t\t\t15,\n\t\t\t[]string{\n\t\t\t\t\"(\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"TABLE Selectables\",\n\t\t\t\"TABLE \",\n\t\t\t6,\n\t\t\t[]string{\n\t\t\t\t\"main\",\n\t\t\t\t\"remote\",\n\t\t\t\t\"default\",\n\t\t\t\t\"system\",\n\t\t\t\t\"film\",\n\t\t\t\t\"factory\",\n\t\t\t},\n\t\t\t0,\n\t\t},\n\t\t{\n\t\t\t\"TABLE namespaced with catalog\",\n\t\t\t\"TABLE remote.\",\n\t\t\t13,\n\t\t\t[]string{\n\t\t\t\t\"film\",\n\t\t\t\t\"factory\",\n\t\t\t},\n\t\t\t7,\n\t\t},\n\t\t{\n\t\t\t\"TABLE namespaced with schema\",\n\t\t\t\"TABLE system.\",\n\t\t\t13,\n\t\t\t[]string{\n\t\t\t\t\"film\",\n\t\t\t\t\"factory\",\n\t\t\t},\n\t\t\t7,\n\t\t},\n\t\t{\n\t\t\t\"TABLE namespaced with catalog.schema\",\n\t\t\t\"TABLE remote.default.f\",\n\t\t\t22,\n\t\t\t[]string{\n\t\t\t\t\"ilm\",\n\t\t\t\t\"actory\",\n\t\t\t},\n\t\t\t16,\n\t\t},\n\t}\n\n\tcompleter := NewDefaultCompleter(WithReader(mockReader{}), WithConnStrings([]string{\"pg://\"}))\n\tfor _, test := range cases {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tsuggestions, length := completer.Do([]rune(test.line), test.start)\n\t\t\t// need at least 2 pairs of nested loops, one for what's missing, second for what's extra\n\t\t\tfor _, exp := range test.expSuggestions {\n\t\t\t\tfound := false\n\t\t\t\tfor _, act := range suggestions {\n\t\t\t\t\tif string(act) == exp {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !found {\n\t\t\t\t\tt.Errorf(\"Missing expected suggestion: %s\", exp)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, act := range suggestions {\n\t\t\t\tfound := false\n\t\t\t\tfor _, exp := range test.expSuggestions {\n\t\t\t\t\tif string(act) == exp {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif !found {\n\t\t\t\t\tt.Errorf(\"Unexpected suggestion: %s\", string(act))\n\t\t\t\t}\n\t\t\t}\n\t\t\tif length != test.expLength {\n\t\t\t\tt.Errorf(\"Expected Do() to return length %d, got %d\", test.expLength, length)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype mockReader struct{}\n\nvar _ metadata.CatalogReader = &mockReader{}\nvar _ metadata.BasicReader = &mockReader{}\n\nfunc (r mockReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) {\n\treturn metadata.NewCatalogSet([]metadata.Catalog{\n\t\t{\n\t\t\tCatalog: \"main\",\n\t\t},\n\t\t{\n\t\t\tCatalog: \"remote\",\n\t\t},\n\t}), nil\n}\n\nfunc (r mockReader) Schemas(metadata.Filter) (*metadata.SchemaSet, error) {\n\treturn metadata.NewSchemaSet([]metadata.Schema{\n\t\t{\n\t\t\tSchema:  \"default\",\n\t\t\tCatalog: \"main\",\n\t\t},\n\t\t{\n\t\t\tSchema:  \"system\",\n\t\t\tCatalog: \"main\",\n\t\t},\n\t}), nil\n}\n\nfunc (r mockReader) Tables(f metadata.Filter) (*metadata.TableSet, error) {\n\treturn metadata.NewTableSet([]metadata.Table{\n\t\t{\n\t\t\tCatalog: f.Catalog,\n\t\t\tSchema:  f.Schema,\n\t\t\tName:    \"film\",\n\t\t},\n\t\t{\n\t\t\tCatalog: f.Catalog,\n\t\t\tSchema:  f.Schema,\n\t\t\tName:    \"factory\",\n\t\t},\n\t}), nil\n}\n\nfunc (r mockReader) Columns(f metadata.Filter) (*metadata.ColumnSet, error) {\n\tif f.Parent == \"film\" {\n\t\treturn metadata.NewColumnSet([]metadata.Column{\n\t\t\t{\n\t\t\t\tName: \"id\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"name\",\n\t\t\t},\n\t\t}), nil\n\t}\n\treturn metadata.NewColumnSet([]metadata.Column{\n\t\t{\n\t\t\tName: f.Catalog,\n\t\t},\n\t\t{\n\t\t\tName: f.Schema,\n\t\t},\n\t\t{\n\t\t\tName: f.Name,\n\t\t},\n\t}), nil\n}\n"
  },
  {
    "path": "drivers/cosmos/cosmos.go",
    "content": "// Package cosmos defines and registers usql's Azure CosmosDB driver.\n//\n// See: https://github.com/btnguyen2k/gocosmos\npackage cosmos\n\nimport (\n\t_ \"github.com/btnguyen2k/gocosmos\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"cosmos\", drivers.Driver{\n\t\tProcess: drivers.StripTrailingSemicolon,\n\t}, \"gocosmos\")\n}\n"
  },
  {
    "path": "drivers/couchbase/couchbase.go",
    "content": "// Package couchbase defines and registers usql's Couchbase driver.\n//\n// See: https://github.com/couchbase/go_n1ql\npackage couchbase\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"strings\"\n\n\t_ \"github.com/couchbase/go_n1ql\" // DRIVER: n1ql\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"n1ql\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tver := \"<unknown>\"\n\t\t\t/*\n\t\t\t\tvar buf []byte\n\t\t\t\tif err := db.QueryRowContext(ctx, `SELECT ds_version() AS version`).Scan(&buf); err == nil {\n\t\t\t\t\tvar m map[string]string\n\t\t\t\t\tif err := json.Unmarshal(buf, &m); err == nil {\n\t\t\t\t\t\tif s, ok := m[\"version\"]; ok {\n\t\t\t\t\t\t\tver = s\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t*/\n\t\t\tvar v string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT RAW ds_version()`).Scan(&v); err == nil {\n\t\t\t\tif s, err := strconv.Unquote(v); err == nil {\n\t\t\t\t\tver = s\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn \"Couchbase \" + ver, nil\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\treturn \"\", strings.TrimPrefix(err.Error(), \"N1QL: \")\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/csvq/csvq.go",
    "content": "// Package csvq defines and registers usql's CSVQ driver.\n//\n// See: https://github.com/mithrandie/csvq-driver\n// Group: base\npackage csvq\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/mithrandie/csvq-driver\" // DRIVER\n\t\"github.com/mithrandie/csvq/lib/query\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tcsvq.SetStdout(query.NewDiscard())\n\tdrivers.Register(\"csvq\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tProcess: func(_ *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) {\n\t\t\ttyp, q := drivers.QueryExecType(prefix, sqlstr)\n\t\t\tif strings.HasPrefix(prefix, \"SHOW\") {\n\t\t\t\tcsvq.SetStdout(os.Stdout)\n\t\t\t\tq = false\n\t\t\t}\n\t\t\treturn typ, sqlstr, q, nil\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT @#VERSION`).Scan(&ver); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"CSVQ \" + ver, nil\n\t\t},\n\t\tCopy: drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t})\n}\n"
  },
  {
    "path": "drivers/databend/databend.go",
    "content": "// Package databend defines and registers usql's Databend driver.\n//\n// See: https://github.com/datafuselabs/databend-go\npackage databend\n\nimport (\n\t\"io\"\n\n\t_ \"github.com/datafuselabs/databend-go\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n)\n\nfunc init() {\n\tnewReader := infos.New(\n\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\tinfos.SequenceColumnsIncrement: \"''\",\n\t\t}),\n\t\tinfos.WithFunctions(false),\n\t\tinfos.WithIndexes(false),\n\t\tinfos.WithConstraints(false),\n\t\tinfos.WithColumnPrivileges(false),\n\t)\n\tdrivers.Register(\"databend\", drivers.Driver{\n\t\tUseColumnTypes:    true,\n\t\tNewMetadataReader: newReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(newReader(db, opts...))(db, w)\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/databricks/databricks.go",
    "content": "// Package databricks defines and registers usql's Databricks driver.\n//\n// See: https://github.com/databricks/databricks-sql-go\npackage databricks\n\nimport (\n\t\"errors\"\n\n\t_ \"github.com/databricks/databricks-sql-go\" // DRIVER\n\tdberrs \"github.com/databricks/databricks-sql-go/errors\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"databricks\", drivers.Driver{\n\t\tErr: func(err error) (string, string) {\n\t\t\tvar e dberrs.DBExecutionError\n\t\t\tif errors.As(err, &e) {\n\t\t\t\treturn e.SqlState(), e.Error()\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/drivers.go",
    "content": "// Package drivers handles the registration, default implementation, and\n// handles hooks for usql database drivers.\npackage drivers\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\n\t\"github.com/alecthomas/chroma/v2\"\n\t\"github.com/alecthomas/chroma/v2/lexers\"\n\t\"github.com/gohxs/readline\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers/completer\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/stmt\"\n\t\"github.com/xo/usql/text\"\n)\n\n// DB is the common interface for database operations, compatible with\n// database/sql.DB and database/sql.Tx.\ntype DB interface {\n\tExec(string, ...interface{}) (sql.Result, error)\n\tExecContext(context.Context, string, ...interface{}) (sql.Result, error)\n\tQuery(string, ...interface{}) (*sql.Rows, error)\n\tQueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)\n\tQueryRow(string, ...interface{}) *sql.Row\n\tQueryRowContext(context.Context, string, ...interface{}) *sql.Row\n\tPrepare(string) (*sql.Stmt, error)\n\tPrepareContext(context.Context, string) (*sql.Stmt, error)\n}\n\n// Driver holds funcs for a driver.\ntype Driver struct {\n\t// Name is a name to override the driver name with.\n\tName string\n\t// AllowDollar will be passed to query buffers to enable dollar ($$) style\n\t// strings.\n\tAllowDollar bool\n\t// AllowMultilineComments will be passed to query buffers to enable\n\t// multiline (/**/) style comments.\n\tAllowMultilineComments bool\n\t// AllowCComments will be passed to query buffers to enable C (//) style\n\t// comments.\n\tAllowCComments bool\n\t// AllowHashComments will be passed to query buffers to enable hash (#)\n\t// style comments.\n\tAllowHashComments bool\n\t// RequirePreviousPassword will be used by RequirePreviousPassword.\n\tRequirePreviousPassword bool\n\t// LexerName is the name of the syntax lexer to use.\n\tLexerName string\n\t// LowerColumnNames will cause column names to be lowered cased.\n\tLowerColumnNames bool\n\t// UseColumnTypes will cause database's ColumnTypes func to be used for\n\t// types.\n\tUseColumnTypes bool\n\t// ForceParams will be used to force parameters if defined.\n\tForceParams func(*dburl.URL)\n\t// Open will be used by Open if defined.\n\tOpen func(context.Context, *dburl.URL, func() io.Writer, func() io.Writer) (func(string, string) (*sql.DB, error), error)\n\t// Version will be used by Version if defined.\n\tVersion func(context.Context, DB) (string, error)\n\t// User will be used by User if defined.\n\tUser func(context.Context, DB) (string, error)\n\t// ChangePassword will be used by ChangePassword if defined.\n\tChangePassword func(DB, string, string, string) error\n\t// IsPasswordErr will be used by IsPasswordErr if defined.\n\tIsPasswordErr func(error) bool\n\t// Process will be used by Process if defined.\n\tProcess func(*dburl.URL, string, string) (string, string, bool, error)\n\t// ColumnTypes is a callback that will be used if\n\tColumnTypes func(*sql.ColumnType) (interface{}, error)\n\t// RowsAffected will be used by RowsAffected if defined.\n\tRowsAffected func(sql.Result) (int64, error)\n\t// Err will be used by Error.Error if defined.\n\tErr func(error) (string, string)\n\t// ConvertBytes will be used by ConvertBytes to convert a raw []byte\n\t// slice to a string if defined.\n\tConvertBytes func([]byte, string) (string, error)\n\t// ConvertMap will be used by ConvertMap to convert a map[string]interface{}\n\t// to a string if defined.\n\tConvertMap func(map[string]interface{}) (string, error)\n\t// ConvertSlice will be used by ConvertSlice to convert a []interface{} to\n\t// a string if defined.\n\tConvertSlice func([]interface{}) (string, error)\n\t// ConvertDefault will be used by ConvertDefault to convert a interface{}\n\t// to a string if defined.\n\tConvertDefault func(interface{}) (string, error)\n\t// BatchAsTransaction will cause batched queries to be done in a\n\t// transaction block.\n\tBatchAsTransaction bool\n\t// BatchQueryPrefixes will be used by BatchQueryPrefixes if defined.\n\tBatchQueryPrefixes map[string]string\n\t// NewMetadataReader returns a db metadata introspector.\n\tNewMetadataReader func(db DB, opts ...metadata.ReaderOption) metadata.Reader\n\t// NewMetadataWriter returns a db metadata printer.\n\tNewMetadataWriter func(db DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer\n\t// NewCompleter returns a db auto-completer.\n\tNewCompleter func(db DB, opts ...completer.Option) readline.AutoCompleter\n\t// Copy rows into the database table\n\tCopy func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error)\n}\n\n// drivers are registered drivers.\nvar drivers = make(map[string]Driver)\n\n// Available returns the available drivers.\nfunc Available() map[string]Driver {\n\treturn drivers\n}\n\n// Register registers driver d with name and associated aliases.\nfunc Register(name string, d Driver, aliases ...string) {\n\tif _, ok := drivers[name]; ok {\n\t\tpanic(fmt.Sprintf(\"driver %s is already registered\", name))\n\t}\n\tdrivers[name] = d\n\tfor _, alias := range aliases {\n\t\tif _, ok := drivers[alias]; ok {\n\t\t\tpanic(fmt.Sprintf(\"alias %s is already registered\", name))\n\t\t}\n\t\tdrivers[alias] = d\n\t}\n}\n\n// Registered returns whether or not a driver is registered.\nfunc Registered(name string) bool {\n\t_, ok := drivers[name]\n\treturn ok\n}\n\n// LowerColumnNames returns whether or not column names should be converted to\n// lower case for a driver.\nfunc LowerColumnNames(u *dburl.URL) bool {\n\tif d, ok := drivers[u.Driver]; ok {\n\t\treturn d.LowerColumnNames\n\t}\n\treturn false\n}\n\n// UseColumnTypes returns whether or not a driver should uses column types.\nfunc UseColumnTypes(u *dburl.URL) bool {\n\tif d, ok := drivers[u.Driver]; ok {\n\t\treturn d.UseColumnTypes\n\t}\n\treturn false\n}\n\n// ForceParams forces parameters on the DSN for a driver.\nfunc ForceParams(u *dburl.URL) {\n\td, ok := drivers[u.Driver]\n\tif ok && d.ForceParams != nil {\n\t\td.ForceParams(u)\n\t}\n}\n\n// Open opens a sql.DB connection for a driver.\nfunc Open(ctx context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (*sql.DB, error) {\n\td, ok := drivers[u.Driver]\n\tif !ok {\n\t\treturn nil, WrapErr(u.Driver, text.ErrDriverNotAvailable)\n\t}\n\tf := sql.Open\n\tif d.Open != nil {\n\t\tvar err error\n\t\tif f, err = d.Open(ctx, u, stdout, stderr); err != nil {\n\t\t\treturn nil, WrapErr(u.Driver, err)\n\t\t}\n\t}\n\tdriver := u.Driver\n\tif u.GoDriver != \"\" {\n\t\tdriver = u.GoDriver\n\t}\n\tdb, err := f(driver, u.DSN)\n\tif err != nil {\n\t\treturn nil, WrapErr(u.Driver, err)\n\t}\n\treturn db, nil\n}\n\n// stmtOpts returns statement options for a driver.\nfunc stmtOpts(u *dburl.URL) []stmt.Option {\n\tif u != nil {\n\t\tif d, ok := drivers[u.Driver]; ok {\n\t\t\treturn []stmt.Option{\n\t\t\t\tstmt.WithAllowDollar(d.AllowDollar),\n\t\t\t\tstmt.WithAllowMultilineComments(d.AllowMultilineComments),\n\t\t\t\tstmt.WithAllowCComments(d.AllowCComments),\n\t\t\t\tstmt.WithAllowHashComments(d.AllowHashComments),\n\t\t\t}\n\t\t}\n\t}\n\treturn []stmt.Option{\n\t\tstmt.WithAllowDollar(true),\n\t\tstmt.WithAllowMultilineComments(true),\n\t\tstmt.WithAllowCComments(true),\n\t\tstmt.WithAllowHashComments(true),\n\t}\n}\n\n// NewStmt wraps creating a new stmt.Stmt for a driver.\nfunc NewStmt(u *dburl.URL, f func() ([]rune, error), opts ...stmt.Option) *stmt.Stmt {\n\treturn stmt.New(f, append(opts, stmtOpts(u)...)...)\n}\n\n// ConfigStmt sets the stmt.Stmt options for a driver.\nfunc ConfigStmt(u *dburl.URL, s *stmt.Stmt) {\n\tif u == nil {\n\t\treturn\n\t}\n\tfor _, o := range stmtOpts(u) {\n\t\to(s)\n\t}\n}\n\n// Version returns information about the database connection for a driver.\nfunc Version(ctx context.Context, u *dburl.URL, db DB) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.Version != nil {\n\t\tver, err := d.Version(ctx, db)\n\t\treturn ver, WrapErr(u.Driver, err)\n\t}\n\tvar ver string\n\terr := db.QueryRowContext(ctx, `SELECT version();`).Scan(&ver)\n\tif err != nil || ver == \"\" {\n\t\tver = \"<unknown>\"\n\t}\n\treturn ver, nil\n}\n\n// User returns the current database user for a driver.\nfunc User(ctx context.Context, u *dburl.URL, db DB) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.User != nil {\n\t\tuser, err := d.User(ctx, db)\n\t\treturn user, WrapErr(u.Driver, err)\n\t}\n\tvar user string\n\t_ = db.QueryRowContext(ctx, `SELECT current_user`).Scan(&user)\n\treturn user, nil\n}\n\n// Process processes the sql query for a driver.\nfunc Process(u *dburl.URL, prefix, sqlstr string) (string, string, bool, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.Process != nil {\n\t\ta, b, c, err := d.Process(u, prefix, sqlstr)\n\t\treturn a, b, c, WrapErr(u.Driver, err)\n\t}\n\ttyp, q := QueryExecType(prefix, sqlstr)\n\treturn typ, sqlstr, q, nil\n}\n\n// ColumnTypes returns the column types callback for a driver.\nfunc ColumnTypes(u *dburl.URL) func(*sql.ColumnType) (interface{}, error) {\n\treturn drivers[u.Driver].ColumnTypes\n}\n\n// IsPasswordErr returns true if an err is a password error for a driver.\nfunc IsPasswordErr(u *dburl.URL, err error) bool {\n\tdrv := u.Driver\n\tif e, ok := err.(*Error); ok {\n\t\tdrv, err = e.Driver, e.Err\n\t}\n\tif d, ok := drivers[drv]; ok && d.IsPasswordErr != nil {\n\t\treturn d.IsPasswordErr(err)\n\t}\n\treturn false\n}\n\n// RequirePreviousPassword returns true if a driver requires a previous\n// password when changing a user's password.\nfunc RequirePreviousPassword(u *dburl.URL) bool {\n\tif d, ok := drivers[u.Driver]; ok {\n\t\treturn d.RequirePreviousPassword\n\t}\n\treturn false\n}\n\n// CanChangePassword returns whether or not the a driver supports changing\n// passwords.\nfunc CanChangePassword(u *dburl.URL) error {\n\tif d, ok := drivers[u.Driver]; ok && d.ChangePassword != nil {\n\t\treturn nil\n\t}\n\treturn text.ErrPasswordNotSupportedByDriver\n}\n\n// ChangePassword initiates a user password change for the a driver. If user is\n// not supplied, then the current user will be retrieved from User.\nfunc ChangePassword(u *dburl.URL, db DB, user, new, old string) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.ChangePassword != nil {\n\t\tif user == \"\" {\n\t\t\tvar err error\n\t\t\tif user, err = User(context.Background(), u, db); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\t\treturn user, d.ChangePassword(db, user, new, old)\n\t}\n\treturn \"\", text.ErrPasswordNotSupportedByDriver\n}\n\n// Columns returns the column names for the SQL row result for a driver.\nfunc Columns(u *dburl.URL, rows *sql.Rows) ([]string, error) {\n\tcols, err := rows.Columns()\n\tif err != nil {\n\t\treturn nil, WrapErr(u.Driver, err)\n\t}\n\tif drivers[u.Driver].LowerColumnNames {\n\t\tfor i, s := range cols {\n\t\t\tif j := strings.IndexFunc(s, func(r rune) bool {\n\t\t\t\treturn unicode.IsLetter(r) && unicode.IsLower(r)\n\t\t\t}); j == -1 {\n\t\t\t\tcols[i] = strings.ToLower(s)\n\t\t\t}\n\t\t}\n\t}\n\tfor i, c := range cols {\n\t\tif strings.TrimSpace(c) == \"\" {\n\t\t\tcols[i] = fmt.Sprintf(\"col%d\", i)\n\t\t}\n\t}\n\treturn cols, nil\n}\n\n// ConvertBytes returns a func to handle converting bytes for a driver.\nfunc ConvertBytes(u *dburl.URL) func([]byte, string) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.ConvertBytes != nil {\n\t\treturn d.ConvertBytes\n\t}\n\treturn func(buf []byte, _ string) (string, error) {\n\t\treturn string(buf), nil\n\t}\n}\n\n// ConvertMap returns a func to handle converting a map[string]interface{} for\n// a driver.\nfunc ConvertMap(u *dburl.URL) func(map[string]interface{}) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.ConvertMap != nil {\n\t\treturn d.ConvertMap\n\t}\n\treturn func(v map[string]interface{}) (string, error) {\n\t\tbuf, err := json.Marshal(v)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn string(buf), nil\n\t}\n}\n\n// ConvertSlice returns a func to handle converting a []interface{} for a\n// driver.\nfunc ConvertSlice(u *dburl.URL) func([]interface{}) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.ConvertSlice != nil {\n\t\treturn d.ConvertSlice\n\t}\n\treturn func(v []interface{}) (string, error) {\n\t\tbuf, err := json.Marshal(v)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn string(buf), nil\n\t}\n}\n\n// ConvertDefault returns a func to handle converting a interface{} for a\n// driver.\nfunc ConvertDefault(u *dburl.URL) func(interface{}) (string, error) {\n\tif d, ok := drivers[u.Driver]; ok && d.ConvertDefault != nil {\n\t\treturn d.ConvertDefault\n\t}\n\treturn func(v interface{}) (string, error) {\n\t\treturn fmt.Sprintf(\"%v\", v), nil\n\t}\n}\n\n// BatchAsTransaction returns whether or not a driver requires batched queries\n// to be done within a transaction block.\nfunc BatchAsTransaction(u *dburl.URL) bool {\n\tif d, ok := drivers[u.Driver]; ok {\n\t\treturn d.BatchAsTransaction\n\t}\n\treturn false\n}\n\n// IsBatchQueryPrefix returns whether or not the supplied query prefix is a\n// batch query prefix, and the closing prefix. Used to direct the handler to\n// continue accumulating statements.\nfunc IsBatchQueryPrefix(u *dburl.URL, prefix string) (string, string, bool) {\n\t// normalize\n\ttyp, q := QueryExecType(prefix, \"\")\n\td, ok := drivers[u.Driver]\n\tif q || !ok || d.BatchQueryPrefixes == nil {\n\t\treturn typ, \"\", false\n\t}\n\tend, ok := d.BatchQueryPrefixes[typ]\n\treturn typ, end, ok\n}\n\n// RowsAffected returns the rows affected for the SQL result for a driver.\nfunc RowsAffected(u *dburl.URL, res sql.Result) (int64, error) {\n\tvar count int64\n\tvar err error\n\tif d, ok := drivers[u.Driver]; ok && d.RowsAffected != nil {\n\t\tcount, err = d.RowsAffected(res)\n\t} else {\n\t\tcount, err = res.RowsAffected()\n\t}\n\tif err != nil && err.Error() == \"no RowsAffected available after DDL statement\" {\n\t\treturn 0, nil\n\t}\n\tif err != nil {\n\t\treturn 0, WrapErr(u.Driver, err)\n\t}\n\treturn count, nil\n}\n\n// Ping pings the database for a driver.\nfunc Ping(ctx context.Context, u *dburl.URL, db *sql.DB) error {\n\treturn WrapErr(u.Driver, db.PingContext(ctx))\n}\n\n// Lexer returns the syntax lexer for a driver.\nfunc Lexer(u *dburl.URL) chroma.Lexer {\n\tvar l chroma.Lexer\n\tif u != nil {\n\t\tif d, ok := drivers[u.Driver]; ok && d.LexerName != \"\" {\n\t\t\tl = lexers.Get(d.LexerName)\n\t\t}\n\t}\n\tif l == nil {\n\t\tl = lexers.Get(\"sql\")\n\t}\n\tl.Config().EnsureNL = false\n\treturn l\n}\n\n// ForceQueryParameters is a utility func that wraps forcing params of name,\n// value pairs.\nfunc ForceQueryParameters(params []string) func(*dburl.URL) {\n\tif len(params)%2 != 0 {\n\t\tpanic(\"invalid query params\")\n\t}\n\treturn func(u *dburl.URL) {\n\t\tif len(params) != 0 {\n\t\t\tv := u.Query()\n\t\t\tfor i := 0; i < len(params); i += 2 {\n\t\t\t\tv.Set(params[i], params[i+1])\n\t\t\t}\n\t\t\tu.RawQuery = v.Encode()\n\t\t}\n\t}\n}\n\n// NewMetadataReader wraps creating a new database introspector for a driver.\nfunc NewMetadataReader(ctx context.Context, u *dburl.URL, db DB, w io.Writer, opts ...metadata.ReaderOption) (metadata.Reader, error) {\n\td, ok := drivers[u.Driver]\n\tif !ok || d.NewMetadataReader == nil {\n\t\treturn nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`, u.Driver)\n\t}\n\treturn d.NewMetadataReader(db, opts...), nil\n}\n\n// NewMetadataWriter wraps creating a new database metadata printer for a driver.\nfunc NewMetadataWriter(ctx context.Context, u *dburl.URL, db DB, w io.Writer, opts ...metadata.ReaderOption) (metadata.Writer, error) {\n\td, ok := drivers[u.Driver]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`, u.Driver)\n\t}\n\tif d.NewMetadataWriter != nil {\n\t\treturn d.NewMetadataWriter(db, w, opts...), nil\n\t}\n\tif d.NewMetadataReader == nil {\n\t\treturn nil, fmt.Errorf(text.NotSupportedByDriver, `describe commands`, u.Driver)\n\t}\n\tnewMetadataWriter := metadata.NewDefaultWriter(d.NewMetadataReader(db, opts...))\n\treturn newMetadataWriter(db, w), nil\n}\n\n// NewCompleter creates a metadata completer for a driver and database\n// connection.\nfunc NewCompleter(ctx context.Context, u *dburl.URL, db DB, readerOpts []metadata.ReaderOption, opts ...completer.Option) readline.AutoCompleter {\n\td, ok := drivers[u.Driver]\n\tif !ok {\n\t\treturn nil\n\t}\n\tif d.NewCompleter != nil {\n\t\treturn d.NewCompleter(db, opts...)\n\t}\n\tif d.NewMetadataReader == nil {\n\t\treturn nil\n\t}\n\t// prepend to allow to override default options\n\treaderOpts = append([]metadata.ReaderOption{\n\t\t// this needs to be relatively low, since autocomplete is very interactive\n\t\tmetadata.WithTimeout(3 * time.Second),\n\t\tmetadata.WithLimit(1000),\n\t}, readerOpts...)\n\topts = append([]completer.Option{\n\t\tcompleter.WithReader(d.NewMetadataReader(db, readerOpts...)),\n\t\tcompleter.WithDB(db),\n\t}, opts...)\n\treturn completer.NewDefaultCompleter(opts...)\n}\n\n// Copy copies the result set to the destination sql.DB.\nfunc Copy(ctx context.Context, u *dburl.URL, stdout, stderr func() io.Writer, rows *sql.Rows, table string) (int64, error) {\n\td, ok := drivers[u.Driver]\n\tif !ok {\n\t\treturn 0, WrapErr(u.Driver, text.ErrDriverNotAvailable)\n\t}\n\tif d.Copy == nil {\n\t\treturn 0, fmt.Errorf(text.NotSupportedByDriver, \"copy\", u.Driver)\n\t}\n\tdb, err := Open(ctx, u, stdout, stderr)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer db.Close()\n\treturn d.Copy(ctx, db, rows, table)\n}\n\n// CopyWithInsert builds a typical copy handler based on insert.\nfunc CopyWithInsert(placeholder func(int) string) func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) {\n\tif placeholder == nil {\n\t\tplaceholder = func(n int) string { return fmt.Sprintf(\"$%d\", n) }\n\t}\n\treturn func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) {\n\t\treturn FlexibleCopyWithInsert(ctx, db, rows, table, placeholder, true)\n\t}\n}\n\nfunc FlexibleCopyWithInsert(ctx context.Context, db *sql.DB, rows *sql.Rows, table string, placeholder func(int) string, withTransaction bool) (int64, error) {\n\tcolumns, err := rows.Columns()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to fetch source rows columns: %w\", err)\n\t}\n\tclen := len(columns)\n\tquery := table\n\tif !strings.HasPrefix(strings.ToLower(query), \"insert into\") {\n\t\tleftParen := strings.IndexRune(table, '(')\n\t\tif leftParen == -1 {\n\t\t\tcolRows, err := db.QueryContext(ctx, \"SELECT * FROM \"+table+\" WHERE 1=0\")\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to execute query to determine target table columns: %w\", err)\n\t\t\t}\n\t\t\tcolumns, err := colRows.Columns()\n\t\t\t_ = colRows.Close()\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to fetch target table columns: %w\", err)\n\t\t\t}\n\t\t\ttable += \"(\" + strings.Join(columns, \", \") + \")\"\n\t\t}\n\t\t// TODO if the db supports multiple rows per insert, create batches of 100 rows\n\t\tplaceholders := make([]string, clen)\n\t\tfor i := 0; i < clen; i++ {\n\t\t\tplaceholders[i] = placeholder(i + 1)\n\t\t}\n\t\tquery = \"INSERT INTO \" + table + \" VALUES (\" + strings.Join(placeholders, \", \") + \")\"\n\t}\n\tvar stmt *sql.Stmt\n\tvar tx *sql.Tx\n\tif withTransaction {\n\t\ttx, err = db.BeginTx(ctx, nil)\n\t\tif err != nil {\n\t\t\treturn 0, fmt.Errorf(\"failed to begin transaction: %w\", err)\n\t\t}\n\t\tstmt, err = tx.PrepareContext(ctx, query)\n\t} else {\n\t\tstmt, err = db.PrepareContext(ctx, query)\n\t}\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to prepare insert query: %w\", err)\n\t}\n\tdefer stmt.Close()\n\tcolumnTypes, err := rows.ColumnTypes()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to fetch source column types: %w\", err)\n\t}\n\tvalues := make([]interface{}, clen)\n\tvalueRefs := make([]reflect.Value, clen)\n\tactuals := make([]interface{}, clen)\n\tfor i := 0; i < len(columnTypes); i++ {\n\t\tvalueRefs[i] = reflect.New(columnTypes[i].ScanType())\n\t\tvalues[i] = valueRefs[i].Interface()\n\t}\n\tvar n int64\n\tfor rows.Next() {\n\t\terr = rows.Scan(values...)\n\t\tif err != nil {\n\t\t\treturn n, fmt.Errorf(\"failed to scan row: %w\", err)\n\t\t}\n\t\t//We can't use values... in Exec() below, because some drivers\n\t\t//don't accept pointer to an argument instead of the arg itself.\n\t\tfor i := range values {\n\t\t\tactuals[i] = valueRefs[i].Elem().Interface()\n\t\t}\n\t\tres, err := stmt.ExecContext(ctx, actuals...)\n\t\tif err != nil {\n\t\t\treturn n, fmt.Errorf(\"failed to exec insert: %w\", err)\n\t\t}\n\t\trn, err := res.RowsAffected()\n\t\tif err != nil {\n\t\t\treturn n, fmt.Errorf(\"failed to check rows affected: %w\", err)\n\t\t}\n\t\tn += rn\n\t}\n\t// TODO if using batches, flush the last batch,\n\t// TODO prepare another statement and count remaining rows\n\tif tx != nil {\n\t\terr = tx.Commit()\n\t\tif err != nil {\n\t\t\treturn n, fmt.Errorf(\"failed to commit transaction: %w\", err)\n\t\t}\n\t}\n\treturn n, rows.Err()\n}\n\nfunc init() {\n\tdburl.OdbcIgnoreQueryPrefixes = []string{\"usql_\"}\n}\n\nvar endRE = regexp.MustCompile(`;?\\s*$`)\n\nfunc StripTrailingSemicolon(_ *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) {\n\tsqlstr = endRE.ReplaceAllString(sqlstr, \"\")\n\ttyp, q := QueryExecType(prefix, sqlstr)\n\treturn typ, sqlstr, q, nil\n}\n"
  },
  {
    "path": "drivers/drivers_test.go",
    "content": "// Package drivers_test runs integration tests for drivers package\n// on real databases running in containers. During development, to avoid rebuilding\n// containers every run, add the `-cleanup=false` flags when calling `go test github.com/xo/usql/drivers`.\npackage drivers_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/url\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\tdt \"github.com/ory/dockertest/v3\"\n\tdc \"github.com/ory/dockertest/v3/docker\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t_ \"github.com/xo/usql/internal\"\n)\n\ntype Database struct {\n\tBuildArgs  []dc.BuildArg\n\tRunOptions *dt.RunOptions\n\tDSN        string\n\tReadyDSN   string\n\tExec       []string\n\n\tDockerPort string\n\tResource   *dt.Resource\n\tURL        *dburl.URL\n\tDB         *sql.DB\n}\n\nconst (\n\tpw = \"yourStrong123_Password\"\n)\n\nvar (\n\tdbs = map[string]*Database{\n\t\t\"pgsql\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"postgres:13\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/postgres-sakila-db/postgres-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/docker-entrypoint-initdb.d\"},\n\t\t\t\t{Name: \"USER\", Value: \"root\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-pgsql\",\n\t\t\t\tCmd:  []string{\"-c\", \"log_statement=all\", \"-c\", \"log_min_duration_statement=0\"},\n\t\t\t\tEnv:  []string{\"POSTGRES_PASSWORD=pw\"},\n\t\t\t},\n\t\t\tDSN:        \"postgres://postgres:pw@localhost:%s/postgres?sslmode=disable\",\n\t\t\tDockerPort: \"5432/tcp\",\n\t\t},\n\t\t\"pgx\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"postgres:13\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/postgres-sakila-db/postgres-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/docker-entrypoint-initdb.d\"},\n\t\t\t\t{Name: \"USER\", Value: \"root\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-pgsql\",\n\t\t\t\tCmd:  []string{\"-c\", \"log_statement=all\", \"-c\", \"log_min_duration_statement=0\"},\n\t\t\t\tEnv:  []string{\"POSTGRES_PASSWORD=pw\"},\n\t\t\t},\n\t\t\tDSN:        \"pgx://postgres:pw@localhost:%s/postgres?sslmode=disable\",\n\t\t\tDockerPort: \"5432/tcp\",\n\t\t},\n\t\t\"mysql\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"mysql:8\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/mysql-sakila-db/mysql-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/docker-entrypoint-initdb.d\"},\n\t\t\t\t{Name: \"USER\", Value: \"root\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-mysql\",\n\t\t\t\tCmd:  []string{\"--general-log=1\", \"--general-log-file=/var/lib/mysql/mysql.log\"},\n\t\t\t\tEnv:  []string{\"MYSQL_ROOT_PASSWORD=pw\"},\n\t\t\t},\n\t\t\tDSN:        \"mysql://root:pw@localhost:%s/sakila?parseTime=true\",\n\t\t\tDockerPort: \"3306/tcp\",\n\t\t},\n\t\t\"sqlserver\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"mcr.microsoft.com/mssql/server:2019-latest\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/sql-server-sakila-db/sql-server-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/schema\"},\n\t\t\t\t{Name: \"USER\", Value: \"mssql:0\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-sqlserver\",\n\t\t\t\tEnv:  []string{\"ACCEPT_EULA=Y\", \"SA_PASSWORD=\" + pw},\n\t\t\t},\n\t\t\tDSN:        \"sqlserver://sa:\" + url.QueryEscape(pw) + \"@127.0.0.1:%s?database=sakila\",\n\t\t\tReadyDSN:   \"sqlserver://sa:\" + url.QueryEscape(pw) + \"@127.0.0.1:%s?database=master\",\n\t\t\tExec:       []string{\"/opt/mssql-tools/bin/sqlcmd\", \"-S\", \"localhost\", \"-U\", \"sa\", \"-P\", pw, \"-d\", \"master\", \"-i\", \"/schema/sql-server-sakila-schema.sql\"},\n\t\t\tDockerPort: \"1433/tcp\",\n\t\t},\n\t\t\"trino\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"trinodb/trino:359\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-trino\",\n\t\t\t},\n\t\t\tDSN:        \"trino://test@localhost:%s/tpch/sf1\",\n\t\t\tDockerPort: \"8080/tcp\",\n\t\t},\n\t\t\"csvq\": {\n\t\t\t// go test sets working directory to current package regardless of initial working directory\n\t\t\tDSN: \"csvq://./testdata/csvq\",\n\t\t},\n\t}\n\tcleanup bool\n)\n\nfunc TestMain(m *testing.M) {\n\tvar only string\n\tflag.BoolVar(&cleanup, \"cleanup\", true, \"delete containers when finished\")\n\tflag.StringVar(&only, \"dbs\", \"\", \"comma separated list of dbs to test: pgsql, mysql, sqlserver, trino\")\n\tflag.Parse()\n\n\tif only != \"\" {\n\t\trunOnly := map[string]struct{}{}\n\t\tfor _, dbName := range strings.Split(only, \",\") {\n\t\t\tdbName = strings.TrimSpace(dbName)\n\t\t\trunOnly[dbName] = struct{}{}\n\t\t}\n\t\tfor dbName := range dbs {\n\t\t\tif _, ok := runOnly[dbName]; !ok {\n\t\t\t\tdelete(dbs, dbName)\n\t\t\t}\n\t\t}\n\t}\n\n\tpool, err := dt.NewPool(\"\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not connect to docker: %s\", err)\n\t}\n\n\tfor dbName, db := range dbs {\n\t\tdsn, hostPort := getConnInfo(dbName, db, pool)\n\t\tdb.URL, err = dburl.Parse(dsn)\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Failed to parse %s URL %s: %v\", dbName, db.DSN, err)\n\t\t}\n\n\t\tif len(db.Exec) != 0 {\n\t\t\treadyDSN := db.ReadyDSN\n\t\t\tif db.ReadyDSN == \"\" {\n\t\t\t\treadyDSN = db.DSN\n\t\t\t}\n\t\t\tif hostPort != \"\" {\n\t\t\t\treadyDSN = fmt.Sprintf(db.ReadyDSN, hostPort)\n\t\t\t}\n\t\t\treadyURL, err := dburl.Parse(readyDSN)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Failed to parse %s ready URL %s: %v\", dbName, db.ReadyDSN, err)\n\t\t\t}\n\t\t\tif err := pool.Retry(func() error {\n\t\t\t\treadyDB, err := drivers.Open(context.Background(), readyURL, nil, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn readyDB.Ping()\n\t\t\t}); err != nil {\n\t\t\t\tlog.Fatalf(\"Timed out waiting for %s to be ready: %s\", dbName, err)\n\t\t\t}\n\t\t\t// No TTY attached to facilitate debugging with delve\n\t\t\texitCode, err := db.Resource.Exec(db.Exec, dt.ExecOptions{})\n\t\t\tif err != nil || exitCode != 0 {\n\t\t\t\tlog.Fatalf(\"Could not load schema for %s: %s\", dbName, err)\n\t\t\t}\n\t\t}\n\n\t\t// exponential backoff-retry, because the application in the container might not be ready to accept connections yet\n\t\tvar openErr error\n\t\tif retryErr := pool.Retry(func() error {\n\t\t\tdb.DB, openErr = drivers.Open(context.Background(), db.URL, nil, nil)\n\t\t\tif openErr != nil {\n\t\t\t\treturn openErr\n\t\t\t}\n\t\t\treturn db.DB.Ping()\n\t\t}); retryErr != nil {\n\t\t\tlog.Fatalf(\"Timed out waiting for %s:\\n%s\\n%s\", dbName, retryErr, openErr)\n\t\t}\n\t}\n\n\tcode := m.Run()\n\n\t// You can't defer this because os.Exit doesn't care for defer\n\tif cleanup {\n\t\tfor _, db := range dbs {\n\t\t\tif db.Resource != nil {\n\t\t\t\tif err := pool.Purge(db.Resource); err != nil {\n\t\t\t\t\tlog.Fatal(\"Could not purge resource: \", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tos.Exit(code)\n}\n\nfunc getConnInfo(dbName string, db *Database, pool *dt.Pool) (string, string) {\n\tif db.RunOptions == nil {\n\t\treturn db.DSN, \"\"\n\t}\n\n\tvar ok bool\n\tdb.Resource, ok = pool.ContainerByName(db.RunOptions.Name)\n\tif ok && !db.Resource.Container.State.Running {\n\t\terr := db.Resource.Close()\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Failed to clean up stale container %s: %s\", dbName, err)\n\t\t}\n\t\tok = false\n\t}\n\tif !ok {\n\t\tbuildOpts := &dt.BuildOptions{\n\t\t\tContextDir: \"./testdata/docker\",\n\t\t\tBuildArgs:  db.BuildArgs,\n\t\t}\n\t\tvar err error\n\t\tdb.Resource, err = pool.BuildAndRunWithBuildOptions(buildOpts, db.RunOptions)\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Failed to start %s: %s\", dbName, err)\n\t\t}\n\t}\n\thostPort := db.Resource.GetPort(db.DockerPort)\n\treturn fmt.Sprintf(db.DSN, hostPort), hostPort\n}\n\nfunc TestWriter(t *testing.T) {\n\ttype testFunc struct {\n\t\tlabel  string\n\t\tf      func(w metadata.Writer, u *dburl.URL) error\n\t\tignore string\n\t}\n\ttestCases := []struct {\n\t\tdbName string\n\t\tfuncs  []testFunc\n\t}{\n\t\t{\n\t\t\tdbName: \"pgsql\",\n\t\t\tfuncs: []testFunc{\n\t\t\t\t{\n\t\t\t\t\tlabel: \"descTable\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeTableDetails(u, \"film*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listTables\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListTables(u, \"tvmsE\", \"film*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listFuncs\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeFunctions(u, \"\", \"\", false, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listIndexes\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListIndexes(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listSchemas\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListSchemas(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdbName: \"mysql\",\n\t\t\tfuncs: []testFunc{\n\t\t\t\t{\n\t\t\t\t\tlabel: \"descTable\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeTableDetails(u, \"film*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listTables\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListTables(u, \"tvmsE\", \"film*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listFuncs\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeFunctions(u, \"\", \"\", false, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listIndexes\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListIndexes(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listSchemas\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListSchemas(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdbName: \"sqlserver\",\n\t\t\tfuncs: []testFunc{\n\t\t\t\t{\n\t\t\t\t\tlabel: \"descTable\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeTableDetails(u, \"film*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t\t// primary key indices get random names; ignore them\n\t\t\t\t\tignore: \"PK__.*__.{16}\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listTables\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListTables(u, \"tvmsE\", \"film*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listFuncs\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeFunctions(u, \"\", \"\", false, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listIndexes\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListIndexes(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t\t// primary key indices get random names; ignore them\n\t\t\t\t\tignore: \"PK__.*__.{16}\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listSchemas\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListSchemas(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdbName: \"trino\",\n\t\t\tfuncs: []testFunc{\n\t\t\t\t{\n\t\t\t\t\tlabel: \"descTable\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.DescribeTableDetails(u, \"order*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listTables\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListTables(u, \"tvmsE\", \"order*\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"listSchemas\",\n\t\t\t\t\tf: func(w metadata.Writer, u *dburl.URL) error {\n\t\t\t\t\t\treturn w.ListSchemas(u, \"\", true, false)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, test := range testCases {\n\t\tfor _, testFunc := range test.funcs {\n\t\t\tactual := fmt.Sprintf(\"testdata/%s.%s.actual.txt\", test.dbName, testFunc.label)\n\t\t\tfo, err := os.Create(actual)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Cannot create results file %s: %v\", actual, err)\n\t\t\t}\n\n\t\t\tdb, ok := dbs[test.dbName]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tw, err := drivers.NewMetadataWriter(context.Background(), db.URL, db.DB, fo)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Could not create writer %s %s: %v\", test.dbName, testFunc.label, err)\n\t\t\t}\n\n\t\t\terr = testFunc.f(w, db.URL)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Could not write %s %s: %v\", test.dbName, testFunc.label, err)\n\t\t\t}\n\t\t\terr = fo.Close()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Cannot close results file %s: %v\", actual, err)\n\t\t\t}\n\n\t\t\texpected := fmt.Sprintf(\"testdata/%s.%s.expected.txt\", test.dbName, testFunc.label)\n\t\t\terr = filesEqual(expected, actual, testFunc.ignore)\n\t\t\tif err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestCopy(t *testing.T) {\n\tpg, ok := dbs[\"pgsql\"]\n\tif !ok {\n\t\tt.Skip(\"Skipping copy tests, as they require PostgreSQL which was not selected for tests\")\n\t}\n\t// setup test data, ignoring errors, since there'll be duplicates\n\t_, _ = pg.DB.Exec(\"ALTER TABLE staff DROP CONSTRAINT staff_address_id_fkey\")\n\t_, _ = pg.DB.Exec(\"ALTER TABLE staff DROP CONSTRAINT staff_store_id_fkey\")\n\t_, _ = pg.DB.Exec(\"INSERT INTO staff VALUES (1, 'John', 'Doe', 1, 'john@invalid.com', 1, true, 'jdoe', 'abc', now(), 'abcd')\")\n\n\ttype setupQuery struct {\n\t\tquery string\n\t\tcheck bool\n\t}\n\n\ttestCases := []struct {\n\t\tdbName       string\n\t\tsetupQueries []setupQuery\n\t\tsrc          string\n\t\tdest         string\n\t}{\n\t\t{\n\t\t\tdbName: \"pgsql\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"DROP TABLE staff_copy\"},\n\t\t\t\t{query: \"CREATE TABLE staff_copy AS SELECT * FROM staff WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select * from staff\",\n\t\t\tdest: \"staff_copy\",\n\t\t},\n\t\t{\n\t\t\tdbName: \"pgsql\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"DROP TABLE staff_copy\"},\n\t\t\t\t{query: \"CREATE TABLE staff_copy AS SELECT * FROM staff WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select * from staff\",\n\t\t\tdest: \"public.staff_copy\",\n\t\t},\n\t\t{\n\t\t\tdbName: \"pgx\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"DROP TABLE staff_copy\"},\n\t\t\t\t{query: \"CREATE TABLE staff_copy AS SELECT * FROM staff WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select * from staff\",\n\t\t\tdest: \"staff_copy\",\n\t\t},\n\t\t{\n\t\t\tdbName: \"pgx\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"DROP TABLE staff_copy\"},\n\t\t\t\t{query: \"CREATE TABLE staff_copy AS SELECT * FROM staff WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select * from staff\",\n\t\t\tdest: \"public.staff_copy\",\n\t\t},\n\t\t{\n\t\t\tdbName: \"mysql\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"DROP TABLE staff_copy\"},\n\t\t\t\t{query: \"CREATE TABLE staff_copy AS SELECT * FROM staff WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select staff_id, first_name, last_name, address_id, picture, email, store_id, active, username, password, last_update from staff\",\n\t\t\tdest: \"staff_copy(staff_id, first_name, last_name, address_id, picture, email, store_id, active, username, password, last_update)\",\n\t\t},\n\t\t{\n\t\t\tdbName: \"sqlserver\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"DROP TABLE staff_copy\"},\n\t\t\t\t{query: \"SELECT * INTO staff_copy FROM staff WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select first_name, last_name, address_id, picture, email, store_id, active, username, password, last_update from staff\",\n\t\t\tdest: \"staff_copy(first_name, last_name, address_id, picture, email, store_id, active, username, password, last_update)\",\n\t\t},\n\t\t{\n\t\t\tdbName: \"csvq\",\n\t\t\tsetupQueries: []setupQuery{\n\t\t\t\t{query: \"CREATE TABLE IF NOT EXISTS staff_copy AS SELECT * FROM `staff.csv` WHERE 0=1\", check: true},\n\t\t\t},\n\t\t\tsrc:  \"select first_name, last_name, address_id, email, store_id, active, username, password, last_update from staff\",\n\t\t\tdest: \"staff_copy\",\n\t\t},\n\t}\n\tfor _, test := range testCases {\n\t\tdb, ok := dbs[test.dbName]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tt.Run(test.dbName, func(t *testing.T) {\n\n\t\t\t// TODO test copy from a different DB, maybe csvq?\n\t\t\t// TODO test copy from same DB\n\n\t\t\tfor _, q := range test.setupQueries {\n\t\t\t\t_, err := db.DB.Exec(q.query)\n\t\t\t\tif q.check && err != nil {\n\t\t\t\t\tt.Fatalf(\"Failed to run setup query `%s`: %v\", q.query, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\trows, err := pg.DB.Query(test.src)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Could not get rows to copy: %v\", err)\n\t\t\t}\n\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\t\tdefer cancel()\n\t\t\tvar rlen int64 = 1\n\t\t\tn, err := drivers.Copy(ctx, db.URL, nil, nil, rows, test.dest)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Could not copy: %v\", err)\n\t\t\t}\n\t\t\tif n != rlen {\n\t\t\t\tt.Fatalf(\"Expected to copy %d rows but got %d\", rlen, n)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// filesEqual compares the files at paths a and b and returns an error if\n// the content is not equal. Ignore is a regex. All matches will be removed\n// from the file contents before comparison.\nfunc filesEqual(a, b, ignore string) error {\n\t// per comment, better to not read an entire file into memory\n\t// this is simply a trivial example.\n\tf1, err := os.ReadFile(a)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Cannot read file %s: %w\", a, err)\n\t}\n\n\tf2, err := os.ReadFile(b)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Cannot read file %s: %w\", b, err)\n\t}\n\n\tif ignore != \"\" {\n\t\treg, err := regexp.Compile(ignore)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Cannot compile regex (%s): %w\", ignore, err)\n\t\t}\n\t\tf1 = reg.ReplaceAllLiteral(f1, []byte{})\n\t\tf2 = reg.ReplaceAllLiteral(f2, []byte{})\n\t}\n\n\tif !bytes.Equal(f1, f2) {\n\t\treturn fmt.Errorf(\"Files %s and %s have different contents\", a, b)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "drivers/duckdb/duckdb.go",
    "content": "// Package duckdb defines and registers usql's DuckDB driver. Requires CGO.\n//\n// See: https://github.com/duckdb/duckdb-go\npackage duckdb\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t_ \"github.com/duckdb/duckdb-go/v2\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n\tmymeta \"github.com/xo/usql/drivers/metadata/mysql\"\n)\n\ntype metaReader struct {\n\tmetadata.LoggingReader\n}\n\nvar (\n\t_ metadata.CatalogReader    = &metaReader{}\n\t_ metadata.ColumnStatReader = &metaReader{}\n)\n\nfunc (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) {\n\tqstr := `SHOW catalogs`\n\trows, closeRows, err := r.Query(qstr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Catalog{}\n\tfor rows.Next() {\n\t\trec := metadata.Catalog{}\n\t\terr = rows.Scan(&rec.Catalog)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewCatalogSet(results), nil\n}\n\nfunc (r metaReader) ColumnStats(f metadata.Filter) (*metadata.ColumnStatSet, error) {\n\tnames := []string{}\n\tif f.Catalog != \"\" {\n\t\tnames = append(names, f.Catalog+\".\")\n\t}\n\tif f.Schema != \"\" {\n\t\tnames = append(names, f.Schema+\".\")\n\t}\n\tnames = append(names, f.Parent)\n\trows, closeRows, err := r.Query(fmt.Sprintf(\"SHOW STATS FOR %s\", strings.Join(names, \"\")))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.ColumnStat{}\n\tfor rows.Next() {\n\t\trec := metadata.ColumnStat{Catalog: f.Catalog, Schema: f.Schema, Table: f.Parent}\n\t\tname := sql.NullString{}\n\t\tavgWidth := sql.NullInt32{}\n\t\tnumDistinct := sql.NullInt64{}\n\t\tnullFrac := sql.NullFloat64{}\n\t\tnumRows := sql.NullInt64{}\n\t\tmin := sql.NullString{}\n\t\tmax := sql.NullString{}\n\t\terr = rows.Scan(\n\t\t\t&name,\n\t\t\t&avgWidth,\n\t\t\t&numDistinct,\n\t\t\t&nullFrac,\n\t\t\t&numRows,\n\t\t\t&min,\n\t\t\t&max,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !name.Valid {\n\t\t\tcontinue\n\t\t}\n\t\trec.Name = name.String\n\t\tif avgWidth.Valid {\n\t\t\trec.AvgWidth = int(avgWidth.Int32)\n\t\t}\n\t\tif numDistinct.Valid {\n\t\t\trec.NumDistinct = numDistinct.Int64\n\t\t}\n\t\tif nullFrac.Valid {\n\t\t\trec.NullFrac = nullFrac.Float64\n\t\t}\n\t\tif min.Valid {\n\t\t\trec.Min = min.String\n\t\t}\n\t\tif max.Valid {\n\t\t\trec.Max = max.String\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\n\treturn metadata.NewColumnStatSet(results), nil\n}\n\nfunc init() {\n\tnewReader := func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\t\tir := infos.New(\n\t\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\tinfos.ColumnsColumnSize:       \"0\",\n\t\t\t\tinfos.ColumnsNumericScale:     \"0\",\n\t\t\t\tinfos.ColumnsNumericPrecRadix: \"0\",\n\t\t\t\tinfos.ColumnsCharOctetLength:  \"0\",\n\t\t\t}),\n\t\t\tinfos.WithFunctions(false),\n\t\t\tinfos.WithSequences(false),\n\t\t\tinfos.WithIndexes(false),\n\t\t\tinfos.WithConstraints(false),\n\t\t\tinfos.WithColumnPrivileges(false),\n\t\t\tinfos.WithUsagePrivileges(false),\n\t\t)(db, opts...)\n\t\tmr := &metaReader{\n\t\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t\t}\n\t\treturn metadata.NewPluginReader(ir, mr)\n\t}\n\tdrivers.Register(\"duckdb\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SELECT library_version FROM pragma_version()`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"DuckDB \" + ver, nil\n\t\t},\n\t\tNewMetadataReader: newReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(newReader(db, opts...))(db, w)\n\t\t},\n\t\tCopy:         drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t\tNewCompleter: mymeta.NewCompleter,\n\t})\n}\n"
  },
  {
    "path": "drivers/dynamodb/dynamodb.go",
    "content": "// Package dynamodb defines and registers usql's DynamoDb driver.\n//\n// See: https://github.com/btnguyen2k/godynamo\npackage dynamodb\n\nimport (\n\t_ \"github.com/btnguyen2k/godynamo\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"godynamo\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/errors.go",
    "content": "package drivers\n\nimport (\n\t\"strings\"\n\t\"unicode\"\n)\n\n// Error is a wrapper to standardize errors.\ntype Error struct {\n\tDriver string\n\tErr    error\n}\n\n// WrapErr wraps an error using the specified driver when err is not nil.\nfunc WrapErr(driver string, err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\t// avoid double wrapping error\n\tif _, ok := err.(*Error); ok {\n\t\treturn err\n\t}\n\treturn &Error{driver, err}\n}\n\n// Error satisfies the error interface, returning simple information about the\n// wrapped error in standardized way.\nfunc (e *Error) Error() string {\n\tif d, ok := drivers[e.Driver]; ok {\n\t\tn := e.Driver\n\t\tif d.Name != \"\" {\n\t\t\tn = d.Name\n\t\t}\n\t\ts := n\n\t\tvar msg string\n\t\tif d.Err != nil {\n\t\t\tvar code string\n\t\t\tcode, msg = d.Err(e.Err)\n\t\t\tif code != \"\" {\n\t\t\t\ts += \": \" + code\n\t\t\t}\n\t\t} else {\n\t\t\tmsg = e.Err.Error()\n\t\t}\n\t\treturn s + \": \" + chop(msg, n)\n\t}\n\treturn e.Driver + \": \" + chop(e.Err.Error(), e.Driver)\n}\n\n// Unwrap returns the original error.\nfunc (e *Error) Unwrap() error {\n\treturn e.Err\n}\n\n// chop chops off a \"prefix: \" prefix from a string.\nfunc chop(s, prefix string) string {\n\treturn strings.TrimLeftFunc(strings.TrimPrefix(strings.TrimSpace(s), prefix+\":\"), unicode.IsSpace)\n}\n"
  },
  {
    "path": "drivers/exasol/exasol.go",
    "content": "// Package exasol defines and registers usql's Exasol driver.\n//\n// See: https://github.com/exasol/exasol-driver-go\npackage exasol\n\nimport (\n\t\"context\"\n\t\"regexp\"\n\n\t_ \"github.com/exasol/exasol-driver-go\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\terrCodeRE := regexp.MustCompile(`^\\[([0-9]+)]\\s+`)\n\tdrivers.Register(\"exasol\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tLowerColumnNames:       true,\n\t\tCopy:                   drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t\tErr: func(err error) (string, string) {\n\t\t\tcode, msg := \"\", err.Error()\n\t\t\tif m := errCodeRE.FindStringSubmatch(msg); m != nil {\n\t\t\t\tcode, msg = m[1], errCodeRE.ReplaceAllString(msg, \"\")\n\t\t\t}\n\t\t\treturn code, msg\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT param_value FROM exa_metadata WHERE param_name = 'databaseProductVersion'`).Scan(&ver); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Exasol \" + ver, nil\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/firebird/firebird.go",
    "content": "// Package firebird defines and registers usql's Firebird driver.\n//\n// See: https://github.com/nakagami/firebirdsql\npackage firebird\n\nimport (\n\t\"context\"\n\n\t_ \"github.com/nakagami/firebirdsql\" // DRIVER: firebirdsql\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"firebirdsql\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(\n\t\t\t\tctx,\n\t\t\t\t`SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') FROM rdb$database;`,\n\t\t\t).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Firebird \" + ver, nil\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/flightsql/flightsql.go",
    "content": "// Package flightsql defines and registers usql's FlightSQL driver.\n//\n// See: https://github.com/apache/arrow/tree/main/go/arrow/flight/flightsql/driver\npackage flightsql\n\nimport (\n\t_ \"github.com/apache/arrow/go/v17/arrow/flight/flightsql/driver\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"flightsql\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/godror/godror.go",
    "content": "// Package godror defines and registers usql's GO DRiver for ORacle driver.\n// Requires CGO. Uses Oracle's ODPI-C (instant client) library.\n//\n// See: https://github.com/godror/godror\n// Group: all\npackage godror\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t_ \"github.com/godror/godror\" // DRIVER\n\t\"github.com/xo/usql/drivers/oracle/orshared\"\n)\n\nfunc init() {\n\torshared.Register(\n\t\t\"godror\",\n\t\t// unwrap error\n\t\tfunc(err error) (string, string) {\n\t\t\tif e := errors.Unwrap(err); e != nil {\n\t\t\t\terr = e\n\t\t\t}\n\t\t\tcode, msg := \"\", err.Error()\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tCode() int\n\t\t\t}); ok {\n\t\t\t\tcode = fmt.Sprintf(\"ORA-%05d\", e.Code())\n\t\t\t}\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tMessage() string\n\t\t\t}); ok {\n\t\t\t\tmsg = e.Message()\n\t\t\t}\n\t\t\tif i := strings.LastIndex(msg, \"ORA-\"); msg == \"\" && i != -1 {\n\t\t\t\tmsg = msg[i:]\n\t\t\t\tif j := strings.Index(msg, \":\"); j != -1 {\n\t\t\t\t\tmsg = msg[j+1:]\n\t\t\t\t\tif code == \"\" {\n\t\t\t\t\t\tcode = msg[i:j]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn code, strings.TrimSpace(msg)\n\t\t},\n\t\t// is password error\n\t\tfunc(err error) bool {\n\t\t\tif e := errors.Unwrap(err); e != nil {\n\t\t\t\terr = e\n\t\t\t}\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tCode() int\n\t\t\t}); ok {\n\t\t\t\treturn e.Code() == 1017 || e.Code() == 1005\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t)\n}\n"
  },
  {
    "path": "drivers/h2/h2.go",
    "content": "// Package h2 defines and registers usql's Apache H2 driver.\n//\n// See: https://github.com/jmrobles/h2go\npackage h2\n\nimport (\n\t_ \"github.com/jmrobles/h2go\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"h2\", drivers.Driver{\n\t\tAllowDollar:            true,\n\t\tAllowMultilineComments: true,\n\t\tAllowCComments:         true,\n\t})\n}\n"
  },
  {
    "path": "drivers/hive/hive.go",
    "content": "// Package hive defines and registers usql's Apache Hive driver.\n//\n// See: https://github.com/sql-machine-learning/gohive\npackage hive\n\nimport (\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t_ \"sqlflow.org/gohive\" // DRIVER\n)\n\nfunc init() {\n\tdrivers.Register(\"hive\", drivers.Driver{\n\t\tForceParams: func(u *dburl.URL) {\n\t\t\tif u.User != nil && u.Query().Get(\"auth\") == \"\" {\n\t\t\t\tdrivers.ForceQueryParameters([]string{\"auth\", \"PLAIN\"})(u)\n\t\t\t}\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/ignite/ignite.go",
    "content": "// Package ignite defines and registers usql's Apache Ignite driver.\n//\n// See: https://github.com/amsokol/ignite-go-client\npackage ignite\n\nimport (\n\t\"strconv\"\n\n\t\"github.com/amsokol/ignite-go-client/binary/errors\"\n\t_ \"github.com/amsokol/ignite-go-client/sql\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"ignite\", drivers.Driver{\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*errors.IgniteError); ok {\n\t\t\t\treturn strconv.Itoa(int(e.IgniteStatus)), e.IgniteMessage\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/impala/impala.go",
    "content": "// Package impala defines and registers usql's Apache Impala driver.\n//\n// See: https://github.com/sclgo/impala-go\npackage impala\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\n\t\"github.com/sclgo/impala-go\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\tmeta \"github.com/xo/usql/drivers/metadata/impala\"\n)\n\nfunc init() {\n\tdrivers.Register(\"impala\", drivers.Driver{\n\t\tNewMetadataReader: meta.New,\n\t\tCopy: func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) {\n\t\t\tplaceholder := func(int) string {\n\t\t\t\treturn \"?\"\n\t\t\t}\n\t\t\treturn drivers.FlexibleCopyWithInsert(ctx, db, rows, table, placeholder, false)\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tvar authError *impala.AuthError\n\t\t\treturn errors.As(err, &authError)\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/maxcompute/maxcompute.go",
    "content": "// Package maxcompute defines and registers usql's Alibaba MaxCompute driver.\n//\n// See: https://github.com/sql-machine-learning/gomaxcompute\npackage maxcompute\n\nimport (\n\t\"github.com/xo/usql/drivers\"\n\t_ \"sqlflow.org/gomaxcompute\" // DRIVER\n)\n\nfunc init() {\n\tdrivers.Register(\"maxcompute\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/metadata/impala/metadata.go",
    "content": "package impala\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\n\tdriver \"github.com/sclgo/impala-go\"\n)\n\ntype MetaReader struct {\n\tmeta *driver.Metadata\n}\n\nfunc (r MetaReader) Columns(filter metadata.Filter) (*metadata.ColumnSet, error) {\n\tcolumnIds, err := r.meta.GetColumns(context.Background(), filter.Schema, filter.Parent, filter.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcolumns := make([]metadata.Column, len(columnIds))\n\tfor i, columnId := range columnIds {\n\t\tcolumns[i] = metadata.Column{\n\t\t\tSchema: columnId.Schema,\n\t\t\tTable:  columnId.TableName,\n\t\t\tName:   columnId.ColumnName,\n\t\t}\n\t}\n\treturn metadata.NewColumnSet(columns), nil\n}\n\nfunc (r MetaReader) Schemas(filter metadata.Filter) (*metadata.SchemaSet, error) {\n\tschemaNames, err := r.meta.GetSchemas(context.Background(), filter.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tschemas := make([]metadata.Schema, len(schemaNames))\n\tfor i, name := range schemaNames {\n\t\tschemas[i] = metadata.Schema{\n\t\t\tSchema: name,\n\t\t}\n\t}\n\treturn metadata.NewSchemaSet(schemas), nil\n}\n\nfunc (r MetaReader) Tables(filter metadata.Filter) (*metadata.TableSet, error) {\n\ttableIds, err := r.meta.GetTables(context.Background(), filter.Schema, filter.Name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttables := make([]metadata.Table, len(tableIds))\n\tfor i, table := range tableIds {\n\t\ttables[i] = metadata.Table{\n\t\t\tSchema: table.Schema,\n\t\t\tName:   table.Name,\n\t\t\tType:   table.Type,\n\t\t}\n\t}\n\treturn metadata.NewTableSet(tables), nil\n}\n\nvar (\n\t_ metadata.SchemaReader = (*MetaReader)(nil)\n\t_ metadata.TableReader  = (*MetaReader)(nil)\n\t_ metadata.ColumnReader = (*MetaReader)(nil)\n)\n\nfunc New(db drivers.DB, _ ...metadata.ReaderOption) metadata.Reader {\n\tif sqlDb, ok := db.(*sql.DB); ok {\n\t\treturn &MetaReader{\n\t\t\tmeta: driver.NewMetadata(sqlDb),\n\t\t}\n\t} else {\n\t\treturn struct{}{} // reader with no capabilities\n\t}\n}\n"
  },
  {
    "path": "drivers/metadata/informationschema/metadata.go",
    "content": "// Package informationschema provides metadata readers that query tables from\n// the information_schema schema. It tries to be database agnostic,\n// but there is a set of options to configure what tables and columns to expect.\npackage informationschema\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/text\"\n)\n\n// InformationSchema metadata reader\ntype InformationSchema struct {\n\tmetadata.LoggingReader\n\tpf                  func(int) string\n\thasFunctions        bool\n\thasSequences        bool\n\thasIndexes          bool\n\thasConstraints      bool\n\thasCheckConstraints bool\n\thasTablePrivileges  bool\n\thasColumnPrivileges bool\n\thasUsagePrivileges  bool\n\tclauses             map[ClauseName]string\n\tlimit               int\n\tsystemSchemas       []string\n\tcurrentSchema       string\n\tdataTypeFormatter   func(metadata.Column) string\n}\n\nvar _ metadata.BasicReader = &InformationSchema{}\n\ntype Logger interface {\n\tPrintln(...interface{})\n}\n\ntype ClauseName string\n\nconst (\n\tColumnsDataType         = ClauseName(\"columns.data_type\")\n\tColumnsColumnSize       = ClauseName(\"columns.column_size\")\n\tColumnsNumericScale     = ClauseName(\"columns.numeric_scale\")\n\tColumnsNumericPrecRadix = ClauseName(\"columns.numeric_precision_radix\")\n\tColumnsCharOctetLength  = ClauseName(\"columns.character_octet_length\")\n\n\tFunctionColumnsColumnSize       = ClauseName(\"function_columns.column_size\")\n\tFunctionColumnsNumericScale     = ClauseName(\"function_columns.numeric_scale\")\n\tFunctionColumnsNumericPrecRadix = ClauseName(\"function_columns.numeric_precision_radix\")\n\tFunctionColumnsCharOctetLength  = ClauseName(\"function_columns.character_octet_length\")\n\n\tFunctionsSecurityType = ClauseName(\"functions.security_type\")\n\n\tConstraintIsDeferrable      = ClauseName(\"constraint_columns.is_deferrable\")\n\tConstraintInitiallyDeferred = ClauseName(\"constraint_columns.initially_deferred\")\n\tConstraintJoinCond          = ClauseName(\"constraint_join.fk\")\n\n\tSequenceColumnsIncrement = ClauseName(\"sequence_columns.increment\")\n\n\tPrivilegesGrantor = ClauseName(\"privileges.grantor\")\n)\n\n// New InformationSchema reader\nfunc New(opts ...metadata.ReaderOption) func(drivers.DB, ...metadata.ReaderOption) metadata.Reader {\n\ts := &InformationSchema{\n\t\tpf:                  func(n int) string { return fmt.Sprintf(\"$%d\", n) },\n\t\thasFunctions:        true,\n\t\thasSequences:        true,\n\t\thasIndexes:          true,\n\t\thasConstraints:      true,\n\t\thasCheckConstraints: true,\n\t\thasTablePrivileges:  true,\n\t\thasColumnPrivileges: true,\n\t\thasUsagePrivileges:  true,\n\t\tclauses: map[ClauseName]string{\n\t\t\tColumnsDataType:                 \"data_type\",\n\t\t\tColumnsColumnSize:               \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, 0)\",\n\t\t\tColumnsNumericScale:             \"COALESCE(numeric_scale, 0)\",\n\t\t\tColumnsNumericPrecRadix:         \"COALESCE(numeric_precision_radix, 10)\",\n\t\t\tColumnsCharOctetLength:          \"COALESCE(character_octet_length, 0)\",\n\t\t\tFunctionColumnsColumnSize:       \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, 0)\",\n\t\t\tFunctionColumnsNumericScale:     \"COALESCE(numeric_scale, 0)\",\n\t\t\tFunctionColumnsNumericPrecRadix: \"COALESCE(numeric_precision_radix, 10)\",\n\t\t\tFunctionColumnsCharOctetLength:  \"COALESCE(character_octet_length, 0)\",\n\t\t\tFunctionsSecurityType:           \"security_type\",\n\t\t\tConstraintIsDeferrable:          \"t.is_deferrable\",\n\t\t\tConstraintInitiallyDeferred:     \"t.initially_deferred\",\n\t\t\tSequenceColumnsIncrement:        \"increment\",\n\t\t\tPrivilegesGrantor:               \"grantor\",\n\t\t},\n\t\tsystemSchemas:     []string{\"information_schema\"},\n\t\tdataTypeFormatter: func(col metadata.Column) string { return col.DataType },\n\t}\n\t// apply InformationSchema specific options\n\tfor _, o := range opts {\n\t\to(s)\n\t}\n\n\treturn func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\t\ts.LoggingReader = metadata.NewLoggingReader(db, opts...)\n\t\treturn s\n\t}\n}\n\n// WithPlaceholder generator function, that usually returns either `?` or `$n`,\n// where `n` is the argument.\nfunc WithPlaceholder(pf func(int) string) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).pf = pf\n\t}\n}\n\n// WithCustomClauses to use different expressions for some columns\nfunc WithCustomClauses(cols map[ClauseName]string) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tfor k, v := range cols {\n\t\t\tr.(*InformationSchema).clauses[k] = v\n\t\t}\n\t}\n}\n\n// WithFunctions when the `routines` and `parameters` tables exists\nfunc WithFunctions(fun bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasFunctions = fun\n\t}\n}\n\n// WithIndexes when the `statistics` table exists\nfunc WithIndexes(ind bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasIndexes = ind\n\t}\n}\n\n// WithConstraints when the `statistics` table exists\nfunc WithConstraints(con bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasConstraints = con\n\t}\n}\n\n// WithCheckConstraints when the `statistics` table exists\nfunc WithCheckConstraints(con bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasCheckConstraints = con\n\t}\n}\n\n// WithSequences when the `sequences` table exists\nfunc WithSequences(seq bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasSequences = seq\n\t}\n}\n\n// WithTablePrivileges when the `table_privileges` table exists\nfunc WithTablePrivileges(t bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasTablePrivileges = t\n\t}\n}\n\n// WithColumnPrivileges when the `column_privileges` table exists\nfunc WithColumnPrivileges(c bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasColumnPrivileges = c\n\t}\n}\n\n// WithUsagePrivileges when the `usage_privileges` table exists\nfunc WithUsagePrivileges(u bool) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).hasUsagePrivileges = u\n\t}\n}\n\n// WithSystemSchemas that are ignored unless WithSystem filter is true\nfunc WithSystemSchemas(schemas []string) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).systemSchemas = schemas\n\t}\n}\n\n// WithCurrentSchema expression to filter by when OnlyVisible filter is true\nfunc WithCurrentSchema(expr string) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).currentSchema = expr\n\t}\n}\n\n// WithDataTypeFormatter function to build updated string representation of data type\n// from Column\nfunc WithDataTypeFormatter(f func(metadata.Column) string) metadata.ReaderOption {\n\treturn func(r metadata.Reader) {\n\t\tr.(*InformationSchema).dataTypeFormatter = f\n\t}\n}\n\nfunc (s *InformationSchema) SetLimit(l int) {\n\ts.limit = l\n}\n\n// Columns from selected catalog (or all, if empty), matching schemas and tables\nfunc (s InformationSchema) Columns(f metadata.Filter) (*metadata.ColumnSet, error) {\n\tcolumns := []string{\n\t\t\"table_catalog\",\n\t\t\"table_schema\",\n\t\t\"table_name\",\n\t\t\"column_name\",\n\t\t\"ordinal_position\",\n\t\ts.clauses[ColumnsDataType],\n\t\t\"COALESCE(column_default, '')\",\n\t\t\"COALESCE(is_nullable, '') AS is_nullable\",\n\t\ts.clauses[ColumnsColumnSize],\n\t\ts.clauses[ColumnsNumericScale],\n\t\ts.clauses[ColumnsNumericPrecRadix],\n\t\ts.clauses[ColumnsCharOctetLength],\n\t}\n\n\tqstr := \"SELECT\\n  \" + strings.Join(columns, \",\\n  \") + \" FROM information_schema.columns\\n\"\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"table_catalog LIKE %s\",\n\t\tschema:     \"table_schema LIKE %s\",\n\t\tnotSchemas: \"table_schema NOT IN (%s)\",\n\t\tparent:     \"table_name LIKE %s\",\n\t})\n\trows, closeRows, err := s.query(qstr, conds, \"table_catalog, table_schema, table_name, ordinal_position\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewColumnSet([]metadata.Column{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Column{}\n\tfor rows.Next() {\n\t\trec := metadata.Column{}\n\t\terr = rows.Scan(\n\t\t\t&rec.Catalog,\n\t\t\t&rec.Schema,\n\t\t\t&rec.Table,\n\t\t\t&rec.Name,\n\t\t\t&rec.OrdinalPosition,\n\t\t\t&rec.DataType,\n\t\t\t&rec.Default,\n\t\t\t&rec.IsNullable,\n\t\t\t&rec.ColumnSize,\n\t\t\t&rec.DecimalDigits,\n\t\t\t&rec.NumPrecRadix,\n\t\t\t&rec.CharOctetLength,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\trec.DataType = s.dataTypeFormatter(rec)\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewColumnSet(results), nil\n}\n\n// Tables from selected catalog (or all, if empty), matching schemas, names and types\nfunc (s InformationSchema) Tables(f metadata.Filter) (*metadata.TableSet, error) {\n\tqstr := `SELECT\n  table_catalog,\n  table_schema,\n  table_name,\n  table_type\nFROM information_schema.tables\n`\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"table_catalog LIKE %s\",\n\t\tschema:     \"table_schema LIKE %s\",\n\t\tnotSchemas: \"table_schema NOT IN (%s)\",\n\t\tname:       \"table_name LIKE %s\",\n\t\ttypes:      \"table_type IN (%s)\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\taddSequences := false\n\tfor _, t := range f.Types {\n\t\tif t == \"SEQUENCE\" && s.hasSequences {\n\t\t\taddSequences = true\n\t\t}\n\t}\n\tif addSequences {\n\t\tqstr += `\nUNION ALL\nSELECT\n  sequence_catalog AS table_catalog,\n  sequence_schema AS table_schema,\n  sequence_name AS table_name,\n  'SEQUENCE' AS table_type\nFROM information_schema.sequences\n`\n\t\tconds, seqVals := s.conditions(len(vals)+1, f, formats{\n\t\t\tcatalog:    \"sequence_catalog LIKE %s\",\n\t\t\tschema:     \"sequence_schema LIKE %s\",\n\t\t\tnotSchemas: \"sequence_schema NOT IN (%s)\",\n\t\t\tname:       \"sequence_name LIKE %s\",\n\t\t})\n\t\tvals = append(vals, seqVals...)\n\t\tif len(conds) != 0 {\n\t\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t\t}\n\t}\n\trows, closeRows, err := s.query(qstr, []string{}, \"table_catalog, table_schema, table_type, table_name\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewTableSet([]metadata.Table{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Table{}\n\tfor rows.Next() {\n\t\trec := metadata.Table{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Name, &rec.Type)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewTableSet(results), nil\n}\n\n// Schemas from selected catalog (or all, if empty), matching schemas and tables\nfunc (s InformationSchema) Schemas(f metadata.Filter) (*metadata.SchemaSet, error) {\n\tqstr := `SELECT\n  schema_name,\n  catalog_name\nFROM information_schema.schemata\n`\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"catalog_name LIKE %s\",\n\t\tname:       \"schema_name LIKE %s\",\n\t\tnotSchemas: \"schema_name NOT IN (%s)\",\n\t})\n\trows, closeRows, err := s.query(qstr, conds, \"catalog_name, schema_name\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewSchemaSet([]metadata.Schema{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Schema{}\n\tfor rows.Next() {\n\t\trec := metadata.Schema{}\n\t\terr = rows.Scan(&rec.Schema, &rec.Catalog)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewSchemaSet(results), nil\n}\n\n// Functions from selected catalog (or all, if empty), matching schemas, names and types\nfunc (s InformationSchema) Functions(f metadata.Filter) (*metadata.FunctionSet, error) {\n\tif !s.hasFunctions {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tcolumns := []string{\n\t\t\"specific_name\",\n\t\t\"routine_catalog\",\n\t\t\"routine_schema\",\n\t\t\"routine_name\",\n\t\t\"COALESCE(routine_type, '')\",\n\t\t\"COALESCE(data_type, '')\",\n\t\t\"routine_definition\",\n\t\t\"COALESCE(external_language, routine_body) AS language\",\n\t\t\"is_deterministic\",\n\t\ts.clauses[FunctionsSecurityType],\n\t}\n\n\tqstr := \"SELECT\\n  \" + strings.Join(columns, \",\\n  \") + \" FROM information_schema.routines\\n\"\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"routine_catalog LIKE %s\",\n\t\tschema:     \"routine_schema LIKE %s\",\n\t\tnotSchemas: \"routine_schema NOT IN (%s)\",\n\t\tname:       \"routine_name LIKE %s\",\n\t\ttypes:      \"routine_type IN (%s)\",\n\t})\n\trows, closeRows, err := s.query(qstr, conds, \"routine_catalog, routine_schema, routine_name, COALESCE(routine_type, '')\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewFunctionSet([]metadata.Function{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Function{}\n\tfor rows.Next() {\n\t\trec := metadata.Function{}\n\t\terr = rows.Scan(\n\t\t\t&rec.SpecificName,\n\t\t\t&rec.Catalog,\n\t\t\t&rec.Schema,\n\t\t\t&rec.Name,\n\t\t\t&rec.Type,\n\t\t\t&rec.ResultType,\n\t\t\t&rec.Source,\n\t\t\t&rec.Language,\n\t\t\t&rec.Volatility,\n\t\t\t&rec.Security,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewFunctionSet(results), nil\n}\n\n// FunctionColumns (arguments) from selected catalog (or all, if empty), matching schemas and functions\nfunc (s InformationSchema) FunctionColumns(f metadata.Filter) (*metadata.FunctionColumnSet, error) {\n\tif !s.hasFunctions {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tcolumns := []string{\n\t\t\"specific_catalog\",\n\t\t\"specific_schema\",\n\t\t\"specific_name\",\n\t\t\"COALESCE(parameter_name, '')\",\n\t\t\"ordinal_position\",\n\t\t\"COALESCE(parameter_mode, '')\",\n\t\t\"COALESCE(data_type, '')\",\n\t\ts.clauses[FunctionColumnsColumnSize],\n\t\ts.clauses[FunctionColumnsNumericScale],\n\t\ts.clauses[FunctionColumnsNumericPrecRadix],\n\t\ts.clauses[FunctionColumnsCharOctetLength],\n\t}\n\n\tqstr := \"SELECT\\n  \" + strings.Join(columns, \",\\n  \") + \" FROM information_schema.parameters\\n\"\n\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"specific_catalog LIKE %s\",\n\t\tschema:     \"specific_schema LIKE %s\",\n\t\tnotSchemas: \"specific_schema NOT IN (%s)\",\n\t\tparent:     \"specific_name LIKE %s\",\n\t})\n\trows, closeRows, err := s.query(qstr, conds, \"specific_catalog, specific_schema, specific_name, ordinal_position, COALESCE(parameter_name, '')\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewFunctionColumnSet([]metadata.FunctionColumn{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.FunctionColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.FunctionColumn{}\n\t\terr = rows.Scan(\n\t\t\t&rec.Catalog,\n\t\t\t&rec.Schema,\n\t\t\t&rec.FunctionName,\n\t\t\t&rec.Name,\n\t\t\t&rec.OrdinalPosition,\n\t\t\t&rec.Type,\n\t\t\t&rec.DataType,\n\t\t\t&rec.ColumnSize,\n\t\t\t&rec.DecimalDigits,\n\t\t\t&rec.NumPrecRadix,\n\t\t\t&rec.CharOctetLength,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewFunctionColumnSet(results), nil\n}\n\n// Indexes from selected catalog (or all, if empty), matching schemas and names\nfunc (s InformationSchema) Indexes(f metadata.Filter) (*metadata.IndexSet, error) {\n\tif !s.hasIndexes {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tqstr := `SELECT\n  table_catalog,\n  index_schema,\n  table_name,\n  index_name,\n  CASE WHEN non_unique = 0 THEN 'YES' ELSE 'NO' END AS is_unique,\n  CASE WHEN index_name = 'PRIMARY' THEN 'YES' ELSE 'NO' END AS is_primary,\n  index_type\nFROM information_schema.statistics\n`\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"table_catalog LIKE %s\",\n\t\tschema:     \"index_schema LIKE %s\",\n\t\tnotSchemas: \"index_schema NOT IN (%s)\",\n\t\tparent:     \"table_name LIKE %s\",\n\t\tname:       \"index_name LIKE %s\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tqstr += `\nGROUP BY table_catalog, index_schema, table_name, index_name,\n  CASE WHEN non_unique = 0 THEN 'YES' ELSE 'NO' END,\n  CASE WHEN index_name = 'PRIMARY' THEN 'YES' ELSE 'NO' END,\n  index_type`\n\trows, closeRows, err := s.query(qstr, []string{}, \"table_catalog, index_schema, table_name, index_name\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewIndexSet([]metadata.Index{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Index{}\n\tfor rows.Next() {\n\t\trec := metadata.Index{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Table, &rec.Name, &rec.IsUnique, &rec.IsPrimary, &rec.Type)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexSet(results), nil\n}\n\n// IndexColumns from selected catalog (or all, if empty), matching schemas and indexes\nfunc (s InformationSchema) IndexColumns(f metadata.Filter) (*metadata.IndexColumnSet, error) {\n\tif !s.hasIndexes {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tqstr := `SELECT\n  i.table_catalog,\n  i.table_schema,\n  i.table_name,\n  i.index_name,\n  i.column_name,\n  c.data_type,\n  i.seq_in_index\n\nFROM information_schema.statistics i\nJOIN information_schema.columns c ON\n  i.table_catalog = c.table_catalog AND\n  i.table_schema = c.table_schema AND\n  i.table_name = c.table_name AND\n  i.column_name = c.column_name\n`\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"i.table_catalog LIKE %s\",\n\t\tschema:     \"index_schema LIKE %s\",\n\t\tnotSchemas: \"index_schema NOT IN (%s)\",\n\t\tparent:     \"i.table_name LIKE %s\",\n\t\tname:       \"index_name LIKE %s\",\n\t})\n\trows, closeRows, err := s.query(qstr, conds, \"i.table_catalog, index_schema, table_name, index_name, seq_in_index\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewIndexColumnSet([]metadata.IndexColumn{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.IndexColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.IndexColumn{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Table, &rec.IndexName, &rec.Name, &rec.DataType, &rec.OrdinalPosition)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexColumnSet(results), nil\n}\n\n// Constraints from selected catalog (or all, if empty), matching schemas and names\nfunc (s InformationSchema) Constraints(f metadata.Filter) (*metadata.ConstraintSet, error) {\n\tif !s.hasConstraints {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tcolumns := []string{\n\t\t\"t.constraint_catalog\",\n\t\t\"t.table_schema\",\n\t\t\"t.table_name\",\n\t\t\"t.constraint_name\",\n\t\t\"t.constraint_type\",\n\t\ts.clauses[ConstraintIsDeferrable],\n\t\ts.clauses[ConstraintInitiallyDeferred],\n\t\t\"COALESCE(r.unique_constraint_catalog, '') AS foreign_catalog\",\n\t\t\"COALESCE(r.unique_constraint_schema, '') AS foreign_schema\",\n\t\t\"COALESCE(f.table_name, '') AS foreign_table\",\n\t\t\"COALESCE(r.unique_constraint_name, '') AS foreign_constraint\",\n\t\t\"COALESCE(r.match_option, '') AS match_options\",\n\t\t\"COALESCE(r.update_rule, '') AS update_rule\",\n\t\t\"COALESCE(r.delete_rule, '') AS delete_rule\",\n\t\t\"COALESCE(c.check_clause, '') AS check_clause\",\n\t}\n\n\tqstr := \"SELECT\\n  \" + strings.Join(columns, \",\\n  \") + `\nFROM information_schema.table_constraints t\nLEFT JOIN information_schema.referential_constraints r ON t.constraint_catalog = r.constraint_catalog\n  AND t.constraint_schema = r.constraint_schema\n  AND t.constraint_name = r.constraint_name\n  AND t.constraint_type = 'FOREIGN KEY'\nLEFT JOIN information_schema.table_constraints f ON r.unique_constraint_catalog = f.constraint_catalog\n  AND r.unique_constraint_schema = f.constraint_schema\n  AND r.unique_constraint_name = f.constraint_name\n  ` + s.clauses[ConstraintJoinCond] + `\nLEFT JOIN information_schema.check_constraints c ON t.constraint_catalog = c.constraint_catalog\n  AND t.constraint_schema = c.constraint_schema\n  AND t.constraint_name = c.constraint_name\n`\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"t.constraint_catalog LIKE %s\",\n\t\tschema:     \"t.table_schema LIKE %s\",\n\t\tnotSchemas: \"t.table_schema NOT IN (%s)\",\n\t\tparent:     \"t.table_name LIKE %s\",\n\t\treference:  \"f.table_name LIKE %s\",\n\t\tname:       \"t.constraint_name LIKE %s\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\trows, closeRows, err := s.query(qstr, []string{}, \"t.constraint_catalog, t.table_schema, t.table_name, t.constraint_name\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewConstraintSet([]metadata.Constraint{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Constraint{}\n\tfor rows.Next() {\n\t\trec := metadata.Constraint{}\n\t\terr = rows.Scan(\n\t\t\t&rec.Catalog,\n\t\t\t&rec.Schema,\n\t\t\t&rec.Table,\n\t\t\t&rec.Name,\n\t\t\t&rec.Type,\n\t\t\t&rec.IsDeferrable,\n\t\t\t&rec.IsInitiallyDeferred,\n\t\t\t&rec.ForeignCatalog,\n\t\t\t&rec.ForeignSchema,\n\t\t\t&rec.ForeignTable,\n\t\t\t&rec.ForeignName,\n\t\t\t&rec.MatchType,\n\t\t\t&rec.UpdateRule,\n\t\t\t&rec.DeleteRule,\n\t\t\t&rec.CheckClause,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewConstraintSet(results), nil\n}\n\n// ConstraintColumns from selected catalog (or all, if empty), matching schemas and constraints\nfunc (s InformationSchema) ConstraintColumns(f metadata.Filter) (*metadata.ConstraintColumnSet, error) {\n\tif !s.hasConstraints {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tvals := []interface{}{}\n\tqstr := \"\"\n\tif s.hasCheckConstraints {\n\t\tqstr = `SELECT\n\t  c.constraint_catalog,\n\t  c.table_schema,\n\t  c.table_name,\n\t  c.constraint_name,\n\t  c.column_name,\n\t  1 AS ordinal_position,\n\t  '' AS foreign_catalog,\n\t  '' AS foreign_schema,\n\t  '' AS foreign_table,\n\t  '' AS foreign_name\n\tFROM information_schema.constraint_column_usage c\n\t`\n\t\tconds, checkVals := s.conditions(len(vals)+1, f, formats{\n\t\t\tcatalog:    \"c.constraint_catalog LIKE %s\",\n\t\t\tschema:     \"c.table_schema LIKE %s\",\n\t\t\tnotSchemas: \"c.table_schema NOT IN (%s)\",\n\t\t\tparent:     \"c.table_name LIKE %s\",\n\t\t\tname:       \"c.constraint_name LIKE %s\",\n\t\t})\n\t\tif len(conds) != 0 {\n\t\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t\t\tvals = append(vals, checkVals...)\n\t\t}\n\t\tqstr += `\nUNION ALL\n`\n\t}\n\tqstr += `SELECT\n  c.constraint_catalog,\n  c.table_schema,\n  c.table_name,\n  c.constraint_name,\n  c.column_name,\n  c.ordinal_position,\n  COALESCE(f.constraint_catalog, '') AS foreign_catalog,\n  COALESCE(f.table_schema, '') AS foreign_schema,\n  COALESCE(f.table_name, '') AS foreign_table,\n  COALESCE(f.column_name, '') AS foreign_name\nFROM information_schema.key_column_usage c\nLEFT JOIN information_schema.referential_constraints r ON c.constraint_catalog = r.constraint_catalog\n  AND c.constraint_schema = r.constraint_schema\n  AND c.constraint_name = r.constraint_name\nLEFT JOIN information_schema.key_column_usage f ON r.unique_constraint_catalog = f.constraint_catalog\n  AND r.unique_constraint_schema = f.constraint_schema\n  AND r.unique_constraint_name = f.constraint_name\n  ` + s.clauses[ConstraintJoinCond] + `\n  AND c.position_in_unique_constraint = f.ordinal_position\n`\n\tconds, keyVals := s.conditions(len(vals)+1, f, formats{\n\t\tcatalog:    \"c.constraint_catalog LIKE %s\",\n\t\tschema:     \"c.table_schema LIKE %s\",\n\t\tnotSchemas: \"c.table_schema NOT IN (%s)\",\n\t\tparent:     \"c.table_name LIKE %s\",\n\t\treference:  \"f.table_name LIKE %s\",\n\t\tname:       \"c.constraint_name LIKE %s\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t\tvals = append(vals, keyVals...)\n\t}\n\trows, closeRows, err := s.query(qstr, []string{}, \"constraint_catalog, table_schema, table_name, constraint_name, ordinal_position, column_name\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewConstraintColumnSet([]metadata.ConstraintColumn{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.ConstraintColumn{}\n\ti := 1\n\tfor rows.Next() {\n\t\trec := metadata.ConstraintColumn{OrdinalPosition: i}\n\t\ti++\n\t\terr = rows.Scan(\n\t\t\t&rec.Catalog,\n\t\t\t&rec.Schema,\n\t\t\t&rec.Table,\n\t\t\t&rec.Constraint,\n\t\t\t&rec.Name,\n\t\t\t&rec.OrdinalPosition,\n\t\t\t&rec.ForeignCatalog,\n\t\t\t&rec.ForeignSchema,\n\t\t\t&rec.ForeignTable,\n\t\t\t&rec.ForeignName,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewConstraintColumnSet(results), nil\n}\n\n// Sequences from selected catalog (or all, if empty), matching schemas and names\nfunc (s InformationSchema) Sequences(f metadata.Filter) (*metadata.SequenceSet, error) {\n\tif !s.hasSequences {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\tcolumns := []string{\n\t\t\"sequence_catalog\",\n\t\t\"sequence_schema\",\n\t\t\"sequence_name\",\n\t\t\"data_type\",\n\t\t\"start_value\",\n\t\t\"minimum_value\",\n\t\t\"maximum_value\",\n\t\ts.clauses[SequenceColumnsIncrement],\n\t\t\"cycle_option\",\n\t}\n\n\tqstr := \"SELECT\\n  \" + strings.Join(columns, \",\\n  \") + \" FROM information_schema.sequences\\n\"\n\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"sequence_catalog LIKE %s\",\n\t\tschema:     \"sequence_schema LIKE %s\",\n\t\tnotSchemas: \"sequence_schema NOT IN (%s)\",\n\t\tname:       \"sequence_name LIKE %s\",\n\t})\n\trows, closeRows, err := s.query(qstr, conds, \"sequence_catalog, sequence_schema, sequence_name\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewSequenceSet([]metadata.Sequence{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Sequence{}\n\tfor rows.Next() {\n\t\trec := metadata.Sequence{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Name, &rec.DataType, &rec.Start, &rec.Min, &rec.Max, &rec.Increment, &rec.Cycles)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewSequenceSet(results), nil\n}\n\n// PrivilegeSummaries of privileges on tables, views and sequences from selected catalog (or all, if empty), matching schemas and names\nfunc (s InformationSchema) PrivilegeSummaries(f metadata.Filter) (*metadata.PrivilegeSummarySet, error) {\n\tif !s.hasTablePrivileges && !s.hasColumnPrivileges && !s.hasUsagePrivileges {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\n\tqstrs := []string{}\n\tconds, vals := s.conditions(1, f, formats{\n\t\tcatalog:    \"object_catalog LIKE %s\",\n\t\tschema:     \"object_schema LIKE %s\",\n\t\tnotSchemas: \"object_schema NOT IN (%s)\",\n\t\tname:       \"object_name LIKE %s\",\n\t\ttypes:      \"object_type IN (%s)\",\n\t})\n\n\tif s.hasTablePrivileges {\n\t\tcolumns := []string{\n\t\t\t\"t.table_catalog AS object_catalog\",\n\t\t\t\"t.table_schema AS object_schema\",\n\t\t\t\"t.table_name AS object_name\",\n\t\t\t\"t.table_type AS object_type\",\n\t\t\t\"'' AS column_name\",\n\t\t\t\"COALESCE(grantee, '') AS grantee\",\n\t\t\t\"COALESCE(\" + s.clauses[PrivilegesGrantor] + \", '') AS grantor\",\n\t\t\t\"COALESCE(privilege_type, '') AS privilege_type\",\n\t\t\t\"CASE WHEN is_grantable='YES' THEN 1 ELSE 0 END AS is_grantable\",\n\t\t}\n\n\t\t// `tables` is on the left side of the join to also list tables that have no privileges set.\n\t\tqstr := \"SELECT\\n\" +\n\t\t\t\"  \" + strings.Join(columns, \", \") + \"\\n\" +\n\t\t\t\"FROM information_schema.tables t\\n\" +\n\t\t\t\"LEFT JOIN information_schema.table_privileges tp\\n\" +\n\t\t\t\"  ON t.table_catalog = tp.table_catalog AND t.table_schema = tp.table_schema AND t.table_name = tp.table_name\"\n\t\tqstrs = append(qstrs, qstr)\n\t}\n\n\tif s.hasColumnPrivileges {\n\t\tcolumns := []string{\n\t\t\t\"t.table_catalog AS object_catalog\",\n\t\t\t\"t.table_schema AS object_schema\",\n\t\t\t\"t.table_name AS object_name\",\n\t\t\t\"t.table_type AS object_type\",\n\t\t\t\"column_name\",\n\t\t\t\"grantee\",\n\t\t\ts.clauses[PrivilegesGrantor] + \" AS grantor\",\n\t\t\t\"privilege_type\",\n\t\t\t\"CASE WHEN is_grantable='YES' THEN 1 ELSE 0 END AS is_grantable\",\n\t\t}\n\n\t\tqstr := \"SELECT\\n\" +\n\t\t\t\"  \" + strings.Join(columns, \", \") + \"\\n\" +\n\t\t\t\"FROM information_schema.column_privileges cp\\n\" +\n\t\t\t\"LEFT JOIN information_schema.tables t\\n\" +\n\t\t\t\"  ON t.table_catalog = cp.table_catalog AND t.table_schema = cp.table_schema AND t.table_name = cp.table_name\"\n\t\tqstrs = append(qstrs, qstr)\n\t}\n\n\tif s.hasUsagePrivileges {\n\t\tcolumns := []string{\n\t\t\t\"object_catalog\",\n\t\t\t\"object_schema\",\n\t\t\t\"object_name\",\n\t\t\t\"object_type\",\n\t\t\t\"'' AS column_name\",\n\t\t\t\"grantee\",\n\t\t\ts.clauses[PrivilegesGrantor] + \" AS grantor\",\n\t\t\t\"privilege_type\",\n\t\t\t\"CASE WHEN is_grantable='YES' THEN 1 ELSE 0 END AS is_grantable\",\n\t\t}\n\n\t\tqstr := \"SELECT\\n\" +\n\t\t\t\"  \" + strings.Join(columns, \", \") + \"\\n\" +\n\t\t\t\"FROM information_schema.usage_privileges\"\n\t\tqstrs = append(qstrs, qstr)\n\t}\n\n\t// In the query result, table and column level privileges will be on separate rows.\n\t// Each table or column can have multiple privileges (i.e rows).\n\t// For table level privileges the `column_name` column is empty.\n\tqstr := \"SELECT * FROM (\\n\" + strings.Join(qstrs, \"\\nUNION ALL\\n\") + \"\\n) AS subquery\"\n\trows, closeRows, err := s.query(\n\t\tqstr,\n\t\tconds,\n\t\t\"object_catalog, object_schema, object_type, object_name, column_name, grantee, grantor, privilege_type\",\n\t\tvals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewPrivilegeSummarySet([]metadata.PrivilegeSummary{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\ttype row struct {\n\t\tCatalog       string\n\t\tSchema        string\n\t\tName          string\n\t\tObjectType    string\n\t\tColumn        string\n\t\tGrantee       string\n\t\tGrantor       string\n\t\tPrivilegeType string\n\t\tIsGrantable   bool\n\t}\n\t// The rows need to be aggregated into one `metadata.PrivilegeSummary` object per table. The rows are ordered by table such that we can append\n\t// to the current `metadata.PrivilegeSummary` as long as we are processing the same table.\n\tresults := []metadata.PrivilegeSummary{}\n\tcurSummary := &metadata.PrivilegeSummary{}\n\tfor rows.Next() {\n\t\tr := row{}\n\t\terr = rows.Scan(&r.Catalog, &r.Schema, &r.Name, &r.ObjectType, &r.Column, &r.Grantee, &r.Grantor, &r.PrivilegeType, &r.IsGrantable)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif curSummary.Catalog != r.Catalog || curSummary.Schema != r.Schema || curSummary.Name != r.Name {\n\t\t\tsummary := metadata.PrivilegeSummary{\n\t\t\t\tCatalog:          r.Catalog,\n\t\t\t\tSchema:           r.Schema,\n\t\t\t\tName:             r.Name,\n\t\t\t\tObjectType:       r.ObjectType,\n\t\t\t\tObjectPrivileges: metadata.ObjectPrivileges{},\n\t\t\t\tColumnPrivileges: metadata.ColumnPrivileges{},\n\t\t\t}\n\t\t\tresults = append(results, summary)\n\t\t\tcurSummary = &results[len(results)-1]\n\t\t}\n\n\t\tswitch {\n\t\t// If the row specifies neither column nor table level privileges\n\t\tcase r.PrivilegeType == \"\":\n\t\t// If row specifies table level privilege\n\t\tcase r.Column == \"\":\n\t\t\tobjPrivilege := metadata.ObjectPrivilege{Grantee: r.Grantee, Grantor: r.Grantor, PrivilegeType: r.PrivilegeType, IsGrantable: r.IsGrantable}\n\t\t\tcurSummary.ObjectPrivileges = append(curSummary.ObjectPrivileges, objPrivilege)\n\t\t// If row specifies column level privilege\n\t\tdefault:\n\t\t\tcolPrivilege := metadata.ColumnPrivilege{Column: r.Column, Grantee: r.Grantee, Grantor: r.Grantor, PrivilegeType: r.PrivilegeType, IsGrantable: r.IsGrantable}\n\t\t\tcurSummary.ColumnPrivileges = append(curSummary.ColumnPrivileges, colPrivilege)\n\t\t}\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewPrivilegeSummarySet(results), nil\n}\n\nfunc (s InformationSchema) conditions(baseParam int, filter metadata.Filter, formats formats) ([]string, []interface{}) {\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif filter.Catalog != \"\" && formats.catalog != \"\" {\n\t\tvals = append(vals, filter.Catalog)\n\t\tconds = append(conds, fmt.Sprintf(formats.catalog, s.pf(baseParam)))\n\t\tbaseParam++\n\t}\n\tif filter.Schema != \"\" && formats.schema != \"\" {\n\t\tvals = append(vals, filter.Schema)\n\t\tconds = append(conds, fmt.Sprintf(formats.schema, s.pf(baseParam)))\n\t\tbaseParam++\n\t}\n\n\tif !filter.WithSystem && formats.notSchemas != \"\" && len(s.systemSchemas) != 0 {\n\t\tpholders := []string{}\n\t\tfor _, v := range s.systemSchemas {\n\t\t\tif v == filter.Schema {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvals = append(vals, v)\n\t\t\tpholders = append(pholders, s.pf(baseParam))\n\t\t\tbaseParam++\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, fmt.Sprintf(formats.notSchemas, strings.Join(pholders, \", \")))\n\t\t}\n\t}\n\tif filter.OnlyVisible && formats.schema != \"\" && s.currentSchema != \"\" {\n\t\tconds = append(conds, fmt.Sprintf(formats.schema, s.currentSchema))\n\t}\n\tif filter.Parent != \"\" && formats.parent != \"\" {\n\t\tvals = append(vals, filter.Parent)\n\t\tconds = append(conds, fmt.Sprintf(formats.parent, s.pf(baseParam)))\n\t\tbaseParam++\n\t}\n\tif filter.Reference != \"\" && formats.reference != \"\" {\n\t\tvals = append(vals, filter.Reference)\n\t\tconds = append(conds, fmt.Sprintf(formats.reference, s.pf(baseParam)))\n\t\tbaseParam++\n\t}\n\tif filter.Name != \"\" && formats.name != \"\" {\n\t\tvals = append(vals, filter.Name)\n\t\tconds = append(conds, fmt.Sprintf(formats.name, s.pf(baseParam)))\n\t\tbaseParam++\n\t}\n\tif len(filter.Types) != 0 && formats.types != \"\" {\n\t\tpholders := []string{}\n\t\tfor _, t := range filter.Types {\n\t\t\tvals = append(vals, t)\n\t\t\tpholders = append(pholders, s.pf(baseParam))\n\t\t\tbaseParam++\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, fmt.Sprintf(formats.types, strings.Join(pholders, \", \")))\n\t\t}\n\t}\n\n\treturn conds, vals\n}\n\ntype formats struct {\n\tcatalog    string\n\tschema     string\n\tnotSchemas string\n\tparent     string\n\treference  string\n\tname       string\n\ttypes      string\n}\n\nfunc (s InformationSchema) query(qstr string, conds []string, order string, vals ...interface{}) (*sql.Rows, func(), error) {\n\tif len(conds) != 0 {\n\t\tqstr += \"\\nWHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tif order != \"\" {\n\t\tqstr += \"\\nORDER BY \" + order\n\t}\n\tif s.limit != 0 {\n\t\tqstr += fmt.Sprintf(\"\\nLIMIT %d\", s.limit)\n\t}\n\treturn s.Query(qstr, vals...)\n}\n"
  },
  {
    "path": "drivers/metadata/informationschema/metadata_test.go",
    "content": "// Package informationschema_test runs integration tests for informationschema package\n// on real databases running in containers. During development, to avoid rebuilding\n// containers every run, add the `-cleanup=false` flags when calling `go test`.\npackage informationschema_test\n\nimport (\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/url\"\n\t\"os\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/google/go-cmp/cmp\"\n\t_ \"github.com/microsoft/go-mssqldb\" // DRIVER: sqlserver\n\tdt \"github.com/ory/dockertest/v3\"\n\tdc \"github.com/ory/dockertest/v3/docker\"\n\t_ \"github.com/trinodb/trino-go-client/trino\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n\t_ \"github.com/xo/usql/drivers/postgres\"\n)\n\ntype Database struct {\n\tBuildArgs    []dc.BuildArg\n\tRunOptions   *dt.RunOptions\n\tExec         []string\n\tDriver       string\n\tURL          string\n\tReadinessURL string\n\tDockerPort   string\n\tResource     *dt.Resource\n\tDB           *sql.DB\n\tOpts         []metadata.ReaderOption\n\tReader       metadata.BasicReader\n}\n\nconst (\n\tpw = \"yourStrong123_Password\"\n)\n\nvar (\n\tdbs = map[string]*Database{\n\t\t\"pgsql\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"postgres:13\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/postgres-sakila-db/postgres-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/docker-entrypoint-initdb.d\"},\n\t\t\t\t{Name: \"USER\", Value: \"root\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-pgsql\",\n\t\t\t\tCmd:  []string{\"-c\", \"log_statement=all\", \"-c\", \"log_min_duration_statement=0\"},\n\t\t\t\tEnv:  []string{\"POSTGRES_PASSWORD=pw\"},\n\t\t\t},\n\t\t\tDriver:     \"postgres\",\n\t\t\tURL:        \"postgres://postgres:pw@localhost:%s/postgres?sslmode=disable\",\n\t\t\tDockerPort: \"5432/tcp\",\n\t\t\tOpts: []metadata.ReaderOption{\n\t\t\t\tinfos.WithIndexes(false),\n\t\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\t\tinfos.ColumnsColumnSize:         \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)\",\n\t\t\t\t\tinfos.FunctionColumnsColumnSize: \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)\",\n\t\t\t\t}),\n\t\t\t\tinfos.WithSystemSchemas([]string{\"pg_catalog\", \"pg_toast\", \"information_schema\"}),\n\t\t\t},\n\t\t},\n\t\t\"mysql\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"mysql:8\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/mysql-sakila-db/mysql-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/docker-entrypoint-initdb.d\"},\n\t\t\t\t{Name: \"USER\", Value: \"root\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-mysql\",\n\t\t\t\tCmd:  []string{\"--general-log=1\", \"--general-log-file=/var/lib/mysql/mysql.log\"},\n\t\t\t\tEnv:  []string{\"MYSQL_ROOT_PASSWORD=pw\"},\n\t\t\t},\n\t\t\tDriver:     \"mysql\",\n\t\t\tURL:        \"root:pw@(localhost:%s)/mysql?parseTime=true\",\n\t\t\tDockerPort: \"3306/tcp\",\n\t\t\tOpts: []metadata.ReaderOption{\n\t\t\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\t\t\tinfos.WithCheckConstraints(false),\n\t\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\t\tinfos.ColumnsDataType:                 \"column_type\",\n\t\t\t\t\tinfos.ColumnsNumericPrecRadix:         \"10\",\n\t\t\t\t\tinfos.FunctionColumnsNumericPrecRadix: \"10\",\n\t\t\t\t\tinfos.ConstraintIsDeferrable:          \"''\",\n\t\t\t\t\tinfos.ConstraintInitiallyDeferred:     \"''\",\n\t\t\t\t\tinfos.PrivilegesGrantor:               \"''\",\n\t\t\t\t\tinfos.ConstraintJoinCond:              \"AND r.referenced_table_name = f.table_name\",\n\t\t\t\t}),\n\t\t\t\tinfos.WithSystemSchemas([]string{\"mysql\", \"information_schema\", \"performance_schema\", \"sys\"}),\n\t\t\t\tinfos.WithCurrentSchema(\"COALESCE(DATABASE(), '%')\"),\n\t\t\t\tinfos.WithUsagePrivileges(false),\n\t\t\t\tinfos.WithSequences(false),\n\t\t\t},\n\t\t},\n\t\t\"sqlserver\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"mcr.microsoft.com/mssql/server:2019-latest\"},\n\t\t\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/sql-server-sakila-db/sql-server-sakila-schema.sql\"},\n\t\t\t\t{Name: \"TARGET\", Value: \"/schema\"},\n\t\t\t\t{Name: \"USER\", Value: \"mssql:0\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-sqlserver\",\n\t\t\t\tEnv:  []string{\"ACCEPT_EULA=Y\", \"SA_PASSWORD=\" + pw},\n\t\t\t},\n\t\t\tExec:         []string{\"/opt/mssql-tools/bin/sqlcmd\", \"-S\", \"localhost\", \"-U\", \"sa\", \"-P\", pw, \"-d\", \"master\", \"-i\", \"/schema/sql-server-sakila-schema.sql\"},\n\t\t\tDriver:       \"sqlserver\",\n\t\t\tURL:          \"sqlserver://sa:\" + url.QueryEscape(pw) + \"@127.0.0.1:%s?database=sakila\",\n\t\t\tReadinessURL: \"sqlserver://sa:\" + url.QueryEscape(pw) + \"@127.0.0.1:%s\",\n\t\t\tDockerPort:   \"1433/tcp\",\n\t\t\tOpts: []metadata.ReaderOption{\n\t\t\t\tinfos.WithPlaceholder(func(n int) string { return fmt.Sprintf(\"@p%d\", n) }),\n\t\t\t\tinfos.WithIndexes(false),\n\t\t\t\tinfos.WithConstraints(false),\n\t\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\t\tinfos.FunctionsSecurityType: \"''\",\n\t\t\t\t}),\n\t\t\t\tinfos.WithSystemSchemas([]string{\n\t\t\t\t\t\"db_accessadmin\",\n\t\t\t\t\t\"db_backupoperator\",\n\t\t\t\t\t\"db_datareader\",\n\t\t\t\t\t\"db_datawriter\",\n\t\t\t\t\t\"db_ddladmin\",\n\t\t\t\t\t\"db_denydatareader\",\n\t\t\t\t\t\"db_denydatawriter\",\n\t\t\t\t\t\"db_owner\",\n\t\t\t\t\t\"db_securityadmin\",\n\t\t\t\t\t\"INFORMATION_SCHEMA\",\n\t\t\t\t\t\"sys\",\n\t\t\t\t}),\n\t\t\t\tinfos.WithUsagePrivileges(false),\n\t\t\t\tinfos.WithSequences(false),\n\t\t\t},\n\t\t},\n\t\t\"trino\": {\n\t\t\tBuildArgs: []dc.BuildArg{\n\t\t\t\t{Name: \"BASE_IMAGE\", Value: \"trinodb/trino:351\"},\n\t\t\t},\n\t\t\tRunOptions: &dt.RunOptions{\n\t\t\t\tName: \"usql-trino\",\n\t\t\t},\n\t\t\tDriver:     \"trino\",\n\t\t\tURL:        \"http://test@localhost:%s?catalog=tpch&schema=sf1\",\n\t\t\tDockerPort: \"8080/tcp\",\n\t\t\tOpts: []metadata.ReaderOption{\n\t\t\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\t\t\tinfos.WithIndexes(false),\n\t\t\t\tinfos.WithConstraints(false),\n\t\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\t\tinfos.ColumnsColumnSize:               \"0\",\n\t\t\t\t\tinfos.ColumnsNumericScale:             \"0\",\n\t\t\t\t\tinfos.ColumnsNumericPrecRadix:         \"0\",\n\t\t\t\t\tinfos.ColumnsCharOctetLength:          \"0\",\n\t\t\t\t\tinfos.FunctionColumnsColumnSize:       \"0\",\n\t\t\t\t\tinfos.FunctionColumnsNumericScale:     \"0\",\n\t\t\t\t\tinfos.FunctionColumnsNumericPrecRadix: \"0\",\n\t\t\t\t\tinfos.FunctionColumnsCharOctetLength:  \"0\",\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\t}\n\tcleanup bool\n)\n\nfunc TestMain(m *testing.M) {\n\tvar only string\n\tflag.BoolVar(&cleanup, \"cleanup\", true, \"delete containers when finished\")\n\tflag.StringVar(&only, \"dbs\", \"\", \"comma separated list of dbs to test: pgsql, mysql, sqlserver, trino\")\n\tflag.Parse()\n\n\tif only != \"\" {\n\t\trunOnly := map[string]struct{}{}\n\t\tfor _, dbName := range strings.Split(only, \",\") {\n\t\t\tdbName = strings.TrimSpace(dbName)\n\t\t\trunOnly[dbName] = struct{}{}\n\t\t}\n\t\tfor dbName := range dbs {\n\t\t\tif _, ok := runOnly[dbName]; !ok {\n\t\t\t\tdelete(dbs, dbName)\n\t\t\t}\n\t\t}\n\t}\n\n\tpool, err := dt.NewPool(\"\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not connect to docker: %s\", err)\n\t}\n\n\tfor dbName, db := range dbs {\n\t\tvar ok bool\n\t\tdb.Resource, ok = pool.ContainerByName(db.RunOptions.Name)\n\t\tif !ok {\n\t\t\tbuildOpts := &dt.BuildOptions{\n\t\t\t\tContextDir: \"../../testdata/docker\",\n\t\t\t\tBuildArgs:  db.BuildArgs,\n\t\t\t}\n\t\t\tdb.Resource, err = pool.BuildAndRunWithBuildOptions(buildOpts, db.RunOptions)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(\"Could not start resource: \", err)\n\t\t\t}\n\t\t}\n\t\tstate := db.Resource.Container.State.Status\n\t\tif state != \"created\" && state != \"running\" {\n\t\t\tlog.Fatalf(\"Unexpected container state for %s: %s\", dbName, state)\n\t\t}\n\t\turl := db.URL\n\t\tif db.ReadinessURL != \"\" {\n\t\t\turl = db.ReadinessURL\n\t\t}\n\t\tport := db.Resource.GetPort(db.DockerPort)\n\t\tif db.DB, err = waitForDbConnection(db.Driver, pool, url, port); err != nil {\n\t\t\tlog.Fatalf(\"Timed out waiting for %s: %s\", dbName, err)\n\t\t}\n\n\t\tif len(db.Exec) != 0 {\n\t\t\texitCode, err := db.Resource.Exec(db.Exec, dt.ExecOptions{\n\t\t\t\tStdIn:  os.Stdin,\n\t\t\t\tStdOut: os.Stdout,\n\t\t\t\tStdErr: os.Stderr,\n\t\t\t\tTTY:    true,\n\t\t\t})\n\t\t\tif err != nil || exitCode != 0 {\n\t\t\t\tlog.Fatal(\"Could not load schema: \", err)\n\t\t\t}\n\t\t}\n\n\t\t// Reconnect with actual URL if a separate URL for readiness checking was used\n\t\tif db.ReadinessURL != \"\" {\n\t\t\tif db.DB, err = waitForDbConnection(db.Driver, pool, db.URL, port); err != nil {\n\t\t\t\tlog.Fatalf(\"Timed out waiting for %s: %s\", dbName, err)\n\t\t\t}\n\t\t}\n\t\tdb.Reader = infos.New(db.Opts...)(db.DB).(metadata.BasicReader)\n\t}\n\n\tcode := m.Run()\n\n\t// You can't defer this because os.Exit doesn't care for defer\n\tif cleanup {\n\t\tfor _, db := range dbs {\n\t\t\tif err := pool.Purge(db.Resource); err != nil {\n\t\t\t\tlog.Fatal(\"Could not purge resource: \", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tos.Exit(code)\n}\n\nfunc waitForDbConnection(driver string, pool *dt.Pool, url string, port string) (*sql.DB, error) {\n\t// exponential backoff-retry, because the application in the container might not be ready to accept connections yet\n\tvar db *sql.DB\n\tif err := pool.Retry(func() error {\n\t\tvar err error\n\t\tdb, err = sql.Open(driver, fmt.Sprintf(url, port))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn db.Ping()\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\treturn db, nil\n}\n\nfunc TestSchemas(t *testing.T) {\n\texpected := map[string]string{\n\t\t\"pgsql\":     \"information_schema, pg_catalog, pg_toast, public\",\n\t\t\"mysql\":     \"information_schema, mysql, performance_schema, sakila, sys\",\n\t\t\"sqlserver\": \"db_accessadmin, db_backupoperator, db_datareader, db_datawriter, db_ddladmin, db_denydatareader, db_denydatawriter, db_owner, db_securityadmin, dbo, guest, INFORMATION_SCHEMA, sys\",\n\t\t\"trino\":     \"information_schema, sf1, sf100, sf1000, sf10000, sf100000, sf300, sf3000, sf30000, tiny\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tr := db.Reader\n\n\t\tresult, err := r.Schemas(metadata.Filter{WithSystem: true})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s schemas: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Schema)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s schema names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestTables(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\":     \"public\",\n\t\t\"mysql\":     \"sakila\",\n\t\t\"sqlserver\": \"dbo\",\n\t\t\"trino\":     \"sf1\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\":     \"actor, address, category, city, country, customer, film, film_actor, film_category, inventory, language, payment, payment_p2007_01, payment_p2007_02, payment_p2007_03, payment_p2007_04, payment_p2007_05, payment_p2007_06, rental, staff, store, actor_info, customer_list, film_list, nicer_but_slower_film_list, sales_by_film_category, sales_by_store, staff_list\",\n\t\t\"mysql\":     \"actor, address, category, city, country, customer, film, film_actor, film_category, film_text, inventory, language, payment, rental, staff, store, actor_info, customer_list, film_list, nicer_but_slower_film_list, sales_by_film_category, sales_by_store, staff_list\",\n\t\t\"sqlserver\": \"actor, address, category, city, country, customer, film, film_actor, film_category, film_text, inventory, language, payment, rental, staff, store, customer_list, film_list, sales_by_film_category, sales_by_store, staff_list\",\n\t\t\"trino\":     \"customer, lineitem, nation, orders, part, partsupp, region, supplier\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tr := db.Reader\n\n\t\tresult, err := r.Tables(metadata.Filter{Schema: schemas[dbName], Types: []string{\"BASE TABLE\", \"TABLE\", \"VIEW\"}})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s tables: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s table names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestColumns(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\":     \"public\",\n\t\t\"mysql\":     \"sakila\",\n\t\t\"sqlserver\": \"dbo\",\n\t\t\"trino\":     \"sf1\",\n\t}\n\ttables := map[string]string{\n\t\t\"pgsql\":     \"film%\",\n\t\t\"mysql\":     \"film%\",\n\t\t\"sqlserver\": \"film%\",\n\t\t\"trino\":     \"orders\",\n\t}\n\texpectedColumns := map[string]string{\n\t\t\"pgsql\":     \"film_id, title, description, release_year, language_id, original_language_id, rental_duration, rental_rate, length, replacement_cost, rating, last_update, special_features, fulltext, actor_id, film_id, last_update, film_id, category_id, last_update, fid, title, description, category, price, length, rating, actors\",\n\t\t\"mysql\":     \"film_id, title, description, release_year, language_id, original_language_id, rental_duration, rental_rate, length, replacement_cost, rating, special_features, last_update, actor_id, film_id, last_update, film_id, category_id, last_update, FID, title, description, category, price, length, rating, actors, film_id, title, description\",\n\t\t\"sqlserver\": \"film_id, title, description, release_year, language_id, original_language_id, rental_duration, rental_rate, length, replacement_cost, rating, special_features, last_update, actor_id, film_id, last_update, film_id, category_id, last_update, FID, title, description, category, price, length, rating, actors, film_id, title, description\",\n\t\t\"trino\":     \"orderkey, custkey, orderstatus, totalprice, orderdate, orderpriority, clerk, shippriority, comment\",\n\t}\n\texpectedTypes := map[string]string{\n\t\t\"mysql\": \"int unsigned, varchar(255), text, year, int unsigned, int unsigned, tinyint unsigned, decimal(4,2), smallint unsigned, decimal(5,2), enum('G','PG','PG-13','R','NC-17'), set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes'), timestamp, int unsigned, int unsigned, timestamp, int unsigned, int unsigned, timestamp, int unsigned, varchar(255), text, varchar(25), decimal(4,2), smallint unsigned, enum('G','PG','PG-13','R','NC-17'), text, int, varchar(255), text\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tr := db.Reader\n\n\t\tresult, err := r.Columns(metadata.Filter{Schema: schemas[dbName], Parent: tables[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s columns: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\ttypes := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t\ttypes = append(types, result.Get().DataType)\n\t\t}\n\t\tactualColumns := strings.Join(names, \", \")\n\t\tactualTypes := strings.Join(types, \", \")\n\t\tif expected, ok := expectedColumns[dbName]; ok && actualColumns != expected {\n\t\t\tt.Errorf(\"Wrong %s column names, expected:\\n  %v, got:\\n  %v\", dbName, expected, names)\n\t\t}\n\t\tif expected, ok := expectedTypes[dbName]; ok && actualTypes != expected {\n\t\t\tt.Errorf(\"Wrong %s column types, expected:\\n  %v, got:\\n  %v\", dbName, expected, types)\n\t\t}\n\t}\n}\n\nfunc TestFunctions(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\": \"public\",\n\t\t\"mysql\": \"sakila\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\": \"_group_concat, film_in_stock, film_not_in_stock, get_customer_balance, group_concat, inventory_held_by_customer, inventory_in_stock, last_day, last_updated, rewards_report\",\n\t\t\"mysql\": \"film_in_stock, film_not_in_stock, get_customer_balance, inventory_held_by_customer, inventory_in_stock, rewards_report\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.FunctionReader)\n\n\t\tresult, err := r.Functions(metadata.Filter{Schema: schemas[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s functions: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s function names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestFunctionColumns(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\": \"public\",\n\t\t\"mysql\": \"sakila\",\n\t}\n\ttables := map[string]string{\n\t\t\"pgsql\": \"film%\",\n\t\t\"mysql\": \"film%\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\": \"p_film_id, p_store_id, p_film_count, p_film_id, p_store_id, p_film_count\",\n\t\t\"mysql\": \"p_film_id, p_store_id, p_film_count, p_film_id, p_store_id, p_film_count\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.FunctionColumnReader)\n\n\t\tresult, err := r.FunctionColumns(metadata.Filter{Schema: schemas[dbName], Parent: tables[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s function columns: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s function column names, expected:\\n  %v, got:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestIndexes(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"mysql\": \"sakila\",\n\t}\n\texpected := map[string]string{\n\t\t\"mysql\": \"actor.idx_actor_last_name, actor.PRIMARY, address.idx_fk_city_id, address.PRIMARY, category.PRIMARY, city.idx_fk_country_id, city.PRIMARY, country.PRIMARY, customer.idx_fk_address_id, customer.idx_fk_store_id, customer.idx_last_name, customer.PRIMARY, film.idx_fk_language_id, film.idx_fk_original_language_id, film.idx_title, film.PRIMARY, film_actor.idx_fk_film_id, film_actor.PRIMARY, film_category.fk_film_category_category, film_category.PRIMARY, film_text.idx_title_description, film_text.PRIMARY, inventory.idx_fk_film_id, inventory.idx_store_id_film_id, inventory.PRIMARY, language.PRIMARY, payment.fk_payment_rental, payment.idx_fk_customer_id, payment.idx_fk_staff_id, payment.PRIMARY, rental.idx_fk_customer_id, rental.idx_fk_inventory_id, rental.idx_fk_staff_id, rental.PRIMARY, rental.rental_date, staff.idx_fk_address_id, staff.idx_fk_store_id, staff.PRIMARY, store.idx_fk_address_id, store.idx_unique_manager, store.PRIMARY\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.IndexReader)\n\n\t\tresult, err := r.Indexes(metadata.Filter{Schema: schemas[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s indexes: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Table+\".\"+result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s index names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestIndexColumns(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"mysql\": \"sakila\",\n\t}\n\ttables := map[string]string{\n\t\t\"mysql\": \"idx%\",\n\t}\n\texpected := map[string]string{\n\t\t\"mysql\": \"last_name, city_id, country_id, address_id, store_id, last_name, language_id, original_language_id, title, film_id, title, description, film_id, store_id, film_id, customer_id, staff_id, customer_id, inventory_id, staff_id, address_id, store_id, address_id, manager_staff_id\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.IndexColumnReader)\n\n\t\tresult, err := r.IndexColumns(metadata.Filter{Schema: schemas[dbName], Name: tables[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s index columns: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s index column names, expected:\\n  %v, got:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestConstraints(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\": \"public\",\n\t\t\"mysql\": \"sakila\",\n\t}\n\tconstraints := map[string]string{\n\t\t\"pgsql\": \"film%\",\n\t\t\"mysql\": \"film%\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\": \"film.2200_16417_10_not_null, film.2200_16417_12_not_null, film.2200_16417_14_not_null, film.2200_16417_1_not_null, film.2200_16417_2_not_null, film.2200_16417_5_not_null, film.2200_16417_7_not_null, film.2200_16417_8_not_null, film.film_language_id_fkey, film.film_original_language_id_fkey, film.film_pkey, film_actor.2200_16429_1_not_null, film_actor.2200_16429_2_not_null, film_actor.2200_16429_3_not_null, film_actor.film_actor_actor_id_fkey, film_actor.film_actor_film_id_fkey, film_actor.film_actor_pkey, film_category.2200_16433_1_not_null, film_category.2200_16433_2_not_null, film_category.2200_16433_3_not_null, film_category.film_category_category_id_fkey, film_category.film_category_film_id_fkey, film_category.film_category_pkey\",\n\t\t\"mysql\": \"film.fk_film_language, film.fk_film_language_original, film.PRIMARY, film_actor.fk_film_actor_actor, film_actor.fk_film_actor_film, film_actor.PRIMARY, film_category.fk_film_category_category, film_category.fk_film_category_film, film_category.PRIMARY, film_text.PRIMARY\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.ConstraintReader)\n\n\t\tresult, err := r.Constraints(metadata.Filter{Schema: schemas[dbName], Parent: constraints[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s constraints: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Table+\".\"+result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s constraint names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestConstraintColumns(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\": \"public\",\n\t\t\"mysql\": \"sakila\",\n\t}\n\tconstraints := map[string]string{\n\t\t\"pgsql\": \"film%\",\n\t\t\"mysql\": \"film%\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\": \"actor_id, category_id, film_id, film_id, language_id, original_language_id, film_id, film_id, actor_id, film_id, actor_id, actor_id, film_id, film_id, category_id, film_id, category_id, film_id, film_id, category_id, language_id, language_id\",\n\t\t\"mysql\": \"\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.ConstraintColumnReader)\n\n\t\tresult, err := r.ConstraintColumns(metadata.Filter{Schema: schemas[dbName], Name: constraints[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s constraint columns: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s constraint column names, expected:\\n  %v, got:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestReverseConstraints(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\": \"public\",\n\t\t\"mysql\": \"sakila\",\n\t}\n\tconstraints := map[string]string{\n\t\t\"pgsql\": \"film%\",\n\t\t\"mysql\": \"film%\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\": \"film_actor.film_actor_film_id_fkey, film_category.film_category_film_id_fkey, inventory.inventory_film_id_fkey\",\n\t\t\"mysql\": \"film_actor.fk_film_actor_film, film_category.fk_film_category_film, inventory.fk_inventory_film\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.ConstraintReader)\n\n\t\tresult, err := r.Constraints(metadata.Filter{Schema: schemas[dbName], Reference: constraints[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s constraints: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Table+\".\"+result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s reverse constraint names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestSequences(t *testing.T) {\n\tschemas := map[string]string{\n\t\t\"pgsql\": \"public\",\n\t}\n\texpected := map[string]string{\n\t\t\"pgsql\": \"actor_actor_id_seq, address_address_id_seq, category_category_id_seq, city_city_id_seq, country_country_id_seq, customer_customer_id_seq, film_film_id_seq, inventory_inventory_id_seq, language_language_id_seq, payment_payment_id_seq, rental_rental_id_seq, staff_staff_id_seq, store_store_id_seq\",\n\t}\n\tfor dbName, db := range dbs {\n\t\tif schemas[dbName] == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tr := infos.New(db.Opts...)(db.DB).(metadata.SequenceReader)\n\n\t\tresult, err := r.Sequences(metadata.Filter{Schema: schemas[dbName]})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not read %s sequences: %v\", dbName, err)\n\t\t}\n\n\t\tnames := []string{}\n\t\tfor result.Next() {\n\t\t\tnames = append(names, result.Get().Name)\n\t\t}\n\t\tactual := strings.Join(names, \", \")\n\t\tif actual != expected[dbName] {\n\t\t\tt.Errorf(\"Wrong %s sequence names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected[dbName], names)\n\t\t}\n\t}\n}\n\nfunc TestPrivilegeSummaries_NonExistent(t *testing.T) {\n\ttype test struct {\n\t\tName   string\n\t\tDb     *Database\n\t\tSchema string\n\t}\n\ttests := map[string]test{\n\t\t\"pgsql\":     {Db: dbs[\"pgsql\"], Schema: \"public\"},\n\t\t\"mysql\":     {Db: dbs[\"mysql\"], Schema: \"sakila\"},\n\t\t\"sqlserver\": {Db: dbs[\"sqlserver\"], Schema: \"dbo\"},\n\t}\n\n\tfor testName, test := range tests {\n\t\tif test.Db != nil {\n\t\t\tt.Run(testName, func(t *testing.T) {\n\t\t\t\ttable := \"privtest_table\"\n\n\t\t\t\t// Read privileges\n\t\t\t\tr := infos.New(test.Db.Opts...)(test.Db.DB).(metadata.PrivilegeSummaryReader)\n\t\t\t\tresult, err := r.PrivilegeSummaries(metadata.Filter{Schema: test.Schema, Name: table})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"Could not read privileges: %v\", err)\n\t\t\t\t}\n\n\t\t\t\t// Check result\n\t\t\t\tif result.Len() != 0 {\n\t\t\t\t\tt.Errorf(\"Wrong result count, expected:\\n  %d, got:\\n  %d\", 0, result.Len())\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestPrivilegeSummaries(t *testing.T) {\n\ttype test struct {\n\t\tDb             *Database\n\t\tSchema         string\n\t\tUser           string\n\t\tCreate         string\n\t\tCreateUserStmt string\n\t\tDropUserStmt   string\n\t\tGrants         []string\n\t\tWantTable      metadata.ObjectPrivileges\n\t\tWantColumn     metadata.ColumnPrivileges\n\t}\n\tsetDefaults := func(t test) test {\n\t\tif t.User == \"\" {\n\t\t\tt.User = \"privtest_user\"\n\t\t}\n\t\tif t.Create == \"\" {\n\t\t\tt.Create = \"TABLE\"\n\t\t}\n\t\tif t.CreateUserStmt == \"\" {\n\t\t\tt.CreateUserStmt = \"CREATE USER %s\"\n\t\t}\n\t\tif t.DropUserStmt == \"\" {\n\t\t\tt.DropUserStmt = \"DROP USER %s\"\n\t\t}\n\t\tif t.Grants == nil {\n\t\t\tt.Grants = []string{}\n\t\t}\n\t\tif t.WantTable == nil {\n\t\t\tt.WantTable = metadata.ObjectPrivileges{}\n\t\t}\n\t\tif t.WantColumn == nil {\n\t\t\tt.WantColumn = metadata.ColumnPrivileges{}\n\t\t}\n\t\treturn t\n\t}\n\tpostgresDefaultTable := func() metadata.ObjectPrivileges {\n\t\treturn metadata.ObjectPrivileges{\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"SELECT\"},\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"UPDATE\"},\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"DELETE\"},\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"TRUNCATE\"},\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"REFERENCES\"},\n\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"TRIGGER\"},\n\t\t}\n\t}\n\tpostgresDefaultColumn := func(columns []string) metadata.ColumnPrivileges {\n\t\tp := metadata.ColumnPrivileges{}\n\t\tfor _, col := range columns {\n\t\t\tp = append(p,\n\t\t\t\tmetadata.ColumnPrivilege{Column: col, Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: col, Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: col, Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"UPDATE\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: col, Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"REFERENCES\"})\n\t\t}\n\t\treturn p\n\t}\n\ttests := map[string]test{\n\t\t\"pgsql-no-grants\": setDefaults(test{\n\t\t\tDb:         dbs[\"pgsql\"],\n\t\t\tSchema:     \"public\",\n\t\t\tGrants:     []string{},\n\t\t\tWantTable:  postgresDefaultTable(),\n\t\t\tWantColumn: postgresDefaultColumn([]string{\"col1\", \"col2\"}),\n\t\t}),\n\t\t\"pgsql-sequence\": setDefaults(test{\n\t\t\tDb:     dbs[\"pgsql\"],\n\t\t\tSchema: \"public\",\n\t\t\tCreate: \"SEQUENCE\",\n\t\t\tGrants: []string{\"USAGE\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"USAGE\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"postgres\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"USAGE\"},\n\t\t\t},\n\t\t}),\n\t\t\"pgsql-view\": setDefaults(test{\n\t\t\tDb:     dbs[\"pgsql\"],\n\t\t\tSchema: \"public\",\n\t\t\tCreate: \"VIEW\",\n\t\t\tGrants: []string{\"SELECT\", \"INSERT*\"},\n\t\t\tWantTable: append(postgresDefaultTable(),\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t),\n\t\t\tWantColumn: append(\n\t\t\t\tpostgresDefaultColumn([]string{\"col1\", \"col2\"}),\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t),\n\t\t}),\n\t\t\"pgsql-table\": setDefaults(test{\n\t\t\tDb:     dbs[\"pgsql\"],\n\t\t\tSchema: \"public\",\n\t\t\tGrants: []string{\"SELECT\", \"INSERT*\"},\n\t\t\tWantTable: append(postgresDefaultTable(),\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t),\n\t\t\tWantColumn: append(\n\t\t\t\tpostgresDefaultColumn([]string{\"col1\", \"col2\"}),\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t),\n\t\t}),\n\t\t\"pgsql-column\": setDefaults(test{\n\t\t\tDb:        dbs[\"pgsql\"],\n\t\t\tSchema:    \"public\",\n\t\t\tGrants:    []string{\"SELECT(col1)\", \"INSERT(col2)*\"},\n\t\t\tWantTable: postgresDefaultTable(),\n\t\t\tWantColumn: append(\n\t\t\t\tpostgresDefaultColumn([]string{\"col1\", \"col2\"}),\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t),\n\t\t}),\n\t\t\"pgsql-table-column\": setDefaults(test{\n\t\t\tDb:     dbs[\"pgsql\"],\n\t\t\tSchema: \"public\",\n\t\t\tGrants: []string{\"SELECT\", \"INSERT(col1)\"},\n\t\t\tWantTable: append(postgresDefaultTable(),\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t),\n\t\t\tWantColumn: append(\n\t\t\t\tpostgresDefaultColumn([]string{\"col1\", \"col2\"}),\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"INSERT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"postgres\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t),\n\t\t}),\n\t\t\"mysql-no-grants\": setDefaults(test{\n\t\t\tDb:        dbs[\"mysql\"],\n\t\t\tSchema:    \"sakila\",\n\t\t\tUser:      \"'privtest_user'@'%'\",\n\t\t\tGrants:    []string{},\n\t\t\tWantTable: metadata.ObjectPrivileges{},\n\t\t}),\n\t\t\"mysql-view\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tCreate: \"VIEW\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT\", \"INSERT\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"mysql-view-grantable\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tCreate: \"VIEW\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT*\", \"INSERT*\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: true, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"mysql-table\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT\", \"INSERT\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"mysql-table-grantable\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT*\", \"INSERT*\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: true, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"mysql-column\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT(col1)\", \"INSERT(col2)\"},\n\t\t\tWantColumn: metadata.ColumnPrivileges{\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"mysql-column-grantable\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT(col1)*\", \"INSERT(col2)*\"},\n\t\t\tWantColumn: metadata.ColumnPrivileges{\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: true, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"mysql-table-column\": setDefaults(test{\n\t\t\tDb:     dbs[\"mysql\"],\n\t\t\tSchema: \"sakila\",\n\t\t\tUser:   \"'privtest_user'@'%'\",\n\t\t\tGrants: []string{\"SELECT\", \"INSERT(col1)\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t},\n\t\t\tWantColumn: metadata.ColumnPrivileges{\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"'privtest_user'@'%'\", Grantor: \"\", IsGrantable: false, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"sqlserver-no-grants\": setDefaults(test{\n\t\t\tDb:             dbs[\"sqlserver\"],\n\t\t\tSchema:         \"dbo\",\n\t\t\tCreateUserStmt: \"CREATE LOGIN %[1]s WITH PASSWORD = 'yourStrong123_Password'; CREATE USER %[1]s FOR LOGIN %[1]s\",\n\t\t\tDropUserStmt:   \"DROP USER %[1]s; DROP LOGIN %[1]s\",\n\t\t\tGrants:         []string{},\n\t\t\tWantTable:      metadata.ObjectPrivileges{},\n\t\t}),\n\t\t\"sqlserver-view\": setDefaults(test{\n\t\t\tDb:             dbs[\"sqlserver\"],\n\t\t\tSchema:         \"dbo\",\n\t\t\tCreate:         \"VIEW\",\n\t\t\tCreateUserStmt: \"CREATE LOGIN %[1]s WITH PASSWORD = 'yourStrong123_Password'; CREATE USER %[1]s FOR LOGIN %[1]s\",\n\t\t\tDropUserStmt:   \"DROP USER %[1]s; DROP LOGIN %[1]s\",\n\t\t\tGrants:         []string{\"SELECT\", \"INSERT*\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"sqlserver-table\": setDefaults(test{\n\t\t\tDb:             dbs[\"sqlserver\"],\n\t\t\tSchema:         \"dbo\",\n\t\t\tCreateUserStmt: \"CREATE LOGIN %[1]s WITH PASSWORD = 'yourStrong123_Password'; CREATE USER %[1]s FOR LOGIN %[1]s\",\n\t\t\tDropUserStmt:   \"DROP USER %[1]s; DROP LOGIN %[1]s\",\n\t\t\tGrants:         []string{\"SELECT\", \"INSERT*\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: true, PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t}),\n\t\t\"sqlserver-column\": setDefaults(test{\n\t\t\tDb:             dbs[\"sqlserver\"],\n\t\t\tSchema:         \"dbo\",\n\t\t\tCreateUserStmt: \"CREATE LOGIN %[1]s WITH PASSWORD = 'yourStrong123_Password'; CREATE USER %[1]s FOR LOGIN %[1]s\",\n\t\t\tDropUserStmt:   \"DROP USER %[1]s; DROP LOGIN %[1]s\",\n\t\t\tGrants:         []string{\"SELECT(col1)\", \"UPDATE(col2)*\"},\n\t\t\tWantColumn: metadata.ColumnPrivileges{\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col2\", Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: true, PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t}),\n\t\t\"sqlserver-table-column\": setDefaults(test{\n\t\t\tDb:             dbs[\"sqlserver\"],\n\t\t\tSchema:         \"dbo\",\n\t\t\tCreateUserStmt: \"CREATE LOGIN %[1]s WITH PASSWORD = 'yourStrong123_Password'; CREATE USER %[1]s FOR LOGIN %[1]s\",\n\t\t\tDropUserStmt:   \"DROP USER %[1]s; DROP LOGIN %[1]s\",\n\t\t\tGrants:         []string{\"SELECT\", \"UPDATE(col1)\"},\n\t\t\tWantTable: metadata.ObjectPrivileges{\n\t\t\t\tmetadata.ObjectPrivilege{Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: false, PrivilegeType: \"SELECT\"},\n\t\t\t},\n\t\t\tWantColumn: metadata.ColumnPrivileges{\n\t\t\t\tmetadata.ColumnPrivilege{Column: \"col1\", Grantee: \"privtest_user\", Grantor: \"dbo\", IsGrantable: false, PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t}),\n\t}\n\n\tfor testName, test := range tests {\n\t\tif test.Db != nil {\n\t\t\tt.Run(testName, func(t *testing.T) {\n\t\t\t\t// Create user, table and grants\n\t\t\t\tconst name = \"privtest\"\n\t\t\t\tvar query string\n\t\t\t\tvar err error\n\t\t\t\tquery = fmt.Sprintf(test.CreateUserStmt, test.User)\n\t\t\t\t_, err = test.Db.DB.Exec(query)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"Could not CREATE USER:\\n%s\\n%s\", query, err)\n\t\t\t\t}\n\t\t\t\tdefer test.Db.DB.Exec(fmt.Sprintf(test.DropUserStmt, test.User))\n\n\t\t\t\tswitch test.Create {\n\t\t\t\tcase \"TABLE\":\n\t\t\t\t\tquery = fmt.Sprintf(\"CREATE TABLE %s.%s (col1 int, col2 varchar(255))\", test.Schema, name)\n\t\t\t\t\t_, err = test.Db.DB.Exec(query)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Fatalf(\"Could not CREATE TABLE:\\n%s\\n%s\", query, err)\n\t\t\t\t\t}\n\t\t\t\t\tdefer test.Db.DB.Exec(fmt.Sprintf(\"DROP TABLE %s.%s\", test.Schema, name))\n\t\t\t\tcase \"VIEW\":\n\t\t\t\t\tquery = fmt.Sprintf(\"CREATE TABLE %s.%s_table (col1 int, col2 varchar(255))\", test.Schema, name)\n\t\t\t\t\t_, err = test.Db.DB.Exec(query)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Fatalf(\"Could not CREATE TABLE:\\n%s\\n%s\", query, err)\n\t\t\t\t\t}\n\t\t\t\t\tdefer test.Db.DB.Exec(fmt.Sprintf(\"DROP TABLE %s.%s_table\", test.Schema, name))\n\t\t\t\t\tquery = fmt.Sprintf(\"CREATE VIEW %s.%s AS SELECT * FROM %[1]s.%[2]s_table\", test.Schema, name)\n\t\t\t\t\t_, err = test.Db.DB.Exec(query)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Fatalf(\"Could not CREATE VIEW:\\n%s\\n%s\", query, err)\n\t\t\t\t\t}\n\t\t\t\t\tdefer test.Db.DB.Exec(fmt.Sprintf(\"DROP VIEW %s.%s\", test.Schema, name))\n\t\t\t\tcase \"SEQUENCE\":\n\t\t\t\t\tquery = fmt.Sprintf(\"CREATE SEQUENCE %s.%s\", test.Schema, name)\n\t\t\t\t\t_, err = test.Db.DB.Exec(query)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Fatalf(\"Could not CREATE SEQUENCE:\\n%s\\n%s\", query, err)\n\t\t\t\t\t}\n\t\t\t\t\tdefer test.Db.DB.Exec(fmt.Sprintf(\"DROP SEQUENCE %s.%s\", test.Schema, name))\n\t\t\t\t}\n\n\t\t\t\tfor _, grant := range test.Grants {\n\t\t\t\t\tisGrantable := false\n\t\t\t\t\tif grant[len(grant)-1] == '*' {\n\t\t\t\t\t\tisGrantable = true\n\t\t\t\t\t\tgrant = grant[:len(grant)-1]\n\t\t\t\t\t}\n\t\t\t\t\tquery = fmt.Sprintf(\"GRANT %s ON %s.%s TO %s\", grant, test.Schema, name, test.User)\n\t\t\t\t\tif isGrantable {\n\t\t\t\t\t\tquery += \" WITH GRANT OPTION\"\n\t\t\t\t\t}\n\t\t\t\t\t_, err = test.Db.DB.Exec(query)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Fatalf(\"Could not GRANT %s:\\n%s\\n%s\", grant, query, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Read privileges\n\t\t\t\tr := infos.New(test.Db.Opts...)(test.Db.DB).(metadata.PrivilegeSummaryReader)\n\t\t\t\ttypes := []string{\"TABLE\", \"BASE TABLE\", \"SYSTEM TABLE\", \"SYNONYM\", \"LOCAL TEMPORARY\", \"GLOBAL TEMPORARY\", \"VIEW\", \"SYSTEM VIEW\", \"MATERIALIZED VIEW\", \"SEQUENCE\"}\n\t\t\t\tresult, err := r.PrivilegeSummaries(metadata.Filter{Schema: test.Schema, Name: name, Types: types})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"Could not read privileges: %v\", err)\n\t\t\t\t}\n\n\t\t\t\t// Check result\n\t\t\t\tif result.Len() != 1 {\n\t\t\t\t\tt.Fatalf(\"Wrong result count\\nWant:\\t%d\\nGot:\\t%d\\n\", 1, result.Len())\n\t\t\t\t}\n\t\t\t\tresult.Next()\n\t\t\t\tif result.Get().Schema != test.Schema {\n\t\t\t\t\tt.Errorf(\"Wrong schema!\\nWant:\\t%s\\nGot:\\t%s\\n\", test.Schema, result.Get().Schema)\n\t\t\t\t}\n\t\t\t\tif result.Get().Name != name {\n\t\t\t\t\tt.Errorf(\"Wrong table!\\nWant:\\t%s\\nGot:\\t%s\\n\", name, result.Get().Name)\n\t\t\t\t}\n\t\t\t\twant := \"\"\n\t\t\t\tswitch test.Create {\n\t\t\t\tcase \"TABLE\":\n\t\t\t\t\twant = \"BASE TABLE\"\n\t\t\t\tdefault:\n\t\t\t\t\twant = test.Create\n\t\t\t\t}\n\t\t\t\tif result.Get().ObjectType != want {\n\t\t\t\t\tt.Errorf(\"Wrong Type!\\nWant:\\t%s\\nGot:\\t%s\\n\", want, result.Get().ObjectType)\n\t\t\t\t}\n\t\t\t\tgotTablePrivileges := result.Get().ObjectPrivileges\n\t\t\t\tsort.Sort(gotTablePrivileges)\n\t\t\t\tsort.Sort(test.WantTable)\n\t\t\t\tif diff := cmp.Diff(test.WantTable, gotTablePrivileges); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Wrong object privileges!\\n(-expected, +got):\\n%s\", diff)\n\t\t\t\t}\n\n\t\t\t\tgotColumnPrivileges := result.Get().ColumnPrivileges\n\t\t\t\tsort.Sort(gotColumnPrivileges)\n\t\t\t\tsort.Sort(test.WantColumn)\n\t\t\t\tif diff := cmp.Diff(test.WantColumn, gotColumnPrivileges); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Wrong column privileges!\\n(-expected, +got):\\n%s\", diff)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "drivers/metadata/metadata.go",
    "content": "package metadata\n\nimport (\n\t\"strings\"\n\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/text\"\n)\n\n// ExtendedReader of all database metadata in a structured format.\ntype ExtendedReader interface {\n\tCatalogReader\n\tSchemaReader\n\tTableReader\n\tColumnReader\n\tColumnStatReader\n\tIndexReader\n\tIndexColumnReader\n\tTriggerReader\n\tConstraintReader\n\tConstraintColumnReader\n\tFunctionReader\n\tFunctionColumnReader\n\tSequenceReader\n\tPrivilegeSummaryReader\n}\n\n// BasicReader of common database metadata like schemas, tables and columns.\ntype BasicReader interface {\n\tSchemaReader\n\tTableReader\n\tColumnReader\n}\n\n// CatalogReader lists database schemas.\ntype CatalogReader interface {\n\tReader\n\tCatalogs(Filter) (*CatalogSet, error)\n}\n\n// SchemaReader lists database schemas.\ntype SchemaReader interface {\n\tReader\n\tSchemas(Filter) (*SchemaSet, error)\n}\n\n// TableReader lists database tables.\ntype TableReader interface {\n\tReader\n\tTables(Filter) (*TableSet, error)\n}\n\n// ColumnReader lists table columns.\ntype ColumnReader interface {\n\tReader\n\tColumns(Filter) (*ColumnSet, error)\n}\n\n// ColumnStatsReader lists table column statistics.\ntype ColumnStatReader interface {\n\tReader\n\tColumnStats(Filter) (*ColumnStatSet, error)\n}\n\n// IndexReader lists table indexes.\ntype IndexReader interface {\n\tReader\n\tIndexes(Filter) (*IndexSet, error)\n}\n\n// IndexColumnReader lists index columns.\ntype IndexColumnReader interface {\n\tReader\n\tIndexColumns(Filter) (*IndexColumnSet, error)\n}\n\n// TriggerReader lists table triggers.\ntype TriggerReader interface {\n\tReader\n\tTriggers(Filter) (*TriggerSet, error)\n}\n\n// ConstraintReader lists table constraints.\ntype ConstraintReader interface {\n\tReader\n\tConstraints(Filter) (*ConstraintSet, error)\n}\n\n// ConstraintColumnReader lists constraint columns.\ntype ConstraintColumnReader interface {\n\tReader\n\tConstraintColumns(Filter) (*ConstraintColumnSet, error)\n}\n\n// FunctionReader lists database functions.\ntype FunctionReader interface {\n\tReader\n\tFunctions(Filter) (*FunctionSet, error)\n}\n\n// FunctionColumnReader lists function parameters.\ntype FunctionColumnReader interface {\n\tReader\n\tFunctionColumns(Filter) (*FunctionColumnSet, error)\n}\n\n// SequenceReader lists sequences.\ntype SequenceReader interface {\n\tReader\n\tSequences(Filter) (*SequenceSet, error)\n}\n\n// PrivilegeSummaryReader lists summaries of privileges granted on tables, views and sequences.\ntype PrivilegeSummaryReader interface {\n\tReader\n\tPrivilegeSummaries(Filter) (*PrivilegeSummarySet, error)\n}\n\n// Reader of any database metadata in a structured format.\ntype Reader interface{}\n\n// Filter objects returned by Readers\ntype Filter struct {\n\t// Catalog name pattern that objects must belong to;\n\t// use Name to filter catalogs by name\n\tCatalog string\n\t// Schema name pattern that objects must belong to;\n\t// use Name to filter schemas by name\n\tSchema string\n\t// Parent name pattern that objects must belong to;\n\t// does not apply to schema and catalog containing matching objects\n\tParent string\n\t// Reference name pattern of other objects referencing this one,\n\tReference string\n\t// Name pattern that object name must match\n\tName string\n\t// Types of the object\n\tTypes []string\n\t// WithSystem objects\n\tWithSystem bool\n\t// OnlyVisible objects\n\tOnlyVisible bool\n}\n\n// Writer of database metadata in a human readable format.\ntype Writer interface {\n\t// DescribeFunctions \\df, \\dfa, \\dfn, \\dft, \\dfw, etc.\n\tDescribeFunctions(*dburl.URL, string, string, bool, bool) error\n\t// DescribeTableDetails \\d foo\n\tDescribeTableDetails(*dburl.URL, string, bool, bool) error\n\t// ListAllDbs \\l\n\tListAllDbs(*dburl.URL, string, bool) error\n\t// ListTables \\dt, \\dv, \\dm, etc.\n\tListTables(*dburl.URL, string, string, bool, bool) error\n\t// ListSchemas \\dn\n\tListSchemas(*dburl.URL, string, bool, bool) error\n\t// ListIndexes \\di\n\tListIndexes(*dburl.URL, string, bool, bool) error\n\t// ShowStats \\ss\n\tShowStats(*dburl.URL, string, string, bool, int) error\n\t// ListPrivilegeSummaries \\dp\n\tListPrivilegeSummaries(*dburl.URL, string, bool) error\n}\n\ntype CatalogSet struct {\n\tresultSet\n}\n\nfunc NewCatalogSet(v []Catalog) *CatalogSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &CatalogSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\"Catalog\"},\n\t\t},\n\t}\n}\n\nfunc NewCatalogSetWithColumns(v []Result, cols []string) *CatalogSet {\n\treturn &CatalogSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: v,\n\t\t\tcolumns: cols,\n\t\t},\n\t}\n}\n\ntype CatalogProvider interface {\n\tGetCatalog() Catalog\n}\n\nfunc (s CatalogSet) Get() Catalog {\n\tr := s.results[s.current-1]\n\treturn r.(CatalogProvider).GetCatalog()\n}\n\ntype Catalog struct {\n\tCatalog string\n}\n\nfunc (s Catalog) Values() []interface{} {\n\treturn []interface{}{s.Catalog}\n}\n\nfunc (s Catalog) GetCatalog() Catalog {\n\treturn s\n}\n\ntype SchemaSet struct {\n\tresultSet\n}\n\nfunc NewSchemaSet(v []Schema) *SchemaSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &SchemaSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\"Schema\", \"Catalog\"},\n\t\t},\n\t}\n}\n\nfunc (s SchemaSet) Get() *Schema {\n\treturn s.results[s.current-1].(*Schema)\n}\n\ntype Schema struct {\n\tSchema  string\n\tCatalog string\n}\n\nfunc (s Schema) Values() []interface{} {\n\treturn []interface{}{s.Schema, s.Catalog}\n}\n\ntype TableSet struct {\n\tresultSet\n}\n\nfunc NewTableSet(v []Table) *TableSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &TableSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\n\t\t\t\t\"Name\",\n\t\t\t\t\"Type\",\n\n\t\t\t\t\"Rows\",\n\t\t\t\t\"Size\",\n\t\t\t\t\"Comment\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (t TableSet) Get() *Table {\n\treturn t.results[t.current-1].(*Table)\n}\n\ntype Table struct {\n\tCatalog string\n\tSchema  string\n\tName    string\n\tType    string\n\tRows    int64\n\tSize    string\n\tComment string\n}\n\nfunc (t Table) Values() []interface{} {\n\treturn []interface{}{\n\t\tt.Catalog,\n\t\tt.Schema,\n\t\tt.Name,\n\t\tt.Type,\n\t\tt.Rows,\n\t\tt.Size,\n\t\tt.Comment,\n\t}\n}\n\ntype ColumnSet struct {\n\tresultSet\n}\n\nfunc NewColumnSet(v []Column) *ColumnSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &ColumnSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Table\",\n\n\t\t\t\t\"Name\",\n\t\t\t\t\"Type\",\n\t\t\t\t\"Nullable\",\n\t\t\t\t\"Default\",\n\n\t\t\t\t\"Size\",\n\t\t\t\t\"Decimal Digits\",\n\t\t\t\t\"Precision Radix\",\n\t\t\t\t\"Octet Length\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (c ColumnSet) Get() *Column {\n\treturn c.results[c.current-1].(*Column)\n}\n\ntype Column struct {\n\tCatalog         string\n\tSchema          string\n\tTable           string\n\tName            string\n\tOrdinalPosition int\n\tDataType        string\n\t// ScanType        reflect.Type\n\tDefault         string\n\tColumnSize      int\n\tDecimalDigits   int\n\tNumPrecRadix    int\n\tCharOctetLength int\n\tIsNullable      Bool\n}\n\ntype Bool string\n\nvar (\n\tUNKNOWN Bool = \"\"\n\tYES     Bool = \"YES\"\n\tNO      Bool = \"NO\"\n)\n\nfunc (c Column) Values() []interface{} {\n\treturn []interface{}{\n\t\tc.Catalog,\n\t\tc.Schema,\n\t\tc.Table,\n\t\tc.Name,\n\t\tc.DataType,\n\t\tc.IsNullable,\n\t\tc.Default,\n\t\tc.ColumnSize,\n\t\tc.DecimalDigits,\n\t\tc.NumPrecRadix,\n\t\tc.CharOctetLength,\n\t}\n}\n\ntype ColumnStatSet struct {\n\tresultSet\n}\n\nfunc NewColumnStatSet(v []ColumnStat) *ColumnStatSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &ColumnStatSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Table\",\n\t\t\t\t\"Name\",\n\n\t\t\t\t\"Average width\",\n\t\t\t\t\"Nulls fraction\",\n\t\t\t\t\"Distinct values\",\n\t\t\t\t\"Minimum value\",\n\t\t\t\t\"Maximum value\",\n\t\t\t\t\"Mean value\",\n\t\t\t\t\"Top N common values\",\n\t\t\t\t\"Top N values freqs\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (c ColumnStatSet) Get() *ColumnStat {\n\treturn c.results[c.current-1].(*ColumnStat)\n}\n\ntype ColumnStat struct {\n\tCatalog     string\n\tSchema      string\n\tTable       string\n\tName        string\n\tAvgWidth    int\n\tNullFrac    float64\n\tNumDistinct int64\n\tMin         string\n\tMax         string\n\tMean        string\n\tTopN        []string\n\tTopNFreqs   []float64\n}\n\nfunc (c ColumnStat) Values() []interface{} {\n\treturn []interface{}{\n\t\tc.Catalog,\n\t\tc.Schema,\n\t\tc.Table,\n\t\tc.Name,\n\t\tc.AvgWidth,\n\t\tc.NullFrac,\n\t\tc.NumDistinct,\n\t\tc.Min,\n\t\tc.Max,\n\t\tc.Mean,\n\t\tc.TopN,\n\t\tc.TopNFreqs,\n\t}\n}\n\ntype IndexSet struct {\n\tresultSet\n}\n\nfunc NewIndexSet(v []Index) *IndexSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &IndexSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\n\t\t\t\t\"Name\",\n\t\t\t\t\"Table\",\n\n\t\t\t\t\"Is primary\",\n\t\t\t\t\"Is unique\",\n\t\t\t\t\"Type\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (i IndexSet) Get() *Index {\n\treturn i.results[i.current-1].(*Index)\n}\n\ntype Index struct {\n\tCatalog   string\n\tSchema    string\n\tTable     string\n\tName      string\n\tIsPrimary Bool\n\tIsUnique  Bool\n\tType      string\n\tColumns   string\n}\n\nfunc (i Index) Values() []interface{} {\n\treturn []interface{}{\n\t\ti.Catalog,\n\t\ti.Schema,\n\t\ti.Name,\n\t\ti.Table,\n\t\ti.IsPrimary,\n\t\ti.IsUnique,\n\t\ti.Type,\n\t}\n}\n\ntype IndexColumnSet struct {\n\tresultSet\n}\n\nfunc NewIndexColumnSet(v []IndexColumn) *IndexColumnSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &IndexColumnSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Table\",\n\t\t\t\t\"Index name\",\n\n\t\t\t\t\"Name\",\n\t\t\t\t\"Data type\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (c IndexColumnSet) Get() *IndexColumn {\n\treturn c.results[c.current-1].(*IndexColumn)\n}\n\ntype IndexColumn struct {\n\tCatalog         string\n\tSchema          string\n\tTable           string\n\tIndexName       string\n\tName            string\n\tDataType        string\n\tOrdinalPosition int\n}\n\nfunc (c IndexColumn) Values() []interface{} {\n\treturn []interface{}{\n\t\tc.Catalog,\n\t\tc.Schema,\n\t\tc.Table,\n\t\tc.IndexName,\n\t\tc.Name,\n\t\tc.DataType,\n\t}\n}\n\ntype ConstraintSet struct {\n\tresultSet\n}\n\nfunc NewConstraintSet(v []Constraint) *ConstraintSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &ConstraintSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Table\",\n\t\t\t\t\"Name\",\n\n\t\t\t\t\"Type\",\n\t\t\t\t\"Is deferrable\",\n\t\t\t\t\"Initially deferred\",\n\n\t\t\t\t\"Foreign catalog\",\n\t\t\t\t\"Foreign schema\",\n\t\t\t\t\"Foreign table\",\n\t\t\t\t\"Foreign name\",\n\t\t\t\t\"Match type\",\n\t\t\t\t\"Update rule\",\n\t\t\t\t\"Delete rule\",\n\n\t\t\t\t\"Check Clause\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (i ConstraintSet) Get() *Constraint {\n\treturn i.results[i.current-1].(*Constraint)\n}\n\ntype Constraint struct {\n\tCatalog string\n\tSchema  string\n\tTable   string\n\tName    string\n\tType    string\n\n\tIsDeferrable        Bool\n\tIsInitiallyDeferred Bool\n\n\tForeignCatalog string\n\tForeignSchema  string\n\tForeignTable   string\n\tForeignName    string\n\tMatchType      string\n\tUpdateRule     string\n\tDeleteRule     string\n\n\tCheckClause string\n}\n\nfunc (i Constraint) Values() []interface{} {\n\treturn []interface{}{\n\t\ti.Catalog,\n\t\ti.Schema,\n\t\ti.Table,\n\t\ti.Name,\n\t\ti.Type,\n\t\ti.IsDeferrable,\n\t\ti.IsInitiallyDeferred,\n\t\ti.ForeignCatalog,\n\t\ti.ForeignSchema,\n\t\ti.ForeignTable,\n\t\ti.ForeignName,\n\t\ti.MatchType,\n\t\ti.UpdateRule,\n\t\ti.DeleteRule,\n\t}\n}\n\ntype ConstraintColumnSet struct {\n\tresultSet\n}\n\nfunc NewConstraintColumnSet(v []ConstraintColumn) *ConstraintColumnSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &ConstraintColumnSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Table\",\n\t\t\t\t\"Constraint\",\n\t\t\t\t\"Name\",\n\t\t\t\t\"Foreign Catalog\",\n\t\t\t\t\"Foreign Schema\",\n\t\t\t\t\"Foreign Table\",\n\t\t\t\t\"Foreign Constraint\",\n\t\t\t\t\"Foreign Name\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (c ConstraintColumnSet) Get() *ConstraintColumn {\n\treturn c.results[c.current-1].(*ConstraintColumn)\n}\n\ntype ConstraintColumn struct {\n\tCatalog         string\n\tSchema          string\n\tTable           string\n\tConstraint      string\n\tName            string\n\tOrdinalPosition int\n\n\tForeignCatalog    string\n\tForeignSchema     string\n\tForeignTable      string\n\tForeignConstraint string\n\tForeignName       string\n}\n\nfunc (c ConstraintColumn) Values() []interface{} {\n\treturn []interface{}{\n\t\tc.Catalog,\n\t\tc.Schema,\n\t\tc.Table,\n\t\tc.Constraint,\n\t\tc.Name,\n\t\tc.ForeignCatalog,\n\t\tc.ForeignSchema,\n\t\tc.ForeignTable,\n\t\tc.ForeignConstraint,\n\t\tc.ForeignName,\n\t}\n}\n\ntype FunctionSet struct {\n\tresultSet\n}\n\nfunc NewFunctionSet(v []Function) *FunctionSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &FunctionSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\n\t\t\t\t\"Name\",\n\t\t\t\t\"Result data type\",\n\t\t\t\t\"Argument data types\",\n\t\t\t\t\"Type\",\n\n\t\t\t\t\"Volatility\",\n\t\t\t\t\"Security\",\n\t\t\t\t\"Language\",\n\t\t\t\t\"Source code\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (f FunctionSet) Get() *Function {\n\treturn f.results[f.current-1].(*Function)\n}\n\ntype Function struct {\n\tCatalog    string\n\tSchema     string\n\tName       string\n\tResultType string\n\tArgTypes   string\n\tType       string\n\tVolatility string\n\tSecurity   string\n\tLanguage   string\n\tSource     string\n\n\tSpecificName string\n}\n\nfunc (f Function) Values() []interface{} {\n\treturn []interface{}{\n\t\tf.Catalog,\n\t\tf.Schema,\n\t\tf.Name,\n\t\tf.ResultType,\n\t\tf.ArgTypes,\n\t\tf.Type,\n\t\tf.Volatility,\n\t\tf.Security,\n\t\tf.Language,\n\t\tf.Source,\n\t}\n}\n\ntype FunctionColumnSet struct {\n\tresultSet\n}\n\nfunc NewFunctionColumnSet(v []FunctionColumn) *FunctionColumnSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &FunctionColumnSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Function name\",\n\n\t\t\t\t\"Name\",\n\t\t\t\t\"Type\",\n\t\t\t\t\"Data type\",\n\n\t\t\t\t\"Size\",\n\t\t\t\t\"Decimal Digits\",\n\t\t\t\t\"Precision Radix\",\n\t\t\t\t\"Octet Length\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (c FunctionColumnSet) Get() *FunctionColumn {\n\treturn c.results[c.current-1].(*FunctionColumn)\n}\n\ntype FunctionColumn struct {\n\tCatalog         string\n\tSchema          string\n\tTable           string\n\tName            string\n\tFunctionName    string\n\tOrdinalPosition int\n\tType            string\n\tDataType        string\n\t// ScanType        reflect.Type\n\tColumnSize      int\n\tDecimalDigits   int\n\tNumPrecRadix    int\n\tCharOctetLength int\n}\n\nfunc (c FunctionColumn) Values() []interface{} {\n\treturn []interface{}{\n\t\tc.Catalog,\n\t\tc.Schema,\n\t\tc.FunctionName,\n\t\tc.Name,\n\t\tc.Type,\n\t\tc.DataType,\n\t\tc.ColumnSize,\n\t\tc.DecimalDigits,\n\t\tc.NumPrecRadix,\n\t\tc.CharOctetLength,\n\t}\n}\n\ntype SequenceSet struct {\n\tresultSet\n}\n\nfunc NewSequenceSet(v []Sequence) *SequenceSet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &SequenceSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Type\",\n\t\t\t\t\"Start\",\n\t\t\t\t\"Min\",\n\t\t\t\t\"Max\",\n\t\t\t\t\"Increment\",\n\t\t\t\t\"Cycles?\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (s SequenceSet) Get() *Sequence {\n\treturn s.results[s.current-1].(*Sequence)\n}\n\ntype Sequence struct {\n\tCatalog   string\n\tSchema    string\n\tName      string\n\tDataType  string\n\tStart     string\n\tMin       string\n\tMax       string\n\tIncrement string\n\tCycles    Bool\n}\n\nfunc (s Sequence) Values() []interface{} {\n\treturn []interface{}{\n\t\ts.DataType,\n\t\ts.Start,\n\t\ts.Min,\n\t\ts.Max,\n\t\ts.Increment,\n\t\ts.Cycles,\n\t}\n}\n\ntype PrivilegeSummarySet struct {\n\tresultSet\n}\n\nfunc NewPrivilegeSummarySet(v []PrivilegeSummary) *PrivilegeSummarySet {\n\tr := make([]Result, len(v))\n\tfor i := range v {\n\t\tr[i] = &v[i]\n\t}\n\treturn &PrivilegeSummarySet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Name\",\n\t\t\t\t\"Type\",\n\t\t\t\t\"Access privileges\",\n\t\t\t\t\"Column privileges\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (s PrivilegeSummarySet) Get() *PrivilegeSummary {\n\treturn s.results[s.current-1].(*PrivilegeSummary)\n}\n\n// PrivilegeSummary summarizes the privileges granted on a database object\ntype PrivilegeSummary struct {\n\tCatalog          string\n\tSchema           string\n\tName             string\n\tObjectType       string\n\tObjectPrivileges ObjectPrivileges\n\tColumnPrivileges ColumnPrivileges\n}\n\nfunc (s PrivilegeSummary) Values() []interface{} {\n\treturn []interface{}{\n\t\ts.Catalog,\n\t\ts.Schema,\n\t\ts.Name,\n\t\ts.ObjectType,\n\t\ts.ObjectPrivileges,\n\t\ts.ColumnPrivileges,\n\t}\n}\n\n// ObjectPrivilege represents a privilege granted on a database object.\ntype ObjectPrivilege struct {\n\tGrantee       string\n\tGrantor       string\n\tPrivilegeType string\n\tIsGrantable   bool\n}\n\n// ColumnPrivilege represents a privilege granted on a column.\ntype ColumnPrivilege struct {\n\tColumn        string\n\tGrantee       string\n\tGrantor       string\n\tPrivilegeType string\n\tIsGrantable   bool\n}\n\n// ObjectPrivileges represents privileges granted on a database object.\n// The privileges are assumed to be sorted. Otherwise the\n// String() method will fail.\ntype ObjectPrivileges []ObjectPrivilege\n\n// ColumnPrivileges represents privileges granted on a column.\n// The privileges are assumed to be sorted. Otherwise the\n// String() method will fail.\ntype ColumnPrivileges []ColumnPrivilege\n\nfunc (p ObjectPrivileges) Len() int      { return len(p) }\nfunc (p ObjectPrivileges) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\nfunc (p ObjectPrivileges) Less(i, j int) bool {\n\tswitch {\n\tcase p[i].Grantee != p[j].Grantee:\n\t\treturn p[i].Grantee < p[j].Grantee\n\tcase p[i].Grantor != p[j].Grantor:\n\t\treturn p[i].Grantor < p[j].Grantor\n\t}\n\treturn p[i].PrivilegeType < p[j].PrivilegeType\n}\n\n// String returns a string representation of ObjectPrivileges.\n// Assumes the ObjectPrivileges to be sorted.\nfunc (p ObjectPrivileges) String() string {\n\tif len(p) == 0 {\n\t\treturn \"\"\n\t}\n\n\tlines := []string{}\n\ttypes := []string{}\n\tfor i := range p {\n\t\tswitch {\n\t\t// Is last privilege or next privilege has new grantee or grantor; finalize line\n\t\tcase i == len(p)-1 || p[i].Grantee != p[i+1].Grantee || p[i].Grantor != p[i+1].Grantor:\n\t\t\ttypes = append(types, typeStr(p[i].PrivilegeType, p[i].IsGrantable))\n\t\t\tlines = append(lines, lineStr(p[i].Grantee, p[i].Grantor, types))\n\t\t\ttypes = types[:0]\n\t\tdefault:\n\t\t\ttypes = append(types, typeStr(p[i].PrivilegeType, p[i].IsGrantable))\n\t\t}\n\t}\n\treturn strings.Join(lines, \"\\n\")\n}\n\nfunc (p ColumnPrivileges) Len() int      { return len(p) }\nfunc (p ColumnPrivileges) Swap(i, j int) { p[i], p[j] = p[j], p[i] }\nfunc (p ColumnPrivileges) Less(i, j int) bool {\n\tswitch {\n\tcase p[i].Column != p[j].Column:\n\t\treturn p[i].Column < p[j].Column\n\tcase p[i].Grantee != p[j].Grantee:\n\t\treturn p[i].Grantee < p[j].Grantee\n\tcase p[i].Grantor != p[j].Grantor:\n\t\treturn p[i].Grantor < p[j].Grantor\n\t}\n\treturn p[i].PrivilegeType < p[j].PrivilegeType\n}\n\n// String returns a string representation of ColumnPrivileges.\n// Assumes the ColumnPrivileges to be sorted.\nfunc (p ColumnPrivileges) String() string {\n\tif len(p) == 0 {\n\t\treturn \"\"\n\t}\n\n\tcolBlocks := []string{}\n\tlines := []string{}\n\ttypes := []string{}\n\tfor i := range p {\n\t\tswitch {\n\t\t// Is last privilege or next privilege has new column; finalize column block\n\t\tcase i == len(p)-1 || p[i].Column != p[i+1].Column:\n\t\t\ttypes = append(types, typeStr(p[i].PrivilegeType, p[i].IsGrantable))\n\t\t\tlines = append(lines, \"  \"+lineStr(p[i].Grantee, p[i].Grantor, types))\n\t\t\tcolBlocks = append(colBlocks, p[i].Column+\":\\n\"+strings.Join(lines, \"\\n\"))\n\t\t\tlines = lines[:0]\n\t\t\ttypes = types[:0]\n\t\t// Next privilege has new grantee or grantor; finalize line\n\t\tcase p[i].Grantee != p[i+1].Grantee || p[i].Grantor != p[i+1].Grantor:\n\t\t\ttypes = append(types, typeStr(p[i].PrivilegeType, p[i].IsGrantable))\n\t\t\tlines = append(lines, \"  \"+lineStr(p[i].Grantee, p[i].Grantor, types))\n\t\t\ttypes = types[:0]\n\t\tdefault:\n\t\t\ttypes = append(types, typeStr(p[i].PrivilegeType, p[i].IsGrantable))\n\t\t}\n\t}\n\treturn strings.Join(colBlocks, \"\\n\")\n}\n\n// typeStr appends an asterisk suffix to grantable privileges\nfunc typeStr(privilege string, grantable bool) string {\n\tif grantable {\n\t\treturn privilege + \"*\"\n\t} else {\n\t\treturn privilege\n\t}\n}\n\n// lineStr compiles grantee, grantor and privilege types into a line of output\nfunc lineStr(grantee, grantor string, types []string) string {\n\tif grantor != \"\" {\n\t\treturn grantee + \"=\" + strings.Join(types, \",\") + \"/\" + grantor\n\t} else {\n\t\treturn grantee + \"=\" + strings.Join(types, \",\")\n\t}\n}\n\ntype resultSet struct {\n\tresults    []Result\n\tcolumns    []string\n\tcurrent    int\n\tfilter     func(Result) bool\n\tscanValues func(Result) []interface{}\n}\n\ntype Result interface {\n\tValues() []interface{}\n}\n\nfunc (r *resultSet) SetFilter(f func(Result) bool) {\n\tr.filter = f\n}\n\nfunc (r *resultSet) SetColumns(c []string) {\n\tr.columns = c\n}\n\nfunc (r *resultSet) SetScanValues(s func(Result) []interface{}) {\n\tr.scanValues = s\n}\n\nfunc (r *resultSet) Len() int {\n\tif r.filter == nil {\n\t\treturn len(r.results)\n\t}\n\tlen := 0\n\tfor _, rec := range r.results {\n\t\tif r.filter(rec) {\n\t\t\tlen++\n\t\t}\n\t}\n\treturn len\n}\n\nfunc (r *resultSet) Reset() {\n\tr.current = 0\n}\n\nfunc (r *resultSet) Next() bool {\n\tr.current++\n\tif r.filter != nil {\n\t\tfor r.current <= len(r.results) && !r.filter(r.results[r.current-1]) {\n\t\t\tr.current++\n\t\t}\n\t}\n\treturn r.current <= len(r.results)\n}\n\nfunc (r resultSet) Columns() ([]string, error) {\n\treturn r.columns, nil\n}\n\nfunc (r resultSet) Scan(dest ...interface{}) error {\n\tvar v []interface{}\n\tif r.scanValues == nil {\n\t\tv = r.results[r.current-1].Values()\n\t} else {\n\t\tv = r.scanValues(r.results[r.current-1])\n\t}\n\tif len(v) != len(dest) {\n\t\treturn text.ErrWrongNumberOfArguments\n\t}\n\tfor i, d := range dest {\n\t\tp := d.(*interface{})\n\t\t*p = v[i]\n\t}\n\treturn nil\n}\n\nfunc (r resultSet) Close() error {\n\treturn nil\n}\n\nfunc (r resultSet) Err() error {\n\treturn nil\n}\n\nfunc (r resultSet) NextResultSet() bool {\n\treturn false\n}\n\ntype Trigger struct {\n\tCatalog    string\n\tSchema     string\n\tTable      string\n\tName       string\n\tDefinition string\n}\n\nfunc (t Trigger) Values() []interface{} {\n\treturn []interface{}{\n\t\tt.Catalog,\n\t\tt.Schema,\n\t\tt.Table,\n\t\tt.Name,\n\t\tt.Definition,\n\t}\n}\n\ntype TriggerSet struct {\n\tresultSet\n}\n\nfunc NewTriggerSet(t []Trigger) *TriggerSet {\n\tr := make([]Result, len(t))\n\tfor i := range t {\n\t\tr[i] = &t[i]\n\t}\n\treturn &TriggerSet{\n\t\tresultSet: resultSet{\n\t\t\tresults: r,\n\t\t\tcolumns: []string{\n\t\t\t\t\"Catalog\",\n\t\t\t\t\"Schema\",\n\t\t\t\t\"Table\",\n\t\t\t\t\"Name\",\n\t\t\t\t\"Definition\",\n\t\t\t},\n\t\t},\n\t}\n}\n\nfunc (t TriggerSet) Get() *Trigger {\n\treturn t.results[t.current-1].(*Trigger)\n}\n"
  },
  {
    "path": "drivers/metadata/metadata_test.go",
    "content": "package metadata\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestAccessPrivileges_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tps   ObjectPrivileges\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"multi\",\n\t\t\tps: ObjectPrivileges{\n\t\t\t\t{Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Grantee: \"user3\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Grantee: \"user3\", Grantor: \"user2\", PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t\twant: \"user1=INSERT*,SELECT/user1\\n\" +\n\t\t\t\t\"user2=INSERT,SELECT*/user1\\n\" +\n\t\t\t\t\"user3=SELECT*/user1\\n\" +\n\t\t\t\t\"user3=UPDATE/user2\",\n\t\t},\n\t\t{\n\t\t\tname: \"one\",\n\t\t\tps: ObjectPrivileges{\n\t\t\t\t{Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t\twant: \"user1=INSERT/user1\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tps:   ObjectPrivileges{},\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty-grantor\",\n\t\t\tps: ObjectPrivileges{\n\t\t\t\t{Grantee: \"user1\", Grantor: \"\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Grantee: \"user1\", Grantor: \"\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Grantee: \"user2\", Grantor: \"\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Grantee: \"user2\", Grantor: \"\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Grantee: \"user3\", Grantor: \"\", PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t\twant: \"user1=INSERT*,SELECT\\n\" +\n\t\t\t\t\"user2=INSERT,SELECT*\\n\" +\n\t\t\t\t\"user3=UPDATE\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := tt.ps.String()\n\t\t\tif diff := cmp.Diff(tt.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"Wrong AccessPrivileges.String(): (-expected, +got):\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestColumnPrivileges_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tps   ColumnPrivileges\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"multi\",\n\t\t\tps: ColumnPrivileges{\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Column: \"col1\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Column: \"col1\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user3\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user3\", Grantor: \"user2\", PrivilegeType: \"UPDATE\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col2\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col2\", Grantee: \"user3\", Grantor: \"user2\", PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t\twant: \"col1:\\n\" +\n\t\t\t\t\"  user1=INSERT*,SELECT/user1\\n\" +\n\t\t\t\t\"  user2=INSERT,SELECT*/user1\\n\" +\n\t\t\t\t\"  user3=SELECT*/user1\\n\" +\n\t\t\t\t\"  user3=UPDATE/user2\\n\" +\n\t\t\t\t\"col2:\\n\" +\n\t\t\t\t\"  user1=INSERT*,SELECT/user1\\n\" +\n\t\t\t\t\"  user2=INSERT,SELECT*/user1\\n\" +\n\t\t\t\t\"  user3=UPDATE/user2\",\n\t\t},\n\t\t{\n\t\t\tname: \"one-multi\",\n\t\t\tps: ColumnPrivileges{\n\t\t\t\t{Column: \"col2\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col3\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col3\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Column: \"col3\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Column: \"col3\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col3\", Grantee: \"user3\", Grantor: \"user2\", PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t\twant: \"col2:\\n\" +\n\t\t\t\t\"  user1=INSERT*/user1\\n\" +\n\t\t\t\t\"col3:\\n\" +\n\t\t\t\t\"  user1=INSERT*,SELECT/user1\\n\" +\n\t\t\t\t\"  user2=INSERT,SELECT*/user1\\n\" +\n\t\t\t\t\"  user3=UPDATE/user2\",\n\t\t},\n\t\t{\n\t\t\tname: \"multi-one\",\n\t\t\tps: ColumnPrivileges{\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Column: \"col1\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Column: \"col1\", Grantee: \"user2\", Grantor: \"user1\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user3\", Grantor: \"user2\", PrivilegeType: \"UPDATE\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t},\n\t\t\twant: \"col1:\\n\" +\n\t\t\t\t\"  user1=INSERT*,SELECT/user1\\n\" +\n\t\t\t\t\"  user2=INSERT,SELECT*/user1\\n\" +\n\t\t\t\t\"  user3=UPDATE/user2\\n\" +\n\t\t\t\t\"col2:\\n\" +\n\t\t\t\t\"  user1=INSERT*/user1\",\n\t\t},\n\t\t{\n\t\t\tname: \"one\",\n\t\t\tps: ColumnPrivileges{\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"user1\", PrivilegeType: \"INSERT\"},\n\t\t\t},\n\t\t\twant: \"col1:\\n  user1=INSERT/user1\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tps:   ColumnPrivileges{},\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"empty-grantor\",\n\t\t\tps: ColumnPrivileges{\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user1\", Grantor: \"\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Column: \"col1\", Grantee: \"user2\", Grantor: \"\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Column: \"col1\", Grantee: \"user2\", Grantor: \"\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col1\", Grantee: \"user3\", Grantor: \"\", PrivilegeType: \"UPDATE\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user1\", Grantor: \"\", PrivilegeType: \"INSERT\", IsGrantable: true},\n\t\t\t\t{Column: \"col2\", Grantee: \"user1\", Grantor: \"\", PrivilegeType: \"SELECT\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user2\", Grantor: \"\", PrivilegeType: \"INSERT\"},\n\t\t\t\t{Column: \"col2\", Grantee: \"user2\", Grantor: \"\", PrivilegeType: \"SELECT\", IsGrantable: true},\n\t\t\t\t{Column: \"col2\", Grantee: \"user3\", Grantor: \"\", PrivilegeType: \"UPDATE\"},\n\t\t\t},\n\t\t\twant: \"col1:\\n\" +\n\t\t\t\t\"  user1=INSERT*,SELECT\\n\" +\n\t\t\t\t\"  user2=INSERT,SELECT*\\n\" +\n\t\t\t\t\"  user3=UPDATE\\n\" +\n\t\t\t\t\"col2:\\n\" +\n\t\t\t\t\"  user1=INSERT*,SELECT\\n\" +\n\t\t\t\t\"  user2=INSERT,SELECT*\\n\" +\n\t\t\t\t\"  user3=UPDATE\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := tt.ps.String()\n\t\t\tif diff := cmp.Diff(tt.want, got); diff != \"\" {\n\t\t\t\tt.Errorf(\"Wrong ColumnPrivileges.String(): (-expected, +got):\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "drivers/metadata/mysql/metadata.go",
    "content": "package mysql\n\nimport (\n\t\"time\"\n\n\t\"github.com/gohxs/readline\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/completer\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n)\n\nvar (\n\t// NewReader for MySQL databases\n\tNewReader = infos.New(\n\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\tinfos.WithSequences(false),\n\t\tinfos.WithCheckConstraints(false),\n\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\tinfos.ColumnsDataType:                 \"column_type\",\n\t\t\tinfos.ColumnsNumericPrecRadix:         \"10\",\n\t\t\tinfos.FunctionColumnsNumericPrecRadix: \"10\",\n\t\t\tinfos.ConstraintIsDeferrable:          \"''\",\n\t\t\tinfos.ConstraintInitiallyDeferred:     \"''\",\n\t\t\tinfos.PrivilegesGrantor:               \"''\",\n\t\t\tinfos.ConstraintJoinCond:              \"AND r.referenced_table_name = f.table_name\",\n\t\t}),\n\t\tinfos.WithSystemSchemas([]string{\"mysql\", \"information_schema\", \"performance_schema\", \"sys\"}),\n\t\tinfos.WithCurrentSchema(\"COALESCE(DATABASE(), '%')\"),\n\t\tinfos.WithUsagePrivileges(false),\n\t)\n\t// NewCompleter for MySQL databases\n\tNewCompleter = func(db drivers.DB, opts ...completer.Option) readline.AutoCompleter {\n\t\treaderOpts := []metadata.ReaderOption{\n\t\t\t// this needs to be relatively low, since autocomplete is very interactive\n\t\t\tmetadata.WithTimeout(3 * time.Second),\n\t\t\tmetadata.WithLimit(1000),\n\t\t}\n\t\treader := NewReader(db, readerOpts...)\n\t\topts = append([]completer.Option{\n\t\t\tcompleter.WithReader(reader),\n\t\t\tcompleter.WithDB(db),\n\t\t\tcompleter.WithSQLStartCommands(append(completer.CommonSqlStartCommands, \"USE\")),\n\t\t\tcompleter.WithBeforeComplete(complete(reader)),\n\t\t}, opts...)\n\t\treturn completer.NewDefaultCompleter(opts...)\n\t}\n)\n\nfunc complete(reader metadata.Reader) completer.CompleteFunc {\n\treturn func(previousWords []string, text []rune) [][]rune {\n\t\tif completer.TailMatches(completer.IGNORE_CASE, previousWords, `USE`) {\n\t\t\treturn completeWithSchemas(reader, text)\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc completeWithSchemas(reader metadata.Reader, text []rune) [][]rune {\n\tschemaNames := []string{}\n\tschemas, err := reader.(metadata.SchemaReader).Schemas(metadata.Filter{WithSystem: true})\n\tif err != nil {\n\t\treturn nil\n\t}\n\tfor schemas.Next() {\n\t\tschemaNames = append(schemaNames, schemas.Get().Schema)\n\t}\n\treturn completer.CompleteFromList(text, schemaNames...)\n}\n"
  },
  {
    "path": "drivers/metadata/oracle/metadata.go",
    "content": "// Package oracle provides a metadata reader\npackage oracle\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n)\n\ntype metaReader struct {\n\tmetadata.LoggingReader\n\tsystemSchemas string\n}\n\nvar _ metadata.BasicReader = &metaReader{}\nvar _ metadata.IndexReader = &metaReader{}\nvar _ metadata.IndexColumnReader = &metaReader{}\n\nfunc NewReader() func(drivers.DB, ...metadata.ReaderOption) metadata.Reader {\n\treturn func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\t\tr := &metaReader{\n\t\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t\t\tsystemSchemas: \"'CTXSYS', 'FLOWS_FILES', 'MDSYS', 'OUTLN', 'SYS', 'SYSTEM', 'XDB', 'XS$NULL'\",\n\t\t}\n\t\treturn r\n\t}\n}\n\nfunc (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) {\n\tqstr := `SELECT\n  UPPER(Value) AS catalog\nFROM v$parameter o\nWHERE name = 'db_name'\nUNION ALL\nSELECT\n  db_link AS catalog\nFROM dba_db_links\nORDER BY catalog\n`\n\n\trows, closeRows, err := r.Query(qstr)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewCatalogSet([]metadata.Catalog{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Catalog{}\n\tfor rows.Next() {\n\t\trec := metadata.Catalog{}\n\t\terr = rows.Scan(&rec.Catalog)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewCatalogSet(results), nil\n}\n\nfunc (r metaReader) Schemas(f metadata.Filter) (*metadata.SchemaSet, error) {\n\tqstr := `SELECT\n  username\nFROM all_users\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tname:       \"username LIKE :%d\",\n\t\tnotSchemas: \"username NOT IN (%s)\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tqstr += `\nORDER BY username`\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewSchemaSet([]metadata.Schema{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Schema{}\n\tfor rows.Next() {\n\t\trec := metadata.Schema{}\n\t\terr = rows.Scan(&rec.Schema)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewSchemaSet(results), nil\n}\n\n// Tables from selected catalog (or all, if empty), matching schemas, names and types\nfunc (r metaReader) Tables(f metadata.Filter) (*metadata.TableSet, error) {\n\tqstr := `SELECT\no.owner AS table_schem,\no.object_name AS table_name,\no.object_type AS table_type\nFROM all_objects o\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tschema:     \"o.owner LIKE %s\",\n\t\tnotSchemas: \"o.owner NOT IN (%s)\",\n\t\tname:       \"o.object_name LIKE :%d\",\n\t\ttypes:      \"o.object_type IN (%s)\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\taddSynonyms := false\n\tfor _, t := range f.Types {\n\t\tif t == \"SYNONYM\" {\n\t\t\taddSynonyms = true\n\t\t}\n\t}\n\tif addSynonyms {\n\t\tqstr += `\nUNION ALL\nSELECT\n  s.owner AS table_schem,\n  s.synonym_name AS table_name,\n  'SYNONYM' AS table_type\nFROM all_synonyms s\n`\n\t\tconds, seqVals := r.conditions(f, formats{\n\t\t\tschema:     \"s.owner LIKE %s\",\n\t\t\tnotSchemas: \"s.owner NOT IN (%s)\",\n\t\t\tname:       \"s.synonym_name LIKE :%d\",\n\t\t})\n\t\tvals = append(vals, seqVals...)\n\t\tif len(conds) != 0 {\n\t\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t\t}\n\t}\n\tqstr += `\nORDER BY table_schem, table_name, table_type`\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewTableSet([]metadata.Table{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Table{}\n\tfor rows.Next() {\n\t\trec := metadata.Table{}\n\t\terr = rows.Scan(&rec.Schema, &rec.Name, &rec.Type)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewTableSet(results), nil\n}\n\nfunc (r metaReader) Columns(f metadata.Filter) (*metadata.ColumnSet, error) {\n\tqstr := `SELECT\n  c.owner,\n  c.table_name,\n  c.column_name,\n  c.column_id AS ordinal_position,\n  c.data_type,\n  CASE c.nullable\n    WHEN 'Y' THEN 'YES'\n    ELSE  'NO'  END AS nullable,\n  COALESCE(c.data_length, c.data_precision, 0),\n  COALESCE(c.data_scale, 0),\n  CASE c.data_type\n           WHEN 'FLOAT'  THEN  2\n           WHEN 'NUMBER' THEN 10\n  ELSE  0  END AS num_prec_radix,\n  COALESCE(c.char_col_decl_length, 0) as char_octet_length\nFROM all_tab_columns c\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tschema:     \"c.owner LIKE %s\",\n\t\tnotSchemas: \"c.owner NOT IN (%s)\",\n\t\tparent:     \"c.table_name LIKE :%d\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tqstr += `\nORDER BY c.owner, c.table_name, c.column_id`\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewColumnSet([]metadata.Column{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Column{}\n\tfor rows.Next() {\n\t\trec := metadata.Column{}\n\t\ttargets := []interface{}{\n\t\t\t&rec.Schema,\n\t\t\t&rec.Table,\n\t\t\t&rec.Name,\n\t\t\t&rec.OrdinalPosition,\n\t\t\t&rec.DataType,\n\t\t\t&rec.IsNullable,\n\t\t\t&rec.ColumnSize,\n\t\t\t&rec.DecimalDigits,\n\t\t\t&rec.NumPrecRadix,\n\t\t\t&rec.CharOctetLength,\n\t\t}\n\t\terr = rows.Scan(targets...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewColumnSet(results), nil\n}\n\nfunc (r metaReader) Functions(f metadata.Filter) (*metadata.FunctionSet, error) {\n\tqstr := `SELECT\n  decode (b.object_type,'PACKAGE',CONCAT(CONCAT(b.object_name,'.'), a.object_name)\n         ,b.object_name) as specific_name,\n  b.owner   as procedure_schem,\n  decode (b.object_type,'PACKAGE',CONCAT(CONCAT(b.object_name,'.'), a.object_name)\n         ,b.object_name) as procedure_name,\n  decode (b.object_type,'PACKAGE',decode(a.position,0,2,1,1,0),\n          decode(b.object_type,'PROCEDURE',1,'FUNCTION',2,0)) as procedure_type\nFROM all_arguments a\nJOIN all_objects b ON b.object_id = a.object_id AND a.sequence  = 1\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tschema:     \"b.owner LIKE %s\",\n\t\tnotSchemas: \"b.owner NOT IN (%s)\",\n\t\tname:       \"b.object_name LIKE :%d\",\n\t\ttypes:      \"b.object_type IN (%s)\",\n\t})\n\tconds = append(conds, \"(b.object_type = 'PROCEDURE' OR b.object_type = 'FUNCTION' OR b.object_type = 'PACKAGE')\")\n\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\tqstr += `\nORDER BY procedure_schem, procedure_name, procedure_type`\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewFunctionSet([]metadata.Function{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Function{}\n\tfor rows.Next() {\n\t\trec := metadata.Function{}\n\t\terr = rows.Scan(\n\t\t\t&rec.SpecificName,\n\t\t\t&rec.Schema,\n\t\t\t&rec.Name,\n\t\t\t&rec.Type,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewFunctionSet(results), nil\n}\n\nfunc (r metaReader) FunctionColumns(f metadata.Filter) (*metadata.FunctionColumnSet, error) {\n\tqstr := `SELECT\n     a.owner   as procedure_schem,\n     decode (b.object_type,'PACKAGE',CONCAT(CONCAT(b.object_name,'.'),a.object_name),\n             b.object_name) as procedure_name,\n     decode(a.position,0,'RETURN_VALUE',a.argument_name) as column_name,\n     a.position       as ordinal_position,\n     decode(a.position,0,5,decode(a.in_out,'IN',1,'IN/OUT',2,'OUT',4)) as column_type,\n     a.data_type      as type_name,\n     COALESCE(a.data_length, a.data_precision, 0) as column_size,\n     COALESCE(a.data_scale, 0) as decimal_digits,\n     COALESCE(a.radix, 0) as num_prec_radix\nFROM all_objects b\nJOIN all_arguments a ON b.object_id = a.object_id AND a.data_level = 0\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tschema:     \"a.owner LIKE %s\",\n\t\tnotSchemas: \"a.owner NOT IN (%s)\",\n\t\tparent:     \"b.object_name LIKE :%d\",\n\t})\n\tconds = append(conds, \"b.object_type = 'PROCEDURE' OR b.object_type = 'FUNCTION'\")\n\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\tqstr += `\nORDER BY procedure_schem, procedure_name, ordinal_position`\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewFunctionColumnSet([]metadata.FunctionColumn{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.FunctionColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.FunctionColumn{}\n\t\terr = rows.Scan(\n\t\t\t&rec.Schema,\n\t\t\t&rec.FunctionName,\n\t\t\t&rec.Name,\n\t\t\t&rec.OrdinalPosition,\n\t\t\t&rec.Type,\n\t\t\t&rec.DataType,\n\t\t\t&rec.ColumnSize,\n\t\t\t&rec.DecimalDigits,\n\t\t\t&rec.NumPrecRadix,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewFunctionColumnSet(results), nil\n}\n\nfunc (r metaReader) Indexes(f metadata.Filter) (*metadata.IndexSet, error) {\n\tqstr := `SELECT\n  o.owner,\n  o.table_name,\n  o.index_name,\n  decode(o.uniqueness,'UNIQUE','NO','YES')\nFROM all_indexes o\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tschema:     \"o.owner LIKE %s\",\n\t\tnotSchemas: \"o.owner NOT IN (%s)\",\n\t\tparent:     \"o.table_name LIKE :%d\",\n\t\tname:       \"o.index_name LIKE :%d\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tqstr += `\nORDER BY o.owner, o.table_name, o.index_name`\n\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewIndexSet([]metadata.Index{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Index{}\n\tfor rows.Next() {\n\t\trec := metadata.Index{}\n\t\terr = rows.Scan(&rec.Schema, &rec.Table, &rec.Name, &rec.IsUnique)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexSet(results), nil\n}\n\nfunc (r metaReader) IndexColumns(f metadata.Filter) (*metadata.IndexColumnSet, error) {\n\tqstr := `SELECT\n  o.owner,\n  o.table_name,\n  o.index_name,\n  b.column_name,\n  b.column_position\nFROM all_indexes o\nJOIN all_ind_columns b ON o.owner = b.index_owner AND o.index_name = b.index_name\n`\n\tconds, vals := r.conditions(f, formats{\n\t\tschema:     \"o.owner LIKE %s\",\n\t\tnotSchemas: \"o.owner NOT IN (%s)\",\n\t\tparent:     \"o.table_name LIKE :%d\",\n\t\tname:       \"o.index_name LIKE :%d\",\n\t})\n\tif len(conds) != 0 {\n\t\tqstr += \" WHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tqstr += `\nORDER BY o.owner, o.table_name, o.index_name, b.column_position`\n\trows, closeRows, err := r.Query(qstr, vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewIndexColumnSet([]metadata.IndexColumn{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.IndexColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.IndexColumn{}\n\t\terr = rows.Scan(&rec.Schema, &rec.Table, &rec.IndexName, &rec.Name, &rec.OrdinalPosition)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexColumnSet(results), nil\n}\n\nfunc (r metaReader) conditions(filter metadata.Filter, formats formats) ([]string, []interface{}) {\n\tbaseParam := 1\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif filter.Schema != \"\" && formats.schema != \"\" {\n\t\tvals = append(vals, strings.ToUpper(filter.Schema))\n\t\tconds = append(conds, fmt.Sprintf(formats.schema, fmt.Sprintf(\":%d\", baseParam)))\n\t\tbaseParam++\n\t}\n\n\tif !filter.WithSystem && formats.notSchemas != \"\" {\n\t\tconds = append(conds, fmt.Sprintf(formats.notSchemas, r.systemSchemas))\n\t}\n\tif filter.OnlyVisible && formats.schema != \"\" {\n\t\tconds = append(conds, fmt.Sprintf(formats.schema, \"user\"))\n\t}\n\tif filter.Parent != \"\" && formats.parent != \"\" {\n\t\tvals = append(vals, strings.ToUpper(filter.Parent))\n\t\tconds = append(conds, fmt.Sprintf(formats.parent, baseParam))\n\t\tbaseParam++\n\t}\n\tif filter.Name != \"\" && formats.name != \"\" {\n\t\tvals = append(vals, strings.ToUpper(filter.Name))\n\t\tconds = append(conds, fmt.Sprintf(formats.name, baseParam))\n\t\tbaseParam++\n\t}\n\tif len(filter.Types) != 0 && formats.types != \"\" {\n\t\tpholders := []string{}\n\t\tfor _, t := range filter.Types {\n\t\t\tvals = append(vals, strings.ToUpper(t))\n\t\t\tpholders = append(pholders, fmt.Sprintf(\":%d\", baseParam))\n\t\t\tbaseParam++\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, fmt.Sprintf(formats.types, strings.Join(pholders, \", \")))\n\t\t}\n\t}\n\n\treturn conds, vals\n}\n\ntype formats struct {\n\tschema     string\n\tnotSchemas string\n\tparent     string\n\tname       string\n\ttypes      string\n}\n"
  },
  {
    "path": "drivers/metadata/postgres/metadata.go",
    "content": "// Package postgres provides a metadata reader\npackage postgres\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/lib/pq\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n)\n\ntype metaReader struct {\n\tmetadata.LoggingReader\n\tlimit int\n}\n\nvar _ metadata.CatalogReader = &metaReader{}\nvar _ metadata.TableReader = &metaReader{}\nvar _ metadata.ColumnStatReader = &metaReader{}\nvar _ metadata.IndexReader = &metaReader{}\nvar _ metadata.IndexColumnReader = &metaReader{}\nvar _ metadata.TriggerReader = &metaReader{}\n\nfunc NewReader() func(drivers.DB, ...metadata.ReaderOption) metadata.Reader {\n\treturn func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\t\tnewIS := infos.New(\n\t\t\tinfos.WithIndexes(false),\n\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\tinfos.ColumnsColumnSize:         \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)\",\n\t\t\t\tinfos.FunctionColumnsColumnSize: \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)\",\n\t\t\t}),\n\t\t\tinfos.WithSystemSchemas([]string{\"pg_catalog\", \"pg_toast\", \"information_schema\"}),\n\t\t\tinfos.WithCurrentSchema(\"CURRENT_SCHEMA\"),\n\t\t\tinfos.WithDataTypeFormatter(dataTypeFormatter))\n\t\treturn metadata.NewPluginReader(\n\t\t\tnewIS(db, opts...),\n\t\t\t&metaReader{\n\t\t\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t\t\t},\n\t\t)\n\t}\n}\n\nfunc dataTypeFormatter(col metadata.Column) string {\n\tswitch col.DataType {\n\tcase \"bit\", \"character\":\n\t\treturn fmt.Sprintf(\"%s(%d)\", col.DataType, col.ColumnSize)\n\tcase \"bit varying\", \"character varying\":\n\t\tif col.ColumnSize != 0 {\n\t\t\treturn fmt.Sprintf(\"%s(%d)\", col.DataType, col.ColumnSize)\n\t\t} else {\n\t\t\treturn col.DataType\n\t\t}\n\tcase \"numeric\":\n\t\tif col.ColumnSize != 0 {\n\t\t\treturn fmt.Sprintf(\"numeric(%d,%d)\", col.ColumnSize, col.DecimalDigits)\n\t\t} else {\n\t\t\treturn col.DataType\n\t\t}\n\tcase \"time without time zone\":\n\t\treturn fmt.Sprintf(\"time(%d) without time zone\", col.ColumnSize)\n\tcase \"time with time zone\":\n\t\treturn fmt.Sprintf(\"time(%d) with time zone\", col.ColumnSize)\n\tcase \"timestamp without time zone\":\n\t\treturn fmt.Sprintf(\"timestamp(%d) without time zone\", col.ColumnSize)\n\tcase \"timestamp with time zone\":\n\t\treturn fmt.Sprintf(\"timestamp(%d) with time zone\", col.ColumnSize)\n\tdefault:\n\t\treturn col.DataType\n\t}\n}\n\nfunc (r *metaReader) SetLimit(l int) {\n\tr.limit = l\n}\n\ntype Catalog struct {\n\tmetadata.Catalog\n\tOwner            string\n\tEncoding         string\n\tCollate          string\n\tCtype            string\n\tAccessPrivileges string\n}\n\nfunc (s Catalog) Values() []interface{} {\n\treturn []interface{}{s.Catalog.Catalog, s.Owner, s.Encoding, s.Collate, s.Ctype, s.AccessPrivileges}\n}\n\nfunc (s Catalog) GetCatalog() metadata.Catalog {\n\treturn s.Catalog\n}\n\nvar (\n\tcatalogsColumnName = []string{\"Catalog\", \"Owner\", \"Encoding\", \"Collate\", \"Ctype\", \"Access privileges\"}\n)\n\nfunc (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) {\n\tqstr := `SELECT d.datname as \"Name\",\n       pg_catalog.pg_get_userbyid(d.datdba) as \"Owner\",\n       pg_catalog.pg_encoding_to_char(d.encoding) as \"Encoding\",\n       d.datcollate as \"Collate\",\n       d.datctype as \"Ctype\",\n       COALESCE(pg_catalog.array_to_string(d.datacl, E'\\n'),'') AS \"Access privileges\"\nFROM pg_catalog.pg_database d`\n\trows, closeRows, err := r.query(qstr, []string{}, \"1\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tvar results []metadata.Result\n\tfor rows.Next() {\n\t\trec := Catalog{\n\t\t\tCatalog: metadata.Catalog{},\n\t\t}\n\t\terr = rows.Scan(&rec.Catalog.Catalog, &rec.Owner, &rec.Encoding, &rec.Collate, &rec.Ctype, &rec.AccessPrivileges)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, &rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewCatalogSetWithColumns(results, catalogsColumnName), nil\n}\n\nfunc (r metaReader) Tables(f metadata.Filter) (*metadata.TableSet, error) {\n\tqstr := `SELECT n.nspname as \"Schema\",\n  c.relname as \"Name\",\n  CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' ELSE 'unknown' END as \"Type\",\n  COALESCE((c.reltuples / NULLIF(c.relpages, 0)) * (pg_catalog.pg_relation_size(c.oid) / current_setting('block_size')::int), 0)::bigint as \"Rows\",\n  pg_catalog.pg_size_pretty(pg_catalog.pg_table_size(c.oid)) as \"Size\",\n  COALESCE(pg_catalog.obj_description(c.oid, 'pg_class'), '') as \"Description\"\nFROM pg_catalog.pg_class c\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n`\n\tconds := []string{\"n.nspname !~ '^pg_toast' AND c.relkind != 'c'\"}\n\tvals := []interface{}{}\n\tif f.OnlyVisible {\n\t\tconds = append(conds, \"pg_catalog.pg_table_is_visible(c.oid)\")\n\t}\n\tif !f.WithSystem {\n\t\tconds = append(conds, \"n.nspname NOT IN ('pg_catalog', 'information_schema')\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"n.nspname LIKE $%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"c.relname LIKE $%d\", len(vals)))\n\t}\n\tif len(f.Types) != 0 {\n\t\ttableTypes := map[string][]rune{\n\t\t\t\"TABLE\":             {'r', 'p', 's', 'f'},\n\t\t\t\"VIEW\":              {'v'},\n\t\t\t\"MATERIALIZED VIEW\": {'m'},\n\t\t\t\"SEQUENCE\":          {'S'},\n\t\t}\n\t\tpholders := []string{\"''\"}\n\t\tfor _, t := range f.Types {\n\t\t\tfor _, k := range tableTypes[t] {\n\t\t\t\tvals = append(vals, string(k))\n\t\t\t\tpholders = append(pholders, fmt.Sprintf(\"$%d\", len(vals)))\n\t\t\t}\n\t\t}\n\t\tconds = append(conds, fmt.Sprintf(\"c.relkind IN (%s)\", strings.Join(pholders, \", \")))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"1, 3, 2\", vals...)\n\tif err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn metadata.NewTableSet([]metadata.Table{}), nil\n\t\t}\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Table{}\n\tfor rows.Next() {\n\t\trec := metadata.Table{}\n\t\terr = rows.Scan(&rec.Schema, &rec.Name, &rec.Type, &rec.Rows, &rec.Size, &rec.Comment)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewTableSet(results), nil\n}\n\nfunc (r metaReader) ColumnStats(f metadata.Filter) (*metadata.ColumnStatSet, error) {\n\ttables, err := r.Tables(metadata.Filter{Schema: f.Schema, Name: f.Parent, WithSystem: true})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trowNum := int64(0)\n\tif tables.Next() {\n\t\trowNum = tables.Get().Rows\n\t}\n\n\tqstr := `\nSELECT\n  n.nspname,\n  c.relname,\n  a.attname,\n  COALESCE(s.avg_width, 0),\n  COALESCE(s.null_frac, 0.0),\n  COALESCE(CASE WHEN n_distinct >= 0 THEN n_distinct ELSE (-n_distinct * $1) END::bigint, 0) AS n_distinct,\n  COALESCE((histogram_bounds::text::text[])[1], ''),\n  COALESCE((histogram_bounds::text::text[])[array_length(histogram_bounds::text::text[], 1)], ''),\n  most_common_vals::text::text[],\n  most_common_freqs::text::text[]\nFROM pg_catalog.pg_namespace n\nJOIN pg_catalog.pg_class c ON c.relnamespace = n.oid\nJOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum > 0\nLEFT JOIN pg_catalog.pg_stats s ON n.nspname = s.schemaname AND c.relname = s.tablename AND a.attname = s.attname\n`\n\tconds := []string{}\n\tvals := []interface{}{rowNum}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"n.nspname LIKE $%d\", len(vals)))\n\t}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, fmt.Sprintf(\"c.relname LIKE $%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"a.attname LIKE $%d\", len(vals)))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"a.attnum\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.ColumnStat{}\n\tfor rows.Next() {\n\t\trec := metadata.ColumnStat{}\n\t\terr = rows.Scan(\n\t\t\t&rec.Schema,\n\t\t\t&rec.Table,\n\t\t\t&rec.Name,\n\t\t\t&rec.AvgWidth,\n\t\t\t&rec.NullFrac,\n\t\t\t&rec.NumDistinct,\n\t\t\t&rec.Min,\n\t\t\t&rec.Max,\n\t\t\tpq.Array(&rec.TopN),\n\t\t\tpq.Array(&rec.TopNFreqs),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewColumnStatSet(results), nil\n}\n\nfunc (r metaReader) Indexes(f metadata.Filter) (*metadata.IndexSet, error) {\n\tqstr := `\nSELECT\n  'postgres' as \"Catalog\",\n  n.nspname as \"Schema\",\n  c2.relname as \"Table\",\n  c.relname as \"Name\",\n  CASE i.indisprimary WHEN TRUE THEN 'YES' ELSE 'NO' END,\n  CASE i.indisunique WHEN TRUE THEN 'YES' ELSE 'NO' END,\n  COALESCE(am.amname, \n  \tCASE c.relkind \n\t\tWHEN 'i' THEN 'index' \n\t\tWHEN 'I' THEN 'partitioned index' \n\tEND\n   ) as \"Type\"\nFROM pg_catalog.pg_class c\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n     LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid\n     LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid\n     LEFT JOIN pg_am am ON am.oid=c.relam`\n\tconds := []string{\n\t\t\"c.relkind IN ('i','I','')\",\n\t\t\"n.nspname !~ '^pg_toast'\",\n\t}\n\tif f.OnlyVisible {\n\t\tconds = append(conds, \"pg_catalog.pg_table_is_visible(c.oid)\")\n\t}\n\tvals := []interface{}{}\n\tif !f.WithSystem {\n\t\tconds = append(conds, \"n.nspname NOT IN ('pg_catalog', 'information_schema')\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"n.nspname LIKE $%d\", len(vals)))\n\t}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, fmt.Sprintf(\"c2.relname LIKE $%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"c.relname LIKE $%d\", len(vals)))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"1, 2, 4\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Index{}\n\tfor rows.Next() {\n\t\trec := metadata.Index{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Table, &rec.Name, &rec.IsUnique, &rec.IsPrimary, &rec.Type)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexSet(results), nil\n}\n\nfunc (r metaReader) IndexColumns(f metadata.Filter) (*metadata.IndexColumnSet, error) {\n\tqstr := `\nSELECT\n  'postgres' as \"Catalog\",\n  n.nspname as \"Schema\",\n  c2.relname as \"Table\",\n  c.relname as \"IndexName\",\n  a.attname AS \"Name\",\n  pg_catalog.format_type(a.atttypid, a.atttypmod) AS \"DataType\",\n  a.attnum AS \"OrdinalPosition\"\nFROM pg_catalog.pg_class c\n     JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n     JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid\n     JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid\n     JOIN pg_catalog.pg_attribute a ON c.oid = a.attrelid\n`\n\tconds := []string{\n\t\t\"c.relkind IN ('i','I','')\",\n\t\t\"n.nspname <> 'pg_catalog'\",\n\t\t\"n.nspname <> 'information_schema'\",\n\t\t\"n.nspname !~ '^pg_toast'\",\n\t\t\"a.attnum > 0\",\n\t\t\"NOT a.attisdropped\",\n\t}\n\tif f.OnlyVisible {\n\t\tconds = append(conds, \"pg_catalog.pg_table_is_visible(c.oid)\")\n\t}\n\tvals := []interface{}{}\n\tif !f.WithSystem {\n\t\tconds = append(conds, \"n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"n.nspname LIKE $%d\", len(vals)))\n\t}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, fmt.Sprintf(\"c2.relname LIKE $%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"c.relname LIKE $%d\", len(vals)))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"1, 2, 3, 4, 7\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.IndexColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.IndexColumn{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Table, &rec.IndexName, &rec.Name, &rec.DataType, &rec.OrdinalPosition)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexColumnSet(results), nil\n}\n\nfunc (r metaReader) Triggers(f metadata.Filter) (*metadata.TriggerSet, error) {\n\tqstr := `SELECT\n\tn.nspname,\n\tc.relname,\n    t.tgname, \n    pg_catalog.pg_get_triggerdef(t.oid, true)\nFROM \n    pg_catalog.pg_trigger t \n    JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid\n\tLEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace`\n\tconds := []string{`(\n\tNOT t.tgisinternal OR (t.tgisinternal AND t.tgenabled = 'D') \n\t\t\tOR \n\t\t\t\tEXISTS (SELECT 1 FROM pg_catalog.pg_depend WHERE objid = t.oid \n\t\t\tAND \n\t\t\t\trefclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass)\n\t)`}\n\tvals := []interface{}{}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"n.nspname LIKE $%d\", len(vals)))\n\t}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, fmt.Sprintf(\"c.relname LIKE $%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"t.tgname LIKE $%d\", len(vals)))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"t.tgname\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Trigger{}\n\tfor rows.Next() {\n\t\trec := metadata.Trigger{}\n\t\terr = rows.Scan(\n\t\t\t&rec.Schema,\n\t\t\t&rec.Table,\n\t\t\t&rec.Name,\n\t\t\t&rec.Definition,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewTriggerSet(results), nil\n}\n\nfunc (r metaReader) query(qstr string, conds []string, order string, vals ...interface{}) (*sql.Rows, func(), error) {\n\tif len(conds) != 0 {\n\t\tqstr += \"\\nWHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tif order != \"\" {\n\t\tqstr += \"\\nORDER BY \" + order\n\t}\n\tif r.limit != 0 {\n\t\tqstr += fmt.Sprintf(\"\\nLIMIT %d\", r.limit)\n\t}\n\treturn r.Query(qstr, vals...)\n}\n"
  },
  {
    "path": "drivers/metadata/postgres/metadata_test.go",
    "content": "package postgres_test\n\nimport (\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\tdt \"github.com/ory/dockertest/v3\"\n\tdc \"github.com/ory/dockertest/v3/docker\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/drivers/metadata/postgres\"\n\t_ \"github.com/xo/usql/drivers/postgres\"\n)\n\ntype Database struct {\n\tBuildArgs  []dc.BuildArg\n\tRunOptions *dt.RunOptions\n\tExec       []string\n\tDriver     string\n\tURL        string\n\tDockerPort string\n\tResource   *dt.Resource\n\tDB         *sql.DB\n\tOpts       []metadata.ReaderOption\n\tReader     metadata.BasicReader\n}\n\nvar dbName string = \"postgres\"\n\nvar db = Database{\n\tBuildArgs: []dc.BuildArg{\n\t\t{Name: \"BASE_IMAGE\", Value: \"postgres:13\"},\n\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/postgres-sakila-db/postgres-sakila-schema.sql\"},\n\t\t{Name: \"TARGET\", Value: \"/docker-entrypoint-initdb.d\"},\n\t\t{Name: \"USER\", Value: \"root\"},\n\t},\n\tRunOptions: &dt.RunOptions{\n\t\tName: \"usql-pgsql\",\n\t\tCmd:  []string{\"-c\", \"log_statement=all\", \"-c\", \"log_min_duration_statement=0\"},\n\t\tEnv:  []string{\"POSTGRES_PASSWORD=pw\"},\n\t},\n\tDriver:     \"postgres\",\n\tURL:        \"postgres://postgres:pw@localhost:%s/postgres?sslmode=disable\",\n\tDockerPort: \"5432/tcp\",\n}\n\nfunc TestMain(m *testing.M) {\n\tcleanup := true\n\tflag.BoolVar(&cleanup, \"cleanup\", true, \"delete containers when finished\")\n\tflag.Parse()\n\tpool, err := dt.NewPool(\"\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not connect to docker: %s\", err)\n\t}\n\tvar ok bool\n\tdb.Resource, ok = pool.ContainerByName(db.RunOptions.Name)\n\tif !ok {\n\t\tbuildOpts := &dt.BuildOptions{\n\t\t\tContextDir: \"../../testdata/docker\",\n\t\t\tBuildArgs:  db.BuildArgs,\n\t\t}\n\t\tdb.Resource, err = pool.BuildAndRunWithBuildOptions(buildOpts, db.RunOptions)\n\t\tif err != nil {\n\t\t\tlog.Fatal(\"Could not start resource: \", err)\n\t\t}\n\t}\n\n\t// exponential backoff-retry, because the application in the container might not be ready to accept connections yet\n\tif err := pool.Retry(func() error {\n\t\thostPort := db.Resource.GetPort(db.DockerPort)\n\t\tvar err error\n\t\tdb.DB, err = sql.Open(db.Driver, fmt.Sprintf(db.URL, hostPort))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn db.DB.Ping()\n\t}); err != nil {\n\t\tlog.Fatal(\"Timed out waiting for db: \", err)\n\t}\n\tdb.Reader = postgres.NewReader()(db.DB).(metadata.BasicReader)\n\n\tif len(db.Exec) != 0 {\n\t\texitCode, err := db.Resource.Exec(db.Exec, dt.ExecOptions{\n\t\t\tStdIn:  os.Stdin,\n\t\t\tStdOut: os.Stdout,\n\t\t\tStdErr: os.Stderr,\n\t\t\tTTY:    true,\n\t\t})\n\t\tif err != nil || exitCode != 0 {\n\t\t\tlog.Fatal(\"Could not load schema: \", err)\n\t\t}\n\t}\n\tcode := m.Run()\n\t// You can't defer this because os.Exit doesn't care for defer\n\tif cleanup {\n\t\tif err := pool.Purge(db.Resource); err != nil {\n\t\t\tlog.Fatal(\"Could not purge resource: \", err)\n\t\t}\n\t}\n\tos.Exit(code)\n}\n\nfunc TestTriggers(t *testing.T) {\n\tschema := \"public\"\n\texpected := \"film_fulltext_trigger, last_updated\"\n\tparent := \"film\"\n\tr := postgres.NewReader()(db.DB).(metadata.TriggerReader)\n\n\tresult, err := r.Triggers(metadata.Filter{Schema: schema, Parent: parent})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read %s triggers: %v\", dbName, err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Name)\n\t}\n\tactual := strings.Join(names, \", \")\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong %s trigger names, expected:\\n  %v\\ngot:\\n  %v\", dbName, expected, names)\n\t}\n}\n\nfunc TestColumns(t *testing.T) {\n\t// Only testing postgres specific datatype formatting.\n\t// The rest of the functionality is covered by informationschema/metadata_test.go:TestColumns\n\ttype test struct {\n\t\ttypeDef string\n\t\twant    string\n\t}\n\tschema := \"public\"\n\ttable := \"test_dtypes\"\n\ttests := []test{\n\t\t{typeDef: \"bit\", want: \"bit(1)\"},\n\t\t{typeDef: \"bit(1)\", want: \"bit(1)\"},\n\t\t{typeDef: \"bit varying\", want: \"bit varying\"},\n\t\t{typeDef: \"bit varying(2)\", want: \"bit varying(2)\"},\n\t\t{typeDef: \"character\", want: \"character(1)\"},\n\t\t{typeDef: \"character(3)\", want: \"character(3)\"},\n\t\t{typeDef: \"character varying\", want: \"character varying\"},\n\t\t{typeDef: \"character varying(4)\", want: \"character varying(4)\"},\n\t\t{typeDef: \"numeric\", want: \"numeric\"},\n\t\t{typeDef: \"numeric(1,0)\", want: \"numeric(1,0)\"},\n\t\t{typeDef: \"time\", want: \"time(6) without time zone\"},\n\t\t{typeDef: \"time(4)\", want: \"time(4) without time zone\"},\n\t\t{typeDef: \"time(6)\", want: \"time(6) without time zone\"},\n\t\t{typeDef: \"time with time zone\", want: \"time(6) with time zone\"},\n\t\t{typeDef: \"time(3) with time zone\", want: \"time(3) with time zone\"},\n\t\t{typeDef: \"timestamp\", want: \"timestamp(6) without time zone\"},\n\t\t{typeDef: \"timestamp(2)\", want: \"timestamp(2) without time zone\"},\n\t\t{typeDef: \"timestamp with time zone\", want: \"timestamp(6) with time zone\"},\n\t\t{typeDef: \"timestamp(1) with time zone\", want: \"timestamp(1) with time zone\"},\n\t\t{typeDef: \"bigint\", want: \"bigint\"},\n\t\t{typeDef: \"bigserial\", want: \"bigint\"},\n\t\t{typeDef: \"boolean\", want: \"boolean\"},\n\t\t{typeDef: \"box\", want: \"box\"},\n\t\t{typeDef: \"bytea\", want: \"bytea\"},\n\t\t{typeDef: \"cidr\", want: \"cidr\"},\n\t\t{typeDef: \"circle\", want: \"circle\"},\n\t\t{typeDef: \"date\", want: \"date\"},\n\t\t{typeDef: \"double precision\", want: \"double precision\"},\n\t\t{typeDef: \"inet\", want: \"inet\"},\n\t\t{typeDef: \"integer\", want: \"integer\"},\n\t\t{typeDef: \"json\", want: \"json\"},\n\t\t{typeDef: \"jsonb\", want: \"jsonb\"},\n\t\t{typeDef: \"line\", want: \"line\"},\n\t\t{typeDef: \"lseg\", want: \"lseg\"},\n\t\t{typeDef: \"macaddr\", want: \"macaddr\"},\n\t\t{typeDef: \"macaddr8\", want: \"macaddr8\"},\n\t\t{typeDef: \"money\", want: \"money\"},\n\t\t{typeDef: \"path\", want: \"path\"},\n\t\t{typeDef: \"pg_lsn\", want: \"pg_lsn\"},\n\t\t{typeDef: \"pg_snapshot\", want: \"pg_snapshot\"},\n\t\t{typeDef: \"point\", want: \"point\"},\n\t\t{typeDef: \"polygon\", want: \"polygon\"},\n\t\t{typeDef: \"real\", want: \"real\"},\n\t\t{typeDef: \"smallint\", want: \"smallint\"},\n\t\t{typeDef: \"smallserial\", want: \"smallint\"},\n\t\t{typeDef: \"serial\", want: \"integer\"},\n\t\t{typeDef: \"text\", want: \"text\"},\n\t\t{typeDef: \"tsvector\", want: \"tsvector\"},\n\t\t{typeDef: \"txid_snapshot\", want: \"txid_snapshot\"},\n\t\t{typeDef: \"uuid\", want: \"uuid\"},\n\t\t{typeDef: \"xml\", want: \"xml\"},\n\t}\n\n\t// Create table\n\tcolExpressions := []string{}\n\tfor i, test := range tests {\n\t\tcolExpressions = append(colExpressions, fmt.Sprintf(\"column_%d %s\", i, test.typeDef))\n\t}\n\tquery := fmt.Sprintf(\"CREATE TABLE %s.%s (%s)\", schema, table, strings.Join(colExpressions, \", \"))\n\tdb.DB.Exec(query)\n\tdefer db.DB.Exec(fmt.Sprintf(\"DROP TABLE %s.%s\", schema, table))\n\n\t// Read data types\n\tr := postgres.NewReader()(db.DB).(metadata.ColumnReader)\n\tresult, err := r.Columns(metadata.Filter{Schema: schema, Parent: table})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read %s columns: %v\", dbName, err)\n\t}\n\tactualTypes := []string{}\n\tfor result.Next() {\n\t\tactualTypes = append(actualTypes, result.Get().DataType)\n\t}\n\n\t// Compare\n\tfor i, test := range tests {\n\t\tif actualTypes[i] != test.want {\n\t\t\tt.Errorf(\"Wrong %s column data type, expected:\\n  %s, got:\\n  %s\", dbName, test.want, actualTypes[i])\n\t\t}\n\t}\n}\n\nfunc TestIndexes(t *testing.T) {\n\tschema := \"public\"\n\ttable := \"tmp_table\"\n\n\ttests := []struct {\n\t\tindexType string\n\t\twant      string\n\t}{\n\t\t{\n\t\t\tindexType: \"btree\",\n\t\t\twant:      \"btree\",\n\t\t},\n\t\t{\n\t\t\tindexType: \"hash\",\n\t\t\twant:      \"hash\",\n\t\t},\n\t}\n\n\tcolumns := []string{}\n\tfor _, v := range tests {\n\t\tcolumns = append(columns, fmt.Sprintf(\"column_%s integer\", v.indexType))\n\t}\n\tindexes := []string{}\n\tfor _, v := range tests {\n\t\tindexes = append(indexes, fmt.Sprintf(\"CREATE INDEX %s_index ON %s.%s USING %[1]s (column_%[1]s)\", v.indexType, schema, table))\n\t}\n\tquery := `\n\t\tCREATE TABLE %s.%s (%s);\n\t\t-- Indexes creation sql\n\t\t%s\n\t`\n\tdb.DB.Exec(fmt.Sprintf(query, schema, table, strings.Join(columns, \", \"), strings.Join(indexes, \";\")))\n\tdefer db.DB.Exec(fmt.Sprintf(\"DROP TABLE %s.%s\", schema, table))\n\n\tr := postgres.NewReader()(db.DB).(metadata.IndexReader)\n\n\tt.Run(\"Get info about access method for specyfic index.\", func(t *testing.T) {\n\t\taccessMethods := []string{}\n\t\tfor _, v := range tests {\n\t\t\tresult, err := r.Indexes(metadata.Filter{Name: fmt.Sprintf(\"%s_index\", v.indexType)})\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Could not get Index information: %s\", err)\n\t\t\t}\n\t\t\tfor result.Next() {\n\t\t\t\taccessMethods = append(accessMethods, result.Get().Type)\n\t\t\t}\n\t\t}\n\n\t\tfor i, test := range tests {\n\t\t\tif accessMethods[i] != test.want {\n\t\t\t\tt.Errorf(\"Wrong %s index access method, expected:\\n  %s, got:\\n  %s\", dbName, test.want, accessMethods[i])\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"Get info about index access method for all table indexes.\", func(t *testing.T) {\n\t\tresult, err := r.Indexes(metadata.Filter{Schema: schema, Parent: table})\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"Could not get Index information: %s\", err)\n\t\t}\n\t\taccessMethods := []string{}\n\t\tfor result.Next() {\n\t\t\taccessMethods = append(accessMethods, result.Get().Type)\n\t\t}\n\n\t\tfor i, test := range tests {\n\t\t\tif accessMethods[i] != test.want {\n\t\t\t\tt.Errorf(\"Wrong %s index access method, expected:\\n  %s, got:\\n  %s\", dbName, test.want, accessMethods[i])\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "drivers/metadata/reader.go",
    "content": "package metadata\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"time\"\n\n\t\"github.com/xo/usql/text\"\n)\n\n// PluginReader allows to be easily composed from other readers\ntype PluginReader struct {\n\tcatalogs           func(Filter) (*CatalogSet, error)\n\tschemas            func(Filter) (*SchemaSet, error)\n\ttables             func(Filter) (*TableSet, error)\n\tcolumns            func(Filter) (*ColumnSet, error)\n\tcolumnStats        func(Filter) (*ColumnStatSet, error)\n\tindexes            func(Filter) (*IndexSet, error)\n\tindexColumns       func(Filter) (*IndexColumnSet, error)\n\ttriggers           func(Filter) (*TriggerSet, error)\n\tconstraints        func(Filter) (*ConstraintSet, error)\n\tconstraintColumns  func(Filter) (*ConstraintColumnSet, error)\n\tfunctions          func(Filter) (*FunctionSet, error)\n\tfunctionColumns    func(Filter) (*FunctionColumnSet, error)\n\tsequences          func(Filter) (*SequenceSet, error)\n\tprivilegeSummaries func(Filter) (*PrivilegeSummarySet, error)\n}\n\nvar _ ExtendedReader = &PluginReader{}\n\n// NewPluginReader allows to be easily composed from other readers\nfunc NewPluginReader(readers ...Reader) Reader {\n\tp := PluginReader{}\n\tfor _, i := range readers {\n\t\tif r, ok := i.(CatalogReader); ok {\n\t\t\tp.catalogs = r.Catalogs\n\t\t}\n\t\tif r, ok := i.(SchemaReader); ok {\n\t\t\tp.schemas = r.Schemas\n\t\t}\n\t\tif r, ok := i.(TableReader); ok {\n\t\t\tp.tables = r.Tables\n\t\t}\n\t\tif r, ok := i.(ColumnReader); ok {\n\t\t\tp.columns = r.Columns\n\t\t}\n\t\tif r, ok := i.(ColumnStatReader); ok {\n\t\t\tp.columnStats = r.ColumnStats\n\t\t}\n\t\tif r, ok := i.(IndexReader); ok {\n\t\t\tp.indexes = r.Indexes\n\t\t}\n\t\tif r, ok := i.(IndexColumnReader); ok {\n\t\t\tp.indexColumns = r.IndexColumns\n\t\t}\n\t\tif r, ok := i.(TriggerReader); ok {\n\t\t\tp.triggers = r.Triggers\n\t\t}\n\t\tif r, ok := i.(ConstraintReader); ok {\n\t\t\tp.constraints = r.Constraints\n\t\t}\n\t\tif r, ok := i.(ConstraintColumnReader); ok {\n\t\t\tp.constraintColumns = r.ConstraintColumns\n\t\t}\n\t\tif r, ok := i.(FunctionReader); ok {\n\t\t\tp.functions = r.Functions\n\t\t}\n\t\tif r, ok := i.(FunctionColumnReader); ok {\n\t\t\tp.functionColumns = r.FunctionColumns\n\t\t}\n\t\tif r, ok := i.(SequenceReader); ok {\n\t\t\tp.sequences = r.Sequences\n\t\t}\n\t\tif r, ok := i.(PrivilegeSummaryReader); ok {\n\t\t\tp.privilegeSummaries = r.PrivilegeSummaries\n\t\t}\n\t}\n\treturn &p\n}\n\nfunc (p PluginReader) Catalogs(f Filter) (*CatalogSet, error) {\n\tif p.catalogs == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.catalogs(f)\n}\n\nfunc (p PluginReader) Schemas(f Filter) (*SchemaSet, error) {\n\tif p.schemas == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.schemas(f)\n}\n\nfunc (p PluginReader) Tables(f Filter) (*TableSet, error) {\n\tif p.tables == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.tables(f)\n}\n\nfunc (p PluginReader) Columns(f Filter) (*ColumnSet, error) {\n\tif p.columns == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.columns(f)\n}\n\nfunc (p PluginReader) ColumnStats(f Filter) (*ColumnStatSet, error) {\n\tif p.columnStats == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.columnStats(f)\n}\n\nfunc (p PluginReader) Indexes(f Filter) (*IndexSet, error) {\n\tif p.indexes == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.indexes(f)\n}\n\nfunc (p PluginReader) IndexColumns(f Filter) (*IndexColumnSet, error) {\n\tif p.indexColumns == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.indexColumns(f)\n}\n\nfunc (p PluginReader) Triggers(f Filter) (*TriggerSet, error) {\n\tif p.triggers == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.triggers(f)\n}\n\nfunc (p PluginReader) Constraints(f Filter) (*ConstraintSet, error) {\n\tif p.constraints == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.constraints(f)\n}\n\nfunc (p PluginReader) ConstraintColumns(f Filter) (*ConstraintColumnSet, error) {\n\tif p.constraintColumns == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.constraintColumns(f)\n}\n\nfunc (p PluginReader) Functions(f Filter) (*FunctionSet, error) {\n\tif p.functions == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.functions(f)\n}\n\nfunc (p PluginReader) FunctionColumns(f Filter) (*FunctionColumnSet, error) {\n\tif p.functionColumns == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.functionColumns(f)\n}\n\nfunc (p PluginReader) Sequences(f Filter) (*SequenceSet, error) {\n\tif p.sequences == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.sequences(f)\n}\n\nfunc (p PluginReader) PrivilegeSummaries(f Filter) (*PrivilegeSummarySet, error) {\n\tif p.privilegeSummaries == nil {\n\t\treturn nil, text.ErrNotSupported\n\t}\n\treturn p.privilegeSummaries(f)\n}\n\ntype LoggingReader struct {\n\tdb      DB\n\tlogger  logger\n\tdryRun  bool\n\ttimeout time.Duration\n}\n\ntype logger interface {\n\tPrintln(...interface{})\n}\n\nfunc NewLoggingReader(db DB, opts ...ReaderOption) LoggingReader {\n\tr := LoggingReader{\n\t\tdb: db,\n\t}\n\tfor _, o := range opts {\n\t\to(&r)\n\t}\n\treturn r\n}\n\n// ReaderOption to configure the reader\ntype ReaderOption func(Reader)\n\n// WithLogger used to log queries before executing them\nfunc WithLogger(l logger) ReaderOption {\n\treturn func(r Reader) {\n\t\tr.(loggerSetter).setLogger(l)\n\t}\n}\n\n// WithDryRun allows to avoid running any queries\nfunc WithDryRun(d bool) ReaderOption {\n\treturn func(r Reader) {\n\t\tr.(loggerSetter).setDryRun(d)\n\t}\n}\n\n// WithTimeout for a single query\nfunc WithTimeout(t time.Duration) ReaderOption {\n\treturn func(r Reader) {\n\t\tr.(loggerSetter).setTimeout(t)\n\t}\n}\n\n// WithLimit for a single query, if the reader supports it\nfunc WithLimit(l int) ReaderOption {\n\treturn func(r Reader) {\n\t\tif rl, ok := r.(limiter); ok {\n\t\t\trl.SetLimit(l)\n\t\t}\n\t}\n}\n\ntype loggerSetter interface {\n\tsetLogger(logger)\n\tsetDryRun(bool)\n\tsetTimeout(t time.Duration)\n}\n\ntype limiter interface {\n\tSetLimit(l int)\n}\n\nfunc (r *LoggingReader) setLogger(l logger) {\n\tr.logger = l\n}\n\nfunc (r *LoggingReader) setDryRun(d bool) {\n\tr.dryRun = d\n}\n\nfunc (r *LoggingReader) setTimeout(t time.Duration) {\n\tr.timeout = t\n}\n\nfunc (r LoggingReader) Query(q string, v ...interface{}) (*sql.Rows, CloseFunc, error) {\n\tif r.logger != nil {\n\t\tr.logger.Println(q)\n\t\tr.logger.Println(v)\n\t}\n\tif r.dryRun {\n\t\treturn nil, nil, sql.ErrNoRows\n\t}\n\tif r.timeout != 0 {\n\t\tctx, cancel := context.WithTimeout(context.Background(), r.timeout)\n\t\trows, err := r.db.QueryContext(ctx, q, v...)\n\t\treturn rows, func() { cancel(); rows.Close() }, err\n\t}\n\trows, err := r.db.Query(q, v...)\n\treturn rows, func() { rows.Close() }, err\n}\n\n// CloseFunc should be called when result won't be processed anymore\ntype CloseFunc func()\n"
  },
  {
    "path": "drivers/metadata/writer.go",
    "content": "package metadata\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/tblfmt\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/text\"\n)\n\n// DB is the common interface for database operations, compatible with\n// database/sql.DB and database/sql.Tx.\ntype DB interface {\n\tExec(string, ...interface{}) (sql.Result, error)\n\tExecContext(context.Context, string, ...interface{}) (sql.Result, error)\n\tQuery(string, ...interface{}) (*sql.Rows, error)\n\tQueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)\n\tQueryRow(string, ...interface{}) *sql.Row\n\tQueryRowContext(context.Context, string, ...interface{}) *sql.Row\n\tPrepare(string) (*sql.Stmt, error)\n\tPrepareContext(context.Context, string) (*sql.Stmt, error)\n}\n\n// DefaultWriter using an existing db introspector\ntype DefaultWriter struct {\n\tr             Reader\n\tdb            DB\n\tw             io.Writer\n\ttableTypes    map[rune][]string\n\tfuncTypes     map[rune][]string\n\tsystemSchemas map[string]struct{}\n\n\t// custom functions for easier overloading\n\tlistAllDbs func(string, bool) error\n}\n\nfunc NewDefaultWriter(r Reader, opts ...WriterOption) func(db DB, w io.Writer) Writer {\n\tdefaultWriter := &DefaultWriter{\n\t\tr: r,\n\t\ttableTypes: map[rune][]string{\n\t\t\t't': {\"TABLE\", \"BASE TABLE\", \"SYSTEM TABLE\", \"SYNONYM\", \"LOCAL TEMPORARY\", \"GLOBAL TEMPORARY\"},\n\t\t\t'v': {\"VIEW\", \"SYSTEM VIEW\"},\n\t\t\t'm': {\"MATERIALIZED VIEW\"},\n\t\t\t's': {\"SEQUENCE\"},\n\t\t},\n\t\tfuncTypes: map[rune][]string{\n\t\t\t'a': {\"AGGREGATE\"},\n\t\t\t'n': {\"FUNCTION\"},\n\t\t\t'p': {\"PROCEDURE\", \"PACKAGE\"},\n\t\t\t't': {\"TRIGGER\"},\n\t\t\t'w': {\"WINDOW\"},\n\t\t},\n\t\tsystemSchemas: map[string]struct{}{\n\t\t\t\"information_schema\": {},\n\t\t},\n\t}\n\tfor _, o := range opts {\n\t\to(defaultWriter)\n\t}\n\treturn func(db DB, w io.Writer) Writer {\n\t\tdefaultWriter.db = db\n\t\tdefaultWriter.w = w\n\t\treturn defaultWriter\n\t}\n}\n\n// WriterOption to configure the DefaultWriter\ntype WriterOption func(*DefaultWriter)\n\n// WithSystemSchemas that are ignored unless showSystem is true\nfunc WithSystemSchemas(schemas []string) WriterOption {\n\treturn func(w *DefaultWriter) {\n\t\tw.systemSchemas = make(map[string]struct{}, len(schemas))\n\t\tfor _, s := range schemas {\n\t\t\tw.systemSchemas[s] = struct{}{}\n\t\t}\n\t}\n}\n\n// WithListAllDbs that lists all catalogs\nfunc WithListAllDbs(f func(string, bool) error) WriterOption {\n\treturn func(w *DefaultWriter) {\n\t\tw.listAllDbs = f\n\t}\n}\n\n// DescribeFunctions matching pattern\nfunc (w DefaultWriter) DescribeFunctions(u *dburl.URL, funcTypes, pattern string, verbose, showSystem bool) error {\n\tr, ok := w.r.(FunctionReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\df`, u.Driver)\n\t}\n\ttypes := []string{}\n\tfor k, v := range w.funcTypes {\n\t\tif strings.ContainsRune(funcTypes, k) {\n\t\t\ttypes = append(types, v...)\n\t\t}\n\t}\n\tsp, tp, err := parsePattern(pattern)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse search pattern: %w\", err)\n\t}\n\tres, err := r.Functions(Filter{Schema: sp, Name: tp, Types: types, WithSystem: showSystem})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list functions: %w\", err)\n\t}\n\tdefer res.Close()\n\n\tif !showSystem {\n\t\t// in case the reader doesn't implement WithSystem\n\t\tres.SetFilter(func(r Result) bool {\n\t\t\t_, ok := w.systemSchemas[r.(*Function).Schema]\n\t\t\treturn !ok\n\t\t})\n\t}\n\n\tif _, ok := w.r.(FunctionColumnReader); ok {\n\t\tfor res.Next() {\n\t\t\tf := res.Get()\n\t\t\tf.ArgTypes, err = w.getFunctionColumns(f.Catalog, f.Schema, f.SpecificName)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to get columns of function %s.%s: %w\", f.Schema, f.SpecificName, err)\n\t\t\t}\n\t\t}\n\t\tres.Reset()\n\t}\n\n\tcolumns := []string{\"Schema\", \"Name\", \"Result data type\", \"Argument data types\", \"Type\"}\n\tif verbose {\n\t\tcolumns = append(columns, \"Volatility\", \"Security\", \"Language\", \"Source code\")\n\t}\n\tres.SetColumns(columns)\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*Function)\n\t\tv := []interface{}{f.Schema, f.Name, f.ResultType, f.ArgTypes, f.Type}\n\t\tif verbose {\n\t\t\tv = append(v, f.Volatility, f.Security, f.Language, f.Source)\n\t\t}\n\t\treturn v\n\t})\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"List of functions\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\nfunc (w DefaultWriter) getFunctionColumns(c, s, f string) (string, error) {\n\tr := w.r.(FunctionColumnReader)\n\tcols, err := r.FunctionColumns(Filter{Catalog: c, Schema: s, Parent: f})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\targs := []string{}\n\tfor cols.Next() {\n\t\tc := cols.Get()\n\t\t// skip result params\n\t\tif c.OrdinalPosition == 0 {\n\t\t\tcontinue\n\t\t}\n\t\ttyp := \"\"\n\t\tif c.Type != \"IN\" && c.Type != \"\" {\n\t\t\ttyp = c.Type + \" \"\n\t\t}\n\t\tname := c.Name\n\t\tif name != \"\" {\n\t\t\tname += \" \"\n\t\t}\n\t\targs = append(args, fmt.Sprintf(\"%s%s%s\", typ, name, c.DataType))\n\t}\n\treturn strings.Join(args, \", \"), nil\n}\n\n// DescribeTableDetails matching pattern\nfunc (w DefaultWriter) DescribeTableDetails(u *dburl.URL, pattern string, verbose, showSystem bool) error {\n\tsp, tp, err := parsePattern(pattern)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse search pattern: %w\", err)\n\t}\n\n\tfound := 0\n\n\ttr, isTR := w.r.(TableReader)\n\t_, isCR := w.r.(ColumnReader)\n\tif isTR && isCR {\n\t\tres, err := tr.Tables(Filter{Schema: sp, Name: tp, WithSystem: showSystem})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to list tables: %w\", err)\n\t\t}\n\t\tdefer res.Close()\n\t\tif !showSystem {\n\t\t\t// in case the reader doesn't implement WithSystem\n\t\t\tres.SetFilter(func(r Result) bool {\n\t\t\t\t_, ok := w.systemSchemas[r.(*Table).Schema]\n\t\t\t\treturn !ok\n\t\t\t})\n\t\t}\n\t\tfor res.Next() {\n\t\t\tt := res.Get()\n\t\t\terr = w.describeTableDetails(t.Type, t.Schema, t.Name, verbose, showSystem)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to describe %s %s.%s: %w\", t.Type, t.Schema, t.Name, err)\n\t\t\t}\n\t\t\tfound++\n\t\t}\n\t}\n\n\tif _, ok := w.r.(SequenceReader); ok {\n\t\tfoundSeq, err := w.describeSequences(sp, tp, verbose, showSystem)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to describe sequences: %w\", err)\n\t\t}\n\t\tfound += foundSeq\n\t}\n\n\tir, isIR := w.r.(IndexReader)\n\t_, isICR := w.r.(IndexColumnReader)\n\tif isIR && isICR {\n\t\tres, err := ir.Indexes(Filter{Schema: sp, Name: tp, WithSystem: showSystem})\n\t\tif err != nil && err != text.ErrNotSupported {\n\t\t\treturn fmt.Errorf(\"failed to list indexes for table %s: %w\", tp, err)\n\t\t}\n\t\tif res != nil {\n\t\t\tdefer res.Close()\n\t\t\tif !showSystem {\n\t\t\t\t// in case the reader doesn't implement WithSystem\n\t\t\t\tres.SetFilter(func(r Result) bool {\n\t\t\t\t\t_, ok := w.systemSchemas[r.(*Index).Schema]\n\t\t\t\t\treturn !ok\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor res.Next() {\n\t\t\t\ti := res.Get()\n\t\t\t\terr = w.describeIndex(i)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to describe index %s from table %s.%s: %w\", i.Name, i.Schema, i.Table, err)\n\t\t\t\t}\n\t\t\t\tfound++\n\t\t\t}\n\t\t}\n\t}\n\n\tif found == 0 {\n\t\tfmt.Fprintf(w.w, text.RelationNotFound, pattern)\n\t\tfmt.Fprintln(w.w)\n\t}\n\treturn nil\n}\n\nfunc (w DefaultWriter) describeTableDetails(typ, sp, tp string, verbose, showSystem bool) error {\n\tr := w.r.(ColumnReader)\n\tres, err := r.Columns(Filter{Schema: sp, Parent: tp, WithSystem: showSystem})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list columns for table %s: %w\", tp, err)\n\t}\n\tdefer res.Close()\n\n\tcolumns := []string{\"Name\", \"Type\", \"Nullable\", \"Default\"}\n\tif verbose {\n\t\tcolumns = append(columns, \"Size\", \"Decimal Digits\", \"Radix\", \"Octet Length\")\n\t}\n\tres.SetColumns(columns)\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*Column)\n\t\tv := []interface{}{f.Name, f.DataType, f.IsNullable, f.Default}\n\t\tif verbose {\n\t\t\tv = append(v, f.ColumnSize, f.DecimalDigits, f.NumPrecRadix, f.CharOctetLength)\n\t\t}\n\t\treturn v\n\t})\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = fmt.Sprintf(\"%s %s\\n\", typ, qualifiedIdentifier(sp, tp))\n\treturn w.encodeWithSummary(res, params, w.tableDetailsSummary(sp, tp))\n}\n\nfunc (w DefaultWriter) encodeWithSummary(res tblfmt.ResultSet, params map[string]string, summary func(io.Writer, int) (int, error)) error {\n\tnewEnc, opts := tblfmt.FromMap(params)\n\topts = append(opts, tblfmt.WithSummary(\n\t\tmap[int]func(io.Writer, int) (int, error){\n\t\t\t-1: summary,\n\t\t},\n\t))\n\tenc, err := newEnc(res, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn enc.EncodeAll(w.w)\n}\n\nfunc (w DefaultWriter) tableDetailsSummary(sp, tp string) func(io.Writer, int) (int, error) {\n\treturn func(out io.Writer, _ int) (int, error) {\n\t\terr := w.describeTableIndexes(out, sp, tp)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\terr = w.describeTableConstraints(\n\t\t\tout,\n\t\t\tFilter{Schema: sp, Parent: tp},\n\t\t\tfunc(r Result) bool {\n\t\t\t\tc := r.(*Constraint)\n\t\t\t\treturn c.Type == \"CHECK\" && c.CheckClause != \"\" && !strings.HasSuffix(c.CheckClause, \" IS NOT NULL\")\n\t\t\t},\n\t\t\t\"Check constraints:\",\n\t\t\tfunc(out io.Writer, c *Constraint) error {\n\t\t\t\t_, err := fmt.Fprintf(out, \"  \\\"%s\\\" %s (%s)\\n\", c.Name, c.Type, c.CheckClause)\n\t\t\t\treturn err\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\terr = w.describeTableConstraints(\n\t\t\tout,\n\t\t\tFilter{Schema: sp, Parent: tp},\n\t\t\tfunc(r Result) bool { return r.(*Constraint).Type == \"FOREIGN KEY\" },\n\t\t\t\"Foreign-key constraints:\",\n\t\t\tfunc(out io.Writer, c *Constraint) error {\n\t\t\t\tcolumns, foreignColumns, err := w.getConstraintColumns(c.Catalog, c.Schema, c.Table, c.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t_, err = fmt.Fprintf(out, \"  \\\"%s\\\" %s (%s) REFERENCES %s(%s) ON UPDATE %s ON DELETE %s\\n\",\n\t\t\t\t\tc.Name,\n\t\t\t\t\tc.Type,\n\t\t\t\t\tcolumns,\n\t\t\t\t\tc.ForeignTable,\n\t\t\t\t\tforeignColumns,\n\t\t\t\t\tc.UpdateRule,\n\t\t\t\t\tc.DeleteRule)\n\t\t\t\treturn err\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\terr = w.describeTableConstraints(\n\t\t\tout,\n\t\t\tFilter{Schema: sp, Reference: tp},\n\t\t\tfunc(r Result) bool { return r.(*Constraint).Type == \"FOREIGN KEY\" },\n\t\t\t\"Referenced by:\",\n\t\t\tfunc(out io.Writer, c *Constraint) error {\n\t\t\t\tcolumns, foreignColumns, err := w.getConstraintColumns(c.Catalog, c.Schema, c.Table, c.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t_, err = fmt.Fprintf(out, \"  TABLE \\\"%s\\\" CONSTRAINT \\\"%s\\\" %s (%s) REFERENCES %s(%s) ON UPDATE %s ON DELETE %s\\n\",\n\t\t\t\t\tc.Table,\n\t\t\t\t\tc.Name,\n\t\t\t\t\tc.Type,\n\t\t\t\t\tcolumns,\n\t\t\t\t\tc.ForeignTable,\n\t\t\t\t\tforeignColumns,\n\t\t\t\t\tc.UpdateRule,\n\t\t\t\t\tc.DeleteRule)\n\t\t\t\treturn err\n\t\t\t},\n\t\t)\n\t\terr = w.describeTableTriggers(out, sp, tp)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn 0, err\n\t}\n}\n\nfunc (w DefaultWriter) describeTableTriggers(out io.Writer, sp, tp string) error {\n\tr, ok := w.r.(TriggerReader)\n\tif !ok {\n\t\treturn nil\n\t}\n\tres, err := r.Triggers(Filter{Schema: sp, Parent: tp})\n\tif err != nil && err != text.ErrNotSupported {\n\t\treturn fmt.Errorf(\"failed to list triggers for table %s: %w\", tp, err)\n\t}\n\tif res == nil {\n\t\treturn nil\n\t}\n\tdefer res.Close()\n\n\tif res.Len() == 0 {\n\t\treturn nil\n\t}\n\tfmt.Fprintln(out, \"Triggers:\")\n\tfor res.Next() {\n\t\tt := res.Get()\n\t\tfmt.Fprintf(out, \"  \\\"%s\\\" %s\\n\", t.Name, t.Definition)\n\t}\n\treturn nil\n}\n\nfunc (w DefaultWriter) describeTableIndexes(out io.Writer, sp, tp string) error {\n\tr, ok := w.r.(IndexReader)\n\tif !ok {\n\t\treturn nil\n\t}\n\tres, err := r.Indexes(Filter{Schema: sp, Parent: tp})\n\tif err != nil && err != text.ErrNotSupported {\n\t\treturn fmt.Errorf(\"failed to list indexes for table %s: %w\", tp, err)\n\t}\n\tif res == nil {\n\t\treturn nil\n\t}\n\tdefer res.Close()\n\n\tif res.Len() == 0 {\n\t\treturn nil\n\t}\n\tfmt.Fprintln(out, \"Indexes:\")\n\tfor res.Next() {\n\t\ti := res.Get()\n\t\tprimary := \"\"\n\t\tunique := \"\"\n\t\tif i.IsPrimary == YES {\n\t\t\tprimary = \"PRIMARY_KEY, \"\n\t\t}\n\t\tif i.IsUnique == YES {\n\t\t\tunique = \"UNIQUE, \"\n\t\t}\n\t\ti.Columns, err = w.getIndexColumns(i.Catalog, i.Schema, i.Table, i.Name)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get columns of index %s: %w\", i.Name, err)\n\t\t}\n\t\tfmt.Fprintf(out, \"  \\\"%s\\\" %s%s%s (%s)\\n\", i.Name, primary, unique, i.Type, i.Columns)\n\t}\n\treturn nil\n}\n\nfunc (w DefaultWriter) getIndexColumns(c, s, t, i string) (string, error) {\n\tr := w.r.(IndexColumnReader)\n\tcols, err := r.IndexColumns(Filter{Catalog: c, Schema: s, Parent: t, Name: i})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tresult := []string{}\n\tfor cols.Next() {\n\t\tresult = append(result, cols.Get().Name)\n\t}\n\treturn strings.Join(result, \", \"), nil\n}\n\nfunc (w DefaultWriter) describeTableConstraints(out io.Writer, filter Filter, postFilter func(r Result) bool, label string, printer func(io.Writer, *Constraint) error) error {\n\tr, ok := w.r.(ConstraintReader)\n\tif !ok {\n\t\treturn nil\n\t}\n\tres, err := r.Constraints(filter)\n\tif err != nil && err != text.ErrNotSupported {\n\t\treturn fmt.Errorf(\"failed to list constraints: %w\", err)\n\t}\n\tif res == nil {\n\t\treturn nil\n\t}\n\tdefer res.Close()\n\n\tres.SetFilter(postFilter)\n\tif res.Len() == 0 {\n\t\treturn nil\n\t}\n\tfmt.Fprintln(out, label)\n\tfor res.Next() {\n\t\tc := res.Get()\n\t\terr := printer(out, c)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (w DefaultWriter) getConstraintColumns(c, s, t, n string) (string, string, error) {\n\tr := w.r.(ConstraintColumnReader)\n\tcols, err := r.ConstraintColumns(Filter{Catalog: c, Schema: s, Parent: t, Name: n})\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tcolumns := []string{}\n\tforeignColumns := []string{}\n\tfor cols.Next() {\n\t\tcolumns = append(columns, cols.Get().Name)\n\t\tforeignColumns = append(foreignColumns, cols.Get().ForeignName)\n\t}\n\treturn strings.Join(columns, \", \"), strings.Join(foreignColumns, \", \"), nil\n}\n\nfunc (w DefaultWriter) describeSequences(sp, tp string, verbose, showSystem bool) (int, error) {\n\tr := w.r.(SequenceReader)\n\tres, err := r.Sequences(Filter{Schema: sp, Name: tp, WithSystem: showSystem})\n\tif err != nil && err != text.ErrNotSupported {\n\t\treturn 0, err\n\t}\n\tif res == nil {\n\t\treturn 0, nil\n\t}\n\tdefer res.Close()\n\n\tfound := 0\n\tfor res.Next() {\n\t\ts := res.Get()\n\t\t// wrap current record into a separate recordSet\n\t\trows := NewSequenceSet([]Sequence{*s})\n\t\tparams := env.Vars().Print()\n\t\tparams[\"footer\"] = \"off\"\n\t\tparams[\"title\"] = fmt.Sprintf(\"Sequence \\\"%s.%s\\\"\\n\", s.Schema, s.Name)\n\t\terr = tblfmt.EncodeAll(w.w, rows, params)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\t// TODO footer should say which table this sequence belongs to\n\t\tfound++\n\t}\n\n\treturn found, nil\n}\n\nfunc (w DefaultWriter) describeIndex(i *Index) error {\n\tr := w.r.(IndexColumnReader)\n\tres, err := r.IndexColumns(Filter{Schema: i.Schema, Parent: i.Table, Name: i.Name})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get index columns: %w\", err)\n\t}\n\tdefer res.Close()\n\tif res.Len() == 0 {\n\t\treturn nil\n\t}\n\n\tres.SetColumns([]string{\"Name\", \"Type\"})\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*IndexColumn)\n\t\treturn []interface{}{f.Name, f.DataType}\n\t})\n\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = fmt.Sprintf(\"Index %s\\n\", qualifiedIdentifier(i.Schema, i.Name))\n\treturn w.encodeWithSummary(res, params, func(out io.Writer, _ int) (int, error) {\n\t\tprimary := \"\"\n\t\tif i.IsPrimary == YES {\n\t\t\tprimary = \"primary key, \"\n\t\t}\n\t\t_, err := fmt.Fprintf(out, \"%s%s, for table %s\", primary, i.Type, i.Table)\n\t\treturn 0, err\n\t})\n}\n\n// ListAllDbs matching pattern\nfunc (w DefaultWriter) ListAllDbs(u *dburl.URL, pattern string, verbose bool) error {\n\tif w.listAllDbs != nil {\n\t\treturn w.listAllDbs(pattern, verbose)\n\t}\n\tr, ok := w.r.(CatalogReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\l`, u.Driver)\n\t}\n\tres, err := r.Catalogs(Filter{Name: pattern})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list catalogs: %w\", err)\n\t}\n\tdefer res.Close()\n\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"List of databases\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\n// ListTables matching pattern\nfunc (w DefaultWriter) ListTables(u *dburl.URL, tableTypes, pattern string, verbose, showSystem bool) error {\n\tr, ok := w.r.(TableReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\dt`, u.Driver)\n\t}\n\ttypes := []string{}\n\tfor k, v := range w.tableTypes {\n\t\tif strings.ContainsRune(tableTypes, k) {\n\t\t\ttypes = append(types, v...)\n\t\t}\n\t}\n\tsp, tp, err := parsePattern(pattern)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse search pattern: %w\", err)\n\t}\n\tres, err := r.Tables(Filter{Schema: sp, Name: tp, Types: types, WithSystem: showSystem})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list tables: %w\", err)\n\t}\n\tdefer res.Close()\n\tif !showSystem {\n\t\t// in case the reader doesn't implement WithSystem\n\t\tres.SetFilter(func(r Result) bool {\n\t\t\t_, ok := w.systemSchemas[r.(*Table).Schema]\n\t\t\treturn !ok\n\t\t})\n\t}\n\tif res.Len() == 0 {\n\t\tfmt.Fprintf(w.w, text.RelationNotFound, pattern)\n\t\tfmt.Fprintln(w.w)\n\t\treturn nil\n\t}\n\tcolumns := []string{\"Schema\", \"Name\", \"Type\"}\n\tif verbose {\n\t\tcolumns = append(columns, \"Rows\", \"Size\", \"Comment\")\n\t}\n\tres.SetColumns(columns)\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*Table)\n\t\tv := []interface{}{f.Schema, f.Name, f.Type}\n\t\tif verbose {\n\t\t\tv = append(v, f.Rows, f.Size, f.Comment)\n\t\t}\n\t\treturn v\n\t})\n\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"List of relations\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\n// ListSchemas matching pattern\nfunc (w DefaultWriter) ListSchemas(u *dburl.URL, pattern string, verbose, showSystem bool) error {\n\tr, ok := w.r.(SchemaReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\d`, u.Driver)\n\t}\n\tres, err := r.Schemas(Filter{Name: pattern, WithSystem: showSystem})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list schemas: %w\", err)\n\t}\n\tdefer res.Close()\n\n\tif !showSystem {\n\t\t// in case the reader doesn't implement WithSystem\n\t\tres.SetFilter(func(r Result) bool {\n\t\t\t_, ok := w.systemSchemas[r.(*Schema).Schema]\n\t\t\treturn !ok\n\t\t})\n\t}\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"List of schemas\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\n// ListIndexes matching pattern\nfunc (w DefaultWriter) ListIndexes(u *dburl.URL, pattern string, verbose, showSystem bool) error {\n\tr, ok := w.r.(IndexReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\di`, u.Driver)\n\t}\n\tsp, tp, err := parsePattern(pattern)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse search pattern: %w\", err)\n\t}\n\tres, err := r.Indexes(Filter{Schema: sp, Name: tp, WithSystem: showSystem})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list indexes: %w\", err)\n\t}\n\tdefer res.Close()\n\n\tif !showSystem {\n\t\t// in case the reader doesn't implement WithSystem\n\t\tres.SetFilter(func(r Result) bool {\n\t\t\t_, ok := w.systemSchemas[r.(*Index).Schema]\n\t\t\treturn !ok\n\t\t})\n\t}\n\tif res.Len() == 0 {\n\t\tfmt.Fprintf(w.w, text.RelationNotFound, pattern)\n\t\tfmt.Fprintln(w.w)\n\t\treturn nil\n\t}\n\n\tcolumns := []string{\"Schema\", \"Name\", \"Type\", \"Table\"}\n\tif verbose {\n\t\tcolumns = append(columns, \"Primary?\", \"Unique?\")\n\t}\n\tres.SetColumns(columns)\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*Index)\n\t\tv := []interface{}{f.Schema, f.Name, f.Type, f.Table}\n\t\tif verbose {\n\t\t\tv = append(v, f.IsPrimary, f.IsUnique)\n\t\t}\n\t\treturn v\n\t})\n\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"List of indexes\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\n// ShowStats of columns for tables matching pattern\nfunc (w DefaultWriter) ShowStats(u *dburl.URL, statTypes, pattern string, verbose bool, k int) error {\n\tr, ok := w.r.(ColumnStatReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\ss`, u.Driver)\n\t}\n\tsp, tp, err := parsePattern(pattern)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse search pattern: %w\", err)\n\t}\n\n\trows := int64(0)\n\ttr, ok := w.r.(TableReader)\n\tif ok {\n\t\ttables, err := tr.Tables(Filter{Schema: sp, Name: tp})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to get table entry: %w\", err)\n\t\t}\n\t\tdefer tables.Close()\n\t\tif tables.Next() {\n\t\t\trows = tables.Get().Rows\n\t\t}\n\t}\n\n\ttypes := []string{\"basic\"}\n\tif verbose {\n\t\ttypes = append(types, \"extended\")\n\t}\n\tres, err := r.ColumnStats(Filter{Schema: sp, Parent: tp, Types: types})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get column stats: %w\", err)\n\t}\n\tdefer res.Close()\n\n\tif res.Len() == 0 {\n\t\tfmt.Fprintf(w.w, text.RelationNotFound, pattern)\n\t\tfmt.Fprintln(w.w)\n\t\treturn nil\n\t}\n\tcolumns := []string{\"Schema\", \"Table\", \"Name\", \"Average width\", \"Nulls fraction\", \"Distinct values\", \"Dist. fraction\"}\n\tif verbose {\n\t\tcolumns = append(columns, \"Minimum value\", \"Maximum value\", \"Mean value\", \"Top N common values\", \"Top N values freqs\")\n\t}\n\tres.SetColumns(columns)\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*ColumnStat)\n\t\tfreqs := []string{}\n\t\tfor _, freq := range f.TopNFreqs {\n\t\t\tfreqs = append(freqs, fmt.Sprintf(\"%.4f\", freq))\n\t\t}\n\t\tn := k\n\t\tif n > len(freqs) {\n\t\t\tn = len(freqs)\n\t\t}\n\t\tdistFrac := 1.0\n\t\tif rows != 0 && f.NumDistinct != rows {\n\t\t\tdistFrac = float64(f.NumDistinct) / float64(rows)\n\t\t}\n\t\tv := []interface{}{\n\t\t\tf.Schema,\n\t\t\tf.Table,\n\t\t\tf.Name,\n\t\t\tf.AvgWidth,\n\t\t\tf.NullFrac,\n\t\t\tf.NumDistinct,\n\t\t\tfmt.Sprintf(\"%.4f\", distFrac),\n\t\t}\n\t\tif verbose {\n\t\t\tv = append(v,\n\t\t\t\tf.Min,\n\t\t\t\tf.Max,\n\t\t\t\tf.Mean,\n\t\t\t\tstrings.Join(f.TopN[:n], \", \"),\n\t\t\t\tstrings.Join(freqs[:n], \", \"),\n\t\t\t)\n\t\t}\n\t\treturn v\n\t})\n\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"Column stats\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\n// ListPrivilegeSummaries matching pattern\nfunc (w DefaultWriter) ListPrivilegeSummaries(u *dburl.URL, pattern string, showSystem bool) error {\n\tr, ok := w.r.(PrivilegeSummaryReader)\n\tif !ok {\n\t\treturn fmt.Errorf(text.NotSupportedByDriver, `\\dp`, u.Driver)\n\t}\n\tsp, tp, err := parsePattern(pattern)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse search pattern: %w\", err)\n\t}\n\t// filter for tables, views and sequences\n\tconst tableTypes = \"tvms\"\n\ttypes := []string{}\n\tfor k, v := range w.tableTypes {\n\t\tif strings.ContainsRune(tableTypes, k) {\n\t\t\ttypes = append(types, v...)\n\t\t}\n\t}\n\tres, err := r.PrivilegeSummaries(Filter{Schema: sp, Name: tp, WithSystem: showSystem, Types: types})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list table privileges: %w\", err)\n\t}\n\tdefer res.Close()\n\tif !showSystem {\n\t\t// in case the reader doesn't implement WithSystem\n\t\tres.SetFilter(func(r Result) bool {\n\t\t\t_, ok := w.systemSchemas[r.(*PrivilegeSummary).Schema]\n\t\t\treturn !ok\n\t\t})\n\t}\n\n\tres.SetScanValues(func(r Result) []interface{} {\n\t\tf := r.(*PrivilegeSummary)\n\n\t\tv := []interface{}{\n\t\t\tf.Schema,\n\t\t\tf.Name,\n\t\t\tf.ObjectType,\n\t\t\tf.ObjectPrivileges,\n\t\t\tf.ColumnPrivileges,\n\t\t}\n\t\treturn v\n\t})\n\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"Access privileges\"\n\treturn tblfmt.EncodeAll(w.w, res, params)\n}\n\nfunc parsePattern(pattern string) (string, string, error) {\n\t// TODO do proper escaping, quoting etc\n\tif strings.ContainsRune(pattern, '.') {\n\t\tparts := strings.SplitN(pattern, \".\", 2)\n\t\treturn strings.ReplaceAll(parts[0], \"*\", \"%\"), strings.ReplaceAll(parts[1], \"*\", \"%\"), nil\n\t}\n\treturn \"\", strings.ReplaceAll(pattern, \"*\", \"%\"), nil\n}\n\nfunc qualifiedIdentifier(schema, name string) string {\n\tif schema == \"\" {\n\t\treturn fmt.Sprintf(\"\\\"%s\\\"\", name)\n\t}\n\treturn fmt.Sprintf(\"\\\"%s.%s\\\"\", schema, name)\n}\n"
  },
  {
    "path": "drivers/moderncsqlite/moderncsqlite.go",
    "content": "// Package moderncsqlite defines and registers usql's ModernC SQLite3 driver.\n// Transpilation of SQLite3 to Go.\n//\n// See: https://gitlab.com/cznic/sqlite\npackage moderncsqlite\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/sqlite3/sqshared\"\n\t\"modernc.org/sqlite\" // DRIVER\n)\n\nfunc init() {\n\tdrivers.Register(\"moderncsqlite\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tOpen: func(_ context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) {\n\t\t\treturn func(_ string, params string) (*sql.DB, error) {\n\t\t\t\treturn sql.Open(\"sqlite\", params)\n\t\t\t}, nil\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SELECT sqlite_version()`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"ModernC SQLite \" + ver, nil\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*sqlite.Error); ok {\n\t\t\t\treturn strconv.Itoa(e.Code()), e.Error()\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tConvertBytes:      sqshared.ConvertBytes,\n\t\tNewMetadataReader: sqshared.NewMetadataReader,\n\t\tCopy:              drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t})\n}\n"
  },
  {
    "path": "drivers/mymysql/mymysql.go",
    "content": "// Package mymysql defines and registers usql's MySQL MyMySQL driver.\n//\n// See: https://github.com/ziutek/mymysql\npackage mymysql\n\nimport (\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tmymeta \"github.com/xo/usql/drivers/metadata/mysql\"\n\t_ \"github.com/ziutek/mymysql/godrv\" // DRIVER\n\t\"github.com/ziutek/mymysql/mysql\"\n)\n\nfunc init() {\n\tdrivers.Register(\"mymysql\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tAllowHashComments:      true,\n\t\tLexerName:              \"mysql\",\n\t\tUseColumnTypes:         true,\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*mysql.Error); ok {\n\t\t\t\treturn strconv.Itoa(int(e.Code)), string(e.Msg)\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tif e, ok := err.(*mysql.Error); ok {\n\t\t\t\treturn e.Code == mysql.ER_ACCESS_DENIED_ERROR\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tNewMetadataReader: mymeta.NewReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(mymeta.NewReader(db, opts...))(db, w)\n\t\t},\n\t\tCopy:         drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t\tNewCompleter: mymeta.NewCompleter,\n\t})\n}\n"
  },
  {
    "path": "drivers/mysql/mysql.go",
    "content": "// Package mysql defines and registers usql's MySQL driver.\n//\n// Alias: memsql, SingleStore MemSQL\n// Alias: vitess, Vitess Database\n// Alias: tidb, TiDB\n//\n// See: https://github.com/go-sql-driver/mysql\n// Group: base\npackage mysql\n\nimport (\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/go-sql-driver/mysql\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tmymeta \"github.com/xo/usql/drivers/metadata/mysql\"\n)\n\nfunc init() {\n\tdrivers.Register(\"mysql\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tAllowHashComments:      true,\n\t\tLexerName:              \"mysql\",\n\t\tUseColumnTypes:         true,\n\t\tForceParams: drivers.ForceQueryParameters([]string{\n\t\t\t\"parseTime\", \"true\",\n\t\t\t\"loc\", \"Local\",\n\t\t\t\"sql_mode\", \"ansi\",\n\t\t}),\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*mysql.MySQLError); ok {\n\t\t\t\treturn strconv.Itoa(int(e.Number)), e.Message\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tif e, ok := err.(*mysql.MySQLError); ok {\n\t\t\t\treturn e.Number == 1045\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tNewMetadataReader: mymeta.NewReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(mymeta.NewReader(db, opts...))(db, w)\n\t\t},\n\t\tCopy:         drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t\tNewCompleter: mymeta.NewCompleter,\n\t}, \"memsql\", \"vitess\", \"tidb\")\n}\n"
  },
  {
    "path": "drivers/netezza/netezza.go",
    "content": "// Package netezza defines and registers usql's Netezza driver.\n//\n// See: https://github.com/IBM/nzgo\npackage netezza\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\n\t\"github.com/IBM/nzgo/v12\" // DRIVER: nzgo\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n)\n\nfunc init() {\n\tnzgo.Debug = log.New(io.Discard, \"\", 0)\n\tnzgo.Info = log.New(io.Discard, \"\", 0)\n\tnzgo.Fatal = log.New(io.Discard, \"\", 0)\n\tnewReader := infos.New(\n\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\tinfos.WithIndexes(false),\n\t\tinfos.WithConstraints(false),\n\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\tinfos.ColumnsColumnSize:         \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)\",\n\t\t\tinfos.FunctionColumnsColumnSize: \"COALESCE(character_maximum_length, numeric_precision, datetime_precision, interval_precision, 0)\",\n\t\t}),\n\t\tinfos.WithSystemSchemas([]string{\"DEFINITION_SCHEMA\", \"INFORMATION_SCHEMA\"}),\n\t\tinfos.WithCurrentSchema(\"CURRENT_SCHEMA\"),\n\t)\n\tdrivers.Register(\"nzgo\", drivers.Driver{\n\t\tName:                   \"nz\",\n\t\tAllowDollar:            true,\n\t\tAllowMultilineComments: true,\n\t\tLexerName:              \"postgres\",\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SELECT version()`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Netezza \" + ver, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, _ string) error {\n\t\t\t_, err := db.Exec(`ALTER USER ` + user + ` PASSWORD '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*nzgo.Error); ok {\n\t\t\t\treturn string(e.Code), e.Message\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tif e, ok := err.(*nzgo.Error); ok {\n\t\t\t\treturn e.Code.Name() == \"invalid_password\"\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tNewMetadataReader: newReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(newReader(db, opts...))(db, w)\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/odbc/odbc.go",
    "content": "// Package odbc defines and registers usql's ODBC driver. Requires CGO. Uses\n// respective platform's standard ODBC packages.\n//\n// See: https://github.com/alexbrainman/odbc\n// Group: all\npackage odbc\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/alexbrainman/odbc\" // DRIVER\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tendRE := regexp.MustCompile(`;?\\s*$`)\n\tendAnchorRE := regexp.MustCompile(`(?i)\\send\\s*;\\s*$`)\n\tdrivers.Register(\"odbc\", drivers.Driver{\n\t\tLexerName: \"tsql\",\n\t\tProcess: func(u *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) {\n\t\t\t// trim last ; but only when not END;\n\t\t\tif s := strings.ToLower(u.Query().Get(\"usql_trim\")); s != \"\" && s != \"off\" && s != \"0\" && s != \"false\" {\n\t\t\t\tif !endAnchorRE.MatchString(sqlstr) {\n\t\t\t\t\tsqlstr = endRE.ReplaceAllString(sqlstr, \"\")\n\t\t\t\t}\n\t\t\t}\n\t\t\ttyp, q := drivers.QueryExecType(prefix, sqlstr)\n\t\t\treturn typ, sqlstr, q, nil\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tif e, ok := err.(*odbc.Error); ok {\n\t\t\t\tmsg := strings.ToLower(e.Error())\n\t\t\t\treturn strings.Contains(msg, \"failed\") &&\n\t\t\t\t\t(strings.Contains(msg, \"login\") ||\n\t\t\t\t\t\tstrings.Contains(msg, \"authentication\") ||\n\t\t\t\t\t\tstrings.Contains(msg, \"password\"))\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/oracle/oracle.go",
    "content": "// Package oracle defines and registers usql's Oracle Database driver.\n//\n// See: https://github.com/sijms/go-ora\n// Group: base\npackage oracle\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t_ \"github.com/sijms/go-ora/v2\" // DRIVER\n\t\"github.com/xo/usql/drivers/oracle/orshared\"\n)\n\nfunc init() {\n\torshared.Register(\n\t\t\"oracle\",\n\t\t// unwrap error\n\t\tfunc(err error) (string, string) {\n\t\t\tif e := errors.Unwrap(err); e != nil {\n\t\t\t\terr = e\n\t\t\t}\n\t\t\tcode, msg := \"\", err.Error()\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tCode() int\n\t\t\t}); ok {\n\t\t\t\tcode = fmt.Sprintf(\"ORA-%05d\", e.Code())\n\t\t\t}\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tMessage() string\n\t\t\t}); ok {\n\t\t\t\tmsg = e.Message()\n\t\t\t}\n\t\t\tif i := strings.LastIndex(msg, \"ORA-\"); msg == \"\" && i != -1 {\n\t\t\t\tmsg = msg[i:]\n\t\t\t\tif j := strings.Index(msg, \":\"); j != -1 {\n\t\t\t\t\tmsg = msg[j+1:]\n\t\t\t\t\tif code == \"\" {\n\t\t\t\t\t\tcode = msg[i:j]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn code, strings.TrimSpace(msg)\n\t\t},\n\t\t// is password error\n\t\tfunc(err error) bool {\n\t\t\tif e := errors.Unwrap(err); e != nil {\n\t\t\t\terr = e\n\t\t\t}\n\t\t\treturn strings.Contains(err.Error(), \"empty password\")\n\t\t},\n\t)\n}\n"
  },
  {
    "path": "drivers/oracle/orshared/orshared.go",
    "content": "// Package orshared contains shared a shared driver implementation for the\n// Oracle Database. Used by Oracle and Godror drivers.\npackage orshared\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\torameta \"github.com/xo/usql/drivers/metadata/oracle\"\n\t\"github.com/xo/usql/env\"\n)\n\n// Register registers an oracle driver.\nfunc Register(name string, err func(error) (string, string), isPasswordErr func(error) bool) {\n\tendRE := regexp.MustCompile(`;?\\s*$`)\n\tendAnchorRE := regexp.MustCompile(`(?i)\\send\\s*;\\s*$`)\n\tdrivers.Register(name, drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tLowerColumnNames:       true,\n\t\tForceParams: func(u *dburl.URL) {\n\t\t\t// if the service name is not specified, use the environment\n\t\t\t// variable if present\n\t\t\tif strings.TrimPrefix(u.Path, \"/\") == \"\" {\n\t\t\t\tif n, ok := env.Getenv(\"ORACLE_SID\", \"ORASID\"); ok && n != \"\" {\n\t\t\t\t\tu.Path = \"/\" + n\n\t\t\t\t\tif u.Host == \"\" {\n\t\t\t\t\t\tu.Host = \"localhost\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT version FROM v$instance`).Scan(&ver); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Oracle Database \" + ver, nil\n\t\t},\n\t\tUser: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar user string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT user FROM dual`).Scan(&user); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn user, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, _ string) error {\n\t\t\t_, err := db.Exec(`ALTER USER ` + user + ` IDENTIFIED BY ` + newpw)\n\t\t\treturn err\n\t\t},\n\t\tErr:           err,\n\t\tIsPasswordErr: isPasswordErr,\n\t\tProcess: func(_ *dburl.URL, prefix string, sqlstr string) (string, string, bool, error) {\n\t\t\tif !endAnchorRE.MatchString(sqlstr) {\n\t\t\t\t// trim last ; but only when not END;\n\t\t\t\tsqlstr = endRE.ReplaceAllString(sqlstr, \"\")\n\t\t\t}\n\t\t\ttyp, q := drivers.QueryExecType(prefix, sqlstr)\n\t\t\treturn typ, sqlstr, q, nil\n\t\t},\n\t\tNewMetadataReader: orameta.NewReader(),\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(orameta.NewReader()(db, opts...))(db, w)\n\t\t},\n\t\tCopy: drivers.CopyWithInsert(func(n int) string {\n\t\t\treturn fmt.Sprintf(\":%d\", n)\n\t\t}),\n\t})\n}\n"
  },
  {
    "path": "drivers/ots/ots.go",
    "content": "// Package ots defines and registers usql's Alibaba Tablestore driver.\n//\n// See: https://github.com/aliyun/aliyun-tablestore-go-sql-driver\npackage ots\n\nimport (\n\t_ \"github.com/aliyun/aliyun-tablestore-go-sql-driver\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"ots\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/pgx/pgx.go",
    "content": "// Package pgx defines and registers usql's PostgreSQL PGX driver.\n//\n// See: https://github.com/jackc/pgx\npackage pgx\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgconn\"\n\t\"github.com/jackc/pgx/v5/stdlib\" // DRIVER\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tpgmeta \"github.com/xo/usql/drivers/metadata/postgres\"\n\t\"github.com/xo/usql/text\"\n)\n\nfunc init() {\n\tdrivers.Register(\"pgx\", drivers.Driver{\n\t\tAllowDollar:            true,\n\t\tAllowMultilineComments: true,\n\t\tLexerName:              \"postgres\",\n\t\tOpen: func(ctx context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) {\n\t\t\treturn func(_, dsn string) (*sql.DB, error) {\n\t\t\t\tconfig, err := pgx.ParseConfig(dsn)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tconfig.OnNotice = func(_ *pgconn.PgConn, notice *pgconn.Notice) {\n\t\t\t\t\tout := stderr()\n\t\t\t\t\tfmt.Fprintln(out, notice.Severity+\": \", notice.Message)\n\t\t\t\t\tif notice.Hint != \"\" {\n\t\t\t\t\t\tfmt.Fprintln(out, \"HINT: \", notice.Hint)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconfig.OnNotification = func(_ *pgconn.PgConn, notification *pgconn.Notification) {\n\t\t\t\t\tvar payload string\n\t\t\t\t\tif notification.Payload != \"\" {\n\t\t\t\t\t\tpayload = fmt.Sprintf(text.NotificationPayload, notification.Payload)\n\t\t\t\t\t}\n\t\t\t\t\tfmt.Fprintln(stdout(), fmt.Sprintf(text.NotificationReceived, notification.Channel, payload, notification.PID))\n\t\t\t\t}\n\t\t\t\t// NOTE: as opposed to the github.com/lib/pq driver, this\n\t\t\t\t// NOTE: driver has a \"prefer\" mode that is enabled by default.\n\t\t\t\t// NOTE: as such there is no logic here to try to reconnect as\n\t\t\t\t// NOTE: in the postgres driver.\n\t\t\t\treturn stdlib.OpenDB(*config), nil\n\t\t\t}, nil\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SHOW server_version`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"PostgreSQL \" + ver, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, _ string) error {\n\t\t\t_, err := db.Exec(`ALTER USER ` + user + ` PASSWORD '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tvar e *pgconn.PgError\n\t\t\tif errors.As(err, &e) {\n\t\t\t\treturn e.Code, e.Message\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tvar e *pgconn.PgError\n\t\t\tif errors.As(err, &e) {\n\t\t\t\treturn e.Code == \"28P01\"\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tNewMetadataReader: pgmeta.NewReader(),\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(pgmeta.NewReader()(db, opts...))(db, w)\n\t\t},\n\t\tCopy: func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) {\n\t\t\tconn, err := db.Conn(context.Background())\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to get a connection from pool: %w\", err)\n\t\t\t}\n\n\t\t\tleftParen := strings.IndexRune(table, '(')\n\t\t\tcolQuery := \"SELECT * FROM \" + table + \" WHERE 1=0\"\n\t\t\tif leftParen != -1 {\n\t\t\t\t// pgx's CopyFrom needs a slice of column names and splitting them by a comma is unreliable\n\t\t\t\t// so evaluate the possible expressions against the target table\n\t\t\t\tcolQuery = \"SELECT \" + table[leftParen+1:len(table)-1] + \" FROM \" + table[:leftParen] + \" WHERE 1=0\"\n\t\t\t\ttable = table[:leftParen]\n\t\t\t}\n\t\t\tcolStmt, err := db.PrepareContext(ctx, colQuery)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to prepare query to determine target table columns: %w\", err)\n\t\t\t}\n\t\t\tcolRows, err := colStmt.QueryContext(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to execute query to determine target table columns: %w\", err)\n\t\t\t}\n\t\t\tcolumns, err := colRows.Columns()\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to fetch target table columns: %w\", err)\n\t\t\t}\n\t\t\tclen := len(columns)\n\n\t\t\tcrows := &copyRows{\n\t\t\t\trows:   rows,\n\t\t\t\tvalues: make([]interface{}, clen),\n\t\t\t}\n\t\t\tfor i := 0; i < clen; i++ {\n\t\t\t\tcrows.values[i] = new(interface{})\n\t\t\t}\n\n\t\t\tvar n int64\n\t\t\terr = conn.Raw(func(driverConn interface{}) error {\n\t\t\t\tconn := driverConn.(*stdlib.Conn).Conn()\n\t\t\t\tn, err = conn.CopyFrom(ctx, pgx.Identifier(strings.SplitN(table, \".\", 2)), columns, crows)\n\t\t\t\treturn err\n\t\t\t})\n\t\t\treturn n, err\n\t\t},\n\t})\n}\n\ntype copyRows struct {\n\trows   *sql.Rows\n\tvalues []interface{}\n}\n\nfunc (r *copyRows) Next() bool {\n\treturn r.rows.Next()\n}\n\nfunc (r *copyRows) Values() ([]interface{}, error) {\n\terr := r.rows.Scan(r.values...)\n\tactuals := make([]interface{}, len(r.values))\n\tfor i, v := range r.values {\n\t\tactuals[i] = *(v.(*interface{}))\n\t}\n\treturn actuals, err\n}\n\nfunc (r *copyRows) Err() error {\n\treturn r.rows.Err()\n}\n"
  },
  {
    "path": "drivers/postgres/postgres.go",
    "content": "// Package postgres defines and registers usql's PostgreSQL driver.\n//\n// Alias: cockroachdb, CockroachDB\n// Alias: redshift, Amazon Redshift\n//\n// See: https://github.com/lib/pq\n// Group: base\npackage postgres\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/lib/pq\" // DRIVER\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tpgmeta \"github.com/xo/usql/drivers/metadata/postgres\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/text\"\n)\n\nfunc init() {\n\topenConn := func(stdout, stderr func() io.Writer, dsn string) (*sql.DB, error) {\n\t\tconn, err := pq.NewConnector(dsn)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tnoticeConn := pq.ConnectorWithNoticeHandler(conn, func(notice *pq.Error) {\n\t\t\tout := stderr()\n\t\t\tfmt.Fprintln(out, notice.Severity+\": \", notice.Message)\n\t\t\tif notice.Hint != \"\" {\n\t\t\t\tfmt.Fprintln(out, \"HINT: \", notice.Hint)\n\t\t\t}\n\t\t})\n\t\tnotificationConn := pq.ConnectorWithNotificationHandler(noticeConn, func(notification *pq.Notification) {\n\t\t\tvar payload string\n\t\t\tif notification.Extra != \"\" {\n\t\t\t\tpayload = fmt.Sprintf(text.NotificationPayload, notification.Extra)\n\t\t\t}\n\t\t\tfmt.Fprintln(stdout(), fmt.Sprintf(text.NotificationReceived, notification.Channel, payload, notification.BePid))\n\t\t})\n\t\treturn sql.OpenDB(notificationConn), nil\n\t}\n\tdrivers.Register(\"postgres\", drivers.Driver{\n\t\tName:                   \"pq\",\n\t\tAllowDollar:            true,\n\t\tAllowMultilineComments: true,\n\t\tLexerName:              \"postgres\",\n\t\tForceParams: func(u *dburl.URL) {\n\t\t\tif u.Scheme == \"cockroachdb\" {\n\t\t\t\tdrivers.ForceQueryParameters([]string{\"sslmode\", \"disable\"})(u)\n\t\t\t}\n\t\t},\n\t\tOpen: func(ctx context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) {\n\t\t\treturn func(_, dsn string) (*sql.DB, error) {\n\t\t\t\tconn, err := openConn(stdout, stderr, dsn)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\t// special retry handling case, since there's no lib/pq retry mode\n\t\t\t\tif env.Get(\"SSLMODE\") == \"retry\" && !u.Query().Has(\"sslmode\") {\n\t\t\t\t\tswitch err = conn.PingContext(ctx); {\n\t\t\t\t\tcase errors.Is(err, pq.ErrSSLNotSupported):\n\t\t\t\t\t\ts := \"sslmode=disable \" + dsn\n\t\t\t\t\t\tconn, err = openConn(stdout, stderr, s)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t\t}\n\t\t\t\t\t\tu.DSN = s\n\t\t\t\t\tcase err != nil:\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn conn, nil\n\t\t\t}, nil\n\t\t},\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\t// numeric version\n\t\t\t// SHOW server_version_num;\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SHOW server_version`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"PostgreSQL \" + ver, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, _ string) error {\n\t\t\t_, err := db.Exec(`ALTER USER ` + user + ` PASSWORD '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*pq.Error); ok {\n\t\t\t\treturn string(e.Code), e.Message\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\tif e, ok := err.(*pq.Error); ok {\n\t\t\t\treturn e.Code.Name() == \"invalid_password\"\n\t\t\t}\n\t\t\treturn false\n\t\t},\n\t\tNewMetadataReader: pgmeta.NewReader(),\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(pgmeta.NewReader()(db, opts...))(db, w)\n\t\t},\n\t\tCopy: func(ctx context.Context, db *sql.DB, rows *sql.Rows, table string) (int64, error) {\n\t\t\tcolumns, err := rows.Columns()\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to fetch source rows columns: %w\", err)\n\t\t\t}\n\t\t\tclen := len(columns)\n\n\t\t\tquery := table\n\t\t\tif !strings.HasPrefix(strings.ToLower(query), \"insert into\") {\n\t\t\t\tleftParen := strings.IndexRune(table, '(')\n\t\t\t\tcolQuery := \"SELECT * FROM \" + table + \" WHERE 1=0\"\n\t\t\t\tif leftParen != -1 {\n\t\t\t\t\tcolQuery = \"SELECT \" + table[leftParen+1:len(table)-1] + \" FROM \" + table[:leftParen] + \" WHERE 1=0\"\n\t\t\t\t\ttable = table[:leftParen]\n\t\t\t\t}\n\t\t\t\tcolStmt, err := db.PrepareContext(ctx, colQuery)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0, fmt.Errorf(\"failed to prepare query to determine target table columns: %w\", err)\n\t\t\t\t}\n\t\t\t\tdefer colStmt.Close()\n\t\t\t\tcolRows, err := colStmt.QueryContext(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0, fmt.Errorf(\"failed to execute query to determine target table columns: %w\", err)\n\t\t\t\t}\n\t\t\t\tcolumns, err := colRows.Columns()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn 0, fmt.Errorf(\"failed to fetch target table columns: %w\", err)\n\t\t\t\t}\n\t\t\t\tif schemaSep := strings.Index(table, \".\"); schemaSep >= 0 {\n\t\t\t\t\tquery = pq.CopyInSchema(table[:schemaSep], table[schemaSep+1:], columns...)\n\t\t\t\t} else {\n\t\t\t\t\tquery = pq.CopyIn(table, columns...)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttx, err := db.BeginTx(ctx, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to begin transaction: %w\", err)\n\t\t\t}\n\t\t\tstmt, err := tx.PrepareContext(ctx, query)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, fmt.Errorf(\"failed to prepare insert query: %w\", err)\n\t\t\t}\n\t\t\tdefer stmt.Close()\n\n\t\t\tvalues := make([]interface{}, clen)\n\t\t\tfor i := 0; i < clen; i++ {\n\t\t\t\tvalues[i] = new(interface{})\n\t\t\t}\n\n\t\t\tvar n int64\n\t\t\tfor rows.Next() {\n\t\t\t\terr = rows.Scan(values...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn n, fmt.Errorf(\"failed to scan row: %w\", err)\n\t\t\t\t}\n\t\t\t\t_, err := stmt.ExecContext(ctx, values...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn n, fmt.Errorf(\"failed to exec copy: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tres, err := stmt.ExecContext(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn n, fmt.Errorf(\"failed to final exec copy: %w\", err)\n\t\t\t}\n\t\t\trn, err := res.RowsAffected()\n\t\t\tif err != nil {\n\t\t\t\treturn n, fmt.Errorf(\"failed to check rows affected: %w\", err)\n\t\t\t}\n\t\t\tn += rn\n\n\t\t\terr = tx.Commit()\n\t\t\tif err != nil {\n\t\t\t\treturn n, fmt.Errorf(\"failed to commit transaction: %w\", err)\n\t\t\t}\n\n\t\t\treturn n, rows.Err()\n\t\t},\n\t}, \"cockroachdb\", \"redshift\")\n}\n"
  },
  {
    "path": "drivers/presto/presto.go",
    "content": "// Package presto defines and registers usql's Presto driver.\n//\n// See: https://github.com/prestodb/presto-go-client\npackage presto\n\nimport (\n\t\"context\"\n\n\t_ \"github.com/prestodb/presto-go-client/presto\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"presto\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tProcess:                drivers.StripTrailingSemicolon,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(\n\t\t\t\tctx,\n\t\t\t\t`SELECT node_version FROM system.runtime.nodes LIMIT 1`,\n\t\t\t).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Presto \" + ver, nil\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/ql/ql.go",
    "content": "// Package ql defines and registers usql's Cznic QL driver.\n//\n// See: https://gitlab.com/cznic/ql\npackage ql\n\nimport (\n\t\"github.com/xo/usql/drivers\"\n\t\"modernc.org/ql\" // DRIVER\n)\n\nfunc init() {\n\tql.RegisterDriver()\n\t// ql.RegisterMemDriver()\n\tdrivers.Register(\"ql\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tAllowCComments:         true,\n\t\tBatchQueryPrefixes: map[string]string{\n\t\t\t\"BEGIN TRANSACTION\": \"COMMIT\",\n\t\t},\n\t\tBatchAsTransaction: true,\n\t})\n}\n"
  },
  {
    "path": "drivers/qtype.go",
    "content": "package drivers\n\nimport (\n\t\"strings\"\n)\n\n// queryMap is the map of SQL prefixes use as queries.\nvar queryMap = map[string]bool{\n\t\"WITH\":       true,\n\t\"PRAGMA\":     true,\n\t\"EXPLAIN\":    true, // show the execution plan of a statement\n\t\"DESCRIBE\":   true, // describe (mysql)\n\t\"DESC\":       true, // describe (mysql)\n\t\"FETCH\":      true, // retrieve rows from a query using a cursor\n\t\"SELECT\":     true, // retrieve rows from a table or view\n\t\"SHOW\":       true, // show the value of a run-time parameter\n\t\"ADMIN SHOW\": true,\n\t\"VALUES\":     true, // compute a set of rows\n\t\"LIST\":       true, // list permissions, roles, users (cassandra)\n\t\"EXEC\":       true, // execute a stored procedure that returns rows (not postgres)\n\t\"TABLE\":      true, // shortcut for select * from <table> (postgresql)\n\t\"CALL\":       true,\n\t\"FROM\":       true,\n}\n\n// execMap is the map of SQL prefixes to execute.\n//\n// Unless noted, these are extracted from the PostgreSQL docs.\n//\n// Note: originally extracted via a script, but maintained by hand as the\n// documentation for any new queries introduced by PostgreSQL need to be\n// manually scrutinized for variations.\nvar execMap = map[string]bool{\n\t// cassandra\n\t\"ALTER KEYSPACE\":  true, // alter a keyspace\n\t\"CREATE KEYSPACE\": true, // create a keyspace\n\t\"DROP KEYSPACE\":   true, // drop a keyspace\n\t\"BEGIN BATCH\":     true, // begin batch\n\t\"APPLY BATCH\":     true, // apply batch\n\t// sqlserver\n\t\"CREATE LOGIN\":     true, // create login\n\t\"CREATE PROCEDURE\": true, // create procedure\n\t\"DROP LOGIN\":       true, // drop login\n\t\"DROP PROCEDURE\":   true, // drop procedure\n\t// ql\n\t\"BEGIN TRANSACTION\": true, // begin batch\n\t// postgresql\n\t\"ABORT\":                            true, // abort the current transaction\n\t\"ALTER AGGREGATE\":                  true, // change the definition of an aggregate function\n\t\"ALTER COLLATION\":                  true, // change the definition of a collation\n\t\"ALTER CONVERSION\":                 true, // change the definition of a conversion\n\t\"ALTER DATABASE\":                   true, // change a database\n\t\"ALTER DEFAULT PRIVILEGES\":         true, // define default access privileges\n\t\"ALTER DOMAIN\":                     true, // change the definition of a domain\n\t\"ALTER EVENT TRIGGER\":              true, // change the definition of an event trigger\n\t\"ALTER EXTENSION\":                  true, // change the definition of an extension\n\t\"ALTER FOREIGN DATA WRAPPER\":       true, // change the definition of a foreign-data wrapper\n\t\"ALTER FOREIGN TABLE\":              true, // change the definition of a foreign table\n\t\"ALTER FUNCTION\":                   true, // change the definition of a function\n\t\"ALTER GROUP\":                      true, // change role name or membership\n\t\"ALTER INDEX\":                      true, // change the definition of an index\n\t\"ALTER LANGUAGE\":                   true, // change the definition of a procedural language\n\t\"ALTER LARGE OBJECT\":               true, // change the definition of a large object\n\t\"ALTER MATERIALIZED VIEW\":          true, // change the definition of a materialized view\n\t\"ALTER OPERATOR CLASS\":             true, // change the definition of an operator class\n\t\"ALTER OPERATOR FAMILY\":            true, // change the definition of an operator family\n\t\"ALTER OPERATOR\":                   true, // change the definition of an operator\n\t\"ALTER POLICY\":                     true, // change the definition of a row level security policy\n\t\"ALTER ROLE\":                       true, // change a database role\n\t\"ALTER RULE\":                       true, // change the definition of a rule\n\t\"ALTER SCHEMA\":                     true, // change the definition of a schema\n\t\"ALTER SEQUENCE\":                   true, // change the definition of a sequence generator\n\t\"ALTER SERVER\":                     true, // change the definition of a foreign server\n\t\"ALTER SYSTEM\":                     true, // change a server configuration parameter\n\t\"ALTER TABLESPACE\":                 true, // change the definition of a tablespace\n\t\"ALTER TABLE\":                      true, // change the definition of a table\n\t\"ALTER TEXT SEARCH CONFIGURATION\":  true, // change the definition of a text search configuration\n\t\"ALTER TEXT SEARCH DICTIONARY\":     true, // change the definition of a text search dictionary\n\t\"ALTER TEXT SEARCH PARSER\":         true, // change the definition of a text search parser\n\t\"ALTER TEXT SEARCH TEMPLATE\":       true, // change the definition of a text search template\n\t\"ALTER TRIGGER\":                    true, // change the definition of a trigger\n\t\"ALTER TYPE\":                       true, // change the definition of a type\n\t\"ALTER USER MAPPING\":               true, // change the definition of a user mapping\n\t\"ALTER USER\":                       true, // change a database role\n\t\"ALTER VIEW\":                       true, // change the definition of a view\n\t\"ANALYZE\":                          true, // collect statistics about a database\n\t\"BEGIN\":                            true, // start a transaction block\n\t\"CHECKPOINT\":                       true, // force a transaction log checkpoint\n\t\"CLOSE\":                            true, // close a cursor\n\t\"CLUSTER\":                          true, // cluster a table according to an index\n\t\"COMMENT\":                          true, // define or change the comment of an object\n\t\"COMMIT PREPARED\":                  true, // commit a transaction that was earlier prepared for two-phase commit\n\t\"COMMIT\":                           true, // commit the current transaction\n\t\"COPY\":                             true, // copy data between a file and a table\n\t\"CREATE ACCESS METHOD\":             true, // define a new access method\n\t\"CREATE AGGREGATE\":                 true, // define a new aggregate function\n\t\"CREATE CAST\":                      true, // define a new cast\n\t\"CREATE COLLATION\":                 true, // define a new collation\n\t\"CREATE CONVERSION\":                true, // define a new encoding conversion\n\t\"CREATE DATABASE\":                  true, // create a new database\n\t\"CREATE DOMAIN\":                    true, // define a new domain\n\t\"CREATE EVENT TRIGGER\":             true, // define a new event trigger\n\t\"CREATE EXTENSION\":                 true, // install an extension\n\t\"CREATE FOREIGN DATA WRAPPER\":      true, // define a new foreign-data wrapper\n\t\"CREATE FOREIGN TABLE\":             true, // define a new foreign table\n\t\"CREATE FUNCTION\":                  true, // define a new function\n\t\"CREATE GROUP\":                     true, // define a new database role\n\t\"CREATE INDEX\":                     true, // define a new index\n\t\"CREATE LANGUAGE\":                  true, // define a new procedural language\n\t\"CREATE MATERIALIZED VIEW\":         true, // define a new materialized view\n\t\"CREATE OPERATOR CLASS\":            true, // define a new operator class\n\t\"CREATE OPERATOR FAMILY\":           true, // define a new operator family\n\t\"CREATE OPERATOR\":                  true, // define a new operator\n\t\"CREATE POLICY\":                    true, // define a new row level security policy for a table\n\t\"CREATE ROLE\":                      true, // define a new database role\n\t\"CREATE RULE\":                      true, // define a new rewrite rule\n\t\"CREATE SCHEMA\":                    true, // define a new schema\n\t\"CREATE SEQUENCE\":                  true, // define a new sequence generator\n\t\"CREATE SERVER\":                    true, // define a new foreign server\n\t\"CREATE STATISTICS\":                true, // define extended statistics\n\t\"CREATE SUBSCRIPTION\":              true, // define a new subscription\n\t\"CREATE TABLE AS\":                  true, // define a new table from the results of a query\n\t\"CREATE TABLESPACE\":                true, // define a new tablespace\n\t\"CREATE TABLE\":                     true, // define a new table\n\t\"CREATE TEXT SEARCH CONFIGURATION\": true, // define a new text search configuration\n\t\"CREATE TEXT SEARCH DICTIONARY\":    true, // define a new text search dictionary\n\t\"CREATE TEXT SEARCH PARSER\":        true, // define a new text search parser\n\t\"CREATE TEXT SEARCH TEMPLATE\":      true, // define a new text search template\n\t\"CREATE TRANSFORM\":                 true, // define a new transform\n\t\"CREATE TRIGGER\":                   true, // define a new trigger\n\t\"CREATE TYPE\":                      true, // define a new data type\n\t\"CREATE USER MAPPING\":              true, // define a new mapping of a user to a foreign server\n\t\"CREATE USER\":                      true, // define a new database role\n\t\"CREATE VIEW\":                      true, // define a new view\n\t\"DEALLOCATE ALL\":                   true, // deallocate all prepared statements\n\t\"DEALLOCATE\":                       true, // deallocate a prepared statement\n\t\"DECLARE\":                          true, // define a cursor\n\t\"DELETE\":                           true, // delete rows of a table\n\t\"DISCARD\":                          true, // discard session state\n\t\"DO\":                               true, // execute an anonymous code block\n\t\"DROP ACCESS METHOD\":               true, // remove an access method\n\t\"DROP AGGREGATE\":                   true, // remove an aggregate function\n\t\"DROP CAST\":                        true, // remove a cast\n\t\"DROP COLLATION\":                   true, // remove a collation\n\t\"DROP CONVERSION\":                  true, // remove a conversion\n\t\"DROP DATABASE\":                    true, // remove a database\n\t\"DROP DOMAIN\":                      true, // remove a domain\n\t\"DROP EVENT TRIGGER\":               true, // remove an event trigger\n\t\"DROP EXTENSION\":                   true, // remove an extension\n\t\"DROP FOREIGN DATA WRAPPER\":        true, // remove a foreign-data wrapper\n\t\"DROP FOREIGN TABLE\":               true, // remove a foreign table\n\t\"DROP FUNCTION\":                    true, // remove a function\n\t\"DROP GROUP\":                       true, // remove a database role\n\t\"DROP INDEX\":                       true, // remove an index\n\t\"DROP LANGUAGE\":                    true, // remove a procedural language\n\t\"DROP MATERIALIZED VIEW\":           true, // remove a materialized view\n\t\"DROP OPERATOR CLASS\":              true, // remove an operator class\n\t\"DROP OPERATOR FAMILY\":             true, // remove an operator family\n\t\"DROP OPERATOR\":                    true, // remove an operator\n\t\"DROP OWNED\":                       true, // remove database objects owned by a database role\n\t\"DROP POLICY\":                      true, // remove a row level security policy from a table\n\t\"DROP PUBLICATION\":                 true, // remove a publication\n\t\"DROP ROLE\":                        true, // remove a database role\n\t\"DROP RULE\":                        true, // remove a rewrite rule\n\t\"DROP SCHEMA\":                      true, // remove a schema\n\t\"DROP SEQUENCE\":                    true, // remove a sequence\n\t\"DROP SERVER\":                      true, // remove a foreign server descriptor\n\t\"DROP STATISTICS\":                  true, // remove extended statistics\n\t\"DROP SUBSCRIPTION\":                true, // remove a subscription\n\t\"DROP TABLESPACE\":                  true, // remove a tablespace\n\t\"DROP TABLE\":                       true, // remove a table\n\t\"DROP TEXT SEARCH CONFIGURATION\":   true, // remove a text search configuration\n\t\"DROP TEXT SEARCH DICTIONARY\":      true, // remove a text search dictionary\n\t\"DROP TEXT SEARCH PARSER\":          true, // remove a text search parser\n\t\"DROP TEXT SEARCH TEMPLATE\":        true, // remove a text search template\n\t\"DROP TRANSFORM\":                   true, // remove a transform\n\t\"DROP TRIGGER\":                     true, // remove a trigger\n\t\"DROP TYPE\":                        true, // remove a data type\n\t\"DROP USER MAPPING\":                true, // remove a user mapping for a foreign server\n\t\"DROP USER\":                        true, // remove a database role\n\t\"DROP VIEW\":                        true, // remove a view\n\t\"END\":                              true, // commit the current transaction\n\t\"EXECUTE\":                          true, // execute a prepared statement\n\t\"GRANT\":                            true, // define access privileges\n\t\"IMPORT FOREIGN SCHEMA\":            true, // import table definitions from a foreign server\n\t\"INSERT\":                           true, // create new rows in a table\n\t\"LISTEN\":                           true, // listen for a notification\n\t\"LOAD\":                             true, // load a shared library file\n\t\"LOCK\":                             true, // lock a table\n\t\"MOVE\":                             true, // position a cursor\n\t\"NOTIFY\":                           true, // generate a notification\n\t\"PREPARE TRANSACTION\":              true, // prepare the current transaction for two-phase commit\n\t\"PREPARE\":                          true, // prepare a statement for execution\n\t\"REASSIGN OWNED\":                   true, // change the ownership of database objects owned by a database role\n\t\"REFRESH MATERIALIZED VIEW\":        true, // replace the contents of a materialized view\n\t\"REINDEX\":                          true, // rebuild indexes\n\t\"RELEASE\":                          true, // destroy a previously defined savepoint\n\t\"RESET\":                            true, // restore the value of a run-time parameter to the default value\n\t\"REVOKE\":                           true, // remove access privileges\n\t\"ROLLBACK PREPARED\":                true, // cancel a transaction that was earlier prepared for two-phase commit\n\t\"ROLLBACK TO SAVEPOINT\":            true, // roll back to a savepoint\n\t\"ROLLBACK\":                         true, // abort the current transaction\n\t\"SAVEPOINT\":                        true, // define a new savepoint within the current transaction\n\t\"SECURITY LABEL\":                   true, // define or change a security label applied to an object\n\t\"SELECT INTO\":                      true, // define a new table from the results of a query\n\t\"SET CONSTRAINTS\":                  true, // set constraint check timing for the current transaction\n\t\"SET ROLE\":                         true, // set the current user identifier of the current session\n\t\"SET SESSION AUTHORIZATION\":        true, // set the session user identifier and the current user identifier of the current session\n\t\"SET TRANSACTION\":                  true, // set the characteristics of the current transaction\n\t\"SET\":                              true, // change a run-time parameter\n\t\"START TRANSACTION\":                true, // start a transaction block\n\t\"TRUNCATE\":                         true, // empty a table or set of tables\n\t\"UNLISTEN\":                         true, // stop listening for a notification\n\t\"UPDATE\":                           true, // update rows of a table\n\t\"VACUUM\":                           true, // garbage-collect and optionally analyze a database\n\t// oracle\n\t\"ADMINISTER KEY MANAGEMENT\":   true,\n\t\"ALTER ANALYTIC VIEW\":         true,\n\t\"ALTER ATTRIBUTE DIMENSION\":   true,\n\t\"ALTER AUDIT POLICY\":          true,\n\t\"ALTER CLUSTER\":               true,\n\t\"ALTER DATABASE DICTIONARY\":   true,\n\t\"ALTER DATABASE LINK\":         true,\n\t\"ALTER DIMENSION\":             true,\n\t\"ALTER DISKGROUP\":             true,\n\t\"ALTER FLASHBACK ARCHIVE\":     true,\n\t\"ALTER HEIRARCHY\":             true,\n\t\"ALTER INMEMORY JOIN GROUP\":   true,\n\t\"ALTER JAVA\":                  true,\n\t\"ALTER LIBRARY\":               true,\n\t\"ALTER LOCKDOWN PROFILE\":      true,\n\t\"ALTER MATERIALIZED VIEW LOG\": true,\n\t\"ALTER MATERIALIZED ZONEMAP\":  true,\n\t\"ALTER PACKAGE\":               true,\n\t\"ALTER PLUGGABLE DATABASE\":    true,\n\t\"ALTER PROCEDURE\":             true,\n\t\"ALTER PROFILE\":               true,\n\t\"ALTER RESOURCE COST\":         true,\n\t\"ALTER ROLLBACK SEGMENT\":      true,\n\t\"ALTER SESSION\":               true,\n\t\"ALTER SYNONYM\":               true,\n\t\"ALTER TABLESPACE SET\":        true,\n\t\"ASSOCIATE STATISTICS\":        true,\n}\n\n// createIgnore are parts of the query exec type after CREATE to ignore.\nvar createIgnore = map[string]bool{\n\t\"DEFAULT\":    true,\n\t\"GLOBAL\":     true,\n\t\"LOCAL\":      true,\n\t\"OR\":         true,\n\t\"PROCEDURAL\": true,\n\t\"RECURSIVE\":  true,\n\t\"REPLACE\":    true,\n\t\"TEMPORARY\":  true,\n\t\"TEMP\":       true,\n\t\"TRUSTED\":    true,\n\t\"UNIQUE\":     true,\n\t\"UNLOGGED\":   true,\n}\n\n// QueryExecType is the default way to determine the \"EXEC\" prefix for a SQL\n// query and whether or not it should be Exec'd or Query'd.\nfunc QueryExecType(prefix, sqlstr string) (string, bool) {\n\tif prefix == \"\" {\n\t\treturn \"EXEC\", false\n\t}\n\ts := strings.Split(prefix, \" \")\n\tif len(s) > 0 {\n\t\t// check query map\n\t\tif _, ok := queryMap[s[0]]; ok {\n\t\t\ttyp := s[0]\n\t\t\tswitch {\n\t\t\tcase typ == \"SELECT\" && len(s) >= 2 && s[1] == \"INTO\":\n\t\t\t\treturn \"SELECT INTO\", false\n\t\t\tcase typ == \"PRAGMA\":\n\t\t\t\treturn typ, !strings.ContainsRune(sqlstr, '=')\n\t\t\t}\n\t\t\treturn typ, true\n\t\t}\n\t\t// normalize prefixes\n\t\tswitch s[0] {\n\t\t// CREATE statements have a large number of variants\n\t\tcase \"CREATE\":\n\t\t\tn := []string{\"CREATE\"}\n\t\t\tfor _, x := range s[1:] {\n\t\t\t\tif _, ok := createIgnore[x]; ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tn = append(n, x)\n\t\t\t}\n\t\t\ts = n\n\t\tcase \"DROP\":\n\t\t\t// \"DROP [PROCEDURAL] LANGUAGE\" => \"DROP LANGUAGE\"\n\t\t\tn := []string{\"DROP\"}\n\t\t\tfor _, x := range s[1:] {\n\t\t\t\tif x == \"PROCEDURAL\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tn = append(n, x)\n\t\t\t}\n\t\t\ts = n\n\t\t}\n\t\t// find longest match\n\t\tfor i := len(s); i > 0; i-- {\n\t\t\ttyp := strings.Join(s[:i], \" \")\n\t\t\tif _, ok := execMap[typ]; ok {\n\t\t\t\treturn typ, false\n\t\t\t}\n\t\t}\n\t}\n\treturn s[0], false\n}\n"
  },
  {
    "path": "drivers/ramsql/ramsql.go",
    "content": "// Package ramsql defines and registers usql's RamSQL driver.\n//\n// See: https://github.com/proullon/ramsql\npackage ql\n\nimport (\n\t_ \"github.com/proullon/ramsql/driver\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"ramsql\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/sapase/sapase.go",
    "content": "// Package sapase defines and registers usql's SAP ASE driver.\n//\n// See: https://github.com/thda/tds\npackage sapase\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/thda/tds\" // DRIVER: tds\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"tds\", drivers.Driver{\n\t\tAllowMultilineComments:  true,\n\t\tRequirePreviousPassword: true,\n\t\tLexerName:               \"tsql\",\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SELECT @@version`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn ver, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, oldpw string) error {\n\t\t\tif user != \"\" {\n\t\t\t\treturn errors.New(\"Cannot change password for another user\")\n\t\t\t}\n\t\t\t_, err := db.Exec(`exec sp_password '` + oldpw + `', '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(tds.SybError); ok {\n\t\t\t\treturn strconv.Itoa(int(e.MsgNumber)), e.Message\n\t\t\t}\n\t\t\tmsg := err.Error()\n\t\t\tif i := strings.LastIndex(msg, \"tds:\"); i != -1 {\n\t\t\t\tmsg = msg[i:]\n\t\t\t}\n\t\t\treturn \"\", msg\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\treturn strings.Contains(err.Error(), \"Login failed\")\n\t\t},\n\t\tProcess: drivers.StripTrailingSemicolon,\n\t})\n}\n"
  },
  {
    "path": "drivers/saphana/saphana.go",
    "content": "// Package saphana defines and registers usql's SAP HANA driver.\n//\n// See: https://github.com/SAP/go-hdb\npackage saphana\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\t_ \"github.com/SAP/go-hdb/driver\" // DRIVER: hdb\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"hdb\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT version FROM m_database`).Scan(&ver); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"SAP HANA \" + ver, nil\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tcode, msg := \"\", err.Error()\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tCode() int\n\t\t\t}); ok {\n\t\t\t\tcode = strconv.Itoa(e.Code())\n\t\t\t}\n\t\t\tif e, ok := err.(interface {\n\t\t\t\tText() string\n\t\t\t}); ok {\n\t\t\t\tmsg = e.Text()\n\t\t\t}\n\t\t\treturn code, msg\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "drivers/snowflake/snowflake.go",
    "content": "// Package snowflake defines and registers usql's Snowflake driver.\n//\n// See: https://github.com/snowflakedb/gosnowflake\npackage snowflake\n\nimport (\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/snowflakedb/gosnowflake\" // DRIVER\n\t\"github.com/xo/tblfmt\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n\t\"github.com/xo/usql/env\"\n)\n\nfunc init() {\n\tgosnowflake.GetLogger().SetOutput(io.Discard)\n\tnewReader := infos.New(\n\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\tinfos.SequenceColumnsIncrement: \"''\",\n\t\t}),\n\t\tinfos.WithFunctions(false),\n\t\tinfos.WithIndexes(false),\n\t\tinfos.WithConstraints(false),\n\t\tinfos.WithColumnPrivileges(false),\n\t)\n\tdrivers.Register(\"snowflake\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(*gosnowflake.SnowflakeError); ok {\n\t\t\t\treturn strconv.Itoa(e.Number), e.Message\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t\tNewMetadataReader: newReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\twriterOpts := []metadata.WriterOption{\n\t\t\t\tmetadata.WithListAllDbs(func(pattern string, verbose bool) error {\n\t\t\t\t\treturn listAllDbs(db, w, pattern, verbose)\n\t\t\t\t}),\n\t\t\t}\n\t\t\treturn metadata.NewDefaultWriter(newReader(db, opts...), writerOpts...)(db, w)\n\t\t},\n\t})\n}\n\nfunc listAllDbs(db drivers.DB, w io.Writer, _ string, _ bool) error {\n\trows, err := db.Query(\"SHOW databases\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\tparams := env.Vars().Print()\n\tparams[\"title\"] = \"List of databases\"\n\treturn tblfmt.EncodeAll(w, rows, params)\n}\n"
  },
  {
    "path": "drivers/spanner/spanner.go",
    "content": "// Package spanner defines and registers usql's Google Spanner driver.\n//\n// See: https://github.com/googleapis/go-sql-spanner\npackage spanner\n\nimport (\n\t_ \"github.com/googleapis/go-sql-spanner\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"spanner\", drivers.Driver{})\n}\n"
  },
  {
    "path": "drivers/sqlite3/sqlite3.go",
    "content": "// Package sqlite3 defines and registers usql's SQLite3 driver. Requires CGO.\n//\n// See: https://github.com/mattn/go-sqlite3\n// Group: base\npackage sqlite3\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\n\t\"github.com/mattn/go-sqlite3\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/sqlite3/sqshared\"\n)\n\nfunc init() {\n\tdrivers.Register(\"sqlite3\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tForceParams: drivers.ForceQueryParameters([]string{\n\t\t\t\"loc\", \"auto\",\n\t\t}),\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(ctx, `SELECT sqlite_version()`).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"SQLite3 \" + ver, nil\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(sqlite3.Error); ok {\n\t\t\t\treturn strconv.Itoa(int(e.Code)), e.Error()\n\t\t\t}\n\t\t\tcode, msg := \"\", err.Error()\n\t\t\tif e, ok := err.(sqlite3.ErrNo); ok {\n\t\t\t\tcode = strconv.Itoa(int(e))\n\t\t\t}\n\t\t\treturn code, msg\n\t\t},\n\t\tConvertBytes:      sqshared.ConvertBytes,\n\t\tNewMetadataReader: sqshared.NewMetadataReader,\n\t\tCopy:              drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t})\n}\n"
  },
  {
    "path": "drivers/sqlite3/sqshared/reader.go",
    "content": "package sqshared\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n)\n\ntype MetadataReader struct {\n\tmetadata.LoggingReader\n\tlimit int\n}\n\n// NewMetadataReader creates the metadata reader for sqlite3 databases.\nfunc NewMetadataReader(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\treturn &MetadataReader{\n\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t}\n}\n\nvar (\n\t_ metadata.BasicReader          = &MetadataReader{}\n\t_ metadata.FunctionReader       = &MetadataReader{}\n\t_ metadata.FunctionColumnReader = &MetadataReader{}\n\t_ metadata.IndexReader          = &MetadataReader{}\n\t_ metadata.IndexColumnReader    = &MetadataReader{}\n)\n\nfunc (r *MetadataReader) SetLimit(l int) {\n\tr.limit = l\n}\n\n// Columns from selected catalog (or all, if empty), matching schemas and tables\nfunc (r MetadataReader) Columns(f metadata.Filter) (*metadata.ColumnSet, error) {\n\ttables, err := r.Tables(metadata.Filter{Catalog: f.Catalog, Schema: f.Schema, Name: f.Parent})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresults := []metadata.Column{}\n\tfor tables.Next() {\n\t\ttable := tables.Get()\n\t\tqstr := `SELECT\n  cid,\n  name,\n  type,\n  CASE WHEN \"notnull\" = 1 THEN 'NO' ELSE 'YES' END,\n  COALESCE(dflt_value, '')\nFROM pragma_table_info(?)`\n\t\trows, closeRows, err := r.query(qstr, []string{}, \"name\", table.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer closeRows()\n\n\t\trec := metadata.Column{\n\t\t\tCatalog: table.Catalog,\n\t\t\tSchema:  table.Schema,\n\t\t\tTable:   table.Name,\n\t\t}\n\t\tfor rows.Next() {\n\t\t\terr = rows.Scan(\n\t\t\t\t&rec.OrdinalPosition,\n\t\t\t\t&rec.Name,\n\t\t\t\t&rec.DataType,\n\t\t\t\t&rec.IsNullable,\n\t\t\t\t&rec.Default,\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresults = append(results, rec)\n\t\t}\n\t\tif rows.Err() != nil {\n\t\t\treturn nil, rows.Err()\n\t\t}\n\t}\n\n\treturn metadata.NewColumnSet(results), nil\n}\n\nfunc (r MetadataReader) Tables(f metadata.Filter) (*metadata.TableSet, error) {\n\tqstr := `SELECT\n  '' AS table_catalog,\n  '' AS table_schem,\n  table_name,\n  table_type\nFROM (\n    SELECT\n      name AS table_name,\n      UPPER(type) AS table_type\n    FROM sqlite_master\n    WHERE name NOT LIKE 'sqlite\\_%' ESCAPE '\\' AND UPPER(type) IN ('TABLE', 'VIEW')\n    UNION ALL\n    SELECT\n      name AS table_name,\n      'GLOBAL TEMPORARY' AS table_type\n    FROM sqlite_temp_master\n    UNION ALL\n    SELECT\n      name AS table_name,\n      'SYSTEM TABLE' AS table_type\n    FROM sqlite_master\n    WHERE name LIKE 'sqlite\\_%' ESCAPE '\\' AND UPPER(type) IN ('TABLE', 'VIEW')\n    UNION ALL\n    SELECT\n      name AS table_name,\n      'SYSTEM TABLE' AS table_type\n    FROM pragma_module_list\n)`\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif f.Catalog != \"\" {\n\t\tvals = append(vals, f.Catalog)\n\t\tconds = append(conds, \"table_catalog = ?\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, \"table_schema LIKE ?\")\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"table_name LIKE ?\")\n\t}\n\tif len(f.Types) != 0 {\n\t\tpholders := []string{}\n\t\tfor _, t := range f.Types {\n\t\t\tvals = append(vals, t)\n\t\t\tpholders = append(pholders, \"?\")\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, \"table_type IN (\"+strings.Join(pholders, \", \")+\")\")\n\t\t}\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"table_type, table_name\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Table{}\n\tfor rows.Next() {\n\t\trec := metadata.Table{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Name, &rec.Type)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewTableSet(results), nil\n}\n\nfunc (r MetadataReader) Schemas(f metadata.Filter) (*metadata.SchemaSet, error) {\n\tqstr := `SELECT\n  name AS schema_name,\n  '' AS catalog_name\nFROM pragma_database_list`\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"schema_name LIKE ?\")\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"seq\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Schema{}\n\tfor rows.Next() {\n\t\trec := metadata.Schema{}\n\t\terr = rows.Scan(&rec.Schema, &rec.Catalog)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewSchemaSet(results), nil\n}\n\nfunc (r MetadataReader) Functions(f metadata.Filter) (*metadata.FunctionSet, error) {\n\tqstr := `SELECT\n  name AS specific_name,\n  name AS routine_name,\n  type AS routine_type\nFROM pragma_function_list`\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"name LIKE ?\")\n\t}\n\tif len(f.Types) != 0 {\n\t\tpholders := []string{}\n\t\tfor _, t := range f.Types {\n\t\t\tvals = append(vals, t)\n\t\t\tpholders = append(pholders, \"?\")\n\t\t}\n\t\tif len(pholders) != 0 {\n\t\t\tconds = append(conds, \"type IN (\"+strings.Join(pholders, \", \")+\")\")\n\t\t}\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"name, type\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Function{}\n\tfor rows.Next() {\n\t\trec := metadata.Function{}\n\t\terr = rows.Scan(\n\t\t\t&rec.SpecificName,\n\t\t\t&rec.Name,\n\t\t\t&rec.Type,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewFunctionSet(results), nil\n}\n\nfunc (r MetadataReader) FunctionColumns(metadata.Filter) (*metadata.FunctionColumnSet, error) {\n\treturn &metadata.FunctionColumnSet{}, nil\n}\n\nfunc (r MetadataReader) Indexes(f metadata.Filter) (*metadata.IndexSet, error) {\n\tqstr := `SELECT\n  m.name,\n  i.name,\n  CASE WHEN i.\"unique\" = 1 THEN 'YES' ELSE 'NO' END,\n  CASE WHEN i.origin = 'pk' THEN 'YES' ELSE 'NO' END\nFROM sqlite_master m\nJOIN pragma_index_list(m.name) i`\n\tconds := []string{\"m.type = 'table'\"}\n\tvals := []interface{}{}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, \"m.name LIKE ?\")\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"i.name LIKE ?\")\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"m.name, i.seq\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Index{}\n\tfor rows.Next() {\n\t\trec := metadata.Index{}\n\t\terr = rows.Scan(&rec.Table, &rec.Name, &rec.IsUnique, &rec.IsPrimary)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexSet(results), nil\n}\n\nfunc (r MetadataReader) IndexColumns(f metadata.Filter) (*metadata.IndexColumnSet, error) {\n\tqstr := `SELECT\n  m.name,\n  i.name,\n  ic.name,\n  ic.seqno\nFROM sqlite_master m\nJOIN pragma_index_list(m.name) i\nJOIN pragma_index_xinfo(i.name) ic`\n\tconds := []string{\"m.type = 'table' AND ic.cid >= 0\"}\n\tvals := []interface{}{}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, \"m.name LIKE ?\")\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, \"i.name LIKE ?\")\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"m.name, i.seq, ic.seqno\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.IndexColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.IndexColumn{}\n\t\terr = rows.Scan(&rec.Table, &rec.IndexName, &rec.Name, &rec.OrdinalPosition)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexColumnSet(results), nil\n}\n\nfunc (r MetadataReader) query(qstr string, conds []string, order string, vals ...interface{}) (*sql.Rows, func(), error) {\n\tif len(conds) != 0 {\n\t\tqstr += \"\\nWHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tif order != \"\" {\n\t\tqstr += \"\\nORDER BY \" + order\n\t}\n\tif r.limit != 0 {\n\t\tqstr += fmt.Sprintf(\"\\nLIMIT %d\", r.limit)\n\t}\n\treturn r.Query(qstr, vals...)\n}\n"
  },
  {
    "path": "drivers/sqlite3/sqshared/reader_test.go",
    "content": "package sqshared\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/user\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/client\"\n\t\"github.com/docker/docker/pkg/archive\"\n\t\"github.com/xo/usql/drivers/metadata\"\n)\n\nvar (\n\tdb     *sql.DB\n\treader *MetadataReader\n)\n\nfunc TestMain(m *testing.M) {\n\terr := createDb(\"testdata\", \"sakila.db\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not prepare the database: %s\", err)\n\t}\n\tdb, err = sql.Open(\"sqlite3\", \"testdata/sakila.db\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not open the database: %s\", err)\n\t}\n\treader = &MetadataReader{LoggingReader: metadata.NewLoggingReader(db)}\n\n\tcode := m.Run()\n\tos.Exit(code)\n}\n\nfunc createDb(location, name string) error {\n\tctx := context.Background()\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttar, err := archive.TarWithOptions(\"../metadata/testdata/docker\", &archive.TarOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\tbaseImage := \"centos:7\"\n\tschemaURL := \"https://raw.githubusercontent.com/jOOQ/sakila/main/sqlite-sakila-db/sqlite-sakila-schema.sql\"\n\ttarget := \"/schema\"\n\tbuildOptions := types.ImageBuildOptions{\n\t\tTags: []string{\"usql-sqlite\"},\n\t\tBuildArgs: map[string]*string{\n\t\t\t\"BASE_IMAGE\": &baseImage,\n\t\t\t\"SCHEMA_URL\": &schemaURL,\n\t\t\t\"TARGET\":     &target,\n\t\t},\n\t}\n\n\tres, err := cli.ImageBuild(ctx, tar, buildOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer res.Body.Close()\n\tscanner := bufio.NewScanner(res.Body)\n\tfor scanner.Scan() {\n\t}\n\n\tcwd, err := os.Getwd()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := user.Current()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresp, err := cli.ContainerCreate(ctx, &container.Config{\n\t\tImage:           \"usql-sqlite\",\n\t\tCmd:             []string{\"bash\", \"-xc\", \"sqlite3 -batch -echo -init /schema/sqlite-sakila-schema.sql /data/\" + name},\n\t\tUser:            u.Uid + \":\" + u.Gid,\n\t\tNetworkDisabled: true,\n\t}, &container.HostConfig{\n\t\tBinds: []string{\n\t\t\tpath.Join(cwd, location) + \":/data\",\n\t\t},\n\t}, nil, nil, \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = cli.ContainerStart(ctx, resp.ID, container.StartOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstatusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)\n\tselect {\n\tcase err := <-errCh:\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\tcase status := <-statusCh:\n\t\tfmt.Println(status.StatusCode, status.Error)\n\t}\n\n\t//out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true})\n\t//if err != nil {\n\t//\treturn err\n\t//}\n\n\t//_, err = stdcopy.StdCopy(os.Stdout, os.Stderr, out)\n\t//if err != nil {\n\t//\treturn err\n\t//}\n\n\treturn cli.ContainerRemove(ctx, resp.ID, container.RemoveOptions{})\n}\n\nfunc TestSchemas(t *testing.T) {\n\tresult, err := reader.Schemas(metadata.Filter{})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read schemas: %v\", err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Schema)\n\t}\n\tactual := strings.Join(names, \", \")\n\texpected := \"main\"\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong schema names, expected:\\n  %v\\ngot:\\n  %v\", expected, names)\n\t}\n}\n\nfunc TestTables(t *testing.T) {\n\tresult, err := reader.Tables(metadata.Filter{Types: []string{\"BASE TABLE\", \"TABLE\", \"VIEW\"}})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read tables: %v\", err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Name)\n\t}\n\tactual := strings.Join(names, \", \")\n\texpected := \"actor, address, category, city, country, customer, film, film_actor, film_category, film_text, inventory, language, payment, rental, staff, store, customer_list, film_list, sales_by_film_category, sales_by_store, staff_list\"\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong table names, expected:\\n  %v\\ngot:\\n  %v\", expected, names)\n\t}\n}\n\nfunc TestColumns(t *testing.T) {\n\tresult, err := reader.Columns(metadata.Filter{Parent: \"film%\"})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read columns: %v\", err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Name)\n\t}\n\tactual := strings.Join(names, \", \")\n\texpected := \"description, film_id, language_id, last_update, length, original_language_id, rating, release_year, rental_duration, rental_rate, replacement_cost, special_features, title, actor_id, film_id, last_update, category_id, film_id, last_update, description, film_id, title, FID, actors, category, description, length, price, rating, title\"\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong column names, expected:\\n  %v, got:\\n  %v\", expected, names)\n\t}\n}\n\nfunc TestFunctions(t *testing.T) {\n\tresult, err := reader.Functions(metadata.Filter{})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read functions: %v\", err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Name)\n\t}\n\tactual := strings.Join(names, \", \")\n\texpected := \"abs, auth_enabled, auth_user_add, auth_user_change, auth_user_delete, authenticate, avg, changes, char, coalesce, count, count, cume_dist, current_date, current_time, current_timestamp, date, datetime, dense_rank, first_value, fts3_tokenizer, fts3_tokenizer, glob, group_concat, group_concat, hex, ifnull, instr, julianday, lag, lag, lag, last_insert_rowid, last_value, lead, lead, lead, length, like, like, likelihood, likely, load_extension, load_extension, lower, ltrim, ltrim, match, matchinfo, matchinfo, max, max, min, min, nth_value, ntile, nullif, offsets, optimize, percent_rank, printf, quote, random, randomblob, rank, replace, round, round, row_number, rtreecheck, rtreedepth, rtreenode, rtrim, rtrim, snippet, sqlite_compileoption_get, sqlite_compileoption_used, sqlite_log, sqlite_source_id, sqlite_version, strftime, substr, substr, sum, time, total, total_changes, trim, trim, typeof, unicode, unlikely, upper, zeroblob\"\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong function names, expected:\\n  %v\\ngot:\\n  %v\", expected, names)\n\t}\n}\n\nfunc TestIndexes(t *testing.T) {\n\tresult, err := reader.Indexes(metadata.Filter{})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read indexes: %v\", err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Table+\".\"+result.Get().Name)\n\t}\n\tactual := strings.Join(names, \", \")\n\texpected := \"actor.idx_actor_last_name, actor.sqlite_autoindex_actor_1, address.idx_fk_city_id, address.sqlite_autoindex_address_1, category.sqlite_autoindex_category_1, city.idx_fk_country_id, city.sqlite_autoindex_city_1, country.sqlite_autoindex_country_1, customer.idx_customer_last_name, customer.idx_customer_fk_address_id, customer.idx_customer_fk_store_id, customer.sqlite_autoindex_customer_1, film.idx_fk_original_language_id, film.idx_fk_language_id, film.sqlite_autoindex_film_1, film_actor.idx_fk_film_actor_actor, film_actor.idx_fk_film_actor_film, film_actor.sqlite_autoindex_film_actor_1, film_category.idx_fk_film_category_category, film_category.idx_fk_film_category_film, film_category.sqlite_autoindex_film_category_1, film_text.sqlite_autoindex_film_text_1, inventory.idx_fk_film_id_store_id, inventory.idx_fk_film_id, inventory.sqlite_autoindex_inventory_1, language.sqlite_autoindex_language_1, payment.idx_fk_customer_id, payment.idx_fk_staff_id, payment.sqlite_autoindex_payment_1, rental.idx_rental_uq, rental.idx_rental_fk_staff_id, rental.idx_rental_fk_customer_id, rental.idx_rental_fk_inventory_id, rental.sqlite_autoindex_rental_1, staff.idx_fk_staff_address_id, staff.idx_fk_staff_store_id, staff.sqlite_autoindex_staff_1, store.idx_fk_store_address, store.idx_store_fk_manager_staff_id, store.sqlite_autoindex_store_1\"\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong index names, expected:\\n  %v\\ngot:\\n  %v\", expected, names)\n\t}\n}\n\nfunc TestIndexColumns(t *testing.T) {\n\tresult, err := reader.IndexColumns(metadata.Filter{Name: \"idx%\"})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read index columns: %v\", err)\n\t}\n\n\tnames := []string{}\n\tfor result.Next() {\n\t\tnames = append(names, result.Get().Name)\n\t}\n\tactual := strings.Join(names, \", \")\n\texpected := \"last_name, city_id, country_id, last_name, address_id, store_id, original_language_id, language_id, actor_id, film_id, category_id, film_id, store_id, film_id, film_id, customer_id, staff_id, rental_date, inventory_id, customer_id, staff_id, customer_id, inventory_id, address_id, store_id, address_id, manager_staff_id\"\n\tif actual != expected {\n\t\tt.Errorf(\"Wrong index column names, expected:\\n  %v, got:\\n  %v\", expected, names)\n\t}\n}\n"
  },
  {
    "path": "drivers/sqlite3/sqshared/sqshared.go",
    "content": "// Package sqshared contains shared types for the sqlite3 and moderncsqlite\n// drivers.\npackage sqshared\n\nimport (\n\t\"database/sql/driver\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\n// ConvertBytes is the byte formatter func for sqlite3 databases.\nfunc ConvertBytes(buf []byte, tfmt string) (string, error) {\n\t// attempt to convert buf if it matches a time format, and if it\n\t// does, then return a formatted time string.\n\ts := string(buf)\n\tif s != \"\" && strings.TrimSpace(s) != \"\" {\n\t\tt := new(Time)\n\t\tif err := t.Scan(buf); err == nil {\n\t\t\treturn time.Time(*t).Format(tfmt), nil\n\t\t}\n\t}\n\treturn s, nil\n}\n\n// Time provides a type that will correctly scan the various timestamps\n// values stored by the github.com/mattn/go-sqlite3 driver for time.Time\n// values, as well as correctly satisfying the sql/driver/Valuer interface.\ntype Time time.Time\n\n// Value satisfies the Valuer interface.\nfunc (t *Time) Value() (driver.Value, error) {\n\treturn t, nil\n}\n\n// Scan satisfies the Scanner interface.\nfunc (t *Time) Scan(v interface{}) error {\n\tswitch x := v.(type) {\n\tcase time.Time:\n\t\t*t = Time(x)\n\t\treturn nil\n\tcase []byte:\n\t\treturn t.Parse(string(x))\n\tcase string:\n\t\treturn t.Parse(x)\n\t}\n\treturn fmt.Errorf(\"cannot convert type %T to Time\", v)\n}\n\n// Parse attempts to Parse string s to t.\nfunc (t *Time) Parse(s string) error {\n\tif s == \"\" {\n\t\treturn nil\n\t}\n\tfor _, f := range SQLiteTimestampFormats {\n\t\tif z, err := time.Parse(f, s); err == nil {\n\t\t\t*t = Time(z)\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn errors.New(\"could not parse time\")\n}\n\n// SQLiteTimestampFormats is timestamp formats understood by both this module\n// and SQLite.  The first format in the slice will be used when saving time\n// values into the database. When parsing a string from a timestamp or datetime\n// column, the formats are tried in order.\nvar SQLiteTimestampFormats = []string{\n\t// By default, store timestamps with whatever timezone they come with.\n\t// When parsed, they will be returned with the same timezone.\n\t\"2006-01-02 15:04:05.999999999-07:00\",\n\t\"2006-01-02T15:04:05.999999999-07:00\",\n\t\"2006-01-02 15:04:05.999999999\",\n\t\"2006-01-02T15:04:05.999999999\",\n\t\"2006-01-02 15:04:05\",\n\t\"2006-01-02T15:04:05\",\n\t\"2006-01-02 15:04\",\n\t\"2006-01-02T15:04\",\n\t\"2006-01-02\",\n}\n"
  },
  {
    "path": "drivers/sqlserver/reader.go",
    "content": "package sqlserver\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n)\n\ntype metaReader struct {\n\tmetadata.LoggingReader\n\tlimit int\n}\n\nvar _ metadata.CatalogReader = &metaReader{}\nvar _ metadata.IndexReader = &metaReader{}\nvar _ metadata.IndexColumnReader = &metaReader{}\n\nfunc NewReader(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\tir := infos.New(\n\t\tinfos.WithPlaceholder(placeholder),\n\t\tinfos.WithIndexes(false),\n\t\tinfos.WithSequences(false),\n\t\tinfos.WithConstraints(false),\n\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\tinfos.FunctionsSecurityType: \"''\",\n\t\t}),\n\t\tinfos.WithSystemSchemas([]string{\n\t\t\t\"db_accessadmin\",\n\t\t\t\"db_backupoperator\",\n\t\t\t\"db_datareader\",\n\t\t\t\"db_datawriter\",\n\t\t\t\"db_ddladmin\",\n\t\t\t\"db_denydatareader\",\n\t\t\t\"db_denydatawriter\",\n\t\t\t\"db_owner\",\n\t\t\t\"db_securityadmin\",\n\t\t\t\"INFORMATION_SCHEMA\",\n\t\t\t\"sys\",\n\t\t}),\n\t\tinfos.WithCurrentSchema(\"schema_name()\"),\n\t\tinfos.WithDataTypeFormatter(dataTypeFormatter),\n\t\tinfos.WithUsagePrivileges(false),\n\t)(db, opts...)\n\tmr := &metaReader{\n\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t}\n\treturn metadata.NewPluginReader(ir, mr)\n}\n\nfunc dataTypeFormatter(col metadata.Column) string {\n\tswitch col.DataType {\n\tcase \"numeric\", \"decimal\":\n\t\tif col.ColumnSize == 18 && col.DecimalDigits == 0 {\n\t\t\treturn col.DataType\n\t\t} else {\n\t\t\treturn fmt.Sprintf(\"%s(%d,%d)\", col.DataType, col.ColumnSize, col.DecimalDigits)\n\t\t}\n\tcase \"datetimeoffset\", \"datetime2\", \"time\":\n\t\tif col.ColumnSize == 7 {\n\t\t\treturn col.DataType\n\t\t} else {\n\t\t\treturn fmt.Sprintf(\"%s(%d)\", col.DataType, col.ColumnSize)\n\t\t}\n\tcase \"char\", \"nchar\", \"binary\":\n\t\tif col.ColumnSize == 1 {\n\t\t\treturn col.DataType\n\t\t} else {\n\t\t\treturn fmt.Sprintf(\"%s(%d)\", col.DataType, col.ColumnSize)\n\t\t}\n\tcase \"varchar\", \"nvarchar\", \"varbinary\":\n\t\tif col.ColumnSize == -1 {\n\t\t\treturn col.DataType + \"(max)\"\n\t\t} else if col.ColumnSize == 1 {\n\t\t\treturn col.DataType\n\t\t} else {\n\t\t\treturn fmt.Sprintf(\"%s(%d)\", col.DataType, col.ColumnSize)\n\t\t}\n\tdefault:\n\t\treturn col.DataType\n\t}\n}\n\nfunc (r *metaReader) SetLimit(l int) {\n\tr.limit = l\n}\n\nfunc (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) {\n\tqstr := `SELECT name\nFROM sys.databases`\n\trows, closeRows, err := r.query(qstr, []string{}, \"name\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Catalog{}\n\tfor rows.Next() {\n\t\trec := metadata.Catalog{}\n\t\terr = rows.Scan(&rec.Catalog)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewCatalogSet(results), nil\n}\n\nfunc (r metaReader) Indexes(f metadata.Filter) (*metadata.IndexSet, error) {\n\tqstr := `\nSELECT\n  db_name(),\n  s.name,\n  t.name,\n  COALESCE(i.name, ''),\n  CASE WHEN i.is_primary_key = 1 THEN 'YES' ELSE 'NO' END,\n  CASE WHEN i.is_unique = 1 THEN 'YES' ELSE 'NO' END,\n  i.type_desc\nFROM sys.schemas s\nJOIN sys.tables t on t.schema_id = s.schema_id\nJOIN sys.indexes i ON i.object_id = t.object_id\n`\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif f.OnlyVisible {\n\t\tconds = append(conds, \"s.name = schema_name()\")\n\t}\n\tif !f.WithSystem {\n\t\tconds = append(conds, \"s.name NOT IN ('db_accessadmin', 'db_backupoperator', 'db_datareader', 'db_datawriter', 'db_ddladmin', 'db_denydatareader', 'db_denydatawriter', 'db_owner', 'db_securityadmin', 'INFORMATION_SCHEMA', 'sys')\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"s.name LIKE @p%d\", len(vals)))\n\t}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, fmt.Sprintf(\"t.name LIKE @p%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"i.name LIKE @p%d\", len(vals)))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"s.name, t.name, i.name\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Index{}\n\tfor rows.Next() {\n\t\trec := metadata.Index{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Table, &rec.Name, &rec.IsUnique, &rec.IsPrimary, &rec.Type)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexSet(results), nil\n}\n\nfunc (r metaReader) IndexColumns(f metadata.Filter) (*metadata.IndexColumnSet, error) {\n\tqstr := `\nSELECT\n  db_name(),\n  s.name,\n  t.name,\n  COALESCE(i.name, ''),\n  c.name,\n  t.name,\n  ic.key_ordinal\nFROM sys.schemas s\nJOIN sys.tables t on t.schema_id = s.schema_id\nJOIN sys.indexes i ON i.object_id = t.object_id\nJOIN sys.index_columns ic ON i.object_id = ic.object_id and i.index_id = ic.index_id\nJOIN sys.columns c ON ic.object_id = c.object_id and ic.column_id = c.column_id\nJOIN sys.types ty ON ty.user_type_id = c.user_type_id\n`\n\tconds := []string{}\n\tvals := []interface{}{}\n\tif f.OnlyVisible {\n\t\tconds = append(conds, \"s.name = schema_name()\")\n\t}\n\tif !f.WithSystem {\n\t\tconds = append(conds, \"s.name NOT IN ('db_accessadmin', 'db_backupoperator', 'db_datareader', 'db_datawriter', 'db_ddladmin', 'db_denydatareader', 'db_denydatawriter', 'db_owner', 'db_securityadmin', 'INFORMATION_SCHEMA', 'sys')\")\n\t}\n\tif f.Schema != \"\" {\n\t\tvals = append(vals, f.Schema)\n\t\tconds = append(conds, fmt.Sprintf(\"s.name LIKE @p%d\", len(vals)))\n\t}\n\tif f.Parent != \"\" {\n\t\tvals = append(vals, f.Parent)\n\t\tconds = append(conds, fmt.Sprintf(\"t.name LIKE @p%d\", len(vals)))\n\t}\n\tif f.Name != \"\" {\n\t\tvals = append(vals, f.Name)\n\t\tconds = append(conds, fmt.Sprintf(\"i.name LIKE @p%d\", len(vals)))\n\t}\n\trows, closeRows, err := r.query(qstr, conds, \"s.name, t.name, i.name, ic.index_column_id\", vals...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.IndexColumn{}\n\tfor rows.Next() {\n\t\trec := metadata.IndexColumn{}\n\t\terr = rows.Scan(&rec.Catalog, &rec.Schema, &rec.Table, &rec.IndexName, &rec.Name, &rec.DataType, &rec.OrdinalPosition)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewIndexColumnSet(results), nil\n}\n\nfunc (r metaReader) query(qstr string, conds []string, order string, vals ...interface{}) (*sql.Rows, func(), error) {\n\tif len(conds) != 0 {\n\t\tqstr += \"\\nWHERE \" + strings.Join(conds, \" AND \")\n\t}\n\tif order != \"\" {\n\t\tqstr += \"\\nORDER BY \" + order\n\t}\n\tif r.limit != 0 {\n\t\tqstr += fmt.Sprintf(\"\\nFETCH FIRST %d ROWS ONLY\", r.limit)\n\t}\n\treturn r.Query(qstr, vals...)\n}\n"
  },
  {
    "path": "drivers/sqlserver/sqlserver.go",
    "content": "// Package sqlserver defines and registers usql's Microsoft SQL Server driver.\n//\n// See: https://github.com/microsoft/go-mssqldb\n// Group: base\npackage sqlserver\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\n\tmssql \"github.com/microsoft/go-mssqldb\"\n\tsqlserver \"github.com/microsoft/go-mssqldb\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\n\t// needed for azuresql authentication, named pipes, and shared memory transport protocols\n\t_ \"github.com/microsoft/go-mssqldb/azuread\"\n\t_ \"github.com/microsoft/go-mssqldb/namedpipe\"\n\t_ \"github.com/microsoft/go-mssqldb/sharedmemory\"\n)\n\nfunc init() {\n\tdrivers.Register(\"sqlserver\", drivers.Driver{\n\t\tAllowMultilineComments:  true,\n\t\tRequirePreviousPassword: true,\n\t\tLexerName:               \"tsql\",\n\t\t/*\n\t\t\t// NOTE: this has been commented out, as it is not necessary. if\n\t\t\t// NOTE: the azuread.DriverName is changed from `azuresql`, then\n\t\t\t// NOTE: this func will be necessary as dburl will never import non\n\t\t\t// NOTE: stdlib package. as is, dburl.Open will handle the call\n\t\t\t// NOTE: to sql.Open and will pass the `azuresql` driver name\n\t\t\tOpen: func(_ context.Context, u *dburl.URL, _, _ func() io.Writer) (func(string, string) (*sql.DB, error), error) {\n\t\t\t\treturn func(_ string, params string) (*sql.DB, error) {\n\t\t\t\t\tdriver := \"sqlserver\"\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase u.Query().Has(\"fedauth\"),\n\t\t\t\t\t\tstrings.Contains(strings.ToLower(u.OriginalScheme), \"azuresql\"):\n\t\t\t\t\t\tdriver = azuread.DriverName\n\t\t\t\t\t}\n\t\t\t\t\treturn sql.Open(driver, params)\n\t\t\t\t}, nil\n\t\t\t},\n\t\t*/\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver, level, edition string\n\t\t\terr := db.QueryRowContext(\n\t\t\t\tctx,\n\t\t\t\t`SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition')`,\n\t\t\t).Scan(&ver, &level, &edition)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Microsoft SQL Server \" + ver + \", \" + level + \", \" + edition, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, oldpw string) error {\n\t\t\t_, err := db.Exec(`ALTER LOGIN ` + user + ` WITH password = '` + newpw + `' old_password = '` + oldpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tColumnTypes: func(col *sql.ColumnType) (interface{}, error) {\n\t\t\tswitch col.DatabaseTypeName() {\n\t\t\tcase \"UNIQUEIDENTIFIER\":\n\t\t\t\tif nullable, ok := col.Nullable(); ok && nullable {\n\t\t\t\t\treturn new(NullUniqueIdentifier), nil\n\t\t\t\t}\n\t\t\t\treturn new(mssql.UniqueIdentifier), nil\n\t\t\t}\n\t\t\treturn new(interface{}), nil\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tif e, ok := err.(sqlserver.Error); ok {\n\t\t\t\treturn strconv.Itoa(int(e.Number)), e.Message\n\t\t\t}\n\t\t\tmsg := err.Error()\n\t\t\tif i := strings.LastIndex(msg, \"sqlserver:\"); i != -1 {\n\t\t\t\tmsg = msg[i:]\n\t\t\t}\n\t\t\treturn \"\", msg\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\treturn strings.Contains(err.Error(), \"Login failed for\")\n\t\t},\n\t\tNewMetadataReader: NewReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(NewReader(db, opts...))(db, w)\n\t\t},\n\t\tCopy: drivers.CopyWithInsert(placeholder),\n\t})\n}\n\nfunc placeholder(n int) string {\n\treturn fmt.Sprintf(\"@p%d\", n)\n}\n\ntype NullUniqueIdentifier struct {\n\tID    mssql.UniqueIdentifier\n\tValid bool\n}\n\nfunc (nui *NullUniqueIdentifier) Scan(v interface{}) error {\n\tnui.Valid = false\n\tif v == nil {\n\t\treturn nil\n\t}\n\tif err := nui.ID.Scan(v); err != nil {\n\t\treturn err\n\t}\n\tnui.Valid = true\n\treturn nil\n}\n\nfunc (nui NullUniqueIdentifier) String() string {\n\tif nui.Valid {\n\t\treturn nui.ID.String()\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "drivers/sqlserver/sqlserver_test.go",
    "content": "package sqlserver_test\n\nimport (\n\t\"database/sql\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\tdt \"github.com/ory/dockertest/v3\"\n\tdc \"github.com/ory/dockertest/v3/docker\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/drivers/sqlserver\"\n)\n\ntype Database struct {\n\tBuildArgs    []dc.BuildArg\n\tRunOptions   *dt.RunOptions\n\tExec         []string\n\tDriver       string\n\tURL          string\n\tReadinessURL string\n\tDockerPort   string\n\tResource     *dt.Resource\n\tDB           *sql.DB\n\tOpts         []metadata.ReaderOption\n\tReader       metadata.BasicReader\n}\n\nvar dbName string = \"sakila\"\n\nconst pw = \"yourStrong123_Password\"\n\nvar db = Database{\n\tBuildArgs: []dc.BuildArg{\n\t\t{Name: \"BASE_IMAGE\", Value: \"mcr.microsoft.com/mssql/server:2019-latest\"},\n\t\t{Name: \"SCHEMA_URL\", Value: \"https://raw.githubusercontent.com/jOOQ/sakila/main/sql-server-sakila-db/sql-server-sakila-schema.sql\"},\n\t\t{Name: \"TARGET\", Value: \"/schema\"},\n\t\t{Name: \"USER\", Value: \"mssql:0\"},\n\t},\n\tRunOptions: &dt.RunOptions{\n\t\tName: \"usql-sqlserver\",\n\t\tEnv:  []string{\"ACCEPT_EULA=Y\", \"SA_PASSWORD=\" + pw},\n\t},\n\tExec:         []string{\"/opt/mssql-tools/bin/sqlcmd\", \"-S\", \"localhost\", \"-U\", \"sa\", \"-P\", pw, \"-d\", \"master\", \"-i\", \"/schema/sql-server-sakila-schema.sql\"},\n\tDriver:       \"sqlserver\",\n\tURL:          \"sqlserver://sa:\" + url.QueryEscape(pw) + \"@127.0.0.1:%s?database=\" + dbName,\n\tReadinessURL: \"sqlserver://sa:\" + url.QueryEscape(pw) + \"@127.0.0.1:%s\",\n\tDockerPort:   \"1433/tcp\",\n}\n\nfunc TestMain(m *testing.M) {\n\tcleanup := true\n\tflag.BoolVar(&cleanup, \"cleanup\", true, \"delete containers when finished\")\n\tflag.Parse()\n\tpool, err := dt.NewPool(\"\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not connect to docker: %s\", err)\n\t}\n\tvar ok bool\n\tdb.Resource, ok = pool.ContainerByName(db.RunOptions.Name)\n\tif !ok {\n\t\tbuildOpts := &dt.BuildOptions{\n\t\t\tContextDir: \"../testdata/docker\",\n\t\t\tBuildArgs:  db.BuildArgs,\n\t\t}\n\t\tdb.Resource, err = pool.BuildAndRunWithBuildOptions(buildOpts, db.RunOptions)\n\t\tif err != nil {\n\t\t\tlog.Fatal(\"Could not start resource: \", err)\n\t\t}\n\t}\n\n\turl := db.URL\n\tif db.ReadinessURL != \"\" {\n\t\turl = db.ReadinessURL\n\t}\n\tport := db.Resource.GetPort(db.DockerPort)\n\tif db.DB, err = waitForDbConnection(db.Driver, pool, url, port); err != nil {\n\t\tlog.Fatal(\"Timed out waiting for db: \", err)\n\t}\n\n\tif len(db.Exec) != 0 {\n\t\texitCode, err := db.Resource.Exec(db.Exec, dt.ExecOptions{\n\t\t\tStdIn:  os.Stdin,\n\t\t\tStdOut: os.Stdout,\n\t\t\tStdErr: os.Stderr,\n\t\t\tTTY:    true,\n\t\t})\n\t\tif err != nil || exitCode != 0 {\n\t\t\tlog.Fatal(\"Could not load schema: \", err)\n\t\t}\n\t}\n\n\t// Reconnect with actual URL if a separate URL for readiness checking was used\n\tif db.ReadinessURL != \"\" {\n\t\tif db.DB, err = waitForDbConnection(db.Driver, pool, db.URL, port); err != nil {\n\t\t\tlog.Fatal(\"Timed out waiting for db: \", err)\n\t\t}\n\t}\n\n\tcode := m.Run()\n\n\t// You can't defer this because os.Exit doesn't care for defer\n\tif cleanup {\n\t\tif err := pool.Purge(db.Resource); err != nil {\n\t\t\tlog.Fatal(\"Could not purge resource: \", err)\n\t\t}\n\t}\n\tos.Exit(code)\n}\n\nfunc waitForDbConnection(driver string, pool *dt.Pool, url string, port string) (*sql.DB, error) {\n\t// exponential backoff-retry, because the application in the container might not be ready to accept connections yet\n\tvar db *sql.DB\n\tif err := pool.Retry(func() error {\n\t\tvar err error\n\t\tdb, err = sql.Open(driver, fmt.Sprintf(url, port))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn db.Ping()\n\t}); err != nil {\n\t\treturn nil, err\n\t}\n\treturn db, nil\n}\n\nfunc TestColumns(t *testing.T) {\n\t// Only testing sqlserver specific datatype formatting.\n\t// The rest of the functionality is covered by informationschema/metadata_test.go:TestColumns\n\ttype test struct {\n\t\ttypeDef string\n\t\twant    string\n\t}\n\tschema := \"dbo\"\n\ttable := \"test_dtypes\"\n\ttests := []test{\n\t\t{typeDef: \"bigint\", want: \"bigint\"},\n\t\t{typeDef: \"numeric\", want: \"numeric\"},\n\t\t{typeDef: \"numeric(4,2)\", want: \"numeric(4,2)\"},\n\t\t{typeDef: \"numeric(18,0)\", want: \"numeric\"},\n\t\t{typeDef: \"decimal\", want: \"decimal\"},\n\t\t{typeDef: \"decimal(4,2)\", want: \"decimal(4,2)\"},\n\t\t{typeDef: \"decimal(18,0)\", want: \"decimal\"},\n\t\t{typeDef: \"bit\", want: \"bit\"},\n\t\t{typeDef: \"smallint\", want: \"smallint\"},\n\t\t{typeDef: \"smallmoney\", want: \"smallmoney\"},\n\t\t{typeDef: \"int\", want: \"int\"},\n\t\t{typeDef: \"tinyint\", want: \"tinyint\"},\n\t\t{typeDef: \"money\", want: \"money\"},\n\t\t{typeDef: \"float\", want: \"float\"},\n\t\t{typeDef: \"float(11)\", want: \"real\"},\n\t\t{typeDef: \"float(30)\", want: \"float\"},\n\t\t{typeDef: \"real\", want: \"real\"},\n\t\t{typeDef: \"date\", want: \"date\"},\n\t\t{typeDef: \"datetimeoffset\", want: \"datetimeoffset\"},\n\t\t{typeDef: \"datetimeoffset(5)\", want: \"datetimeoffset(5)\"},\n\t\t{typeDef: \"datetimeoffset(7)\", want: \"datetimeoffset\"},\n\t\t{typeDef: \"datetime2\", want: \"datetime2\"},\n\t\t{typeDef: \"datetime2(5)\", want: \"datetime2(5)\"},\n\t\t{typeDef: \"datetime2(7)\", want: \"datetime2\"},\n\t\t{typeDef: \"smalldatetime\", want: \"smalldatetime\"},\n\t\t{typeDef: \"datetime\", want: \"datetime\"},\n\t\t{typeDef: \"time\", want: \"time\"},\n\t\t{typeDef: \"time(5)\", want: \"time(5)\"},\n\t\t{typeDef: \"time(7)\", want: \"time\"},\n\t\t{typeDef: \"char\", want: \"char\"},\n\t\t{typeDef: \"char(3)\", want: \"char(3)\"},\n\t\t{typeDef: \"char(1)\", want: \"char\"},\n\t\t{typeDef: \"varchar\", want: \"varchar\"},\n\t\t{typeDef: \"varchar(12)\", want: \"varchar(12)\"},\n\t\t{typeDef: \"varchar(1)\", want: \"varchar\"},\n\t\t{typeDef: \"varchar(max)\", want: \"varchar(max)\"},\n\t\t{typeDef: \"text\", want: \"text\"},\n\t\t{typeDef: \"nchar\", want: \"nchar\"},\n\t\t{typeDef: \"nchar(2)\", want: \"nchar(2)\"},\n\t\t{typeDef: \"nchar(1)\", want: \"nchar\"},\n\t\t{typeDef: \"nvarchar\", want: \"nvarchar\"},\n\t\t{typeDef: \"nvarchar(12)\", want: \"nvarchar(12)\"},\n\t\t{typeDef: \"nvarchar(1)\", want: \"nvarchar\"},\n\t\t{typeDef: \"nvarchar(max)\", want: \"nvarchar(max)\"},\n\t\t{typeDef: \"ntext\", want: \"ntext\"},\n\t\t{typeDef: \"binary\", want: \"binary\"},\n\t\t{typeDef: \"binary(12)\", want: \"binary(12)\"},\n\t\t{typeDef: \"binary(1)\", want: \"binary\"},\n\t\t{typeDef: \"varbinary\", want: \"varbinary\"},\n\t\t{typeDef: \"varbinary(12)\", want: \"varbinary(12)\"},\n\t\t{typeDef: \"varbinary(1)\", want: \"varbinary\"},\n\t\t{typeDef: \"varbinary(max)\", want: \"varbinary(max)\"},\n\t\t{typeDef: \"image\", want: \"image\"},\n\t\t{typeDef: \"rowversion\", want: \"timestamp\"},\n\t\t{typeDef: \"hierarchyid\", want: \"hierarchyid\"},\n\t\t{typeDef: \"uniqueidentifier\", want: \"uniqueidentifier\"},\n\t\t{typeDef: \"sql_variant\", want: \"sql_variant\"},\n\t\t{typeDef: \"xml\", want: \"xml\"},\n\t\t{typeDef: \"geometry\", want: \"geometry\"},\n\t\t{typeDef: \"geography\", want: \"geography\"},\n\t}\n\n\t// Create table\n\tcolExpressions := []string{}\n\tfor i, test := range tests {\n\t\tcolExpressions = append(colExpressions, fmt.Sprintf(\"column_%d %s\", i, test.typeDef))\n\t}\n\tquery := fmt.Sprintf(\"CREATE TABLE %s.%s (%s)\", schema, table, strings.Join(colExpressions, \", \"))\n\tdb.DB.Exec(query)\n\tdefer db.DB.Exec(fmt.Sprintf(\"DROP TABLE %s.%s\", schema, table))\n\n\t// Read data types\n\tr := sqlserver.NewReader(db.DB).(metadata.ColumnReader)\n\tresult, err := r.Columns(metadata.Filter{Schema: schema, Parent: table})\n\tif err != nil {\n\t\tlog.Fatalf(\"Could not read %s columns: %v\", dbName, err)\n\t}\n\tactualTypes := []string{}\n\tfor result.Next() {\n\t\tactualTypes = append(actualTypes, result.Get().DataType)\n\t}\n\n\t// Compare\n\tfor i, test := range tests {\n\t\tif actualTypes[i] != test.want {\n\t\t\tt.Errorf(\"Wrong %s column data type, expected:\\n  %s, got:\\n  %s\", dbName, test.want, actualTypes[i])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "drivers/testdata/.gitignore",
    "content": "*.actual.txt\n"
  },
  {
    "path": "drivers/testdata/csvq/.gitignore",
    "content": "*_copy\n"
  },
  {
    "path": "drivers/testdata/docker/Dockerfile",
    "content": "ARG BASE_IMAGE\nFROM $BASE_IMAGE\n\nARG SCHEMA_URL\nARG TARGET\nARG USER\nADD --chown=$USER $SCHEMA_URL $TARGET/\nRUN [ ! -d \"$TARGET\" ] || chmod -R 777 $TARGET/ || echo \"failed to change perms of $TARGET, leaving as $(ls -la $TARGET/)\"\n"
  },
  {
    "path": "drivers/testdata/gen-golden.sh",
    "content": "#!/usr/bin/env bash\n\npgsql_in_docker=false\npgsql_container=usql-pgsql\n\nif [ \"$pgsql_in_docker\" != true ]; then\n    PGHOST=\"${PGHOST:-127.0.0.1}\"\n    port=$(docker port \"$pgsql_container\" 5432/tcp)\n    PGPORT=${port##*:}\nelse\n    PGHOST=\"${PGHOST:-$pgsql_container}\"\n    PGPORT=5432\nfi\nPGUSER=\"${PGUSER:-postgres}\"\nPGPASSWORD=\"${PGPASSWORD:-pw}\"\n\nexport PGHOST PGPORT PGUSER PGPASSWORD\n\ndeclare -A queries\nqueries=(\n    [descTable]=\"\\d+ film*\"\n    [listTables]=\"\\dtvmsE+ film*\"\n    [listFuncs]=\"\\df+\"\n    [listIndexes]=\"\\di+\"\n    [listSchemas]=\"\\dn+\"\n    [listDbs]=\"\\l+\"\n)\n\nfor q in \"${!queries[@]}\"; do\n    query=\"${queries[$q]}\"\n    cmd=(psql --no-psqlrc --command \"$query\")\n    if [ \"$pgsql_in_docker\" == true ]; then\n        docker run -it --rm -e PGHOST -e PGPORT -e PGUSER -e PGPASSWORD --link \"$pgsql_container\" postgres:13 \"${cmd[@]}\" >\"pgsql.$q.golden.txt\"\n    else\n        \"${cmd[@]}\" -o \"pgsql.$q.golden.txt\"\n    fi\ndone\n\nmysql_in_docker=true\nmysql_container=usql-mysql\n\nif [ \"$mysql_in_docker\" != true ]; then\n    MYHOST=\"${MYHOST:-127.0.0.1}\"\n    port=$(docker port \"$mysql_container\" 3306/tcp)\n    MYPORT=${port##*:}\nelse\n    MYHOST=\"${MYHOST:-$mysql_container}\"\n    MYPORT=3306\nfi\nMYUSER=\"${MYUSER:-root}\"\nMYPASSWORD=\"${MYPASSWORD:-pw}\"\n\ndeclare -A queries\nqueries=(\n    [descTable]=\"DESC film; SHOW INDEX FROM film; DESC film_actor; SHOW INDEX FROM film_actor; DESC film_category; SHOW INDEX FROM film_category; DESC film_list; SHOW INDEX FROM film_list; DESC film_text; SHOW INDEX FROM film_text;\"\n    [listTables]=\"SHOW TABLES LIKE 'film%'\"\n    [listSchemas]=\"SHOW DATABASES\"\n)\n\nfor q in \"${!queries[@]}\"; do\n    query=\"${queries[$q]}\"\n    cmd=(mysql -h \"$MYHOST\" -P \"$MYPORT\" -u \"$MYUSER\" --password=\"$MYPASSWORD\" --no-auto-rehash --database sakila --execute \"$query\")\n    if [ \"$mysql_in_docker\" == true ]; then\n        docker run -it --rm --link \"$mysql_container\" mysql:8 \"${cmd[@]}\" 2>/dev/null >\"mysql.$q.golden.txt\"\n    else\n        \"${cmd[@]}\" 2>/dev/null >\"mysql.$q.golden.txt\"\n    fi\ndone\n"
  },
  {
    "path": "drivers/testdata/mysql.descTable.expected.txt",
    "content": "                                                                         BASE TABLE \"sakila.film\"\n         Name         |                                Type                                 | Nullable |      Default      | Size  | Decimal Digits | Radix | Octet Length \n----------------------+---------------------------------------------------------------------+----------+-------------------+-------+----------------+-------+--------------\n film_id              | int unsigned                                                        | \"NO\"     |                   |    10 |              0 |    10 |            0 \n title                | varchar(255)                                                        | \"NO\"     |                   |   255 |              0 |    10 |          765 \n description          | text                                                                | \"YES\"    |                   | 65535 |              0 |    10 |        65535 \n release_year         | year                                                                | \"YES\"    |                   |     0 |              0 |    10 |            0 \n language_id          | int unsigned                                                        | \"NO\"     |                   |    10 |              0 |    10 |            0 \n original_language_id | int unsigned                                                        | \"YES\"    |                   |    10 |              0 |    10 |            0 \n rental_duration      | tinyint unsigned                                                    | \"NO\"     | 3                 |     3 |              0 |    10 |            0 \n rental_rate          | decimal(4,2)                                                        | \"NO\"     | 4.99              |     4 |              2 |    10 |            0 \n length               | smallint unsigned                                                   | \"YES\"    |                   |     5 |              0 |    10 |            0 \n replacement_cost     | decimal(5,2)                                                        | \"NO\"     | 19.99             |     5 |              2 |    10 |            0 \n rating               | enum('G','PG','PG-13','R','NC-17')                                  | \"YES\"    | G                 |     5 |              0 |    10 |           15 \n special_features     | set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') | \"YES\"    |                   |    54 |              0 |    10 |          162 \n last_update          | timestamp                                                           | \"NO\"     | CURRENT_TIMESTAMP |     0 |              0 |    10 |            0 \nIndexes:\n  \"idx_fk_language_id\" BTREE (language_id)\n  \"idx_fk_original_language_id\" BTREE (original_language_id)\n  \"idx_title\" BTREE (title)\n  \"PRIMARY\" PRIMARY_KEY, UNIQUE, BTREE (film_id)\nForeign-key constraints:\n  \"fk_film_language\" FOREIGN KEY (language_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  \"fk_film_language_original\" FOREIGN KEY (original_language_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nReferenced by:\n  TABLE \"film\" CONSTRAINT \"fk_film_language\" FOREIGN KEY (language_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  TABLE \"film\" CONSTRAINT \"fk_film_language_original\" FOREIGN KEY (original_language_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n\n                                      BASE TABLE \"sakila.film_actor\"\n    Name     |     Type     | Nullable |      Default      | Size | Decimal Digits | Radix | Octet Length \n-------------+--------------+----------+-------------------+------+----------------+-------+--------------\n actor_id    | int unsigned | \"NO\"     |                   |   10 |              0 |    10 |            0 \n film_id     | int unsigned | \"NO\"     |                   |   10 |              0 |    10 |            0 \n last_update | timestamp    | \"NO\"     | CURRENT_TIMESTAMP |    0 |              0 |    10 |            0 \nIndexes:\n  \"idx_fk_film_id\" BTREE (film_id)\n  \"PRIMARY\" PRIMARY_KEY, UNIQUE, BTREE (actor_id, film_id)\nForeign-key constraints:\n  \"fk_film_actor_actor\" FOREIGN KEY (actor_id) REFERENCES film_actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  \"fk_film_actor_film\" FOREIGN KEY (film_id) REFERENCES film_actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT\nReferenced by:\n  TABLE \"film_actor\" CONSTRAINT \"fk_film_actor_actor\" FOREIGN KEY (actor_id) REFERENCES film_actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  TABLE \"film_actor\" CONSTRAINT \"fk_film_actor_film\" FOREIGN KEY (film_id) REFERENCES film_actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT\n\n                                    BASE TABLE \"sakila.film_category\"\n    Name     |     Type     | Nullable |      Default      | Size | Decimal Digits | Radix | Octet Length \n-------------+--------------+----------+-------------------+------+----------------+-------+--------------\n film_id     | int unsigned | \"NO\"     |                   |   10 |              0 |    10 |            0 \n category_id | int unsigned | \"NO\"     |                   |   10 |              0 |    10 |            0 \n last_update | timestamp    | \"NO\"     | CURRENT_TIMESTAMP |    0 |              0 |    10 |            0 \nIndexes:\n  \"fk_film_category_category\" BTREE (category_id)\n  \"PRIMARY\" PRIMARY_KEY, UNIQUE, BTREE (film_id, category_id)\nForeign-key constraints:\n  \"fk_film_category_category\" FOREIGN KEY (category_id) REFERENCES film_category(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  \"fk_film_category_film\" FOREIGN KEY (film_id) REFERENCES film_category(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nReferenced by:\n  TABLE \"film_category\" CONSTRAINT \"fk_film_category_category\" FOREIGN KEY (category_id) REFERENCES film_category(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  TABLE \"film_category\" CONSTRAINT \"fk_film_category_film\" FOREIGN KEY (film_id) REFERENCES film_category(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n\n                                  BASE TABLE \"sakila.film_text\"\n    Name     |     Type     | Nullable | Default | Size  | Decimal Digits | Radix | Octet Length \n-------------+--------------+----------+---------+-------+----------------+-------+--------------\n film_id     | int          | \"NO\"     |         |    10 |              0 |    10 |            0 \n title       | varchar(255) | \"NO\"     |         |   255 |              0 |    10 |          765 \n description | text         | \"YES\"    |         | 65535 |              0 |    10 |        65535 \nIndexes:\n  \"idx_title_description\" FULLTEXT (title, description)\n  \"PRIMARY\" PRIMARY_KEY, UNIQUE, BTREE (film_id)\n\n                                                VIEW \"sakila.film_list\"\n    Name     |                Type                | Nullable | Default | Size  | Decimal Digits | Radix | Octet Length \n-------------+------------------------------------+----------+---------+-------+----------------+-------+--------------\n FID         | int unsigned                       | \"YES\"    | 0       |    10 |              0 |    10 |            0 \n title       | varchar(255)                       | \"YES\"    |         |   255 |              0 |    10 |          765 \n description | text                               | \"YES\"    |         | 65535 |              0 |    10 |        65535 \n category    | varchar(25)                        | \"NO\"     |         |    25 |              0 |    10 |           75 \n price       | decimal(4,2)                       | \"YES\"    | 4.99    |     4 |              2 |    10 |            0 \n length      | smallint unsigned                  | \"YES\"    |         |     5 |              0 |    10 |            0 \n rating      | enum('G','PG','PG-13','R','NC-17') | \"YES\"    | G       |     5 |              0 |    10 |           15 \n actors      | text                               | \"YES\"    |         | 65535 |              0 |    10 |        65535 \n\n"
  },
  {
    "path": "drivers/testdata/mysql.descTable.golden.txt",
    "content": "mysql: [Warning] Using a password on the command line interface can be insecure.\r\n+----------------------+---------------------------------------------------------------------+------+-----+-------------------+-----------------------------------------------+\r\n| Field                | Type                                                                | Null | Key | Default           | Extra                                         |\r\n+----------------------+---------------------------------------------------------------------+------+-----+-------------------+-----------------------------------------------+\r\n| film_id              | smallint unsigned                                                   | NO   | PRI | NULL              | auto_increment                                |\r\n| title                | varchar(255)                                                        | NO   | MUL | NULL              |                                               |\r\n| description          | text                                                                | YES  |     | NULL              |                                               |\r\n| release_year         | year                                                                | YES  |     | NULL              |                                               |\r\n| language_id          | tinyint unsigned                                                    | NO   | MUL | NULL              |                                               |\r\n| original_language_id | tinyint unsigned                                                    | YES  | MUL | NULL              |                                               |\r\n| rental_duration      | tinyint unsigned                                                    | NO   |     | 3                 |                                               |\r\n| rental_rate          | decimal(4,2)                                                        | NO   |     | 4.99              |                                               |\r\n| length               | smallint unsigned                                                   | YES  |     | NULL              |                                               |\r\n| replacement_cost     | decimal(5,2)                                                        | NO   |     | 19.99             |                                               |\r\n| rating               | enum('G','PG','PG-13','R','NC-17')                                  | YES  |     | G                 |                                               |\r\n| special_features     | set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') | YES  |     | NULL              |                                               |\r\n| last_update          | timestamp                                                           | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |\r\n+----------------------+---------------------------------------------------------------------+------+-----+-------------------+-----------------------------------------------+\r\n+-------+------------+-----------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| Table | Non_unique | Key_name                    | Seq_in_index | Column_name          | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |\r\n+-------+------------+-----------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| film  |          0 | PRIMARY                     |            1 | film_id              | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film  |          1 | idx_title                   |            1 | title                | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film  |          1 | idx_fk_language_id          |            1 | language_id          | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film  |          1 | idx_fk_original_language_id |            1 | original_language_id | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |\r\n+-------+------------+-----------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+\r\n| Field       | Type              | Null | Key | Default           | Extra                                         |\r\n+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+\r\n| actor_id    | smallint unsigned | NO   | PRI | NULL              |                                               |\r\n| film_id     | smallint unsigned | NO   | PRI | NULL              |                                               |\r\n| last_update | timestamp         | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |\r\n+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+\r\n+------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| Table      | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |\r\n+------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| film_actor |          0 | PRIMARY        |            1 | actor_id    | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film_actor |          0 | PRIMARY        |            2 | film_id     | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film_actor |          1 | idx_fk_film_id |            1 | film_id     | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n+------------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+\r\n| Field       | Type              | Null | Key | Default           | Extra                                         |\r\n+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+\r\n| film_id     | smallint unsigned | NO   | PRI | NULL              |                                               |\r\n| category_id | tinyint unsigned  | NO   | PRI | NULL              |                                               |\r\n| last_update | timestamp         | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |\r\n+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+\r\n+---------------+------------+---------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| Table         | Non_unique | Key_name                  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |\r\n+---------------+------------+---------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| film_category |          0 | PRIMARY                   |            1 | film_id     | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film_category |          0 | PRIMARY                   |            2 | category_id | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film_category |          1 | fk_film_category_category |            1 | category_id | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n+---------------+------------+---------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n+-------------+------------------------------------+------+-----+---------+-------+\r\n| Field       | Type                               | Null | Key | Default | Extra |\r\n+-------------+------------------------------------+------+-----+---------+-------+\r\n| FID         | smallint unsigned                  | YES  |     | 0       |       |\r\n| title       | varchar(255)                       | YES  |     | NULL    |       |\r\n| description | text                               | YES  |     | NULL    |       |\r\n| category    | varchar(25)                        | NO   |     | NULL    |       |\r\n| price       | decimal(4,2)                       | YES  |     | 4.99    |       |\r\n| length      | smallint unsigned                  | YES  |     | NULL    |       |\r\n| rating      | enum('G','PG','PG-13','R','NC-17') | YES  |     | G       |       |\r\n| actors      | text                               | YES  |     | NULL    |       |\r\n+-------------+------------------------------------+------+-----+---------+-------+\r\n+-------------+--------------+------+-----+---------+-------+\r\n| Field       | Type         | Null | Key | Default | Extra |\r\n+-------------+--------------+------+-----+---------+-------+\r\n| film_id     | smallint     | NO   | PRI | NULL    |       |\r\n| title       | varchar(255) | NO   | MUL | NULL    |       |\r\n| description | text         | YES  |     | NULL    |       |\r\n+-------------+--------------+------+-----+---------+-------+\r\n+-----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| Table     | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |\r\n+-----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n| film_text |          0 | PRIMARY               |            1 | film_id     | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |\r\n| film_text |          1 | idx_title_description |            1 | title       | NULL      |        NULL |     NULL |   NULL |      | FULLTEXT   |         |               | YES     | NULL       |\r\n| film_text |          1 | idx_title_description |            2 | description | NULL      |        NULL |     NULL |   NULL | YES  | FULLTEXT   |         |               | YES     | NULL       |\r\n+-----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+\r\n"
  },
  {
    "path": "drivers/testdata/mysql.listFuncs.expected.txt",
    "content": "                                                                        List of functions\n Schema |            Name            | Result data type |                                     Argument data types                                     |   Type    \n--------+----------------------------+------------------+---------------------------------------------------------------------------------------------+-----------\n sakila | film_in_stock              |                  | p_film_id int, p_store_id int, OUT p_film_count int                                         | PROCEDURE \n sakila | film_not_in_stock          |                  | p_film_id int, p_store_id int, OUT p_film_count int                                         | PROCEDURE \n sakila | get_customer_balance       | decimal          | p_customer_id int, p_effective_date datetime                                                | FUNCTION \n sakila | inventory_held_by_customer | int              | p_inventory_id int                                                                          | FUNCTION \n sakila | inventory_in_stock         | tinyint          | p_inventory_id int                                                                          | FUNCTION \n sakila | rewards_report             |                  | min_monthly_purchases tinyint, min_dollar_amount_purchased decimal, OUT count_rewardees int | PROCEDURE \n(6 rows)\n"
  },
  {
    "path": "drivers/testdata/mysql.listIndexes.expected.txt",
    "content": "                                   List of indexes\n Schema |            Name             |   Type   |     Table     | Primary? | Unique? \n--------+-----------------------------+----------+---------------+----------+---------\n sakila | idx_actor_last_name         | BTREE    | actor         | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | actor         | \"YES\"    | \"YES\" \n sakila | idx_fk_city_id              | BTREE    | address       | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | address       | \"YES\"    | \"YES\" \n sakila | PRIMARY                     | BTREE    | category      | \"YES\"    | \"YES\" \n sakila | idx_fk_country_id           | BTREE    | city          | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | city          | \"YES\"    | \"YES\" \n sakila | PRIMARY                     | BTREE    | country       | \"YES\"    | \"YES\" \n sakila | idx_fk_address_id           | BTREE    | customer      | \"NO\"     | \"NO\" \n sakila | idx_fk_store_id             | BTREE    | customer      | \"NO\"     | \"NO\" \n sakila | idx_last_name               | BTREE    | customer      | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | customer      | \"YES\"    | \"YES\" \n sakila | idx_fk_language_id          | BTREE    | film          | \"NO\"     | \"NO\" \n sakila | idx_fk_original_language_id | BTREE    | film          | \"NO\"     | \"NO\" \n sakila | idx_title                   | BTREE    | film          | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | film          | \"YES\"    | \"YES\" \n sakila | idx_fk_film_id              | BTREE    | film_actor    | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | film_actor    | \"YES\"    | \"YES\" \n sakila | fk_film_category_category   | BTREE    | film_category | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | film_category | \"YES\"    | \"YES\" \n sakila | idx_title_description       | FULLTEXT | film_text     | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | film_text     | \"YES\"    | \"YES\" \n sakila | idx_fk_film_id              | BTREE    | inventory     | \"NO\"     | \"NO\" \n sakila | idx_store_id_film_id        | BTREE    | inventory     | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | inventory     | \"YES\"    | \"YES\" \n sakila | PRIMARY                     | BTREE    | language      | \"YES\"    | \"YES\" \n sakila | fk_payment_rental           | BTREE    | payment       | \"NO\"     | \"NO\" \n sakila | idx_fk_customer_id          | BTREE    | payment       | \"NO\"     | \"NO\" \n sakila | idx_fk_staff_id             | BTREE    | payment       | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | payment       | \"YES\"    | \"YES\" \n sakila | idx_fk_customer_id          | BTREE    | rental        | \"NO\"     | \"NO\" \n sakila | idx_fk_inventory_id         | BTREE    | rental        | \"NO\"     | \"NO\" \n sakila | idx_fk_staff_id             | BTREE    | rental        | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | rental        | \"YES\"    | \"YES\" \n sakila | rental_date                 | BTREE    | rental        | \"NO\"     | \"YES\" \n sakila | idx_fk_address_id           | BTREE    | staff         | \"NO\"     | \"NO\" \n sakila | idx_fk_store_id             | BTREE    | staff         | \"NO\"     | \"NO\" \n sakila | PRIMARY                     | BTREE    | staff         | \"YES\"    | \"YES\" \n sakila | idx_fk_address_id           | BTREE    | store         | \"NO\"     | \"NO\" \n sakila | idx_unique_manager          | BTREE    | store         | \"NO\"     | \"YES\" \n sakila | PRIMARY                     | BTREE    | store         | \"YES\"    | \"YES\" \n(41 rows)\n"
  },
  {
    "path": "drivers/testdata/mysql.listSchemas.expected.txt",
    "content": " List of schemas\n Schema | Catalog \n--------+---------\n sakila | def \n(1 row)\n"
  },
  {
    "path": "drivers/testdata/mysql.listSchemas.golden.txt",
    "content": "mysql: [Warning] Using a password on the command line interface can be insecure.\r\n+--------------------+\r\n| Database           |\r\n+--------------------+\r\n| information_schema |\r\n| mysql              |\r\n| performance_schema |\r\n| sakila             |\r\n| sys                |\r\n+--------------------+\r\n"
  },
  {
    "path": "drivers/testdata/mysql.listTables.expected.txt",
    "content": "                      List of relations\n Schema |     Name      |    Type    | Rows | Size | Comment \n--------+---------------+------------+------+------+---------\n sakila | film          | BASE TABLE |    0 |      |  \n sakila | film_actor    | BASE TABLE |    0 |      |  \n sakila | film_category | BASE TABLE |    0 |      |  \n sakila | film_text     | BASE TABLE |    0 |      |  \n sakila | film_list     | VIEW       |    0 |      |  \n(5 rows)\n"
  },
  {
    "path": "drivers/testdata/mysql.listTables.golden.txt",
    "content": "mysql: [Warning] Using a password on the command line interface can be insecure.\r\n+--------------------------+\r\n| Tables_in_sakila (film%) |\r\n+--------------------------+\r\n| film                     |\r\n| film_actor               |\r\n| film_category            |\r\n| film_list                |\r\n| film_text                |\r\n+--------------------------+\r\n"
  },
  {
    "path": "drivers/testdata/pgsql.descTable.expected.txt",
    "content": "                                                                   table \"public.film\"\n         Name         |              Type              | Nullable |                Default                | Size | Decimal Digits | Radix | Octet Length \n----------------------+--------------------------------+----------+---------------------------------------+------+----------------+-------+--------------\n film_id              | integer                        | \"NO\"     | nextval('film_film_id_seq'::regclass) |   32 |              0 |     2 |            0 \n title                | character varying(255)         | \"NO\"     |                                       |  255 |              0 |    10 |         1020 \n description          | text                           | \"YES\"    |                                       |    0 |              0 |    10 |   1073741824 \n release_year         | integer                        | \"YES\"    |                                       |   32 |              0 |     2 |            0 \n language_id          | smallint                       | \"NO\"     |                                       |   16 |              0 |     2 |            0 \n original_language_id | smallint                       | \"YES\"    |                                       |   16 |              0 |     2 |            0 \n rental_duration      | smallint                       | \"NO\"     | 3                                     |   16 |              0 |     2 |            0 \n rental_rate          | numeric(4,2)                   | \"NO\"     | 4.99                                  |    4 |              2 |    10 |            0 \n length               | smallint                       | \"YES\"    |                                       |   16 |              0 |     2 |            0 \n replacement_cost     | numeric(5,2)                   | \"NO\"     | 19.99                                 |    5 |              2 |    10 |            0 \n rating               | USER-DEFINED                   | \"YES\"    | 'G'::mpaa_rating                      |    0 |              0 |    10 |            0 \n last_update          | timestamp(6) without time zone | \"NO\"     | now()                                 |    6 |              0 |    10 |            0 \n special_features     | ARRAY                          | \"YES\"    |                                       |    0 |              0 |    10 |            0 \n fulltext             | tsvector                       | \"NO\"     |                                       |    0 |              0 |    10 |            0 \nIndexes:\n  \"film_fulltext_idx\" gist (fulltext)\n  \"film_pkey\" PRIMARY_KEY, UNIQUE, btree (film_id)\n  \"idx_fk_language_id\" btree (language_id)\n  \"idx_fk_original_language_id\" btree (original_language_id)\n  \"idx_title\" btree (title)\nForeign-key constraints:\n  \"film_language_id_fkey\" FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  \"film_original_language_id_fkey\" FOREIGN KEY (original_language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE RESTRICT\nReferenced by:\n  TABLE \"film_actor\" CONSTRAINT \"film_actor_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  TABLE \"film_category\" CONSTRAINT \"film_category_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  TABLE \"inventory\" CONSTRAINT \"inventory_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nTriggers:\n  \"film_fulltext_trigger\" CREATE TRIGGER film_fulltext_trigger BEFORE INSERT OR UPDATE ON film FOR EACH ROW EXECUTE FUNCTION tsvector_update_trigger('fulltext', 'pg_catalog.english', 'title', 'description')\n  \"last_updated\" CREATE TRIGGER last_updated BEFORE UPDATE ON film FOR EACH ROW EXECUTE FUNCTION last_updated()\n\n                                            table \"public.film_actor\"\n    Name     |              Type              | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n-------------+--------------------------------+----------+---------+------+----------------+-------+--------------\n actor_id    | smallint                       | \"NO\"     |         |   16 |              0 |     2 |            0 \n film_id     | smallint                       | \"NO\"     |         |   16 |              0 |     2 |            0 \n last_update | timestamp(6) without time zone | \"NO\"     | now()   |    6 |              0 |    10 |            0 \nIndexes:\n  \"film_actor_pkey\" PRIMARY_KEY, UNIQUE, btree (actor_id, film_id)\n  \"idx_fk_film_id\" btree (film_id)\nForeign-key constraints:\n  \"film_actor_actor_id_fkey\" FOREIGN KEY (actor_id) REFERENCES actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  \"film_actor_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nTriggers:\n  \"last_updated\" CREATE TRIGGER last_updated BEFORE UPDATE ON film_actor FOR EACH ROW EXECUTE FUNCTION last_updated()\n\n                                           table \"public.film_category\"\n    Name     |              Type              | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n-------------+--------------------------------+----------+---------+------+----------------+-------+--------------\n film_id     | smallint                       | \"NO\"     |         |   16 |              0 |     2 |            0 \n category_id | smallint                       | \"NO\"     |         |   16 |              0 |     2 |            0 \n last_update | timestamp(6) without time zone | \"NO\"     | now()   |    6 |              0 |    10 |            0 \nIndexes:\n  \"film_category_pkey\" PRIMARY_KEY, UNIQUE, btree (film_id, category_id)\nForeign-key constraints:\n  \"film_category_category_id_fkey\" FOREIGN KEY (category_id) REFERENCES category(category_id) ON UPDATE CASCADE ON DELETE RESTRICT\n  \"film_category_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nTriggers:\n  \"last_updated\" CREATE TRIGGER last_updated BEFORE UPDATE ON film_category FOR EACH ROW EXECUTE FUNCTION last_updated()\n\n                                         view \"public.film_list\"\n    Name     |          Type          | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n-------------+------------------------+----------+---------+------+----------------+-------+--------------\n fid         | integer                | \"YES\"    |         |   32 |              0 |     2 |            0 \n title       | character varying(255) | \"YES\"    |         |  255 |              0 |    10 |         1020 \n description | text                   | \"YES\"    |         |    0 |              0 |    10 |   1073741824 \n category    | character varying(25)  | \"YES\"    |         |   25 |              0 |    10 |          100 \n price       | numeric(4,2)           | \"YES\"    |         |    4 |              2 |    10 |            0 \n length      | smallint               | \"YES\"    |         |   16 |              0 |     2 |            0 \n rating      | USER-DEFINED           | \"YES\"    |         |    0 |              0 |    10 |            0 \n actors      | text                   | \"YES\"    |         |    0 |              0 |    10 |   1073741824 \n\n                Sequence \"public.film_film_id_seq\"\n  Type  | Start | Min |         Max         | Increment | Cycles? \n--------+-------+-----+---------------------+-----------+---------\n bigint | 1     | 1   | 9223372036854775807 | 1         | \"NO\" \n\nIndex \"public.film_actor_pkey\"\n   Name   |   Type   \n----------+----------\n actor_id | smallint \n film_id  | smallint \nprimary key, btree, for table film_actor\n\nIndex \"public.film_category_pkey\"\n    Name     |   Type   \n-------------+----------\n film_id     | smallint \n category_id | smallint \nprimary key, btree, for table film_category\n\nIndex \"public.film_fulltext_idx\"\n   Name   |   Type    \n----------+-----------\n fulltext | gtsvector \ngist, for table film\n\nIndex \"public.film_pkey\"\n  Name   |  Type   \n---------+---------\n film_id | integer \nprimary key, btree, for table film\n"
  },
  {
    "path": "drivers/testdata/pgsql.descTable.golden.txt",
    "content": "                                                Table \"public.film\"\n        Column        |            Type             | Collation | Nullable |                Default                \n----------------------+-----------------------------+-----------+----------+---------------------------------------\n film_id              | integer                     |           | not null | nextval('film_film_id_seq'::regclass)\n title                | character varying(255)      |           | not null | \n description          | text                        |           |          | \n release_year         | year                        |           |          | \n language_id          | smallint                    |           | not null | \n original_language_id | smallint                    |           |          | \n rental_duration      | smallint                    |           | not null | 3\n rental_rate          | numeric(4,2)                |           | not null | 4.99\n length               | smallint                    |           |          | \n replacement_cost     | numeric(5,2)                |           | not null | 19.99\n rating               | mpaa_rating                 |           |          | 'G'::mpaa_rating\n last_update          | timestamp without time zone |           | not null | now()\n special_features     | text[]                      |           |          | \n fulltext             | tsvector                    |           | not null | \nIndexes:\n    \"film_pkey\" PRIMARY KEY, btree (film_id)\n    \"film_fulltext_idx\" gist (fulltext)\n    \"idx_fk_language_id\" btree (language_id)\n    \"idx_fk_original_language_id\" btree (original_language_id)\n    \"idx_title\" btree (title)\nForeign-key constraints:\n    \"film_language_id_fkey\" FOREIGN KEY (language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE RESTRICT\n    \"film_original_language_id_fkey\" FOREIGN KEY (original_language_id) REFERENCES language(language_id) ON UPDATE CASCADE ON DELETE RESTRICT\nReferenced by:\n    TABLE \"film_actor\" CONSTRAINT \"film_actor_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n    TABLE \"film_category\" CONSTRAINT \"film_category_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\n    TABLE \"inventory\" CONSTRAINT \"inventory_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nTriggers:\n    film_fulltext_trigger BEFORE INSERT OR UPDATE ON film FOR EACH ROW EXECUTE FUNCTION tsvector_update_trigger('fulltext', 'pg_catalog.english', 'title', 'description')\n    last_updated BEFORE UPDATE ON film FOR EACH ROW EXECUTE FUNCTION last_updated()\n\n                         Table \"public.film_actor\"\n   Column    |            Type             | Collation | Nullable | Default \n-------------+-----------------------------+-----------+----------+---------\n actor_id    | smallint                    |           | not null | \n film_id     | smallint                    |           | not null | \n last_update | timestamp without time zone |           | not null | now()\nIndexes:\n    \"film_actor_pkey\" PRIMARY KEY, btree (actor_id, film_id)\n    \"idx_fk_film_id\" btree (film_id)\nForeign-key constraints:\n    \"film_actor_actor_id_fkey\" FOREIGN KEY (actor_id) REFERENCES actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT\n    \"film_actor_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nTriggers:\n    last_updated BEFORE UPDATE ON film_actor FOR EACH ROW EXECUTE FUNCTION last_updated()\n\n     Index \"public.film_actor_pkey\"\n  Column  |   Type   | Key? | Definition \n----------+----------+------+------------\n actor_id | smallint | yes  | actor_id\n film_id  | smallint | yes  | film_id\nprimary key, btree, for table \"public.film_actor\"\n\n                        Table \"public.film_category\"\n   Column    |            Type             | Collation | Nullable | Default \n-------------+-----------------------------+-----------+----------+---------\n film_id     | smallint                    |           | not null | \n category_id | smallint                    |           | not null | \n last_update | timestamp without time zone |           | not null | now()\nIndexes:\n    \"film_category_pkey\" PRIMARY KEY, btree (film_id, category_id)\nForeign-key constraints:\n    \"film_category_category_id_fkey\" FOREIGN KEY (category_id) REFERENCES category(category_id) ON UPDATE CASCADE ON DELETE RESTRICT\n    \"film_category_film_id_fkey\" FOREIGN KEY (film_id) REFERENCES film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT\nTriggers:\n    last_updated BEFORE UPDATE ON film_category FOR EACH ROW EXECUTE FUNCTION last_updated()\n\n      Index \"public.film_category_pkey\"\n   Column    |   Type   | Key? | Definition  \n-------------+----------+------+-------------\n film_id     | smallint | yes  | film_id\n category_id | smallint | yes  | category_id\nprimary key, btree, for table \"public.film_category\"\n\n                      Sequence \"public.film_film_id_seq\"\n  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache \n--------+-------+---------+---------------------+-----------+---------+-------\n bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1\n\n     Index \"public.film_fulltext_idx\"\n  Column  |   Type    | Key? | Definition \n----------+-----------+------+------------\n fulltext | gtsvector | yes  | fulltext\ngist, for table \"public.film\"\n\n                        View \"public.film_list\"\n   Column    |          Type          | Collation | Nullable | Default \n-------------+------------------------+-----------+----------+---------\n fid         | integer                |           |          | \n title       | character varying(255) |           |          | \n description | text                   |           |          | \n category    | character varying(25)  |           |          | \n price       | numeric(4,2)           |           |          | \n length      | smallint               |           |          | \n rating      | mpaa_rating            |           |          | \n actors      | text                   |           |          | \n\n       Index \"public.film_pkey\"\n Column  |  Type   | Key? | Definition \n---------+---------+------+------------\n film_id | integer | yes  | film_id\nprimary key, btree, for table \"public.film\"\n\n"
  },
  {
    "path": "drivers/testdata/pgsql.listDbs.golden.txt",
    "content": "                                 List of databases\n   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges   \n-----------+----------+----------+------------+------------+-----------------------\n postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 | \n template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +\n           |          |          |            |            | postgres=CTc/postgres\n template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres          +\n           |          |          |            |            | postgres=CTc/postgres\n(3 rows)\n\n"
  },
  {
    "path": "drivers/testdata/pgsql.listFuncs.expected.txt",
    "content": "                                                            List of functions\n Schema |            Name            | Result data type |                         Argument data types                         |   Type   \n--------+----------------------------+------------------+---------------------------------------------------------------------+----------\n public | _group_concat              | text             | text, text                                                          | FUNCTION \n public | film_in_stock              | integer          | p_film_id integer, p_store_id integer, OUT p_film_count integer     | FUNCTION \n public | film_not_in_stock          | integer          | p_film_id integer, p_store_id integer, OUT p_film_count integer     | FUNCTION \n public | get_customer_balance       | numeric          | p_customer_id integer, p_effective_date timestamp without time zone | FUNCTION \n public | group_concat               | text             | text                                                                |  \n public | inventory_held_by_customer | integer          | p_inventory_id integer                                              | FUNCTION \n public | inventory_in_stock         | boolean          | p_inventory_id integer                                              | FUNCTION \n public | last_day                   | date             | timestamp without time zone                                         | FUNCTION \n public | last_updated               | trigger          |                                                                     | FUNCTION \n public | rewards_report             | USER-DEFINED     | min_monthly_purchases integer, min_dollar_amount_purchased numeric  | FUNCTION \n(10 rows)\n"
  },
  {
    "path": "drivers/testdata/pgsql.listFuncs.golden.txt",
    "content": "                                                          List of functions\n Schema |            Name            | Result data type |                         Argument data types                         | Type \n--------+----------------------------+------------------+---------------------------------------------------------------------+------\n public | _group_concat              | text             | text, text                                                          | func\n public | film_in_stock              | SETOF integer    | p_film_id integer, p_store_id integer, OUT p_film_count integer     | func\n public | film_not_in_stock          | SETOF integer    | p_film_id integer, p_store_id integer, OUT p_film_count integer     | func\n public | get_customer_balance       | numeric          | p_customer_id integer, p_effective_date timestamp without time zone | func\n public | group_concat               | text             | text                                                                | agg\n public | inventory_held_by_customer | integer          | p_inventory_id integer                                              | func\n public | inventory_in_stock         | boolean          | p_inventory_id integer                                              | func\n public | last_day                   | date             | timestamp without time zone                                         | func\n public | last_updated               | trigger          |                                                                     | func\n public | rewards_report             | SETOF customer   | min_monthly_purchases integer, min_dollar_amount_purchased numeric  | func\n(10 rows)\n\n"
  },
  {
    "path": "drivers/testdata/pgsql.listIndexes.expected.txt",
    "content": "                                               List of indexes\n Schema |                        Name                         | Type  |      Table       | Primary? | Unique? \n--------+-----------------------------------------------------+-------+------------------+----------+---------\n public | actor_pkey                                          | index | actor            | \"YES\"    | \"YES\" \n public | address_pkey                                        | index | address          | \"YES\"    | \"YES\" \n public | category_pkey                                       | index | category         | \"YES\"    | \"YES\" \n public | city_pkey                                           | index | city             | \"YES\"    | \"YES\" \n public | country_pkey                                        | index | country          | \"YES\"    | \"YES\" \n public | customer_pkey                                       | index | customer         | \"YES\"    | \"YES\" \n public | film_actor_pkey                                     | index | film_actor       | \"YES\"    | \"YES\" \n public | film_category_pkey                                  | index | film_category    | \"YES\"    | \"YES\" \n public | film_fulltext_idx                                   | index | film             | \"NO\"     | \"NO\" \n public | film_pkey                                           | index | film             | \"YES\"    | \"YES\" \n public | idx_actor_last_name                                 | index | actor            | \"NO\"     | \"NO\" \n public | idx_fk_address_id                                   | index | customer         | \"NO\"     | \"NO\" \n public | idx_fk_city_id                                      | index | address          | \"NO\"     | \"NO\" \n public | idx_fk_country_id                                   | index | city             | \"NO\"     | \"NO\" \n public | idx_fk_customer_id                                  | index | payment          | \"NO\"     | \"NO\" \n public | idx_fk_film_id                                      | index | film_actor       | \"NO\"     | \"NO\" \n public | idx_fk_inventory_id                                 | index | rental           | \"NO\"     | \"NO\" \n public | idx_fk_language_id                                  | index | film             | \"NO\"     | \"NO\" \n public | idx_fk_original_language_id                         | index | film             | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_01_customer_id                 | index | payment_p2007_01 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_01_staff_id                    | index | payment_p2007_01 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_02_customer_id                 | index | payment_p2007_02 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_02_staff_id                    | index | payment_p2007_02 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_03_customer_id                 | index | payment_p2007_03 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_03_staff_id                    | index | payment_p2007_03 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_04_customer_id                 | index | payment_p2007_04 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_04_staff_id                    | index | payment_p2007_04 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_05_customer_id                 | index | payment_p2007_05 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_05_staff_id                    | index | payment_p2007_05 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_06_customer_id                 | index | payment_p2007_06 | \"NO\"     | \"NO\" \n public | idx_fk_payment_p2007_06_staff_id                    | index | payment_p2007_06 | \"NO\"     | \"NO\" \n public | idx_fk_staff_id                                     | index | payment          | \"NO\"     | \"NO\" \n public | idx_fk_store_id                                     | index | customer         | \"NO\"     | \"NO\" \n public | idx_last_name                                       | index | customer         | \"NO\"     | \"NO\" \n public | idx_store_id_film_id                                | index | inventory        | \"NO\"     | \"NO\" \n public | idx_title                                           | index | film             | \"NO\"     | \"NO\" \n public | idx_unq_manager_staff_id                            | index | store            | \"YES\"    | \"NO\" \n public | idx_unq_rental_rental_date_inventory_id_customer_id | index | rental           | \"YES\"    | \"NO\" \n public | inventory_pkey                                      | index | inventory        | \"YES\"    | \"YES\" \n public | language_pkey                                       | index | language         | \"YES\"    | \"YES\" \n public | payment_pkey                                        | index | payment          | \"YES\"    | \"YES\" \n public | rental_pkey                                         | index | rental           | \"YES\"    | \"YES\" \n public | staff_pkey                                          | index | staff            | \"YES\"    | \"YES\" \n public | store_pkey                                          | index | store            | \"YES\"    | \"YES\" \n(44 rows)\n"
  },
  {
    "path": "drivers/testdata/pgsql.listIndexes.golden.txt",
    "content": "                                         List of relations\n Schema |                        Name                         | Type  |  Owner   |      Table       \n--------+-----------------------------------------------------+-------+----------+------------------\n public | actor_pkey                                          | index | postgres | actor\n public | address_pkey                                        | index | postgres | address\n public | category_pkey                                       | index | postgres | category\n public | city_pkey                                           | index | postgres | city\n public | country_pkey                                        | index | postgres | country\n public | customer_pkey                                       | index | postgres | customer\n public | film_actor_pkey                                     | index | postgres | film_actor\n public | film_category_pkey                                  | index | postgres | film_category\n public | film_fulltext_idx                                   | index | postgres | film\n public | film_pkey                                           | index | postgres | film\n public | idx_actor_last_name                                 | index | postgres | actor\n public | idx_fk_address_id                                   | index | postgres | customer\n public | idx_fk_city_id                                      | index | postgres | address\n public | idx_fk_country_id                                   | index | postgres | city\n public | idx_fk_customer_id                                  | index | postgres | payment\n public | idx_fk_film_id                                      | index | postgres | film_actor\n public | idx_fk_inventory_id                                 | index | postgres | rental\n public | idx_fk_language_id                                  | index | postgres | film\n public | idx_fk_original_language_id                         | index | postgres | film\n public | idx_fk_payment_p2007_01_customer_id                 | index | postgres | payment_p2007_01\n public | idx_fk_payment_p2007_01_staff_id                    | index | postgres | payment_p2007_01\n public | idx_fk_payment_p2007_02_customer_id                 | index | postgres | payment_p2007_02\n public | idx_fk_payment_p2007_02_staff_id                    | index | postgres | payment_p2007_02\n public | idx_fk_payment_p2007_03_customer_id                 | index | postgres | payment_p2007_03\n public | idx_fk_payment_p2007_03_staff_id                    | index | postgres | payment_p2007_03\n public | idx_fk_payment_p2007_04_customer_id                 | index | postgres | payment_p2007_04\n public | idx_fk_payment_p2007_04_staff_id                    | index | postgres | payment_p2007_04\n public | idx_fk_payment_p2007_05_customer_id                 | index | postgres | payment_p2007_05\n public | idx_fk_payment_p2007_05_staff_id                    | index | postgres | payment_p2007_05\n public | idx_fk_payment_p2007_06_customer_id                 | index | postgres | payment_p2007_06\n public | idx_fk_payment_p2007_06_staff_id                    | index | postgres | payment_p2007_06\n public | idx_fk_staff_id                                     | index | postgres | payment\n public | idx_fk_store_id                                     | index | postgres | customer\n public | idx_last_name                                       | index | postgres | customer\n public | idx_store_id_film_id                                | index | postgres | inventory\n public | idx_title                                           | index | postgres | film\n public | idx_unq_manager_staff_id                            | index | postgres | store\n public | idx_unq_rental_rental_date_inventory_id_customer_id | index | postgres | rental\n public | inventory_pkey                                      | index | postgres | inventory\n public | language_pkey                                       | index | postgres | language\n public | payment_pkey                                        | index | postgres | payment\n public | rental_pkey                                         | index | postgres | rental\n public | staff_pkey                                          | index | postgres | staff\n public | store_pkey                                          | index | postgres | store\n(44 rows)\n\n"
  },
  {
    "path": "drivers/testdata/pgsql.listSchemas.expected.txt",
    "content": "  List of schemas\n Schema | Catalog  \n--------+----------\n public | postgres \n(1 row)\n"
  },
  {
    "path": "drivers/testdata/pgsql.listSchemas.golden.txt",
    "content": "  List of schemas\n  Name  |  Owner   \n--------+----------\n public | postgres\n(1 row)\n\n"
  },
  {
    "path": "drivers/testdata/pgsql.listTables.expected.txt",
    "content": "                         List of relations\n Schema |       Name       |   Type   | Rows |    Size    | Comment \n--------+------------------+----------+------+------------+---------\n public | film_film_id_seq | sequence |    1 | 8192 bytes |  \n public | film             | table    |    0 | 8192 bytes |  \n public | film_actor       | table    |    0 | 0 bytes    |  \n public | film_category    | table    |    0 | 0 bytes    |  \n public | film_list        | view     |    0 | 0 bytes    |  \n(5 rows)\n"
  },
  {
    "path": "drivers/testdata/pgsql.listTables.golden.txt",
    "content": "                List of relations\n Schema |       Name       |   Type   |  Owner   \n--------+------------------+----------+----------\n public | film             | table    | postgres\n public | film_actor       | table    | postgres\n public | film_category    | table    | postgres\n public | film_film_id_seq | sequence | postgres\n public | film_list        | view     | postgres\n(5 rows)\n\n"
  },
  {
    "path": "drivers/testdata/sqlserver.descTable.expected.txt",
    "content": "                                               BASE TABLE \"dbo.film\"\n         Name         |     Type     | Nullable |   Default   |    Size    | Decimal Digits | Radix | Octet Length \n----------------------+--------------+----------+-------------+------------+----------------+-------+--------------\n film_id              | int          | \"NO\"     |             |         10 |              0 |    10 |            0 \n title                | varchar(255) | \"NO\"     |             |        255 |              0 |    10 |          255 \n description          | text         | \"YES\"    | (NULL)      | 2147483647 |              0 |    10 |   2147483647 \n release_year         | varchar(4)   | \"YES\"    |             |          4 |              0 |    10 |            4 \n language_id          | int          | \"NO\"     |             |         10 |              0 |    10 |            0 \n original_language_id | int          | \"YES\"    | (NULL)      |         10 |              0 |    10 |            0 \n rental_duration      | tinyint      | \"NO\"     | ((3))       |          3 |              0 |    10 |            0 \n rental_rate          | decimal(4,2) | \"NO\"     | ((4.99))    |          4 |              2 |    10 |            0 \n length               | smallint     | \"YES\"    | (NULL)      |          5 |              0 |    10 |            0 \n replacement_cost     | decimal(5,2) | \"NO\"     | ((19.99))   |          5 |              2 |    10 |            0 \n rating               | varchar(10)  | \"YES\"    | ('G')       |         10 |              0 |    10 |           10 \n special_features     | varchar(255) | \"YES\"    | (NULL)      |        255 |              0 |    10 |          255 \n last_update          | datetime     | \"NO\"     | (getdate()) |          3 |              0 |    10 |            0 \nIndexes:\n  \"\" HEAP (language_id, original_language_id, film_id)\n  \"idx_fk_language_id\" NONCLUSTERED (language_id)\n  \"idx_fk_original_language_id\" NONCLUSTERED (original_language_id)\n  \"PK__film__349764A85F0D1F82\" PRIMARY_KEY, UNIQUE, NONCLUSTERED (film_id)\n\n                                  BASE TABLE \"dbo.film_actor\"\n    Name     |   Type   | Nullable |   Default   | Size | Decimal Digits | Radix | Octet Length \n-------------+----------+----------+-------------+------+----------------+-------+--------------\n actor_id    | int      | \"NO\"     |             |   10 |              0 |    10 |            0 \n film_id     | int      | \"NO\"     |             |   10 |              0 |    10 |            0 \n last_update | datetime | \"NO\"     | (getdate()) |    3 |              0 |    10 |            0 \nIndexes:\n  \"\" HEAP (actor_id, film_id, actor_id, film_id)\n  \"idx_fk_film_actor_actor\" NONCLUSTERED (actor_id)\n  \"idx_fk_film_actor_film\" NONCLUSTERED (film_id)\n  \"PK__film_act__086D31FFE010698E\" PRIMARY_KEY, UNIQUE, NONCLUSTERED (actor_id, film_id)\n\n                                 BASE TABLE \"dbo.film_category\"\n    Name     |   Type   | Nullable |   Default   | Size | Decimal Digits | Radix | Octet Length \n-------------+----------+----------+-------------+------+----------------+-------+--------------\n film_id     | int      | \"NO\"     |             |   10 |              0 |    10 |            0 \n category_id | int      | \"NO\"     |             |   10 |              0 |    10 |            0 \n last_update | datetime | \"NO\"     | (getdate()) |    3 |              0 |    10 |            0 \nIndexes:\n  \"\" HEAP (category_id, film_id, film_id, category_id)\n  \"idx_fk_film_category_category\" NONCLUSTERED (category_id)\n  \"idx_fk_film_category_film\" NONCLUSTERED (film_id)\n  \"PK__film_cat__69C38A33EABC8336\" PRIMARY_KEY, UNIQUE, NONCLUSTERED (film_id, category_id)\n\n                                      BASE TABLE \"dbo.film_text\"\n    Name     |     Type     | Nullable | Default |    Size    | Decimal Digits | Radix | Octet Length \n-------------+--------------+----------+---------+------------+----------------+-------+--------------\n film_id     | int          | \"NO\"     |         |         10 |              0 |    10 |            0 \n title       | varchar(255) | \"NO\"     |         |        255 |              0 |    10 |          255 \n description | text         | \"YES\"    |         | 2147483647 |              0 |    10 |   2147483647 \nIndexes:\n  \"\" HEAP (film_id)\n  \"PK__film_tex__349764A85D245C83\" PRIMARY_KEY, UNIQUE, NONCLUSTERED (film_id)\n\n                                         VIEW \"dbo.film_list\"\n    Name     |     Type     | Nullable | Default |    Size    | Decimal Digits | Radix | Octet Length \n-------------+--------------+----------+---------+------------+----------------+-------+--------------\n FID         | int          | \"YES\"    |         |         10 |              0 |    10 |            0 \n title       | varchar(255) | \"YES\"    |         |        255 |              0 |    10 |          255 \n description | text         | \"YES\"    |         | 2147483647 |              0 |    10 |   2147483647 \n category    | varchar(25)  | \"NO\"     |         |         25 |              0 |    10 |           25 \n price       | decimal(4,2) | \"YES\"    |         |          4 |              2 |    10 |            0 \n length      | smallint     | \"YES\"    |         |          5 |              0 |    10 |            0 \n rating      | varchar(10)  | \"YES\"    |         |         10 |              0 |    10 |           10 \n actors      | varchar(91)  | \"NO\"     |         |         91 |              0 |    10 |           91 \n\n"
  },
  {
    "path": "drivers/testdata/sqlserver.listFuncs.expected.txt",
    "content": "(0 rows)\n"
  },
  {
    "path": "drivers/testdata/sqlserver.listIndexes.expected.txt",
    "content": "                                       List of indexes\n Schema |              Name              |     Type     |     Table     | Primary? | Unique? \n--------+--------------------------------+--------------+---------------+----------+---------\n dbo    |                                | HEAP         | actor         | \"NO\"     | \"NO\" \n dbo    | idx_actor_last_name            | NONCLUSTERED | actor         | \"NO\"     | \"NO\" \n dbo    | PK__actor__8B2447B565179537    | NONCLUSTERED | actor         | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | address       | \"NO\"     | \"NO\" \n dbo    | idx_fk_city_id                 | NONCLUSTERED | address       | \"NO\"     | \"NO\" \n dbo    | PK__address__CAA247C920A28CDC  | NONCLUSTERED | address       | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | category      | \"NO\"     | \"NO\" \n dbo    | PK__category__D54EE9B5C54BFE50 | NONCLUSTERED | category      | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | city          | \"NO\"     | \"NO\" \n dbo    | idx_fk_country_id              | NONCLUSTERED | city          | \"NO\"     | \"NO\" \n dbo    | PK__city__031491A980EA569B     | NONCLUSTERED | city          | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | country       | \"NO\"     | \"NO\" \n dbo    | PK__country__7E8CD054CAE966BB  | NONCLUSTERED | country       | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | customer      | \"NO\"     | \"NO\" \n dbo    | idx_fk_address_id              | NONCLUSTERED | customer      | \"NO\"     | \"NO\" \n dbo    | idx_fk_store_id                | NONCLUSTERED | customer      | \"NO\"     | \"NO\" \n dbo    | idx_last_name                  | NONCLUSTERED | customer      | \"NO\"     | \"NO\" \n dbo    | PK__customer__CD65CB84BB7D0A31 | NONCLUSTERED | customer      | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | film          | \"NO\"     | \"NO\" \n dbo    | idx_fk_language_id             | NONCLUSTERED | film          | \"NO\"     | \"NO\" \n dbo    | idx_fk_original_language_id    | NONCLUSTERED | film          | \"NO\"     | \"NO\" \n dbo    | PK__film__349764A85F0D1F82     | NONCLUSTERED | film          | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | film_actor    | \"NO\"     | \"NO\" \n dbo    | idx_fk_film_actor_actor        | NONCLUSTERED | film_actor    | \"NO\"     | \"NO\" \n dbo    | idx_fk_film_actor_film         | NONCLUSTERED | film_actor    | \"NO\"     | \"NO\" \n dbo    | PK__film_act__086D31FFE010698E | NONCLUSTERED | film_actor    | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | film_category | \"NO\"     | \"NO\" \n dbo    | idx_fk_film_category_category  | NONCLUSTERED | film_category | \"NO\"     | \"NO\" \n dbo    | idx_fk_film_category_film      | NONCLUSTERED | film_category | \"NO\"     | \"NO\" \n dbo    | PK__film_cat__69C38A33EABC8336 | NONCLUSTERED | film_category | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | film_text     | \"NO\"     | \"NO\" \n dbo    | PK__film_tex__349764A85D245C83 | NONCLUSTERED | film_text     | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | inventory     | \"NO\"     | \"NO\" \n dbo    | idx_fk_film_id                 | NONCLUSTERED | inventory     | \"NO\"     | \"NO\" \n dbo    | idx_fk_film_id_store_id        | NONCLUSTERED | inventory     | \"NO\"     | \"NO\" \n dbo    | PK__inventor__B59ACC48C0DED777 | NONCLUSTERED | inventory     | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | language      | \"NO\"     | \"NO\" \n dbo    | PK__language__804CF6B2AD65E24B | NONCLUSTERED | language      | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | payment       | \"NO\"     | \"NO\" \n dbo    | idx_fk_customer_id             | NONCLUSTERED | payment       | \"NO\"     | \"NO\" \n dbo    | idx_fk_staff_id                | NONCLUSTERED | payment       | \"NO\"     | \"NO\" \n dbo    | PK__payment__ED1FC9EBDD7F3474  | NONCLUSTERED | payment       | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | rental        | \"NO\"     | \"NO\" \n dbo    | idx_fk_customer_id             | NONCLUSTERED | rental        | \"NO\"     | \"NO\" \n dbo    | idx_fk_inventory_id            | NONCLUSTERED | rental        | \"NO\"     | \"NO\" \n dbo    | idx_fk_staff_id                | NONCLUSTERED | rental        | \"NO\"     | \"NO\" \n dbo    | idx_uq                         | NONCLUSTERED | rental        | \"YES\"    | \"NO\" \n dbo    | PK__rental__67DB611A79AF93E5   | NONCLUSTERED | rental        | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | staff         | \"NO\"     | \"NO\" \n dbo    | idx_fk_address_id              | NONCLUSTERED | staff         | \"NO\"     | \"NO\" \n dbo    | idx_fk_store_id                | NONCLUSTERED | staff         | \"NO\"     | \"NO\" \n dbo    | PK__staff__1963DD9DFC0374BE    | NONCLUSTERED | staff         | \"YES\"    | \"YES\" \n dbo    |                                | HEAP         | staff_copy    | \"NO\"     | \"NO\" \n dbo    |                                | HEAP         | store         | \"NO\"     | \"NO\" \n dbo    | idx_fk_address_id              | NONCLUSTERED | store         | \"YES\"    | \"NO\" \n dbo    | idx_fk_store_address           | NONCLUSTERED | store         | \"NO\"     | \"NO\" \n dbo    | PK__store__A2F2A30D66044831    | NONCLUSTERED | store         | \"YES\"    | \"YES\" \n(57 rows)\n"
  },
  {
    "path": "drivers/testdata/sqlserver.listSchemas.expected.txt",
    "content": " List of schemas\n Schema | Catalog \n--------+---------\n dbo    | sakila \n guest  | sakila \n(2 rows)\n"
  },
  {
    "path": "drivers/testdata/sqlserver.listTables.expected.txt",
    "content": "                      List of relations\n Schema |     Name      |    Type    | Rows | Size | Comment \n--------+---------------+------------+------+------+---------\n dbo    | film          | BASE TABLE |    0 |      |  \n dbo    | film_actor    | BASE TABLE |    0 |      |  \n dbo    | film_category | BASE TABLE |    0 |      |  \n dbo    | film_text     | BASE TABLE |    0 |      |  \n dbo    | film_list     | VIEW       |    0 |      |  \n(5 rows)\n"
  },
  {
    "path": "drivers/testdata/trino.descTable.expected.txt",
    "content": "                                     BASE TABLE \"sf1.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                    BASE TABLE \"sf100.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                   BASE TABLE \"sf1000.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                   BASE TABLE \"sf10000.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                  BASE TABLE \"sf100000.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                    BASE TABLE \"sf300.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                   BASE TABLE \"sf3000.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                   BASE TABLE \"sf30000.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n                                    BASE TABLE \"tiny.orders\"\n     Name      |    Type     | Nullable | Default | Size | Decimal Digits | Radix | Octet Length \n---------------+-------------+----------+---------+------+----------------+-------+--------------\n orderkey      | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n custkey       | bigint      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderstatus   | varchar(1)  | \"YES\"    |         |    0 |              0 |     0 |            0 \n totalprice    | double      | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderdate     | date        | \"YES\"    |         |    0 |              0 |     0 |            0 \n orderpriority | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n clerk         | varchar(15) | \"YES\"    |         |    0 |              0 |     0 |            0 \n shippriority  | integer     | \"YES\"    |         |    0 |              0 |     0 |            0 \n comment       | varchar(79) | \"YES\"    |         |    0 |              0 |     0 |            0 \n\n"
  },
  {
    "path": "drivers/testdata/trino.listSchemas.expected.txt",
    "content": "  List of schemas\n  Schema  | Catalog \n----------+---------\n sf1      | tpch \n sf100    | tpch \n sf1000   | tpch \n sf10000  | tpch \n sf100000 | tpch \n sf300    | tpch \n sf3000   | tpch \n sf30000  | tpch \n tiny     | tpch \n(9 rows)\n"
  },
  {
    "path": "drivers/testdata/trino.listTables.expected.txt",
    "content": "                   List of relations\n  Schema  |  Name  |    Type    | Rows | Size | Comment \n----------+--------+------------+------+------+---------\n sf1      | orders | BASE TABLE |    0 |      |  \n sf100    | orders | BASE TABLE |    0 |      |  \n sf1000   | orders | BASE TABLE |    0 |      |  \n sf10000  | orders | BASE TABLE |    0 |      |  \n sf100000 | orders | BASE TABLE |    0 |      |  \n sf300    | orders | BASE TABLE |    0 |      |  \n sf3000   | orders | BASE TABLE |    0 |      |  \n sf30000  | orders | BASE TABLE |    0 |      |  \n tiny     | orders | BASE TABLE |    0 |      |  \n(9 rows)\n"
  },
  {
    "path": "drivers/trino/reader.go",
    "content": "package trino\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers/metadata\"\n)\n\ntype metaReader struct {\n\tmetadata.LoggingReader\n}\n\nvar _ metadata.CatalogReader = &metaReader{}\nvar _ metadata.ColumnStatReader = &metaReader{}\n\nfunc (r metaReader) Catalogs(metadata.Filter) (*metadata.CatalogSet, error) {\n\tqstr := `SHOW catalogs`\n\trows, closeRows, err := r.Query(qstr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.Catalog{}\n\tfor rows.Next() {\n\t\trec := metadata.Catalog{}\n\t\terr = rows.Scan(&rec.Catalog)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\treturn metadata.NewCatalogSet(results), nil\n}\n\nfunc (r metaReader) ColumnStats(f metadata.Filter) (*metadata.ColumnStatSet, error) {\n\tnames := []string{}\n\tif f.Catalog != \"\" {\n\t\tnames = append(names, f.Catalog+\".\")\n\t}\n\tif f.Schema != \"\" {\n\t\tnames = append(names, f.Schema+\".\")\n\t}\n\tnames = append(names, f.Parent)\n\trows, closeRows, err := r.Query(fmt.Sprintf(\"SHOW STATS FOR %s\", strings.Join(names, \"\")))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeRows()\n\n\tresults := []metadata.ColumnStat{}\n\tfor rows.Next() {\n\t\trec := metadata.ColumnStat{Catalog: f.Catalog, Schema: f.Schema, Table: f.Parent}\n\t\tname := sql.NullString{}\n\t\tavgWidth := sql.NullInt32{}\n\t\tnumDistinct := sql.NullInt64{}\n\t\tnullFrac := sql.NullFloat64{}\n\t\tnumRows := sql.NullInt64{}\n\t\tmin := sql.NullString{}\n\t\tmax := sql.NullString{}\n\t\terr = rows.Scan(\n\t\t\t&name,\n\t\t\t&avgWidth,\n\t\t\t&numDistinct,\n\t\t\t&nullFrac,\n\t\t\t&numRows,\n\t\t\t&min,\n\t\t\t&max,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !name.Valid {\n\t\t\tcontinue\n\t\t}\n\t\trec.Name = name.String\n\t\tif avgWidth.Valid {\n\t\t\trec.AvgWidth = int(avgWidth.Int32)\n\t\t}\n\t\tif numDistinct.Valid {\n\t\t\trec.NumDistinct = numDistinct.Int64\n\t\t}\n\t\tif nullFrac.Valid {\n\t\t\trec.NullFrac = nullFrac.Float64\n\t\t}\n\t\tif min.Valid {\n\t\t\trec.Min = min.String\n\t\t}\n\t\tif max.Valid {\n\t\t\trec.Max = max.String\n\t\t}\n\t\tresults = append(results, rec)\n\t}\n\tif rows.Err() != nil {\n\t\treturn nil, rows.Err()\n\t}\n\n\treturn metadata.NewColumnStatSet(results), nil\n}\n"
  },
  {
    "path": "drivers/trino/trino.go",
    "content": "// Package trino defines and registers usql's Trino driver.\n//\n// See: https://github.com/trinodb/trino-go-client\npackage trino\n\nimport (\n\t\"context\"\n\t\"io\"\n\n\t_ \"github.com/trinodb/trino-go-client/trino\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\tinfos \"github.com/xo/usql/drivers/metadata/informationschema\"\n)\n\nfunc init() {\n\tnewReader := func(db drivers.DB, opts ...metadata.ReaderOption) metadata.Reader {\n\t\tir := infos.New(\n\t\t\tinfos.WithPlaceholder(func(int) string { return \"?\" }),\n\t\t\tinfos.WithCustomClauses(map[infos.ClauseName]string{\n\t\t\t\tinfos.ColumnsColumnSize:       \"0\",\n\t\t\t\tinfos.ColumnsNumericScale:     \"0\",\n\t\t\t\tinfos.ColumnsNumericPrecRadix: \"0\",\n\t\t\t\tinfos.ColumnsCharOctetLength:  \"0\",\n\t\t\t}),\n\t\t\tinfos.WithFunctions(false),\n\t\t\tinfos.WithSequences(false),\n\t\t\tinfos.WithIndexes(false),\n\t\t\tinfos.WithConstraints(false),\n\t\t\tinfos.WithColumnPrivileges(false),\n\t\t\tinfos.WithUsagePrivileges(false),\n\t\t)(db, opts...)\n\t\tmr := &metaReader{\n\t\t\tLoggingReader: metadata.NewLoggingReader(db, opts...),\n\t\t}\n\t\treturn metadata.NewPluginReader(ir, mr)\n\t}\n\tdrivers.Register(\"trino\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t\tProcess:                drivers.StripTrailingSemicolon,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\terr := db.QueryRowContext(\n\t\t\t\tctx,\n\t\t\t\t`SELECT node_version FROM system.runtime.nodes LIMIT 1`,\n\t\t\t).Scan(&ver)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"Trino \" + ver, nil\n\t\t},\n\t\tNewMetadataReader: newReader,\n\t\tNewMetadataWriter: func(db drivers.DB, w io.Writer, opts ...metadata.ReaderOption) metadata.Writer {\n\t\t\treturn metadata.NewDefaultWriter(newReader(db, opts...))(db, w)\n\t\t},\n\t\tCopy: drivers.CopyWithInsert(func(int) string { return \"?\" }),\n\t})\n}\n"
  },
  {
    "path": "drivers/vertica/vertica.go",
    "content": "// Package vertica defines and registers usql's Vertica driver.\n//\n// See: https://github.com/vertica/vertica-sql-go\npackage vertica\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"io\"\n\t\"net/url\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\n\tvertica \"github.com/vertica/vertica-sql-go\" // DRIVER\n\t\"github.com/vertica/vertica-sql-go/logger\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\t// turn off logging\n\tif os.Getenv(\"VERTICA_SQL_GO_LOG_LEVEL\") == \"\" {\n\t\tlogger.SetLogLevel(logger.NONE)\n\t}\n\n\terrCodeRE := regexp.MustCompile(`(?i)^\\[([0-9a-z]+)\\]\\s+(.+)`)\n\tdrivers.Register(\"vertica\", drivers.Driver{\n\t\tAllowDollar:            true,\n\t\tAllowMultilineComments: true,\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT version()`).Scan(&ver); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn ver, nil\n\t\t},\n\t\tOpen: func(_ context.Context, u *dburl.URL, stdout, stderr func() io.Writer) (func(string, string) (*sql.DB, error), error) {\n\t\t\treturn func(driver, dsn string) (*sql.DB, error) {\n\t\t\t\tu, err := url.Parse(dsn)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tq := u.Query()\n\t\t\t\tif name := q.Get(\"ca_path\"); name != \"\" {\n\t\t\t\t\tif q.Get(\"tlsmode\") != \"server-strict\" {\n\t\t\t\t\t\treturn nil, errors.New(\"tlsmode must be set to server-strict: ca_path is set\")\n\t\t\t\t\t}\n\t\t\t\t\tcfg := &tls.Config{\n\t\t\t\t\t\tServerName: u.Hostname(),\n\t\t\t\t\t}\n\t\t\t\t\tif err := addCA(name, cfg); err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tif err := vertica.RegisterTLSConfig(\"custom_tls_config\", cfg); err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\tq.Set(\"tlsmode\", \"custom_tls_config\")\n\t\t\t\t}\n\t\t\t\treturn sql.Open(driver, u.String())\n\t\t\t}, nil\n\t\t},\n\t\tChangePassword: func(db drivers.DB, user, newpw, _ string) error {\n\t\t\t_, err := db.Exec(`ALTER USER ` + user + ` IDENTIFIED BY '` + newpw + `'`)\n\t\t\treturn err\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tmsg := strings.TrimSpace(strings.TrimPrefix(err.Error(), \"Error:\"))\n\t\t\tif m := errCodeRE.FindAllStringSubmatch(msg, -1); m != nil {\n\t\t\t\treturn m[0][1], strings.TrimSpace(m[0][2])\n\t\t\t}\n\t\t\treturn \"\", msg\n\t\t},\n\t\tIsPasswordErr: func(err error) bool {\n\t\t\treturn strings.HasSuffix(strings.TrimSpace(err.Error()), \"Invalid username or password\")\n\t\t},\n\t})\n}\n\n// addCA adds the specified file name as a ca to the tls config.\nfunc addCA(name string, cfg *tls.Config) error {\n\tpool := x509.NewCertPool()\n\tswitch pem, err := os.ReadFile(name); {\n\tcase err != nil:\n\t\treturn err\n\tcase !pool.AppendCertsFromPEM(pem):\n\t\treturn errors.New(\"failed to append pem to cert pool\")\n\t}\n\tcfg.RootCAs = pool\n\treturn nil\n}\n"
  },
  {
    "path": "drivers/voltdb/voltdb.go",
    "content": "// Package voltdb defines and registers usql's VoltDB driver.\n//\n// See: https://github.com/VoltDB/voltdb-client-go\npackage voltdb\n\nimport (\n\t_ \"github.com/VoltDB/voltdb-client-go/voltdbclient\" // DRIVER\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tdrivers.Register(\"voltdb\", drivers.Driver{\n\t\tAllowMultilineComments: true,\n\t})\n}\n"
  },
  {
    "path": "drivers/ydb/ydb.go",
    "content": "// Package ydb defines and registers usql's YDB driver.\n//\n// See: https://github.com/ydb-platform/ydb-go-sdk\npackage ydb\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strconv\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/ydb-platform/ydb-go-sdk/v3\" // DRIVER\n)\n\nfunc init() {\n\tdrivers.Register(\"ydb\", drivers.Driver{\n\t\tVersion: func(ctx context.Context, db drivers.DB) (string, error) {\n\t\t\tvar ver string\n\t\t\tif err := db.QueryRowContext(ctx, `SELECT '<unknown>' AS version`).Scan(&ver); err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t\treturn \"YDB \" + ver, nil\n\t\t},\n\t\tErr: func(err error) (string, string) {\n\t\t\tvar e ydb.Error\n\t\t\tif errors.As(err, &e) {\n\t\t\t\treturn strconv.Itoa(int(e.Code())), e.Error()\n\t\t\t}\n\t\t\treturn \"\", err.Error()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "env/env.go",
    "content": "// Package env contains runtime environment variables for usql, along with\n// various helper funcs to determine the user's configuration.\npackage env\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/kenshaw/rasterm\"\n\t\"github.com/xo/dburl/passfile\"\n\t\"github.com/xo/usql/text\"\n)\n\n// vars are environment variables.\nvar vars *Variables\n\nfunc init() {\n\tvars = NewDefaultVars()\n}\n\n// Vars returns the environment variables.\nfunc Vars() *Variables {\n\treturn vars\n}\n\n// Get returns a standard variable.\nfunc Get(name string) string {\n\tvalue, _ := vars.Get(name)\n\treturn value\n}\n\n// Getenv tries retrieving successive keys from os environment variables.\nfunc Getenv(keys ...string) (string, bool) {\n\tm := make(map[string]string)\n\tfor _, v := range os.Environ() {\n\t\tif i := strings.Index(v, \"=\"); i != -1 {\n\t\t\tm[v[:i]] = v[i+1:]\n\t\t}\n\t}\n\tfor _, key := range keys {\n\t\tif v, ok := m[key]; ok {\n\t\t\treturn v, true\n\t\t}\n\t}\n\treturn \"\", false\n}\n\n// Chdir changes the current working directory to the specified path, or to the\n// user's home directory if path is not specified.\nfunc Chdir(u *user.User, path string) error {\n\tif path != \"\" {\n\t\tpath = passfile.Expand(u.HomeDir, path)\n\t} else {\n\t\tpath = u.HomeDir\n\t}\n\treturn os.Chdir(path)\n}\n\n// OpenFile opens a file for read (os.O_RDONLY), returning the full, expanded\n// path of the file. Callers are responsible for closing the returned file.\nfunc OpenFile(u *user.User, path string) (string, *os.File, error) {\n\tpath, err := filepath.EvalSymlinks(passfile.Expand(u.HomeDir, path))\n\tswitch {\n\tcase err != nil && os.IsNotExist(err):\n\t\treturn \"\", nil, text.ErrNoSuchFileOrDirectory\n\tcase err != nil:\n\t\treturn \"\", nil, err\n\t}\n\tfi, err := os.Stat(path)\n\tswitch {\n\tcase err != nil && os.IsNotExist(err):\n\t\treturn \"\", nil, text.ErrNoSuchFileOrDirectory\n\tcase err != nil:\n\t\treturn \"\", nil, err\n\tcase fi.IsDir():\n\t\treturn \"\", nil, text.ErrCannotIncludeDirectories\n\t}\n\tf, err := os.OpenFile(path, os.O_RDONLY, 0)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\treturn path, f, nil\n}\n\n// EditFile edits a file. If path is empty, then a temporary file will be\n// created.\nfunc EditFile(u *user.User, path, line string, buf []byte) ([]byte, error) {\n\ted, _ := vars.Get(\"EDITOR\")\n\tswitch {\n\tcase ed == \"\":\n\t\tif ed, _ = exec.LookPath(\"vi\"); ed == \"\" {\n\t\t\treturn nil, text.ErrNoEditorDefined\n\t\t}\n\tcase path != \"\":\n\t\tpath = passfile.Expand(u.HomeDir, path)\n\tdefault:\n\t\tf, err := os.CreateTemp(\"\", text.CommandLower()+\".*.sql\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpath = f.Name()\n\t\tif _, err = f.Write(append(bytes.TrimSuffix(buf, lineend), '\\n')); err != nil {\n\t\t\tf.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tif err = f.Close(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\t// setup args\n\targs := []string{path}\n\tif line != \"\" {\n\t\tif s, ok := Getenv(text.CommandUpper() + \"_EDITOR_LINENUMBER_ARG\"); ok {\n\t\t\targs = append(args, s+line)\n\t\t} else {\n\t\t\targs = append(args, \"+\"+line)\n\t\t}\n\t}\n\t// create command\n\tc := exec.Command(ed, args...)\n\tc.Stdin, c.Stdout, c.Stderr = os.Stdin, os.Stdout, os.Stderr\n\tif err := c.Run(); err != nil {\n\t\treturn nil, err\n\t}\n\t// read\n\tbuf, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn bytes.TrimSuffix(buf, lineend), nil\n}\n\n// HistoryFile returns the path to the history file.\n//\n// Defaults to ~/.<command name>_history, overridden by environment variable\n// <COMMAND NAME>_HISTORY (ie, ~/.usql_history and USQL_HISTORY).\nfunc HistoryFile(u *user.User) string {\n\tn := text.CommandUpper() + \"_HISTORY\"\n\tpath := \"~/.\" + strings.ToLower(n)\n\tif s, ok := Getenv(n); ok {\n\t\tpath = s\n\t}\n\treturn passfile.Expand(u.HomeDir, path)\n}\n\n// RCFile returns the path to the RC file.\n//\n// Defaults to ~/.<command name>rc, overridden by environment variable\n// <COMMAND NAME>RC (ie, ~/.usqlrc and USQLRC).\nfunc RCFile(u *user.User) string {\n\tn := text.CommandUpper() + \"RC\"\n\tpath := \"~/.\" + strings.ToLower(n)\n\tif s, ok := Getenv(n); ok {\n\t\tpath = s\n\t}\n\treturn passfile.Expand(u.HomeDir, path)\n}\n\n// Getshell returns the user's defined SHELL, or system default (if found on\n// path) and the appropriate command-line argument for the returned shell.\n//\n// Looks at the SHELL environment variable first, and then COMSPEC/ComSpec on\n// Windows. Defaults to sh on non-Windows systems, and to cmd.exe on Windows.\nfunc Getshell() (string, string) {\n\tshell, ok := Getenv(\"SHELL\")\n\tparam := \"-c\"\n\tif !ok && runtime.GOOS == \"windows\" {\n\t\tshell, _ = Getenv(\"COMSPEC\", \"ComSpec\")\n\t\tparam = \"/c\"\n\t}\n\t// look up path for \"cmd.exe\" if no other SHELL\n\tif shell == \"\" && runtime.GOOS == \"windows\" {\n\t\tshell, _ = exec.LookPath(\"cmd.exe\")\n\t\tif shell != \"\" {\n\t\t\tparam = \"/c\"\n\t\t}\n\t}\n\t// lookup path for \"sh\" if no other SHELL\n\tif shell == \"\" {\n\t\tshell, _ = exec.LookPath(\"sh\")\n\t\tif shell != \"\" {\n\t\t\tparam = \"-c\"\n\t\t}\n\t}\n\treturn shell, param\n}\n\n// Shell runs s as a shell. When s is empty the user's SHELL or COMSPEC is\n// used. See Getshell.\nfunc Shell(s string) error {\n\tshell, param := Getshell()\n\tif shell == \"\" {\n\t\treturn text.ErrNoShellAvailable\n\t}\n\ts = strings.TrimSpace(s)\n\tvar params []string\n\tif s != \"\" {\n\t\tparams = append(params, param, s)\n\t}\n\t// drop to shell\n\tcmd := exec.Command(shell, params...)\n\tcmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr\n\t_ = cmd.Run()\n\treturn nil\n}\n\n// Pipe starts a command and returns its input for writing.\nfunc Pipe(stdout, stderr io.Writer, c string) (io.WriteCloser, *exec.Cmd, error) {\n\tshell, param := Getshell()\n\tif shell == \"\" {\n\t\treturn nil, nil, text.ErrNoShellAvailable\n\t}\n\tcmd := exec.Command(shell, param, c)\n\tcmd.Stdout, cmd.Stderr = stdout, stderr\n\tout, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn out, cmd, cmd.Start()\n}\n\n// Exec executes s using the user's SHELL / COMSPEC with -c (or /c) and\n// returning the captured output. See Getshell.\n//\n// When SHELL or COMSPEC is not defined, then \"sh\" / \"cmd.exe\" will be used\n// instead, assuming it is found on the system's PATH.\nfunc Exec(s string) (string, error) {\n\ts = strings.TrimSpace(s)\n\tif s == \"\" {\n\t\treturn \"\", nil\n\t}\n\tshell, param := Getshell()\n\tif shell == \"\" {\n\t\treturn \"\", text.ErrNoShellAvailable\n\t}\n\tbuf, err := exec.Command(shell, param, s).CombinedOutput()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t// remove ending \\r\\n\n\tbuf = bytes.TrimSuffix(buf, lineend)\n\tbuf = bytes.TrimSuffix(buf, []byte{'\\r'})\n\treturn string(buf), nil\n}\n\n// Unquote unquotes a string.\nfunc Unquote(s string) (string, error) {\n\tswitch n := len(s); {\n\tcase n == 0:\n\t\treturn \"\", nil\n\tcase n < 2, s[n-1] != s[0], s[0] != '\\'' && s[0] != '\"' && s[0] != '`':\n\t\treturn \"\", text.ErrUnterminatedQuotedString\n\t}\n\tquote := s[0]\n\ts = s[1 : len(s)-1]\n\tif quote == '\\'' {\n\t\ts = cleanDoubleRE.ReplaceAllString(s, `$1\\'`)\n\t}\n\t// this is the last part of strconv.Unquote\n\tvar runeTmp [utf8.UTFMax]byte\n\tbuf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.\n\tfor len(s) > 0 {\n\t\tc, multibyte, ss, err := strconv.UnquoteChar(s, quote)\n\t\tswitch {\n\t\tcase err != nil && err == strconv.ErrSyntax:\n\t\t\treturn \"\", text.ErrInvalidQuotedString\n\t\tcase err != nil:\n\t\t\treturn \"\", err\n\t\t}\n\t\ts = ss\n\t\tif c < utf8.RuneSelf || !multibyte {\n\t\t\tbuf = append(buf, byte(c))\n\t\t} else {\n\t\t\tn := utf8.EncodeRune(runeTmp[:], c)\n\t\t\tbuf = append(buf, runeTmp[:n]...)\n\t\t}\n\t}\n\treturn string(buf), nil\n}\n\n// Untick returns a func that unquotes and unticks strings for the user.\n//\n// When exec is true, backtick'd strings will be executed using the provided\n// user's shell (see Exec).\nfunc Untick(u *user.User, v *Variables, exec bool) func(string, bool) (string, bool, error) {\n\treturn func(s string, isvar bool) (string, bool, error) {\n\t\t// fmt.Fprintf(os.Stderr, \"untick: %q\\n\", s)\n\t\tswitch {\n\t\tcase isvar:\n\t\t\tvalue, ok := v.Get(s)\n\t\t\treturn value, ok, nil\n\t\tcase len(s) < 2:\n\t\t\treturn \"\", false, text.ErrInvalidQuotedString\n\t\t}\n\t\tc := s[0]\n\t\tz, err := Unquote(s)\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn \"\", false, err\n\t\tcase c == '\\'', c == '\"':\n\t\t\treturn z, true, nil\n\t\tcase c != '`':\n\t\t\treturn \"\", false, text.ErrInvalidQuotedString\n\t\tcase !exec:\n\t\t\treturn z, true, nil\n\t\t}\n\t\tres, err := Exec(z)\n\t\tif err != nil {\n\t\t\treturn \"\", false, err\n\t\t}\n\t\treturn res, true, nil\n\t}\n}\n\n// Quote quotes a string.\nfunc Quote(s string) string {\n\ts = strconv.QuoteToGraphic(s)\n\treturn \"'\" + s[1:len(s)-1] + \"'\"\n}\n\n// TermGraphics returns the [rasterm.TermType] based on TERM_GRAPHICS\n// environment variable.\nfunc TermGraphics() rasterm.TermType {\n\tvar typ rasterm.TermType\n\ts, _ := vars.Get(\"TERM_GRAPHICS\")\n\t_ = typ.UnmarshalText([]byte(s))\n\treturn typ\n}\n\n// ValidIdentifier returns an error when n is not a valid identifier.\nfunc ValidIdentifier(n string) error {\n\tr := []rune(n)\n\trlen := len(r)\n\tif rlen < 1 {\n\t\treturn text.ErrInvalidIdentifier\n\t}\n\tfor i := 0; i < rlen; i++ {\n\t\tif c := r[i]; c != '_' && !unicode.IsLetter(c) && !unicode.IsNumber(c) {\n\t\t\treturn text.ErrInvalidIdentifier\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc ParseBool(value, name string) (string, error) {\n\tswitch strings.ToLower(value) {\n\tcase \"1\", \"t\", \"tr\", \"tru\", \"true\", \"on\":\n\t\treturn \"on\", nil\n\tcase \"0\", \"f\", \"fa\", \"fal\", \"fals\", \"false\", \"of\", \"off\":\n\t\treturn \"off\", nil\n\t}\n\treturn \"\", fmt.Errorf(text.FormatFieldInvalidValue, value, name, \"Boolean\")\n}\n\nfunc ParseKeywordBool(value, name string, keywords ...string) (string, error) {\n\tv := strings.ToLower(value)\n\tswitch v {\n\tcase \"1\", \"t\", \"tr\", \"tru\", \"true\", \"on\":\n\t\treturn \"on\", nil\n\tcase \"0\", \"f\", \"fa\", \"fal\", \"fals\", \"false\", \"of\", \"off\":\n\t\treturn \"off\", nil\n\t}\n\tfor _, k := range keywords {\n\t\tif v == k {\n\t\t\treturn v, nil\n\t\t}\n\t}\n\treturn \"\", fmt.Errorf(text.FormatFieldInvalid, value, name)\n}\n\n// lineend is the line ending.\nvar lineend = []byte{'\\n'}\n\n// cleanDoubleRE matches double quotes.\nvar cleanDoubleRE = regexp.MustCompile(`(^|[^\\\\])''`)\n"
  },
  {
    "path": "env/list.go",
    "content": "package env\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n\t\"unicode\"\n\n\t\"github.com/xo/usql/text\"\n\t\"github.com/yookoala/realpath\"\n)\n\n// Listing writes a formatted listing of the special environment variables to\n// w, separated in sections based on variable type.\nfunc Listing(w io.Writer) error {\n\tvarsWithDesc := make([]string, len(varNames))\n\tfor i, v := range varNames {\n\t\tvarsWithDesc[i] = v.String()\n\t}\n\tpvarsWithDesc := make([]string, len(pvarNames))\n\tfor i, v := range pvarNames {\n\t\tpvarsWithDesc[i] = v.String()\n\t}\n\n\t// determine config dir name\n\tconfigDir, configExtra := buildConfigDir(\"config.yaml\")\n\n\t// environment var names\n\tconfigDesc := configDir\n\tif configExtra != \"\" {\n\t\tconfigDesc = configExtra\n\t}\n\tev := []varName{\n\t\t{\n\t\t\ttext.CommandUpper() + \"_CONFIG\",\n\t\t\tfmt.Sprintf(`config file path (default %q)`, configDesc),\n\t\t},\n\t}\n\tenvVarsWithDesc := make([]string, len(envVarNames)+1)\n\tfor i, v := range append(ev, envVarNames...) {\n\t\tenvVarsWithDesc[i] = v.String()\n\t}\n\n\tif configExtra != \"\" {\n\t\tconfigExtra = \" (\" + configExtra + \")\"\n\t}\n\tfmt.Fprintf(\n\t\tw,\n\t\tvariableTpl,\n\t\ttext.CommandName,\n\t\tstrings.TrimRightFunc(strings.Join(varsWithDesc, \"\"), unicode.IsSpace),\n\t\tstrings.TrimRightFunc(strings.Join(pvarsWithDesc, \"\"), unicode.IsSpace),\n\t\tstrings.TrimRightFunc(strings.Join(envVarsWithDesc, \"\"), unicode.IsSpace),\n\t\tconfigDir,\n\t\tconfigExtra,\n\t)\n\treturn nil\n}\n\nfunc buildConfigDir(configName string) (string, string) {\n\tdir := `$HOME/.config/usql`\n\tswitch runtime.GOOS {\n\tcase \"darwin\":\n\t\tdir = `$HOME/Library/Application Support`\n\tcase \"windows\":\n\t\tdir = `%AppData%\\usql`\n\t}\n\tconfigDir, err := os.UserConfigDir()\n\tif err != nil {\n\t\treturn filepath.Join(dir, configName), \"\"\n\t}\n\tif configDir, err = realpath.Realpath(configDir); err != nil {\n\t\treturn filepath.Join(dir, configName), \"\"\n\t}\n\treturn filepath.Join(dir, configName), filepath.Join(configDir, \"usql\", configName)\n}\n\ntype varName struct {\n\tname string\n\tdesc string\n}\n\nfunc (v varName) String() string {\n\treturn fmt.Sprintf(\"  %s\\n    %s\\n\", v.name, v.desc)\n}\n\nvar varNames = []varName{\n\t{\n\t\t`ECHO_HIDDEN`,\n\t\t`if set, display internal queries executed by backslash commands; if set to \"noexec\", shows queries without execution`,\n\t},\n\t{\n\t\t`ON_ERROR_STOP`,\n\t\t`stop batch execution after error`,\n\t},\n\t{\n\t\t`PROMPT1`,\n\t\t`specifies the standard ` + text.CommandName + ` prompt`,\n\t},\n\t{\n\t\t`QUIET`,\n\t\t`run quietly (same as -q option)`,\n\t},\n\t{\n\t\t`ROW_COUNT`,\n\t\t`number of rows returned or affected by last query, or 0`,\n\t},\n}\n\nvar (\n\tformatRE    = regexp.MustCompile(`^(unaligned|aligned|wrapped|html|asciidoc|latex|latex-longtable|troff-ms|csv|json|vertical)$`)\n\tlinestlyeRE = regexp.MustCompile(`^(ascii|old-ascii|unicode)$`)\n\tborderRE    = regexp.MustCompile(`^(single|double)$`)\n)\n\nvar pvarNames = []varName{\n\t{\n\t\t`border`,\n\t\t`border style (number)`,\n\t},\n\t{\n\t\t`columns`,\n\t\t`target width for the wrapped format`,\n\t},\n\t{\n\t\t`csv_fieldsep`,\n\t\t`field separator for CSV output (default \",\")`,\n\t},\n\t{\n\t\t`expanded`,\n\t\t`expanded output [on, off, auto]`,\n\t},\n\t{\n\t\t`fieldsep`,\n\t\t`field separator for unaligned output (default \"|\")`,\n\t},\n\t{\n\t\t`fieldsep_zero`,\n\t\t`set field separator for unaligned output to a zero byte`,\n\t},\n\t{\n\t\t`footer`,\n\t\t`enable or disable display of the table footer [on, off]`,\n\t},\n\t{\n\t\t`format`,\n\t\t`set output format [unaligned, aligned, wrapped, vertical, html, asciidoc, csv, json, ...]`,\n\t},\n\t{\n\t\t`linestyle`,\n\t\t`set the border line drawing style [ascii, old-ascii, unicode]`,\n\t},\n\t{\n\t\t`null`,\n\t\t`set the string to be printed in place of a null value`,\n\t},\n\t{\n\t\t`numericlocale`,\n\t\t`enable display of a locale-specific character to separate groups of digits`,\n\t},\n\t{\n\t\t`pager_min_lines`,\n\t\t`minimum number of lines required in the output to use a pager, 0 to disable (default 0)`,\n\t},\n\t{\n\t\t`pager`,\n\t\t`control when an external pager is used [on, off, always]`,\n\t},\n\t{\n\t\t`recordsep`,\n\t\t`record (line) separator for unaligned output`,\n\t},\n\t{\n\t\t`recordsep_zero`,\n\t\t`set record separator for unaligned output to a zero byte`,\n\t},\n\t{\n\t\t`tableattr`,\n\t\t`specify attributes for table tag in html format, or proportional column widths for left-aligned data types in latex-longtable format`,\n\t},\n\t{\n\t\t`time`,\n\t\t`format used to display time/date column values (default RFC3339Nano)`,\n\t},\n\t{\n\t\t`timezone`,\n\t\t`the timezone to display dates in (default \"\")`,\n\t},\n\t{\n\t\t`title`,\n\t\t`set the table title for subsequently printed tables`,\n\t},\n\t{\n\t\t`tuples_only`,\n\t\t`if set, only actual table data is shown`,\n\t},\n\t{\n\t\t`unicode_border_linestyle`,\n\t\t`set the style of Unicode line drawing [single, double]`,\n\t},\n\t{\n\t\t`unicode_column_linestyle`,\n\t\t`set the style of Unicode line drawing [single, double]`,\n\t},\n\t{\n\t\t`unicode_header_linestyle`,\n\t\t`set the style of Unicode line drawing [single, double]`,\n\t},\n}\n\nvar envVarNames = []varName{\n\t{\n\t\ttext.CommandUpper() + `_EDITOR, EDITOR, VISUAL`,\n\t\t`editor used by the \\e, \\ef, and \\ev commands`,\n\t},\n\t{\n\t\ttext.CommandUpper() + `_EDITOR_LINENUMBER_ARG`,\n\t\t`how to specify a line number when invoking the editor`,\n\t},\n\t{\n\t\ttext.CommandUpper() + `_HISTORY`,\n\t\t`alternative location for the command history file`,\n\t},\n\t{\n\t\ttext.CommandUpper() + `_PAGER, PAGER`,\n\t\t`name of external pager program`,\n\t},\n\t{\n\t\ttext.CommandUpper() + `_SHOW_HOST_INFORMATION`,\n\t\t`display host information when connecting to a database`,\n\t},\n\t{\n\t\ttext.CommandUpper() + `RC`,\n\t\t`alternative location for the user's .usqlrc file`,\n\t},\n\t{\n\t\ttext.CommandUpper() + `_SSLMODE, SSLMODE`,\n\t\t`when set to 'retry', allows connections to attempt to reconnect when no ?sslmode= was specified on the url`,\n\t},\n\t{\n\t\t`SYNTAX_HL`,\n\t\t`enable syntax highlighting`,\n\t},\n\t{\n\t\t`SYNTAX_HL_FORMAT`,\n\t\t`chroma library formatter name`,\n\t},\n\t{\n\t\t`SYNTAX_HL_STYLE`,\n\t\t`chroma library style name (default \"monokai\")`,\n\t},\n\t{\n\t\t`SYNTAX_HL_OVERRIDE_BG`,\n\t\t`enables overriding the background color of the chroma styles`,\n\t},\n\t{\n\t\t`TERM_GRAPHICS`,\n\t\t`use the specified terminal graphics`,\n\t},\n\t{\n\t\t`SHELL`,\n\t\t`shell used by the \\! command`,\n\t},\n}\n\nconst variableTpl = `List of specially treated variables\n\n%s variables:\nUsage:\n  %[1]s --set=NAME=VALUE\n  or \\set NAME VALUE inside %[1]s\n\n%[2]s\n\nDisplay settings:\nUsage:\n  %[1]s --pset=NAME[=VALUE]\n  or \\pset NAME [VALUE] inside %[1]s\n\n%[3]s\n\nEnvironment variables:\nUsage:\n  NAME=VALUE [NAME=VALUE] %[1]s ...\n  or \\setenv NAME [VALUE] inside %[1]s\n\n%[4]s\n\nConnection variables:\nUsage:\n  %[1]s --cset NAME[=DSN]\n  or \\cset NAME [DSN] inside %[1]s\n  or \\cset NAME DRIVER PARAMS... inside %[1]s\n  or define in %[5]s%[6]s\n`\n"
  },
  {
    "path": "env/vars.go",
    "content": "package env\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"os/exec\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tsyslocale \"github.com/jeandeaual/go-locale\"\n\t\"github.com/xo/terminfo\"\n\t\"github.com/xo/usql/text\"\n)\n\n// Variables handles the standard, print, and connection variables.\ntype Variables struct {\n\t// vars holds standard variables.\n\tvars map[string]string\n\t// prnt holds print variables (\"print\" is a reserved word).\n\tprnt map[string]string\n\t// conn holds connection variables.\n\tconn map[string][]string\n}\n\n// NewVars creates a set of empty variables.\nfunc NewVars() *Variables {\n\treturn &Variables{\n\t\tvars: make(map[string]string),\n\t\tprnt: make(map[string]string),\n\t\tconn: make(map[string][]string),\n\t}\n}\n\n// NewDefaultVars creates standard, print, and connection variables, based on\n// environment variables.\nfunc NewDefaultVars() *Variables {\n\tcmdNameUpper := strings.ToUpper(text.CommandName)\n\t// get USQL_* variables\n\tshowHostInformation := \"true\"\n\tif v, _ := Getenv(cmdNameUpper + \"_SHOW_HOST_INFORMATION\"); v != \"\" {\n\t\tshowHostInformation = v\n\t}\n\t// get NO_COLOR\n\tnoColor := false\n\tif s, ok := Getenv(\"NO_COLOR\"); ok {\n\t\tnoColor = s != \"0\" && s != \"false\" && s != \"off\"\n\t}\n\t// get color level\n\tcolorLevel, _ := terminfo.ColorLevelFromEnv()\n\tenableSyntaxHL := \"true\"\n\tif noColor || colorLevel < terminfo.ColorLevelBasic {\n\t\tenableSyntaxHL = \"false\"\n\t}\n\t// pager\n\tpagerCmd, ok := Getenv(cmdNameUpper+\"_PAGER\", \"PAGER\")\n\tpager := \"off\"\n\tif !ok {\n\t\tfor _, s := range []string{\"less\", \"more\"} {\n\t\t\tif _, err := exec.LookPath(s); err == nil {\n\t\t\t\tpagerCmd = s\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif pagerCmd != \"\" {\n\t\tpager = \"on\"\n\t}\n\t// editor\n\teditorCmd, _ := Getenv(cmdNameUpper+\"_EDITOR\", \"EDITOR\", \"VISUAL\")\n\t// sslmode\n\tsslmode, ok := Getenv(cmdNameUpper+\"_SSLMODE\", \"SSLMODE\")\n\tif !ok {\n\t\tsslmode = \"retry\"\n\t}\n\t// determine locale\n\tlocale := \"en-US\"\n\tif s, err := syslocale.GetLocale(); err == nil {\n\t\tlocale = s\n\t}\n\treturn &Variables{\n\t\tvars: map[string]string{\n\t\t\t// usql related logic\n\t\t\t\"SHOW_HOST_INFORMATION\": showHostInformation,\n\t\t\t\"PAGER\":                 pagerCmd,\n\t\t\t\"EDITOR\":                editorCmd,\n\t\t\t\"QUIET\":                 \"off\",\n\t\t\t\"ON_ERROR_STOP\":         \"off\",\n\t\t\t// prompts\n\t\t\t\"PROMPT1\": \"%S%N%m%/%R%# \",\n\t\t\t// syntax highlighting variables\n\t\t\t\"SYNTAX_HL\":             enableSyntaxHL,\n\t\t\t\"SYNTAX_HL_FORMAT\":      colorLevel.ChromaFormatterName(),\n\t\t\t\"SYNTAX_HL_STYLE\":       \"monokai\",\n\t\t\t\"SYNTAX_HL_OVERRIDE_BG\": \"true\",\n\t\t\t\"SSLMODE\":               sslmode,\n\t\t\t\"TERM_GRAPHICS\":         \"none\",\n\t\t},\n\t\tprnt: map[string]string{\n\t\t\t\"border\":                   \"1\",\n\t\t\t\"columns\":                  \"0\",\n\t\t\t\"csv_fieldsep\":             \",\",\n\t\t\t\"expanded\":                 \"off\",\n\t\t\t\"fieldsep\":                 \"|\",\n\t\t\t\"fieldsep_zero\":            \"off\",\n\t\t\t\"footer\":                   \"on\",\n\t\t\t\"format\":                   \"aligned\",\n\t\t\t\"linestyle\":                \"ascii\",\n\t\t\t\"locale\":                   locale,\n\t\t\t\"null\":                     \"\",\n\t\t\t\"numericlocale\":            \"off\",\n\t\t\t\"pager_min_lines\":          \"0\",\n\t\t\t\"pager\":                    pager,\n\t\t\t\"recordsep\":                \"\\n\",\n\t\t\t\"recordsep_zero\":           \"off\",\n\t\t\t\"tableattr\":                \"\",\n\t\t\t\"time\":                     \"RFC3339Nano\",\n\t\t\t\"timezone\":                 \"\",\n\t\t\t\"title\":                    \"\",\n\t\t\t\"tuples_only\":              \"off\",\n\t\t\t\"unicode_border_linestyle\": \"single\",\n\t\t\t\"unicode_column_linestyle\": \"single\",\n\t\t\t\"unicode_header_linestyle\": \"single\",\n\t\t},\n\t\tconn: make(map[string][]string),\n\t}\n}\n\n// Vars returns a copy of the standard variables.\nfunc (v *Variables) Vars() map[string]string {\n\treturn maps.Clone(v.vars)\n}\n\n// Print returns a copy of the print variables.\nfunc (v *Variables) Print() map[string]string {\n\treturn maps.Clone(v.prnt)\n}\n\n// Conn returns a copy of the connection variables.\nfunc (v *Variables) Conn() map[string][]string {\n\treturn maps.Clone(v.conn)\n}\n\n// Get retrieves a standard variable.\nfunc (v *Variables) Get(name string) (string, bool) {\n\tvalue, ok := v.vars[name]\n\treturn value, ok\n}\n\n// Set sets a standard variable.\nfunc (v *Variables) Set(name, value string) error {\n\tif err := ValidIdentifier(name); err != nil {\n\t\treturn err\n\t}\n\tswitch name {\n\tcase \"ON_ERROR_STOP\", \"QUIET\":\n\t\tif value == \"\" {\n\t\t\tvalue = \"on\"\n\t\t} else {\n\t\t\tvar err error\n\t\t\tif value, err = ParseBool(value, name); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tv.vars[name] = value\n\treturn nil\n}\n\n// Unset unsets a standard variable.\nfunc (v *Variables) Unset(name string) error {\n\tif err := ValidIdentifier(name); err != nil {\n\t\treturn err\n\t}\n\tdelete(v.vars, name)\n\treturn nil\n}\n\n// Dump dumps the standard variables to w.\nfunc (v *Variables) Dump(w io.Writer) error {\n\tfor _, k := range slices.Sorted(maps.Keys(v.vars)) {\n\t\t_, _ = fmt.Fprintln(w, k, \"=\", Quote(v.vars[k]))\n\t}\n\treturn nil\n}\n\n// GetPrint returns a print variable.\nfunc (v *Variables) GetPrint(name string) (string, error) {\n\tif val, ok := v.prnt[name]; ok {\n\t\treturn val, nil\n\t}\n\treturn \"\", fmt.Errorf(text.UnknownFormatFieldName, name)\n}\n\n// SetPrint sets a print variable.\nfunc (v *Variables) SetPrint(name, value string) (string, error) {\n\tif _, ok := v.prnt[name]; !ok {\n\t\treturn \"\", fmt.Errorf(text.UnknownFormatFieldName, name)\n\t}\n\tswitch name {\n\tcase \"border\", \"columns\", \"pager_min_lines\":\n\t\ti, _ := strconv.Atoi(value)\n\t\tv.prnt[name] = fmt.Sprintf(\"%d\", i)\n\tcase \"pager\":\n\t\ts, err := ParseKeywordBool(value, name, \"always\")\n\t\tif err != nil {\n\t\t\treturn \"\", text.ErrInvalidFormatPagerType\n\t\t}\n\t\tv.prnt[name] = s\n\tcase \"expanded\":\n\t\ts, err := ParseKeywordBool(value, name, \"auto\")\n\t\tif err != nil {\n\t\t\treturn \"\", text.ErrInvalidFormatExpandedType\n\t\t}\n\t\tv.prnt[name] = s\n\tcase \"fieldsep_zero\", \"footer\", \"numericlocale\", \"recordsep_zero\", \"tuples_only\":\n\t\ts, err := ParseBool(value, name)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tv.prnt[name] = s\n\tcase \"format\":\n\t\tif !formatRE.MatchString(value) {\n\t\t\treturn \"\", text.ErrInvalidFormatType\n\t\t}\n\t\tv.prnt[name] = value\n\tcase \"linestyle\":\n\t\tif !linestlyeRE.MatchString(value) {\n\t\t\treturn \"\", text.ErrInvalidFormatLineStyle\n\t\t}\n\t\tv.prnt[name] = value\n\tcase \"csv_fieldsep\", \"fieldsep\", \"null\", \"recordsep\", \"tableattr\", \"time\", \"title\", \"locale\":\n\t\tv.prnt[name] = value\n\tcase \"timezone\":\n\t\tif _, err := time.LoadLocation(value); err != nil {\n\t\t\treturn \"\", text.ErrInvalidTimezoneLocation\n\t\t}\n\t\tv.prnt[name] = value\n\tcase \"unicode_border_linestyle\", \"unicode_column_linestyle\", \"unicode_header_linestyle\":\n\t\tif !borderRE.MatchString(value) {\n\t\t\treturn \"\", text.ErrInvalidFormatBorderLineStyle\n\t\t}\n\t\tv.prnt[name] = value\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"field %s was defined in the print variables, but not in switch\", name))\n\t}\n\treturn v.prnt[name], nil\n}\n\n// TogglePrint toggles a print variable.\nfunc (v *Variables) TogglePrint(name, extra string) (string, error) {\n\tif _, ok := v.prnt[name]; !ok {\n\t\treturn \"\", fmt.Errorf(text.UnknownFormatFieldName, name)\n\t}\n\tswitch name {\n\tcase \"border\", \"columns\", \"pager_min_lines\":\n\tcase \"pager\":\n\t\tswitch v.prnt[name] {\n\t\tcase \"on\", \"always\":\n\t\t\tv.prnt[name] = \"off\"\n\t\tcase \"off\":\n\t\t\tv.prnt[name] = \"on\"\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"invalid state for field %s\", name))\n\t\t}\n\tcase \"expanded\":\n\t\tswitch v.prnt[name] {\n\t\tcase \"on\", \"auto\":\n\t\t\tv.prnt[name] = \"off\"\n\t\tcase \"off\":\n\t\t\tv.prnt[name] = \"on\"\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"invalid state for field %s\", name))\n\t\t}\n\tcase \"fieldsep_zero\", \"footer\", \"numericlocale\", \"recordsep_zero\", \"tuples_only\":\n\t\tswitch v.prnt[name] {\n\t\tcase \"on\":\n\t\t\tv.prnt[name] = \"off\"\n\t\tcase \"off\":\n\t\t\tv.prnt[name] = \"on\"\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"invalid state for field %s\", name))\n\t\t}\n\tcase \"format\":\n\t\tswitch {\n\t\tcase extra != \"\" && v.prnt[name] != extra:\n\t\t\tv.prnt[name] = extra\n\t\tcase v.prnt[name] == \"aligned\":\n\t\t\tv.prnt[name] = \"unaligned\"\n\t\tdefault:\n\t\t\tv.prnt[name] = \"aligned\"\n\t\t}\n\tcase \"linestyle\":\n\tcase \"csv_fieldsep\", \"fieldsep\", \"null\", \"recordsep\", \"time\", \"timezone\", \"locale\":\n\tcase \"tableattr\", \"title\":\n\t\tv.prnt[name] = \"\"\n\tcase \"unicode_border_linestyle\", \"unicode_column_linestyle\", \"unicode_header_linestyle\":\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"field %s was defined in the print variables, but not in switch\", name))\n\t}\n\treturn v.prnt[name], nil\n}\n\n// DumpPrint dumps the print variables to w.\nfunc (v *Variables) DumpPrint(w io.Writer) error {\n\twidth, keys := 0, maps.Keys(v.prnt)\n\tfor k := range keys {\n\t\twidth = max(len(k), width)\n\t}\n\tfor _, k := range slices.Sorted(keys) {\n\t\tval := v.prnt[k]\n\t\tswitch k {\n\t\tcase \"csv_fieldsep\", \"fieldsep\", \"recordsep\", \"null\":\n\t\t\tval = strconv.QuoteToASCII(val)\n\t\tcase \"tableattr\", \"title\":\n\t\t\tif val != \"\" {\n\t\t\t\tval = strconv.QuoteToASCII(val)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintf(w, \"%-*s %s\\n\", width, k, val)\n\t\t// k+strings.Repeat(\" \", width-len(k)), val)\n\t}\n\treturn nil\n}\n\n// PrintTimeFormat returns the user's time format converted to Go's time.Format\n// value.\nfunc (v *Variables) PrintTimeFormat() string {\n\ttfmt := v.prnt[\"time\"]\n\tif s, ok := timeConsts[tfmt]; ok {\n\t\treturn s\n\t}\n\treturn tfmt\n}\n\n// SetConn sets a named connection variable.\nfunc (v *Variables) SetConn(name string, vals ...string) error {\n\tif err := ValidIdentifier(name); err != nil {\n\t\treturn err\n\t}\n\tif _, ok := v.conn[name]; len(vals) == 0 || vals[0] == \"\" && ok {\n\t\tdelete(v.conn, name)\n\t} else {\n\t\tv.conn[name] = slices.Clone(vals)\n\t}\n\treturn nil\n}\n\n// GetConn returns a connection variable.\nfunc (v *Variables) GetConn(name string) ([]string, bool) {\n\tvals, ok := v.conn[name]\n\tif !ok {\n\t\treturn nil, false\n\t}\n\treturn slices.Clone(vals), true\n}\n\n// DumpConn dumps the connection variables to w.\nfunc (v *Variables) DumpConn(w io.Writer) error {\n\tfor _, k := range slices.Sorted(maps.Keys(v.conn)) {\n\t\tfmt.Fprintln(w, k, \"=\", Quote(strings.Join(v.conn[k], \" \")))\n\t}\n\treturn nil\n}\n\n// timeConsts are well known time consts.\nvar timeConsts = map[string]string{\n\t\"ANSIC\":       time.ANSIC,\n\t\"UnixDate\":    time.UnixDate,\n\t\"RubyDate\":    time.RubyDate,\n\t\"RFC822\":      time.RFC822,\n\t\"RFC822Z\":     time.RFC822Z,\n\t\"RFC850\":      time.RFC850,\n\t\"RFC1123\":     time.RFC1123,\n\t\"RFC1123Z\":    time.RFC1123Z,\n\t\"RFC3339\":     time.RFC3339,\n\t\"RFC3339Nano\": time.RFC3339Nano,\n\t\"Kitchen\":     time.Kitchen,\n\t\"Stamp\":       time.Stamp,\n\t\"StampMilli\":  time.StampMilli,\n\t\"StampMicro\":  time.StampMicro,\n\t\"StampNano\":   time.StampNano,\n}\n\n/*\n// Get retrieves a standard variable.\nfunc (v *Vars) Get(s string) (string, bool, error) {\nfunc (v *Vars) Unquote()\n\tq, n := \"\", s\n\tif c := s[0]; c == '\\'' || c == '\"' {\n\t\tvar err error\n\t\tif n, err = Unquote(s); err != nil {\n\t\t\treturn \"\", false, err\n\t\t}\n\t\tq = string(c)\n\t}\n\tif val, ok := v.v[n]; ok {\n\t\treturn q + val + q, true, nil\n\t}\n\treturn s, false, nil\n*/\n"
  },
  {
    "path": "gen.go",
    "content": "//go:build ignore\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/format\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\n\t\"github.com/mattn/go-runewidth\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/yookoala/realpath\"\n)\n\nfunc main() {\n\tlicenseStart := flag.Int(\"license-start\", 2015, \"license start year\")\n\tlicenseAuthor := flag.String(\"license-author\", \"Kenneth Shaw\", \"license author\")\n\tdburlGen := flag.Bool(\"dburl-gen\", false, \"enable dburl generation\")\n\tdburlDir := flag.String(\"dburl-dir\", getDburlDir(), \"dburl dir\")\n\tdburlLicenseStart := flag.Int(\"dburl-license-start\", 2015, \"dburl license start year\")\n\tflag.Parse()\n\tif err := run(*licenseStart, *licenseAuthor, *dburlGen, *dburlDir, *dburlLicenseStart); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc run(licenseStart int, licenseAuthor string, dburlGen bool, dburlDir string, dburlLicenseStart int) error {\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := loadDrivers(filepath.Join(wd, \"drivers\")); err != nil {\n\t\treturn err\n\t}\n\tif err := loadCommands(filepath.Join(wd, \"metacmd\", \"cmds.go\")); err != nil {\n\t\treturn err\n\t}\n\tif err := writeInternal(filepath.Join(wd, \"internal\"), baseDrivers, mostDrivers, allDrivers, badDrivers); err != nil {\n\t\treturn err\n\t}\n\tif err := writeReadme(wd, true); err != nil {\n\t\treturn err\n\t}\n\tif err := writeLicenseFiles(licenseStart, licenseAuthor); err != nil {\n\t\treturn err\n\t}\n\tif err := writeCommands(filepath.Join(wd, \"metacmd\", \"descs.go\")); err != nil {\n\t\treturn err\n\t}\n\tif dburlGen {\n\t\tif err := writeReadme(dburlDir, false); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := writeDburlLicense(dburlDir, dburlLicenseStart, licenseAuthor); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// loadDrivers loads the driver descriptions.\nfunc loadDrivers(wd string) error {\n\tskipDirs := []string{\"completer\", \"metadata\"}\n\terr := fs.WalkDir(os.DirFS(wd), \".\", func(n string, d fs.DirEntry, err error) error {\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase d.IsDir():\n\t\t\treturn nil\n\t\t}\n\t\tm := dirRE.FindAllStringSubmatch(n, -1)\n\t\tif m == nil || m[0][1] != m[0][2] || slices.Contains(skipDirs, m[0][1]) {\n\t\t\treturn nil\n\t\t}\n\t\ttag, dest := m[0][1], mostDrivers\n\t\tdriver, err := parseDriverInfo(tag, filepath.Join(wd, n))\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase driver.Group == \"base\":\n\t\t\tdest = baseDrivers\n\t\tcase driver.Group == \"most\":\n\t\tcase driver.Group == \"all\":\n\t\t\tdest = allDrivers\n\t\tcase driver.Group == \"bad\":\n\t\t\tdest = badDrivers\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"driver %s has invalid group %q\", tag, driver.Group)\n\t\t}\n\t\tdest[tag] = driver\n\t\tif dest[tag].Aliases != nil {\n\t\t\tfor _, alias := range dest[tag].Aliases {\n\t\t\t\twireDrivers[alias[0]] = DriverInfo{\n\t\t\t\t\tTag:    tag,\n\t\t\t\t\tDriver: alias[0],\n\t\t\t\t\tPkg:    dest[tag].Pkg,\n\t\t\t\t\tDesc:   alias[1],\n\t\t\t\t\tWire:   true,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\treturn err\n}\n\n// loadCommands loads command descriptions.\nfunc loadCommands(name string) error {\n\tf, err := parser.ParseFile(token.NewFileSet(), name, nil, parser.ParseComments)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcmds = make(map[string][]desc)\n\tfor _, d := range f.Decls {\n\t\tsection, descs, ok, err := decodeCommand(d)\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase !ok:\n\t\t\tcontinue\n\t\t}\n\t\tcmds[section] = append(cmds[section], descs...)\n\t}\n\t// check all sections have at least one command\n\tfor _, section := range sections {\n\t\tif descs, ok := cmds[section]; !ok || len(descs) == 0 {\n\t\t\treturn fmt.Errorf(\"section %q has no meta commands\", section)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc writeInternal(wd string, drivers ...map[string]DriverInfo) error {\n\t// build known build tags\n\tvar known []DriverInfo\n\tfor _, m := range drivers {\n\t\tfor _, v := range m {\n\t\t\tknown = append(known, v)\n\t\t}\n\t}\n\tsort.Slice(known, func(i, j int) bool {\n\t\treturn known[i].Tag < known[j].Tag\n\t})\n\tknownStr := \"\"\n\tfor _, v := range known {\n\t\tknownStr += fmt.Sprintf(\"\\n%q: %q, // %s\", v.Tag, v.Driver, v.Pkg)\n\t}\n\t// format and write internal.go\n\tbuf, err := format.Source([]byte(fmt.Sprintf(internalGo, knownStr)))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := os.WriteFile(filepath.Join(wd, \"internal.go\"), buf, 0o644); err != nil {\n\t\treturn err\n\t}\n\t// write <tag>.go\n\tfor _, v := range known {\n\t\tvar tags string\n\t\tswitch v.Group {\n\t\tcase \"base\":\n\t\t\ttags = \"(!no_base || \" + v.Tag + \")\"\n\t\tcase \"most\":\n\t\t\ttags = \"(all || most || \" + v.Tag + \")\"\n\t\tcase \"all\":\n\t\t\ttags = \"(all || \" + v.Tag + \")\"\n\t\tcase \"bad\":\n\t\t\ttags = \"(bad || \" + v.Tag + \")\"\n\t\tdefault:\n\t\t\tpanic(v.Tag)\n\t\t}\n\t\ttags += \" && !no_\" + v.Tag\n\t\tbuf, err := format.Source([]byte(fmt.Sprintf(internalTagGo, tags, \"github.com/xo/usql/drivers/\"+v.Tag, v.Desc)))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := os.WriteFile(filepath.Join(wd, v.Tag+\".go\"), buf, 0o644); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc writeReadme(dir string, includeTagSummary bool) error {\n\treadme := filepath.Join(dir, \"README.md\")\n\tbuf, err := os.ReadFile(readme)\n\tif err != nil {\n\t\treturn err\n\t}\n\tstart := bytes.Index(buf, []byte(driverTableStart))\n\tend := bytes.Index(buf, []byte(driverTableEnd))\n\tif start == -1 || end == -1 {\n\t\treturn errors.New(\"unable to find driver table start/end in README.md\")\n\t}\n\tb := new(bytes.Buffer)\n\tif _, err := b.Write(append(buf[:start+len(driverTableStart)], '\\n', '\\n')); err != nil {\n\t\treturn err\n\t}\n\tif _, err := b.Write(append([]byte(buildDriverTable(includeTagSummary)), '\\n')); err != nil {\n\t\treturn err\n\t}\n\tif _, err := b.Write(buf[end:]); err != nil {\n\t\treturn err\n\t}\n\treturn os.WriteFile(readme, b.Bytes(), 0o644)\n}\n\nfunc writeLicenseFiles(licenseStart int, licenseAuthor string) error {\n\ts := fmt.Sprintf(license, licenseStart, time.Now().Year(), licenseAuthor)\n\tif err := os.WriteFile(\"LICENSE\", append([]byte(s), '\\n'), 0o644); err != nil {\n\t\treturn err\n\t}\n\ttextGo := fmt.Sprintf(licenseTextGo, s)\n\tif err := os.WriteFile(\"text/license.go\", []byte(textGo), 0o644); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc writeDburlLicense(dir string, licenseStart int, licenseAuthor string) error {\n\ts := fmt.Sprintf(license, licenseStart, time.Now().Year(), licenseAuthor)\n\tif err := os.WriteFile(filepath.Join(dir, \"LICENSE\"), append([]byte(s), '\\n'), 0o644); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc writeCommands(name string) error {\n\t// format and write internal.go\n\tvar names, descs string\n\tfor _, s := range sections {\n\t\tnames += fmt.Sprintf(\"%q,\\n\", s)\n\t\tdescs += fmt.Sprintf(\"// %s\\n\\t{\\n\", s)\n\t\tfor _, desc := range cmds[s] {\n\t\t\tdescs += fmt.Sprintf(\"\\t%s,\\n\", desc)\n\t\t}\n\t\tdescs += fmt.Sprint(\"},\\n\")\n\t}\n\tbuf, err := format.Source([]byte(fmt.Sprintf(descsTpl, names, descs)))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn os.WriteFile(name, buf, 0644)\n}\n\nvar (\n\t// baseDrivers are drivers included in a build with no build tags listed.\n\tbaseDrivers = map[string]DriverInfo{}\n\t// mostDrivers are drivers included with the most tag. Populated below.\n\tmostDrivers = map[string]DriverInfo{}\n\t// allDrivers are drivers forced to 'all' build tag.\n\tallDrivers = map[string]DriverInfo{}\n\t// badDrivers are drivers forced to 'bad' build tag.\n\tbadDrivers = map[string]DriverInfo{}\n\t// wireDrivers are the wire compatible drivers.\n\twireDrivers = map[string]DriverInfo{}\n)\n\n// cmds are the meta command descriptions.\nvar cmds map[string][]desc\n\ntype DriverInfo struct {\n\t// Tag is the build Tag / name of the directory the driver lives in.\n\tTag string\n\t// Driver is the Go SQL Driver Driver (parsed from the import tagged with //\n\t// DRIVER: <Driver>), otherwise same as the tag / directory Driver.\n\tDriver string\n\t// Pkg is the imported driver package, taken from the import tagged with\n\t// DRIVER.\n\tPkg string\n\t// Desc is the descriptive text of the driver, parsed from doc comment, ie,\n\t// \"Package <tag> defines and registers usql's <Desc>.\"\n\tDesc string\n\t// URL is the driver's reference URL, parsed from doc comment's \"See: <URL>\".\n\tURL string\n\t// CGO is whether or not the driver requires CGO, based on presence of\n\t// 'Requires CGO.' in the comment\n\tCGO bool\n\t// Aliases are the parsed Alias: entries.\n\tAliases [][]string\n\t// Wire indicates it is a Wire compatible driver.\n\tWire bool\n\t// Group is the build Group\n\tGroup string\n}\n\nfunc parseDriverInfo(tag, filename string) (DriverInfo, error) {\n\tf, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.ParseComments)\n\tif err != nil {\n\t\treturn DriverInfo{}, err\n\t}\n\tname := tag\n\tvar pkg string\n\tfor _, imp := range f.Imports {\n\t\tif imp.Comment == nil || len(imp.Comment.List) == 0 || !strings.Contains(imp.Comment.List[0].Text, \"DRIVER\") {\n\t\t\tcontinue\n\t\t}\n\t\tpkg = imp.Path.Value[1 : len(imp.Path.Value)-1]\n\t\tif i := strings.Index(imp.Comment.List[0].Text, \":\"); i != -1 {\n\t\t\tname = strings.TrimSpace(imp.Comment.List[0].Text[i+1:])\n\t\t}\n\t\tbreak\n\t}\n\t// parse doc comment\n\tcomment := f.Doc.Text()\n\tprefix := \"Package \" + tag + \" defines and registers usql's \"\n\tif !strings.HasPrefix(comment, prefix) {\n\t\treturn DriverInfo{}, fmt.Errorf(\"invalid doc comment prefix for driver %q\", tag)\n\t}\n\tdesc := strings.TrimPrefix(comment, prefix)\n\ti := strings.Index(desc, \" driver.\")\n\tif i == -1 {\n\t\treturn DriverInfo{}, fmt.Errorf(\"cannot find description suffix for driver %q\", tag)\n\t}\n\tdesc = strings.TrimSpace(desc[:i])\n\tif desc == \"\" {\n\t\treturn DriverInfo{}, fmt.Errorf(\"unable to parse description for driver %q\", tag)\n\t}\n\t// parse alias:\n\tvar aliases [][]string\n\taliasesm := aliasRE.FindAllStringSubmatch(comment, -1)\n\tfor _, m := range aliasesm {\n\t\ts := strings.Split(m[1], \",\")\n\t\taliases = append(aliases, []string{\n\t\t\tstrings.TrimSpace(s[0]),\n\t\t\tstrings.TrimSpace(s[1]),\n\t\t})\n\t}\n\t// parse see: url\n\turlm := seeRE.FindAllStringSubmatch(comment, -1)\n\tif urlm == nil {\n\t\treturn DriverInfo{}, fmt.Errorf(\"missing See: <URL> for driver %q\", tag)\n\t}\n\t// parse group:\n\tgroup := \"most\"\n\tif groupm := groupRE.FindAllStringSubmatch(comment, -1); groupm != nil {\n\t\tgroup = strings.TrimSpace(groupm[0][1])\n\t}\n\treturn DriverInfo{\n\t\tTag:     tag,\n\t\tDriver:  name,\n\t\tPkg:     pkg,\n\t\tDesc:    cleanRE.ReplaceAllString(desc, \"\"),\n\t\tURL:     strings.TrimSpace(urlm[0][1]),\n\t\tCGO:     strings.Contains(cleanRE.ReplaceAllString(comment, \"\"), \"Requires CGO.\"),\n\t\tAliases: aliases,\n\t\tGroup:   group,\n\t}, nil\n}\n\n// desc is a meta command description.\ntype desc struct {\n\tFunc       string\n\tName       string\n\tParams     string\n\tDesc       string\n\tHidden     bool\n\tDeprecated bool\n}\n\nfunc newDesc(funcName, alias string, v []string) desc {\n\tname, params, descstr, hidden := v[0], \"\", \"\", false\n\tswitch n := len(v); {\n\tcase n == 1:\n\t\tif i := strings.Index(name, \":\"); i != -1 {\n\t\t\tname, alias = name[:i], name[i+1:]\n\t\t}\n\tcase n == 2:\n\t\tdescstr = v[1]\n\tcase n >= 3:\n\t\tparams, descstr = v[1], v[2]\n\t}\n\tif descstr == \"\" {\n\t\thidden, descstr = true, `alias for \\`+alias\n\t}\n\treturn desc{\n\t\tFunc:       funcName,\n\t\tName:       name,\n\t\tParams:     params,\n\t\tDesc:       descstr,\n\t\tHidden:     hidden,\n\t\tDeprecated: v[len(v)-1] == \"DEPRECATED\",\n\t}\n}\n\nfunc (d desc) String() string {\n\ts := strings.ReplaceAll(d.Desc, \"{{CommandName}}\", \"` + text.CommandName + `\")\n\treturn fmt.Sprintf(\"{%s, `%s`, `%s`, `%s`, %t, %t}\", d.Func, d.Name, d.Params, s, d.Hidden, d.Deprecated)\n}\n\nfunc findCommand(name string) string {\n\tfor _, s := range sections {\n\t\tfor _, d := range cmds[s] {\n\t\t\tif d.Func == name {\n\t\t\t\treturn d.Name\n\t\t\t}\n\t\t}\n\t}\n\tpanic(fmt.Sprintf(\"unable to find command for %s\", name))\n}\n\nfunc buildDriverTable(includeTagSummary bool) string {\n\thdr := []string{\"Database\", \"Scheme / Tag\", \"Scheme Aliases\", \"Driver Package / Notes\"}\n\twidths := []int{len(hdr[0]), len(hdr[1]), len(hdr[2]), len(hdr[3])}\n\tbaseRows, widths := buildRows(baseDrivers, widths)\n\tmostRows, widths := buildRows(mostDrivers, widths)\n\tallRows, widths := buildRows(allDrivers, widths)\n\tbadRows, widths := buildRows(badDrivers, widths)\n\twireRows, widths := buildRows(wireDrivers, widths)\n\ts := tableRows(widths, ' ', hdr)\n\ts += tableRows(widths, '-')\n\ts += tableRows(widths, ' ', baseRows...)\n\ts += tableRows(widths, ' ')\n\ts += tableRows(widths, ' ', mostRows...)\n\ts += tableRows(widths, ' ')\n\ts += tableRows(widths, ' ', allRows...)\n\ts += tableRows(widths, ' ')\n\ts += tableRows(widths, ' ', wireRows...)\n\ts += tableRows(widths, ' ')\n\ts += tableRows(widths, ' ', badRows...)\n\tif includeTagSummary {\n\t\ts += tableRows(widths, ' ')\n\t\ts += tableRows(widths, ' ',\n\t\t\t[]string{\"**NO DRIVERS**\", \"`no_base`\", \"\", \"_no base drivers (useful for development)_\"},\n\t\t\t[]string{\"**MOST DRIVERS**\", \"`most`\", \"\", \"_all stable drivers_\"},\n\t\t\t[]string{\"**ALL DRIVERS**\", \"`all`\", \"\", \"_all drivers, excluding bad drivers_\"},\n\t\t\t[]string{\"**BAD DRIVERS**\", \"`bad`\", \"\", \"_bad drivers (broken/non-working drivers)_\"},\n\t\t\t[]string{\"**NO &lt;TAG&gt;**\", \"`no_<tag>`\", \"\", \"_exclude driver with `<tag>`_\"},\n\t\t)\n\t}\n\treturn s + \"\\n\" + buildTableLinks(baseDrivers, mostDrivers, allDrivers, badDrivers)\n}\n\nfunc buildRows(m map[string]DriverInfo, widths []int) ([][]string, []int) {\n\tvar drivers []DriverInfo\n\tfor _, v := range m {\n\t\tdrivers = append(drivers, v)\n\t}\n\tsort.Slice(drivers, func(i, j int) bool {\n\t\tswitch {\n\t\tcase drivers[i].Group == \"base\":\n\t\t\treturn baseOrder[drivers[i].Driver] < baseOrder[drivers[j].Driver]\n\t\t}\n\t\treturn strings.ToLower(drivers[i].Desc) < strings.ToLower(drivers[j].Desc)\n\t})\n\tvar rows [][]string\n\tfor i, v := range drivers {\n\t\tnotes := \"\"\n\t\tif v.CGO {\n\t\t\tnotes += \" <sup>[†][f-cgo]</sup>\"\n\t\t}\n\t\tif v.Wire {\n\t\t\tnotes += \" <sup>[‡][f-wire]</sup>\"\n\t\t}\n\t\trows = append(rows, []string{\n\t\t\tv.Desc,\n\t\t\t\"`\" + v.Tag + \"`\",\n\t\t\tbuildAliases(v),\n\t\t\tfmt.Sprintf(\"[%s][d-%s]%s\", v.Pkg, v.Tag, notes),\n\t\t})\n\t\t// calc max\n\t\tfor j := 0; j < len(rows[i]); j++ {\n\t\t\twidths[j] = max(runewidth.StringWidth(rows[i][j]), widths[j])\n\t\t}\n\t}\n\treturn rows, widths\n}\n\nfunc buildAliases(v DriverInfo) string {\n\tname := v.Tag\n\tif v.Wire {\n\t\tname = v.Driver\n\t}\n\t_, aliases := dburl.SchemeDriverAndAliases(name)\n\tif v.Wire {\n\t\taliases = append(aliases, name)\n\t}\n\tfor i := 0; i < len(aliases); i++ {\n\t\tif !v.Wire && aliases[i] == v.Tag {\n\t\t\taliases[i] = v.Driver\n\t\t}\n\t}\n\tfileTypes := dburl.FileTypes()\n\tif slices.Contains(fileTypes, name) {\n\t\taliases = append(aliases, `file`)\n\t}\n\tif len(aliases) > 0 {\n\t\treturn \"`\" + strings.Join(aliases, \"`, `\") + \"`\"\n\t}\n\treturn \"\"\n}\n\nfunc tableRows(widths []int, c rune, rows ...[]string) string {\n\tpadding := string(c)\n\tif len(rows) == 0 {\n\t\tv := make([]string, len(widths))\n\t\tif c == '-' {\n\t\t\tfor i, w := range widths {\n\t\t\t\tv[i] = strings.Repeat(padding, w)\n\t\t\t}\n\t\t\tpadding = \" \"\n\t\t}\n\t\trows = [][]string{v}\n\t}\n\tvar s string\n\tfor _, row := range rows {\n\t\tfor i := 0; i < len(row); i++ {\n\t\t\ts += \"|\" + padding + row[i] + strings.Repeat(padding, widths[i]-runewidth.StringWidth(row[i])) + padding\n\t\t}\n\t\ts += \"|\\n\"\n\t}\n\treturn s\n}\n\nfunc buildTableLinks(drivers ...map[string]DriverInfo) string {\n\tvar d []DriverInfo\n\tfor _, m := range drivers {\n\t\tfor _, v := range m {\n\t\t\td = append(d, v)\n\t\t}\n\t}\n\tsort.Slice(d, func(i, j int) bool {\n\t\treturn d[i].Tag < d[j].Tag\n\t})\n\tvar s string\n\tfor _, v := range d {\n\t\ts += fmt.Sprintf(\"[d-%s]: %s\\n\", v.Tag, v.URL)\n\t}\n\treturn s\n}\n\nfunc getDburlDir() string {\n\tdir := filepath.Join(os.Getenv(\"GOPATH\"), \"src/github.com/xo/dburl\")\n\tvar err error\n\tif dir, err = realpath.Realpath(dir); err != nil {\n\t\tpanic(err)\n\t}\n\treturn dir\n}\n\n// decodeCommand decodes a command.\nfunc decodeCommand(d ast.Decl) (string, []desc, bool, error) {\n\tf, ok := d.(*ast.FuncDecl)\n\tif !ok || !isCommand(f) {\n\t\treturn \"\", nil, false, nil\n\t}\n\tswitch section, descs, err := decodeCommandDoc(f); {\n\tcase err != nil:\n\t\treturn \"\", nil, false, err\n\tcase !slices.Contains(sections, section):\n\t\treturn \"\", nil, false, fmt.Errorf(\"meta command %s has invalid section name %q\", f.Name, section)\n\tcase len(descs) == 0:\n\t\treturn \"\", nil, false, fmt.Errorf(\"meta command %s has no valid command descriptions\", f.Name)\n\tdefault:\n\t\treturn section, descs, true, nil\n\t}\n}\n\n// isCommand returns true if a meta command matches the signature for a command\n// func.\nfunc isCommand(f *ast.FuncDecl) bool {\n\tswitch {\n\tcase !unicode.IsUpper(rune(f.Name.String()[0])),\n\t\tf.Type.Params.NumFields() != 1,\n\t\tf.Type.Results.NumFields() != 1,\n\t\tfmt.Sprint(f.Type.Results.List[0].Type) != \"error\":\n\t\treturn false\n\t}\n\tif e, ok := f.Type.Params.List[0].Type.(*ast.StarExpr); !ok || fmt.Sprint(e.X) != \"Params\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// decodeCommandDoc decodes a meta command's doc comment.\nfunc decodeCommandDoc(f *ast.FuncDecl) (string, []desc, error) {\n\tname, doc := f.Name.String()+\" is a \", f.Doc.Text()\n\tif !strings.HasPrefix(doc, name) {\n\t\treturn \"\", nil, fmt.Errorf(\"meta command %s doc comment does not start with %q\", f.Name, name)\n\t}\n\ti := strings.Index(doc, \"meta command (\")\n\tif i == -1 {\n\t\treturn \"\", nil, fmt.Errorf(\"meta command %s doc comment missing %q\", f.Name, \"meta command (\")\n\t}\n\tsection := strings.TrimSpace(doc[len(name):i])\n\tif i = strings.Index(doc, \"Descs:\\n\\n\"); i == -1 {\n\t\treturn \"\", nil, fmt.Errorf(\"meta command %s doc comment missing %q\", f.Name, \"Descs:\")\n\t}\n\tdescs, err := decodeCommandDescs(f.Name.String(), doc[i+len(\"Descs:\\n\\n\"):])\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"meta command %s has invalid desc: %v\", f.Name, err)\n\t}\n\treturn section, descs, nil\n}\n\n// decodeCommandDescs\nfunc decodeCommandDescs(funcName string, doc string) ([]desc, error) {\n\ts := bufio.NewScanner(strings.NewReader(doc))\n\tvar descs []desc\n\tvar alias string\n\tfor i := 0; s.Scan(); i++ {\n\t\tline := s.Text()\n\t\tif !strings.HasPrefix(line, \"\\t\") {\n\t\t\treturn nil, fmt.Errorf(\"line %d does not start with \\\\t\", i+1)\n\t\t}\n\t\tv := strings.Split(line[1:], \"\\t\")\n\t\tswitch {\n\t\tcase len(v) == 0:\n\t\t\treturn nil, fmt.Errorf(\"line %d is invalid\", i+1)\n\t\tcase len(v[0]) == 0:\n\t\t\treturn nil, fmt.Errorf(\"line %d has invalid name\", i+1)\n\t\t}\n\t\tdescs = append(descs, newDesc(funcName, alias, v))\n\t\tif alias == \"\" {\n\t\t\talias = descs[0].Name\n\t\t}\n\t}\n\treturn descs, nil\n}\n\nvar baseOrder = map[string]int{\n\t\"postgres\":   0,\n\t\"mysql\":      1,\n\t\"sqlserver\":  2,\n\t\"oracle\":     3,\n\t\"sqlite3\":    4,\n\t\"clickhouse\": 5,\n\t\"csvq\":       6,\n}\n\n// sections are the section names for meta commands.\nvar sections = []string{\n\t\"General\",\n\t\"Help\",\n\t\"Connection\",\n\t\"Query Execute\",\n\t\"Query View\",\n\t\"Query Buffer\",\n\t\"Informational\",\n\t\"Variables\",\n\t\"Input/Output\",\n\t\"Control/Conditional\",\n\t\"Transaction\",\n\t\"Operating System/Environment\",\n}\n\n// regexps.\nvar (\n\taliasRE = regexp.MustCompile(`(?m)^Alias:\\s+(.*)$`)\n\tseeRE   = regexp.MustCompile(`(?m)^See:\\s+(.*)$`)\n\tgroupRE = regexp.MustCompile(`(?m)^Group:\\s+(.*)$`)\n\tcleanRE = regexp.MustCompile(`[\\r\\n]`)\n\tdirRE   = regexp.MustCompile(`^([^/]+)/([^\\./]+)\\.go$`)\n)\n\nconst (\n\tdriverTableStart = \"<!-- DRIVER DETAILS START -->\"\n\tdriverTableEnd   = \"<!-- DRIVER DETAILS END -->\"\n)\n\nconst descsTpl = `package metacmd\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t\"github.com/xo/usql/text\"\n)\n\n// sections are the command description sections.\nvar sections = []string{\n\t%s\n}\n\n// descs are the command descriptions.\nvar descs [][]desc\n\n// cmds are the command lookup map.\nvar cmds map[string]func(*Params) error\n\nfunc init() {\n\tdescs = [][]desc{\n\t\t%s\n\t}\n\tcmds = make(map[string]func(*Params) error)\n\tfor i := range sections {\n\t\tfor _, desc := range descs[i] {\n\t\t\tfor _, n := range desc.Names() {\n\t\t\t\tcmds[n] = desc.Func\n\t\t\t}\n\t\t}\n\t}\n}\n`\n\nconst internalTagGo = `//go:build %s\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n       _ %q // %s driver\n)`\n\nconst internalGo = `// Package internal provides a way to obtain information about which database\n// drivers were included at build.\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\n// KnownBuildTags returns a map of known driver names to its respective build\n// tags.\nfunc KnownBuildTags() map[string]string{\n\treturn map[string]string{%s\n\t}\n}`\n\nconst licenseTextGo = `package text\n\n// Code generated by gen.go. DO NOT EDIT.\n\n// License contains the license text for usql.\nconst License = ` + \"`%s`\" + `\n`\n\nconst license = `The MIT License (MIT)\n\nCopyright (c) %d-%d %s\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.`\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/xo/usql\n\ngo 1.26\n\nrequire (\n\tgithub.com/ClickHouse/clickhouse-go/v2 v2.43.0\n\tgithub.com/IBM/nzgo/v12 v12.0.10\n\tgithub.com/MichaelS11/go-cql-driver v0.1.1\n\tgithub.com/SAP/go-hdb v1.15.1\n\tgithub.com/VoltDB/voltdb-client-go v1.0.18\n\tgithub.com/alecthomas/chroma/v2 v2.23.1\n\tgithub.com/alexbrainman/odbc v0.0.0-20250601004241-49e6b2bc0cf0\n\tgithub.com/aliyun/aliyun-tablestore-go-sql-driver v0.0.0-20220418015234-4d337cb3eed9\n\tgithub.com/amsokol/ignite-go-client v0.12.2\n\tgithub.com/apache/arrow/go/v17 v17.0.0\n\tgithub.com/apache/calcite-avatica-go/v5 v5.4.0\n\tgithub.com/btnguyen2k/gocosmos v1.1.0\n\tgithub.com/btnguyen2k/godynamo v1.3.0\n\tgithub.com/chaisql/chai v0.18.0\n\tgithub.com/couchbase/go_n1ql v0.0.0-20220303011133-0ed4bf93e31d\n\tgithub.com/databricks/databricks-sql-go v1.10.0\n\tgithub.com/datafuselabs/databend-go v0.9.1\n\tgithub.com/docker/docker v28.5.2+incompatible\n\tgithub.com/duckdb/duckdb-go/v2 v2.5.5\n\tgithub.com/exasol/exasol-driver-go v1.0.16\n\tgithub.com/go-git/go-billy/v5 v5.8.0\n\tgithub.com/go-sql-driver/mysql v1.9.3\n\tgithub.com/gocql/gocql v1.7.0\n\tgithub.com/godror/godror v0.50.0\n\tgithub.com/gohxs/readline v0.0.0-20171011095936-a780388e6e7c\n\tgithub.com/google/go-cmp v0.7.0\n\tgithub.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f\n\tgithub.com/googleapis/go-sql-spanner v1.24.0\n\tgithub.com/jackc/pgx/v5 v5.8.0\n\tgithub.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade\n\tgithub.com/jmrobles/h2go v0.5.0\n\tgithub.com/kenshaw/colors v0.2.3\n\tgithub.com/kenshaw/rasterm v0.1.16\n\tgithub.com/lib/pq v1.11.2\n\tgithub.com/mattn/go-adodb v0.0.1\n\tgithub.com/mattn/go-isatty v0.0.20\n\tgithub.com/mattn/go-runewidth v0.0.19\n\tgithub.com/mattn/go-sqlite3 v1.14.34\n\tgithub.com/microsoft/go-mssqldb v1.9.8\n\tgithub.com/mithrandie/csvq v1.18.1\n\tgithub.com/mithrandie/csvq-driver v1.7.0\n\tgithub.com/nakagami/firebirdsql v0.9.15\n\tgithub.com/ory/dockertest/v3 v3.12.0\n\tgithub.com/prestodb/presto-go-client v0.0.0-20240426182841-905ac40a1783\n\tgithub.com/proullon/ramsql v0.1.4\n\tgithub.com/sclgo/impala-go v1.4.1\n\tgithub.com/sijms/go-ora/v2 v2.9.0\n\tgithub.com/snowflakedb/gosnowflake v1.19.0\n\tgithub.com/spf13/cobra v1.10.2\n\tgithub.com/spf13/pflag v1.0.10\n\tgithub.com/spf13/viper v1.21.0\n\tgithub.com/thda/tds v0.1.7\n\tgithub.com/trinodb/trino-go-client v0.333.0\n\tgithub.com/uber/athenadriver v1.1.15\n\tgithub.com/vertica/vertica-sql-go v1.3.5\n\tgithub.com/xo/dburl v0.24.2\n\tgithub.com/xo/echartsgoja v0.1.1\n\tgithub.com/xo/resvg v0.6.0\n\tgithub.com/xo/tblfmt v0.16.0\n\tgithub.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e\n\tgithub.com/ydb-platform/ydb-go-sdk/v3 v3.127.2\n\tgithub.com/yookoala/realpath v1.0.0\n\tgithub.com/ziutek/mymysql v1.5.4\n\tgorm.io/driver/bigquery v1.2.0\n\tmodernc.org/ql v1.4.31\n\tmodernc.org/sqlite v1.46.1\n\tsqlflow.org/gohive v0.0.0-20240730014249-8960223660e2\n\tsqlflow.org/gomaxcompute v0.0.0-20210805062559-c14ae028b44c\n)\n\nrequire (\n\tcel.dev/expr v0.25.1 // indirect\n\tcloud.google.com/go v0.123.0 // indirect\n\tcloud.google.com/go/auth v0.18.2 // indirect\n\tcloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect\n\tcloud.google.com/go/bigquery v1.73.1 // indirect\n\tcloud.google.com/go/compute/metadata v0.9.0 // indirect\n\tcloud.google.com/go/iam v1.5.3 // indirect\n\tcloud.google.com/go/longrunning v0.8.0 // indirect\n\tcloud.google.com/go/monitoring v1.24.3 // indirect\n\tcloud.google.com/go/spanner v1.88.0 // indirect\n\tdario.cat/mergo v1.0.2 // indirect\n\tfilippo.io/edwards25519 v1.1.0 // indirect\n\tgithub.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect\n\tgithub.com/99designs/keyring v1.2.2 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect\n\tgithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect\n\tgithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/ClickHouse/ch-go v0.71.0 // indirect\n\tgithub.com/DATA-DOG/go-sqlmock v1.5.2 // indirect\n\tgithub.com/DataDog/zstd v1.5.7 // indirect\n\tgithub.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0 // indirect\n\tgithub.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect\n\tgithub.com/Masterminds/semver v1.5.0 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect\n\tgithub.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 // indirect\n\tgithub.com/RaduBerinde/btreemap v0.0.0-20250419232817-bf0d809ae648 // indirect\n\tgithub.com/VictoriaMetrics/easyproto v1.2.0 // indirect\n\tgithub.com/aliyun/aliyun-tablestore-go-sdk v1.8.0 // indirect\n\tgithub.com/andybalholm/brotli v1.2.0 // indirect\n\tgithub.com/apache/arrow-go/v18 v18.5.1 // indirect\n\tgithub.com/apache/arrow/go/v12 v12.0.1 // indirect\n\tgithub.com/apache/arrow/go/v15 v15.0.2 // indirect\n\tgithub.com/apache/thrift v0.22.0 // indirect\n\tgithub.com/avast/retry-go v3.0.0+incompatible // indirect\n\tgithub.com/aws/aws-sdk-go v1.55.8 // indirect\n\tgithub.com/aws/aws-sdk-go-v2 v1.41.2 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/config v1.32.10 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/credentials v1.19.10 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.29 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/dynamodb v1.53.5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.32.9 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect\n\tgithub.com/aws/smithy-go v1.24.2 // indirect\n\tgithub.com/beltran/gohive v1.8.1 // indirect\n\tgithub.com/beltran/gosasl v1.0.0 // indirect\n\tgithub.com/beltran/gssapi v0.0.0-20200324152954-d86554db4bab // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/bitfield/gotestdox v0.2.2 // indirect\n\tgithub.com/btnguyen2k/consu/checksum v1.1.1 // indirect\n\tgithub.com/btnguyen2k/consu/g18 v0.1.0 // indirect\n\tgithub.com/btnguyen2k/consu/gjrc v0.2.2 // indirect\n\tgithub.com/btnguyen2k/consu/olaf v0.1.3 // indirect\n\tgithub.com/btnguyen2k/consu/reddo v0.1.9 // indirect\n\tgithub.com/btnguyen2k/consu/semita v0.1.5 // indirect\n\tgithub.com/buger/jsonparser v1.1.1 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.3.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/clipperhouse/stringish v0.1.1 // indirect\n\tgithub.com/clipperhouse/uax29/v2 v2.3.0 // indirect\n\tgithub.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect\n\tgithub.com/cockroachdb/crlib v0.0.0-20251122031428-fe658a2dbda1 // indirect\n\tgithub.com/cockroachdb/errors v1.12.0 // indirect\n\tgithub.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect\n\tgithub.com/cockroachdb/pebble/v2 v2.1.2 // indirect\n\tgithub.com/cockroachdb/redact v1.1.6 // indirect\n\tgithub.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect\n\tgithub.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb // indirect\n\tgithub.com/containerd/continuity v0.4.5 // indirect\n\tgithub.com/containerd/errdefs v1.0.0 // indirect\n\tgithub.com/containerd/errdefs/pkg v0.3.0 // indirect\n\tgithub.com/containerd/log v0.1.0 // indirect\n\tgithub.com/coreos/go-oidc/v3 v3.17.0 // indirect\n\tgithub.com/couchbase/go-couchbase v0.1.1 // indirect\n\tgithub.com/couchbase/gomemcached v0.3.3 // indirect\n\tgithub.com/couchbase/goutils v0.2.0 // indirect\n\tgithub.com/cyphar/filepath-securejoin v0.4.1 // indirect\n\tgithub.com/danieljoos/wincred v1.2.3 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/distribution/reference v0.6.0 // indirect\n\tgithub.com/dlclark/regexp2 v1.11.5 // indirect\n\tgithub.com/dnephin/pflag v1.0.7 // indirect\n\tgithub.com/docker/cli v28.4.0+incompatible // indirect\n\tgithub.com/docker/go-connections v0.6.0 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/dop251/goja v0.0.0-20251201205617-2bb4c724c0f9 // indirect\n\tgithub.com/dop251/goja_nodejs v0.0.0-20251015164255-5e94316bedaf // indirect\n\tgithub.com/dromara/carbon/v2 v2.6.15 // indirect\n\tgithub.com/duckdb/duckdb-go-bindings v0.3.4 // indirect\n\tgithub.com/duckdb/duckdb-go-bindings/lib/darwin-amd64 v0.3.3 // indirect\n\tgithub.com/duckdb/duckdb-go-bindings/lib/darwin-arm64 v0.3.3 // indirect\n\tgithub.com/duckdb/duckdb-go-bindings/lib/linux-amd64 v0.3.3 // indirect\n\tgithub.com/duckdb/duckdb-go-bindings/lib/linux-arm64 v0.3.3 // indirect\n\tgithub.com/duckdb/duckdb-go-bindings/lib/windows-amd64 v0.3.3 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/dvsekhvalnov/jose2go v1.8.0 // indirect\n\tgithub.com/edsrzf/mmap-go v1.2.0 // indirect\n\tgithub.com/elastic/go-sysinfo v1.15.4 // indirect\n\tgithub.com/elastic/go-windows v1.0.2 // indirect\n\tgithub.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect\n\tgithub.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect\n\tgithub.com/exasol/error-reporting-go v0.2.0 // indirect\n\tgithub.com/fatih/color v1.18.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/gabriel-vasile/mimetype v1.4.13 // indirect\n\tgithub.com/getsentry/sentry-go v0.40.0 // indirect\n\tgithub.com/go-faster/city v1.0.1 // indirect\n\tgithub.com/go-faster/errors v0.7.1 // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.3 // indirect\n\tgithub.com/go-logfmt/logfmt v0.6.1 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-ole/go-ole v1.3.0 // indirect\n\tgithub.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect\n\tgithub.com/go-viper/mapstructure/v2 v2.5.0 // indirect\n\tgithub.com/go-zookeeper/zk v1.0.4 // indirect\n\tgithub.com/goccy/go-json v0.10.5 // indirect\n\tgithub.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect\n\tgithub.com/godror/knownpb v0.3.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2 // indirect\n\tgithub.com/golang-jwt/jwt/v5 v5.3.1 // indirect\n\tgithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect\n\tgithub.com/golang-sql/sqlexp v0.1.0 // indirect\n\tgithub.com/golang/protobuf v1.5.4 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/google/flatbuffers/go v0.0.0-20230110200425-62e4d2e5b215 // indirect\n\tgithub.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 // indirect\n\tgithub.com/google/pprof v0.0.0-20251213031049-b05bdaca462f // indirect\n\tgithub.com/google/s2a-go v0.1.9 // indirect\n\tgithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/googleapis/enterprise-certificate-proxy v0.3.12 // indirect\n\tgithub.com/googleapis/gax-go/v2 v2.17.0 // indirect\n\tgithub.com/gorilla/websocket v1.5.3 // indirect\n\tgithub.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect\n\tgithub.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.8 // indirect\n\tgithub.com/hashicorp/go-uuid v1.0.3 // indirect\n\tgithub.com/hashicorp/golang-lru v1.0.2 // indirect\n\tgithub.com/hashicorp/golang-lru/v2 v2.0.7 // indirect\n\tgithub.com/icholy/digest v1.1.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect\n\tgithub.com/jackc/puddle/v2 v2.2.2 // indirect\n\tgithub.com/jcmturner/aescts/v2 v2.0.0 // indirect\n\tgithub.com/jcmturner/dnsutils/v2 v2.0.0 // indirect\n\tgithub.com/jcmturner/gofork v1.7.6 // indirect\n\tgithub.com/jcmturner/goidentity/v6 v6.0.1 // indirect\n\tgithub.com/jcmturner/gokrb5/v8 v8.4.4 // indirect\n\tgithub.com/jcmturner/rpc/v2 v2.0.3 // indirect\n\tgithub.com/jedib0t/go-pretty/v6 v6.7.7 // indirect\n\tgithub.com/jmespath/go-jmespath v0.4.0 // indirect\n\tgithub.com/jonboulle/clockwork v0.5.0 // indirect\n\tgithub.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect\n\tgithub.com/klauspost/asmfmt v1.3.2 // indirect\n\tgithub.com/klauspost/compress v1.18.4 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.3.0 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/kylelemons/godebug v1.1.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-sixel v0.0.8 // indirect\n\tgithub.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect\n\tgithub.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect\n\tgithub.com/minio/minlz v1.0.1 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/mithrandie/go-file/v2 v2.1.0 // indirect\n\tgithub.com/mithrandie/go-text v1.6.0 // indirect\n\tgithub.com/mithrandie/ternary v1.1.1 // indirect\n\tgithub.com/moby/docker-image-spec v1.3.1 // indirect\n\tgithub.com/moby/go-archive v0.1.0 // indirect\n\tgithub.com/moby/patternmatcher v0.6.0 // indirect\n\tgithub.com/moby/sys/atomicwriter v0.1.0 // indirect\n\tgithub.com/moby/sys/sequential v0.6.0 // indirect\n\tgithub.com/moby/sys/user v0.4.0 // indirect\n\tgithub.com/moby/sys/userns v0.1.0 // indirect\n\tgithub.com/moby/term v0.5.2 // indirect\n\tgithub.com/mtibben/percent v0.2.1 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/murfffi/gorich v0.3.0 // indirect\n\tgithub.com/nakagami/chacha20 v0.1.0 // indirect\n\tgithub.com/nathan-fiscaletti/consolesize-go v0.0.0-20220204101620-317176b6684d // indirect\n\tgithub.com/ncruces/go-strftime v1.0.0 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.1 // indirect\n\tgithub.com/opencontainers/runc v1.3.1 // indirect\n\tgithub.com/paulmach/orb v0.12.0 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.4 // indirect\n\tgithub.com/pierrec/lz4 v2.6.1+incompatible // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.25 // indirect\n\tgithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/prometheus/client_golang v1.23.2 // indirect\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.67.4 // indirect\n\tgithub.com/prometheus/procfs v0.20.1 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect\n\tgithub.com/rogpeppe/go-internal v1.14.1 // indirect\n\tgithub.com/rs/zerolog v1.34.0 // indirect\n\tgithub.com/sagikazarmark/locafero v0.12.0 // indirect\n\tgithub.com/samber/lo v1.52.0 // indirect\n\tgithub.com/segmentio/asm v1.2.1 // indirect\n\tgithub.com/shopspring/decimal v1.4.0 // indirect\n\tgithub.com/sirupsen/logrus v1.9.4 // indirect\n\tgithub.com/soniakeys/quant v1.0.0 // indirect\n\tgithub.com/spaolacci/murmur3 v1.1.0 // indirect\n\tgithub.com/spf13/afero v1.15.0 // indirect\n\tgithub.com/spf13/cast v1.10.0 // indirect\n\tgithub.com/spiffe/go-spiffe/v2 v2.6.0 // indirect\n\tgithub.com/stretchr/objx v0.5.3 // indirect\n\tgithub.com/stretchr/testify v1.11.1 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgithub.com/uber-go/tally v3.3.17+incompatible // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgithub.com/xeipuuv/gojsonschema v1.2.0 // indirect\n\tgithub.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 // indirect\n\tgithub.com/ydb-platform/ydb-go-genproto v0.0.0-20260128080146-c4ed16b24b37 // indirect\n\tgithub.com/zeebo/xxh3 v1.1.0 // indirect\n\tgitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect\n\tgo.opencensus.io v0.24.0 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/detectors/gcp v1.41.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.66.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 // indirect\n\tgo.opentelemetry.io/otel v1.41.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.41.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.41.0 // indirect\n\tgo.opentelemetry.io/otel/sdk/metric v1.41.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.41.0 // indirect\n\tgo.uber.org/multierr v1.11.0 // indirect\n\tgo.uber.org/zap v1.27.1 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.3 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/crypto v0.48.0 // indirect\n\tgolang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect\n\tgolang.org/x/mod v0.33.0 // indirect\n\tgolang.org/x/net v0.51.0 // indirect\n\tgolang.org/x/oauth2 v0.35.0 // indirect\n\tgolang.org/x/sync v0.19.0 // indirect\n\tgolang.org/x/sys v0.41.0 // indirect\n\tgolang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1 // indirect\n\tgolang.org/x/term v0.40.0 // indirect\n\tgolang.org/x/text v0.34.0 // indirect\n\tgolang.org/x/time v0.14.0 // indirect\n\tgolang.org/x/tools v0.42.0 // indirect\n\tgolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect\n\tgoogle.golang.org/api v0.269.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20260226221140-a57be14db171 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect\n\tgoogle.golang.org/grpc v1.79.1 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect\n\tgopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect\n\tgopkg.in/jcmturner/gokrb5.v6 v6.1.1 // indirect\n\tgopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tgotest.tools/gotestsum v1.13.0 // indirect\n\thowett.net/plist v1.0.1 // indirect\n\tmodernc.org/b v1.1.0 // indirect\n\tmodernc.org/db v1.0.29 // indirect\n\tmodernc.org/file v1.0.19 // indirect\n\tmodernc.org/fileutil v1.4.0 // indirect\n\tmodernc.org/golex v1.1.0 // indirect\n\tmodernc.org/internal v1.1.9 // indirect\n\tmodernc.org/libc v1.69.0 // indirect\n\tmodernc.org/lldb v1.0.8 // indirect\n\tmodernc.org/mathutil v1.7.1 // indirect\n\tmodernc.org/memory v1.11.0 // indirect\n\tmodernc.org/sortutil v1.2.1 // indirect\n\tmodernc.org/strutil v1.2.1 // indirect\n\tmodernc.org/zappy v1.1.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=\ncel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=\ncloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=\ncloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=\ncloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=\ncloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=\ncloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=\ncloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=\ncloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=\ncloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=\ncloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=\ncloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=\ncloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=\ncloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=\ncloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=\ncloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=\ncloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=\ncloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=\ncloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=\ncloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=\ncloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=\ncloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=\ncloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=\ncloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=\ncloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=\ncloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=\ncloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=\ncloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=\ncloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=\ncloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=\ncloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=\ncloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=\ncloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=\ncloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=\ncloud.google.com/go/bigquery v1.73.1 h1:v//GZwdhtmCbZ87rOnxz7pectOGFS1GNRvrGTvLzka4=\ncloud.google.com/go/bigquery v1.73.1/go.mod h1:KSLx1mKP/yGiA8U+ohSrqZM1WknUnjZAxHAQZ51/b1k=\ncloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=\ncloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=\ncloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=\ncloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=\ncloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=\ncloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=\ncloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=\ncloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=\ncloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=\ncloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=\ncloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=\ncloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=\ncloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=\ncloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=\ncloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=\ncloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=\ncloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=\ncloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=\ncloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=\ncloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=\ncloud.google.com/go/datacatalog v1.26.1 h1:bCRKA8uSQN8wGW3Tw0gwko4E9a64GRmbW1nCblhgC2k=\ncloud.google.com/go/datacatalog v1.26.1/go.mod h1:2Qcq8vsHNxMDgjgadRFmFG47Y+uuIVsyEGUrlrKEdrg=\ncloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=\ncloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=\ncloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=\ncloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=\ncloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=\ncloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=\ncloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=\ncloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=\ncloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=\ncloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=\ncloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=\ncloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=\ncloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=\ncloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=\ncloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=\ncloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=\ncloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=\ncloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=\ncloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=\ncloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=\ncloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=\ncloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=\ncloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=\ncloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=\ncloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=\ncloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=\ncloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=\ncloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=\ncloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=\ncloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=\ncloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=\ncloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=\ncloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=\ncloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=\ncloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=\ncloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=\ncloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=\ncloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=\ncloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=\ncloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=\ncloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=\ncloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=\ncloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=\ncloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=\ncloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=\ncloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=\ncloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=\ncloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=\ncloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=\ncloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=\ncloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=\ncloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=\ncloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=\ncloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=\ncloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=\ncloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=\ncloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=\ncloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=\ncloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=\ncloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=\ncloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=\ncloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=\ncloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=\ncloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=\ncloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=\ncloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=\ncloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=\ncloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=\ncloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=\ncloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=\ncloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=\ncloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=\ncloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=\ncloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=\ncloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=\ncloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=\ncloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=\ncloud.google.com/go/spanner v1.88.0 h1:HS+5TuEYZOVOXj9K+0EtrbTw7bKBLrMe3vgGsbnehmU=\ncloud.google.com/go/spanner v1.88.0/go.mod h1:MzulBwuuYwQUVdkZXBBFapmXee3N+sQrj2T/yup6uEE=\ncloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=\ncloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ncloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=\ncloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=\ncloud.google.com/go/storage v1.59.0 h1:9p3yDzEN9Vet4JnbN90FECIw6n4FCXcKBK1scxtQnw8=\ncloud.google.com/go/storage v1.59.0/go.mod h1:cMWbtM+anpC74gn6qjLh+exqYcfmB9Hqe5z6adx+CLI=\ncloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=\ncloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=\ncloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=\ncloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=\ncloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=\ncloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=\ncloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=\ncloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=\ncloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=\ncloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=\ncloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=\ndario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=\ndario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\nfilippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=\ngithub.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=\ngithub.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=\ngithub.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=\ngithub.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA=\ngithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM=\ngithub.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=\ngithub.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/ClickHouse/ch-go v0.71.0 h1:bUdZ/EZj/LcVHsMqaRUP2holqygrPWQKeMjc6nZoyRM=\ngithub.com/ClickHouse/ch-go v0.71.0/go.mod h1:NwbNc+7jaqfY58dmdDUbG4Jl22vThgx1cYjBw0vtgXw=\ngithub.com/ClickHouse/clickhouse-go/v2 v2.43.0 h1:fUR05TrF1GyvLDa/mAQjkx7KbgwdLRffs2n9O3WobtE=\ngithub.com/ClickHouse/clickhouse-go/v2 v2.43.0/go.mod h1:o6jf7JM/zveWC/PP277BLxjHy5KjnGX/jfljhM4s34g=\ngithub.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=\ngithub.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=\ngithub.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=\ngithub.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=\ngithub.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=\ngithub.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0 h1:BzsL0qE7LvtTEtXG7Dt5NS1EP0CQwI21HZfj9aGghhw=\ngithub.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0/go.mod h1:I7kE2kM3qCr9QPT4cU4cCFYkEpVyVr16YOGUHzy+nR0=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI=\ngithub.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=\ngithub.com/IBM/nzgo/v12 v12.0.10 h1:Mfc+lU/KyvNGMtprQNNGuGBgbrrlvCy0o8EUsm7fiH0=\ngithub.com/IBM/nzgo/v12 v12.0.10/go.mod h1:4pvfEkfsrAdqlljsp8HNwv/uzNKy2fzoXBB1aRIssJg=\ngithub.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=\ngithub.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=\ngithub.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=\ngithub.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=\ngithub.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=\ngithub.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=\ngithub.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=\ngithub.com/MichaelS11/go-cql-driver v0.1.1 h1:ntFKov/39Tl36HckP4tzld3XMeyDYHHO00MiZNdoL1A=\ngithub.com/MichaelS11/go-cql-driver v0.1.1/go.mod h1:rMwGk5bMWiYI/If6r6dbqEfZG6nQLvqJHTplv5yTDaw=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=\ngithub.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 h1:8XBWWQD+vFF+JqOsm16t0Kab1a7YWV8+GISVEP8AuZ8=\ngithub.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y=\ngithub.com/RaduBerinde/btreemap v0.0.0-20250419232817-bf0d809ae648 h1:0s1dtMVp3XcQ1tHazU9OCLCKoqj4TRD8GFU5SscItMM=\ngithub.com/RaduBerinde/btreemap v0.0.0-20250419232817-bf0d809ae648/go.mod h1:0tr7FllbE9gJkHq7CVeeDDFAFKQVy5RnCSSNBOvdqbc=\ngithub.com/SAP/go-hdb v1.15.1 h1:CZP/e14rlZgw6Dtnon+be4VR5NEBW3ygrCSLAnDVce4=\ngithub.com/SAP/go-hdb v1.15.1/go.mod h1:hudYRn/mEuyBvt7ekr4qmRbGpcJo++D/9B9vLHrjtZY=\ngithub.com/UNO-SOFT/zlog v0.8.1 h1:TEFkGJHtUfTRgMkLZiAjLSHALjwSBdw6/zByMC5GJt4=\ngithub.com/UNO-SOFT/zlog v0.8.1/go.mod h1:yqFOjn3OhvJ4j7ArJqQNA+9V+u6t9zSAyIZdWdMweWc=\ngithub.com/VictoriaMetrics/easyproto v1.2.0 h1:FJT9uNXA2isppFuJErbLqD306KoFlehl7Wn2dg/6oIE=\ngithub.com/VictoriaMetrics/easyproto v1.2.0/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=\ngithub.com/VoltDB/voltdb-client-go v1.0.18 h1:iVUw3z2MdV4oc6J45+W17Hn9TVrY5ArYN2ldxyqqxhs=\ngithub.com/VoltDB/voltdb-client-go v1.0.18/go.mod h1:2wVijB3aloOvpaygTJdgundq0hb3/sCPidzFClcmfXo=\ngithub.com/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f h1:JjxwchlOepwsUWcQwD2mLUAGE9aCp0/ehy6yCHFBOvo=\ngithub.com/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f/go.mod h1:tMDTce/yLLN/SK8gMOxQfnyeMeCg8KGzp0D1cbECEeo=\ngithub.com/ahmetb/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:3YVZUqkoev4mL+aCwVOSWV4M7pN+NURHL38Z2zq5JKA=\ngithub.com/ahmetb/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:ymXt5bw5uSNu4jveerFxE0vNYxF8ncqbptntMaFMg3k=\ngithub.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=\ngithub.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=\ngithub.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=\ngithub.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=\ngithub.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=\ngithub.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=\ngithub.com/alexbrainman/odbc v0.0.0-20250601004241-49e6b2bc0cf0 h1:gUrYWktqvF8PVb2SIBQR5WsFxjctn7d1JBIx/FrSzik=\ngithub.com/alexbrainman/odbc v0.0.0-20250601004241-49e6b2bc0cf0/go.mod h1:c5eyz5amZqTKvY3ipqerFO/74a/8CYmXOahSr40c+Ww=\ngithub.com/aliyun/aliyun-tablestore-go-sdk v1.7.3/go.mod h1:PWqq46gZJf7mnYTAuTmxKgx6EwJu3oBpOs1s2V0EZPM=\ngithub.com/aliyun/aliyun-tablestore-go-sdk v1.8.0 h1:qmBTupWC9eFbnN64UiXs1Zgba2wnRe/fm4NfIMunmU0=\ngithub.com/aliyun/aliyun-tablestore-go-sdk v1.8.0/go.mod h1:JzOJMpBPGN+4cuYnrGO5wdwphEyqbeGVY2vCaiAcNW8=\ngithub.com/aliyun/aliyun-tablestore-go-sql-driver v0.0.0-20220418015234-4d337cb3eed9 h1:DpsLZRlqHH1b2QyoLDK1/MtUtm7zuiQweA6hsTY97do=\ngithub.com/aliyun/aliyun-tablestore-go-sql-driver v0.0.0-20220418015234-4d337cb3eed9/go.mod h1:4yTI9ZSYNi4eENMKL8VWP22MzoDKeqDT4j7Fd103BVQ=\ngithub.com/amsokol/ignite-go-client v0.12.2 h1:q4Mr+UUiKVnR7ykjR1YARVS5jp+ZU6ekCIs0V4WgFDo=\ngithub.com/amsokol/ignite-go-client v0.12.2/go.mod h1:K3tKJGcLQORFD+ds7f0f9fl88tv0KZcpfuNhzRyuLVE=\ngithub.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=\ngithub.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/apache/arrow-go/v18 v18.5.1 h1:yaQ6zxMGgf9YCYw4/oaeOU3AULySDlAYDOcnr4LdHdI=\ngithub.com/apache/arrow-go/v18 v18.5.1/go.mod h1:OCCJsmdq8AsRm8FkBSSmYTwL/s4zHW9CqxeBxEytkNE=\ngithub.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg=\ngithub.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw=\ngithub.com/apache/arrow/go/v15 v15.0.2 h1:60IliRbiyTWCWjERBCkO1W4Qun9svcYoZrSLcyOsMLE=\ngithub.com/apache/arrow/go/v15 v15.0.2/go.mod h1:DGXsR3ajT524njufqf95822i+KTh+yea1jass9YXgjA=\ngithub.com/apache/arrow/go/v17 v17.0.0 h1:RRR2bdqKcdbss9Gxy2NS/hK8i4LDMh23L6BbkN5+F54=\ngithub.com/apache/arrow/go/v17 v17.0.0/go.mod h1:jR7QHkODl15PfYyjM2nU+yTLScZ/qfj7OSUZmJ8putc=\ngithub.com/apache/calcite-avatica-go/v5 v5.4.0 h1:snCrhGlwDgqNA2Rp7RUABjNX2zX+EfLk5K7PSJRPD5w=\ngithub.com/apache/calcite-avatica-go/v5 v5.4.0/go.mod h1:ed2DNx4xLzxrVYbvZU9Nv97LwyO6c0J7oGnOP4HbqZk=\ngithub.com/apache/thrift v0.22.0 h1:r7mTJdj51TMDe6RtcmNdQxgn9XcyfGDOzegMDRg47uc=\ngithub.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJeynQ+/g=\ngithub.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=\ngithub.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=\ngithub.com/aws/aws-sdk-go v1.37.32/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=\ngithub.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=\ngithub.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=\ngithub.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls=\ngithub.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4=\ngithub.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q=\ngithub.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5/go.mod h1:nVUlMLVV8ycXSb7mSkcNu9e3v/1TJq2RTlrPwhYWr5c=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE=\ngithub.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.29 h1:dQFhl5Bnl/SK1EVpgElK5dckAE+lMHXnl5WCeRvNEG0=\ngithub.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.29/go.mod h1:BtBP1TCx5BTCh1uTVXpo3b/odnRECBpZdL5oHQarJJs=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc=\ngithub.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.4 h1:s8fbFscel8NLpnz+ggR7ncW+lqhXIkmyHbgbPeT8yyM=\ngithub.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.4/go.mod h1:BazuWe/q/mMJ/NrSJBTbNBJiLq6u8reodbEZ4giRms4=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=\ngithub.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18 h1:eZioDaZGJ0tMM4gzmkNIO2aAoQd+je7Ug7TkvAzlmkU=\ngithub.com/aws/aws-sdk-go-v2/internal/v4a v1.4.18/go.mod h1:CCXwUKAJdoWr6/NcxZ+zsiPr6oH/Q5aTooRGYieAyj4=\ngithub.com/aws/aws-sdk-go-v2/service/dynamodb v1.53.5 h1:mSBrQCXMjEvLHsYyJVbN8QQlcITXwHEuu+8mX9e2bSo=\ngithub.com/aws/aws-sdk-go-v2/service/dynamodb v1.53.5/go.mod h1:eEuD0vTf9mIzsSjGBFWIaNQwtH5/mzViJOVQfnMY5DE=\ngithub.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.32.9 h1:mB79k/ZTxQL4oDPxLAf2rhcUEvXlHkj3loGA2O9xREk=\ngithub.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.32.9/go.mod h1:wXQmLDkBNh60jxAaRldON9poacv+GiSIBw/kRuT/mtE=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc=\ngithub.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10 h1:fJvQ5mIBVfKtiyx0AHY6HeWcRX5LGANLpq8SVR+Uazs=\ngithub.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.10/go.mod h1:Kzm5e6OmNH8VMkgK9t+ry5jEih4Y8whqs+1hrkxim1I=\ngithub.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.16 h1:8g4OLy3zfNzLV20wXmZgx+QumI9WhWHnd4GCdvETxs4=\ngithub.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.16/go.mod h1:5a78jwLMs7BaesU0UIhLfVy2ZmOEgOy6ewYQXKTD37Q=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk=\ngithub.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg=\ngithub.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY=\ngithub.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc=\ngithub.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs=\ngithub.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=\ngithub.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=\ngithub.com/beltran/gohive v1.8.1 h1:qlygmroy3mKtKIQSpV/FqXJHty1LsPxF+JTQA5mbjwU=\ngithub.com/beltran/gohive v1.8.1/go.mod h1:BCgNAhr/wnbyXfp2yN9ZY4pVrGrtVqG4hhNDDXIal1U=\ngithub.com/beltran/gosasl v1.0.0 h1:iiRtLxkvKhrNv3Ohh/n2NiyyfwIo/UbMzy/dZWiUHXE=\ngithub.com/beltran/gosasl v1.0.0/go.mod h1:Qx8cW6jkI8riyzmklj80kAIkv+iezFUTBiGU0qHhHes=\ngithub.com/beltran/gssapi v0.0.0-20200324152954-d86554db4bab h1:ayfcn60tXOSYy5zUN1AMSTQo4nJCf7hrdzAVchpPst4=\ngithub.com/beltran/gssapi v0.0.0-20200324152954-d86554db4bab/go.mod h1:GLe4UoSyvJ3cVG+DVtKen5eAiaD8mAJFuV5PT3Eeg9Q=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE=\ngithub.com/bitfield/gotestdox v0.2.2/go.mod h1:D+gwtS0urjBrzguAkTM2wodsTQYFHdpx8eqRJ3N+9pY=\ngithub.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=\ngithub.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=\ngithub.com/btnguyen2k/consu/checksum v1.1.1 h1:kdIJGk3yl83Nn1HxZRk3bXJM0xvlwTcTYUmZ8BiloPU=\ngithub.com/btnguyen2k/consu/checksum v1.1.1/go.mod h1:/zZ8EXdphDYEkBFua51hK9y3rODCPIkiZYnCDlHT670=\ngithub.com/btnguyen2k/consu/g18 v0.1.0 h1:IoS5w5QlOfkcrNOHJyICD6PgqLh+J5fIDqy3vRBVcVM=\ngithub.com/btnguyen2k/consu/g18 v0.1.0/go.mod h1:gTPcr87XdCLDISusRQyDey22/ZOw6bLh6EChxTLx6/c=\ngithub.com/btnguyen2k/consu/gjrc v0.2.2 h1:CAY8xPgvtWc7EMTE9gxam/BxMgTRRpc4Hs9QEyYxRUc=\ngithub.com/btnguyen2k/consu/gjrc v0.2.2/go.mod h1:Sc0NehbI0i8V6FAY9qX1we9XXbWNnrMOb9jNpYqGBWk=\ngithub.com/btnguyen2k/consu/olaf v0.1.3 h1:0dWWmN5nOB/9pJdo7o1S3wR2+l3kG7pXHv3Vwki8uNM=\ngithub.com/btnguyen2k/consu/olaf v0.1.3/go.mod h1:6ybEnJcdcK/PNiSfkKnMoxYuKyH2vJPBvHRuuZpPvD8=\ngithub.com/btnguyen2k/consu/reddo v0.1.7/go.mod h1:pdY5oIVX3noZIaZu3nvoKZ59+seXL/taXNGWh9xJDbg=\ngithub.com/btnguyen2k/consu/reddo v0.1.8/go.mod h1:pdY5oIVX3noZIaZu3nvoKZ59+seXL/taXNGWh9xJDbg=\ngithub.com/btnguyen2k/consu/reddo v0.1.9 h1:NZyEzRcDXzksNMnvZVZyJmGN6ZQQmHg4hIPCPbfsCBE=\ngithub.com/btnguyen2k/consu/reddo v0.1.9/go.mod h1:pdY5oIVX3noZIaZu3nvoKZ59+seXL/taXNGWh9xJDbg=\ngithub.com/btnguyen2k/consu/semita v0.1.5 h1:fu71xNJTbCV8T+6QPJdJu3bxtmLWvTjCepkvujF74+I=\ngithub.com/btnguyen2k/consu/semita v0.1.5/go.mod h1:fksCe3L4kxiJVnKKhUXKI8mcFdB9974mtedwUVVFu1M=\ngithub.com/btnguyen2k/consu/semver v0.2.1 h1:le0FzrM7u0IOR4MnOyBySHpZ/p3vV4JjofAhPB7edWE=\ngithub.com/btnguyen2k/consu/semver v0.2.1/go.mod h1:jxK/nwIWTXcWlcWcfkhPfLWq9b5dVzAtJLycySBFHTc=\ngithub.com/btnguyen2k/gocosmos v1.1.0 h1:16OIhDTAK6ChyjQMjG+yHEO/MGdO8UsCeJ/2xiY9eRE=\ngithub.com/btnguyen2k/gocosmos v1.1.0/go.mod h1:g599FZ7hAt6XZ108baotFrBr4U/r5xoyyQ8VyAZerL8=\ngithub.com/btnguyen2k/godynamo v1.3.0 h1:8Ri9gVWMvBWlD5P04AEVrl2QcmMQR7KgC3zSCl/YLWw=\ngithub.com/btnguyen2k/godynamo v1.3.0/go.mod h1:vNE48BoUAZS4F5ohrZ7suhw61DmCiXSClKrJfr2maTo=\ngithub.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=\ngithub.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=\ngithub.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=\ngithub.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=\ngithub.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=\ngithub.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\ngithub.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=\ngithub.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chaisql/chai v0.18.0 h1:lfamFZCeg8MeYX9cUeoXyZSOmVXuB+5nzhvXjUx4IBc=\ngithub.com/chaisql/chai v0.18.0/go.mod h1:9X+hiE9DUjCHIznbke4bUs2aWyWxMAfAHf7itlP5Fn0=\ngithub.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=\ngithub.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=\ngithub.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=\ngithub.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=\ngithub.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=\ngithub.com/cockroachdb/crlib v0.0.0-20251122031428-fe658a2dbda1 h1:iX0YCYC5Jbt2/g7zNTP/QxhrV8Syp5kkzNiERKeN1uE=\ngithub.com/cockroachdb/crlib v0.0.0-20251122031428-fe658a2dbda1/go.mod h1:NjNuToN/FbhwH1cCyM9G4Rhtxx+ZaOgtoqFR+thng7w=\ngithub.com/cockroachdb/datadriven v1.0.3-0.20250407164829-2945557346d5 h1:UycK/E0TkisVrQbSoxvU827FwgBBcZ95nRRmpj/12QI=\ngithub.com/cockroachdb/datadriven v1.0.3-0.20250407164829-2945557346d5/go.mod h1:jsaKMvD3RBCATk1/jbUZM8C9idWBJME9+VRZ5+Liq1g=\ngithub.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=\ngithub.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=\ngithub.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k=\ngithub.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo=\ngithub.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA=\ngithub.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA=\ngithub.com/cockroachdb/pebble/v2 v2.1.2 h1:IwYt+Y2Cdw6egblwk1kWzdmJvD2680t5VK/3i0BJ6IA=\ngithub.com/cockroachdb/pebble/v2 v2.1.2/go.mod h1:Aza05DCCc05ghIJZkB4Q/axv/JK9wx5cFwWcnhG0eGw=\ngithub.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4314=\ngithub.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=\ngithub.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI=\ngithub.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg=\ngithub.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g=\ngithub.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=\ngithub.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=\ngithub.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=\ngithub.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=\ngithub.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=\ngithub.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=\ngithub.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=\ngithub.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=\ngithub.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=\ngithub.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=\ngithub.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/couchbase/go-couchbase v0.1.1 h1:ClFXELcKj/ojyoTYbsY34QUrrYCBi/1G749sXSCkdhk=\ngithub.com/couchbase/go-couchbase v0.1.1/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=\ngithub.com/couchbase/go_n1ql v0.0.0-20220303011133-0ed4bf93e31d h1:jOxYt3U9z+tj2WDvacvBhXmHXDt+EUR5Hbu56wTw6QY=\ngithub.com/couchbase/go_n1ql v0.0.0-20220303011133-0ed4bf93e31d/go.mod h1:Rn19fO9CVfhJkqyIED9ixL5Kh5XuH7hXgDTxyfGY7hM=\ngithub.com/couchbase/gomemcached v0.3.3 h1:D7qqXLO8wNa4pn5oE65lT3pA3IeStn4joT7/JgGXzKc=\ngithub.com/couchbase/gomemcached v0.3.3/go.mod h1:pISAjweI42vljCumsJIo7CVhqIMIIP9g3Wfhl1JJw68=\ngithub.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=\ngithub.com/couchbase/goutils v0.2.0 h1:2hTV1VUunDcgd2ctYZ27Zv5ShACS0NAZ86viOFwp1gU=\ngithub.com/couchbase/goutils v0.2.0/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=\ngithub.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\ngithub.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=\ngithub.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=\ngithub.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=\ngithub.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=\ngithub.com/databricks/databricks-sql-go v1.10.0 h1:U17EKVC+hLP87swFMe2N6UUVektwUgTvT2pMDaDc46g=\ngithub.com/databricks/databricks-sql-go v1.10.0/go.mod h1:qC010ucrtqrNXY2UOcoczbfPD4gJ1jr1y6TL7iqyxPk=\ngithub.com/datafuselabs/databend-go v0.9.1 h1:hNQD+gaXPYC/raIrmtCpdW58l+MjGajGZmoGbKibEVc=\ngithub.com/datafuselabs/databend-go v0.9.1/go.mod h1:G90oi9bpPFdwpBeRW1DNBYjLsEtzFVq8R5nZCPqNn1Q=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=\ngithub.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=\ngithub.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=\ngithub.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk=\ngithub.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=\ngithub.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=\ngithub.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=\ngithub.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=\ngithub.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/dop251/goja v0.0.0-20251201205617-2bb4c724c0f9 h1:3uSSOd6mVlwcX3k5OYOpiDqFgRmaE2dBfLvVIFWWHrw=\ngithub.com/dop251/goja v0.0.0-20251201205617-2bb4c724c0f9/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=\ngithub.com/dop251/goja_nodejs v0.0.0-20251015164255-5e94316bedaf h1:gbmvliZnCut4NjaPSNOQlfqBoZ9C5Dpf72mHMMYhgVE=\ngithub.com/dop251/goja_nodejs v0.0.0-20251015164255-5e94316bedaf/go.mod h1:Tb7Xxye4LX7cT3i8YLvmPMGCV92IOi4CDZvm/V8ylc0=\ngithub.com/dromara/carbon/v2 v2.6.15 h1:3HuC3XcWczIHUTbg/f0CSVydtKEdM+P0GM1sdsbwXmI=\ngithub.com/dromara/carbon/v2 v2.6.15/go.mod h1:NGo3reeV5vhWCYWcSqbJRZm46MEwyfYI5EJRdVFoLJo=\ngithub.com/duckdb/duckdb-go-bindings v0.3.4 h1:K0h/G9AdQBAJ0cTI+Z2iDHT2/K94pBZXZawsG3qUAj8=\ngithub.com/duckdb/duckdb-go-bindings v0.3.4/go.mod h1:zS7OpBP8zwVlP38OljRZOnqWYlNd4KLcVfMoA1JFzpk=\ngithub.com/duckdb/duckdb-go-bindings/lib/darwin-amd64 v0.3.3 h1:ue8BtIOSt+2Bt2fEfTAvBcQLxzBFhgfCcyzPtqQWTRA=\ngithub.com/duckdb/duckdb-go-bindings/lib/darwin-amd64 v0.3.3/go.mod h1:EnAvZh1kNJHp5yF+M1ZHNEvapnmt6anq1xXHVrAGqMo=\ngithub.com/duckdb/duckdb-go-bindings/lib/darwin-arm64 v0.3.3 h1:2TrSeTgtwi3WIvub9ba0mny+AClSNo1w0Ghszc2B8lQ=\ngithub.com/duckdb/duckdb-go-bindings/lib/darwin-arm64 v0.3.3/go.mod h1:IGLSeEcFhNeZF16aVjQCULD7TsFZKG5G7SyKJAXKp5c=\ngithub.com/duckdb/duckdb-go-bindings/lib/linux-amd64 v0.3.3 h1:GN0cexhfE7uLb7qgDmsYG324wKF15nW+O7v5+NGalS4=\ngithub.com/duckdb/duckdb-go-bindings/lib/linux-amd64 v0.3.3/go.mod h1:KAIynZ0GHCS7X5fRyuFnQMg/SZBPK/bS9OCOVojClxw=\ngithub.com/duckdb/duckdb-go-bindings/lib/linux-arm64 v0.3.3 h1:bIJV+ct6yvMXjy+N3bfILFd0fkTK50AUhUTerkY40/8=\ngithub.com/duckdb/duckdb-go-bindings/lib/linux-arm64 v0.3.3/go.mod h1:81SGOYoEUs8qaAfSk1wRfM5oobrIJ5KI7AzYhK6/bvQ=\ngithub.com/duckdb/duckdb-go-bindings/lib/windows-amd64 v0.3.3 h1:SK2sunA/MPb2T3113iFzHv6DWeu+qrsw0DizTFrvM+Q=\ngithub.com/duckdb/duckdb-go-bindings/lib/windows-amd64 v0.3.3/go.mod h1:K25pJL26ARblGDeuAkrdblFvUen92+CwksLtPEHRqqQ=\ngithub.com/duckdb/duckdb-go/v2 v2.5.5 h1:TlK8ipnzoKW2aNrjGqRkFWLCDpJDxR/VwH8ezEcvVhw=\ngithub.com/duckdb/duckdb-go/v2 v2.5.5/go.mod h1:6uIbC3gz36NCEygECzboygOo/Z9TeVwox/puG+ohWV0=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/dvsekhvalnov/jose2go v1.8.0 h1:LqkkVKAlHFfH9LOEl5fe4p/zL02OhWE7pCufMBG2jLA=\ngithub.com/dvsekhvalnov/jose2go v1.8.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=\ngithub.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=\ngithub.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=\ngithub.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=\ngithub.com/elastic/go-sysinfo v1.8.1/go.mod h1:JfllUnzoQV/JRYymbH3dO1yggI3mV2oTKSXsDHM+uIM=\ngithub.com/elastic/go-sysinfo v1.15.4 h1:A3zQcunCxik14MgXu39cXFXcIw2sFXZ0zL886eyiv1Q=\ngithub.com/elastic/go-sysinfo v1.15.4/go.mod h1:ZBVXmqS368dOn/jvijV/zHLfakWTYHBZPk3G244lHrU=\ngithub.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU=\ngithub.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI=\ngithub.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=\ngithub.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=\ngithub.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=\ngithub.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=\ngithub.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=\ngithub.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=\ngithub.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=\ngithub.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=\ngithub.com/exasol/error-reporting-go v0.2.0 h1:nKIe4zYiTHbYrKJRlSNJcmGjTJCZredDh5akVHfIbRs=\ngithub.com/exasol/error-reporting-go v0.2.0/go.mod h1:lUzRJqKLiSuYpqRUN2LVyj08WeHzhMEC/8Gmgtuqh1Y=\ngithub.com/exasol/exasol-driver-go v1.0.16 h1:gN5hZccYSEQDOa04ktT3HAFYOl2CqddWLQYcGf6xpdk=\ngithub.com/exasol/exasol-driver-go v1.0.16/go.mod h1:wFlvan1L7KGJ1eyaQzw/oT2pCPs6TEh0tBaUvHVciZM=\ngithub.com/exasol/exasol-test-setup-abstraction-server/go-client v1.0.0 h1:+WyqR/tPCFD5sQSjoTokOOKigT9wu8CXwMWXST9xS+Y=\ngithub.com/exasol/exasol-test-setup-abstraction-server/go-client v1.0.0/go.mod h1:XzrsDcGk0X2BkSUYKEOE9MdXdOwPdA9RyKRAPCPBHKs=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=\ngithub.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=\ngithub.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo=\ngithub.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=\ngithub.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 h1:r5GgOLGbza2wVHRzK7aAj6lWZjfbAwiu/RDCVOKjRyM=\ngithub.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=\ngithub.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=\ngithub.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=\ngithub.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=\ngithub.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=\ngithub.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=\ngithub.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=\ngithub.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gorp/gorp v2.2.0+incompatible h1:xAUh4QgEeqPPhK3vxZN+bzrim1z5Av6q837gtjUlshc=\ngithub.com/go-gorp/gorp v2.2.0+incompatible/go.mod h1:7IfkAQnO7jfT/9IQ3R9wL1dFhukN6aQxzKTHnkxzA/E=\ngithub.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=\ngithub.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=\ngithub.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=\ngithub.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=\ngithub.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=\ngithub.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=\ngithub.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=\ngithub.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I=\ngithub.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=\ngithub.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=\ngithub.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=\ngithub.com/gocql/gocql v0.0.0-20200815110948-5378c8f664e9/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=\ngithub.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus=\ngithub.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4=\ngithub.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=\ngithub.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/godror/godror v0.50.0 h1:c0ZnGSDFT12E8HJfQwxtqcmybaIkbqACNk4lIfkkESc=\ngithub.com/godror/godror v0.50.0/go.mod h1:kTMcxZzRw73RT5kn9v3JkBK4kHI6dqowHotqV72ebU8=\ngithub.com/godror/knownpb v0.3.0 h1:+caUdy8hTtl7X05aPl3tdL540TvCcaQA6woZQroLZMw=\ngithub.com/godror/knownpb v0.3.0/go.mod h1:PpTyfJwiOEAzQl7NtVCM8kdPCnp3uhxsZYIzZ5PV4zU=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/gohxs/readline v0.0.0-20171011095936-a780388e6e7c h1:yE35fKFwcelIte3q5q1/cPiY7pI7vvf5/j/0ddxNCKs=\ngithub.com/gohxs/readline v0.0.0-20171011095936-a780388e6e7c/go.mod h1:9S/fKAutQ6wVHqm1jnp9D9sc5hu689s9AaTWFS92LaU=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=\ngithub.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=\ngithub.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=\ngithub.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=\ngithub.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=\ngithub.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\ngithub.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=\ngithub.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=\ngithub.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=\ngithub.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=\ngithub.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=\ngithub.com/google/flatbuffers/go v0.0.0-20230110200425-62e4d2e5b215 h1:HA3/6NDG1q6qLD2sCqDFT/ua/1/wctoLo7leuFcFdSE=\ngithub.com/google/flatbuffers/go v0.0.0-20230110200425-62e4d2e5b215/go.mod h1:qmRCJW6OqZkfBt584Cmq1im0f4367CLrdABrq5lMOWo=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f h1:7MmqygqdeJtziBUpm4Z9ThROFZUaVGaePMfcDnluf1E=\ngithub.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f/go.mod h1:n1ej5+FqyEytMt/mugVDZLIiqTMO+vsrgY+kM6ohzN0=\ngithub.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=\ngithub.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 h1:CVuJwN34x4xM2aT4sIKhmeib40NeBPhRihNjQmpJsA4=\ngithub.com/google/goterm v0.0.0-20200907032337-555d40f16ae2/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=\ngithub.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=\ngithub.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=\ngithub.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20251213031049-b05bdaca462f h1:HU1RgM6NALf/KW9HEY6zry3ADbDKcmpQ+hJedoNGQYQ=\ngithub.com/google/pprof v0.0.0-20251213031049-b05bdaca462f/go.mod h1:67FPmZWbr+KDT/VlpWtw6sO9XSjpJmLuHpoLmWiTGgY=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=\ngithub.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=\ngithub.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=\ngithub.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.12 h1:Fg+zsqzYEs1ZnvmcztTYxhgCBsx3eEhEwQ1W/lHq/sQ=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.12/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=\ngithub.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=\ngithub.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=\ngithub.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=\ngithub.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=\ngithub.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=\ngithub.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=\ngithub.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=\ngithub.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=\ngithub.com/googleapis/go-sql-spanner v1.24.0 h1:K36siIx0KEka6Xttu2Rti9cBGK7mYQIoo6gUrW87uTU=\ngithub.com/googleapis/go-sql-spanner v1.24.0/go.mod h1:ltBracyoOyIYJjTQcDxuYmJDfPgknsQMs63liLSF4AA=\ngithub.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=\ngithub.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=\ngithub.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=\ngithub.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=\ngithub.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=\ngithub.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=\ngithub.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=\ngithub.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=\ngithub.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=\ngithub.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=\ngithub.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=\ngithub.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=\ngithub.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=\ngithub.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=\ngithub.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4=\ngithub.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=\ngithub.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=\ngithub.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=\ngithub.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=\ngithub.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=\ngithub.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=\ngithub.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=\ngithub.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=\ngithub.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=\ngithub.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=\ngithub.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=\ngithub.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=\ngithub.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=\ngithub.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=\ngithub.com/jedib0t/go-pretty/v6 v6.2.7/go.mod h1:FMkOpgGD3EZ91cW8g/96RfxoV7bdeJyzXPYgz1L1ln0=\ngithub.com/jedib0t/go-pretty/v6 v6.7.7 h1:Y1Id3lJ3k4UB8uwWWy3l8EVFnUlx5chR5+VbsofPNX0=\ngithub.com/jedib0t/go-pretty/v6 v6.7.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=\ngithub.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=\ngithub.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=\ngithub.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=\ngithub.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=\ngithub.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=\ngithub.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=\ngithub.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=\ngithub.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=\ngithub.com/jmrobles/h2go v0.5.0 h1:r+V3J1+8z5tExKHcVc8u0tXJfov391zEffJYALWKhA0=\ngithub.com/jmrobles/h2go v0.5.0/go.mod h1:p7Vjfu/9f7g2RI1CkpwXnwqskV+47HviBg4C4FlW8eI=\ngithub.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=\ngithub.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=\ngithub.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=\ngithub.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=\ngithub.com/kenshaw/colors v0.2.3 h1:z/IIrevOGcc3r179X8WC1DOm5LgYAir0dw9cnVgtsvM=\ngithub.com/kenshaw/colors v0.2.3/go.mod h1:Aok7+9KpR+qEwgCxDEoLBS6IGFhY1iRJIzbcv5ijewI=\ngithub.com/kenshaw/rasterm v0.1.16 h1:IUmAe/C7YTY/em1irbNvd7cMR8HT6BSoN0931BzjmhQ=\ngithub.com/kenshaw/rasterm v0.1.16/go.mod h1:VuAzsiWase7gJtuoKc4xPxdxq+ljBXrh/QNz6yy7uV0=\ngithub.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=\ngithub.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=\ngithub.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=\ngithub.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=\ngithub.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=\ngithub.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=\ngithub.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=\ngithub.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=\ngithub.com/mattn/go-adodb v0.0.1 h1:g/pk3V8m/WFX2IQRI58wAC24OQUFFXEiNsvs7dQ1WKg=\ngithub.com/mattn/go-adodb v0.0.1/go.mod h1:jaSTRde4bohMuQgYQPxW3xRTPtX/cZKyxPrFVseJULo=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=\ngithub.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=\ngithub.com/mattn/go-sixel v0.0.8 h1:H0bBGQVOJoSvzvtTgCInxvg1IZiNlTcIIIx8A6uvjpQ=\ngithub.com/mattn/go-sixel v0.0.8/go.mod h1:wbDSbrwpykVI1qEHyjZYsDgaJTwpVg9wSwmmh2slnBw=\ngithub.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=\ngithub.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=\ngithub.com/microsoft/go-mssqldb v1.9.8 h1:d4IFMvF/o+HdpXUqbBfzHvn/NlFA75YGcfHUUvDFJEM=\ngithub.com/microsoft/go-mssqldb v1.9.8/go.mod h1:eGSRSGAW4hKMy5YcAenhCDjIRm2rhqIdmmwgciMzLus=\ngithub.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs=\ngithub.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=\ngithub.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=\ngithub.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=\ngithub.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=\ngithub.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mithrandie/csvq v1.18.1 h1:f7NB2scbb7xx2ffPduJ2VtZ85RpWXfvanYskAkGlCBU=\ngithub.com/mithrandie/csvq v1.18.1/go.mod h1:MRJj7AtcXfk7jhNGxLuJGP3LORmh4lpiPWxQ7VyCRn8=\ngithub.com/mithrandie/csvq-driver v1.7.0 h1:ejiavXNWwTPMyr3fJFnhcqd1L1cYudA0foQy9cZrqhw=\ngithub.com/mithrandie/csvq-driver v1.7.0/go.mod h1:HcN3xL9UCJnBYA/AIQOOB/KlyfXAiYr5yxDmiwrGk5o=\ngithub.com/mithrandie/go-file/v2 v2.1.0 h1:XA5Tl+73GXMDvgwSE3Sg0uC5FkLr3hnXs8SpUas0hyg=\ngithub.com/mithrandie/go-file/v2 v2.1.0/go.mod h1:9YtTF3Xo59GqC1Pxw6KyGVcM/qubAMlxVsqI/u9r++c=\ngithub.com/mithrandie/go-text v1.6.0 h1:8gOXTMPbMY8DJbKMTv8kHhADcJlDWXqS/YQH4SyWO6s=\ngithub.com/mithrandie/go-text v1.6.0/go.mod h1:xCgj1xiNbI/d4xA9sLVvXkjh5B2tNx2ZT2/3rpmh8to=\ngithub.com/mithrandie/ternary v1.1.1 h1:k/joD6UGVYxHixYmSR8EGgDFNONBMqyD373xT4QRdC4=\ngithub.com/mithrandie/ternary v1.1.1/go.mod h1:0D9Ba3+09K2TdSZO7/bFCC0GjSXetCvYuYq0u8FY/1g=\ngithub.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=\ngithub.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=\ngithub.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=\ngithub.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=\ngithub.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=\ngithub.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=\ngithub.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=\ngithub.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=\ngithub.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=\ngithub.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=\ngithub.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=\ngithub.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=\ngithub.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=\ngithub.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=\ngithub.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=\ngithub.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=\ngithub.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/murfffi/gorich v0.3.0 h1:cRsCCTD0A2eyiAjeSMwyOkjALDsVzgJO0ghY+cXsDJY=\ngithub.com/murfffi/gorich v0.3.0/go.mod h1:fozPmSzPmc1r0xnNtk3HE6xxxlzlBqo4rlChD/iJZcM=\ngithub.com/nakagami/chacha20 v0.1.0 h1:2fbf5KeVUw7oRpAe6/A7DqvBJLYYu0ka5WstFbnkEVo=\ngithub.com/nakagami/chacha20 v0.1.0/go.mod h1:xpoujepNFA7MvYLvX5xKHzlOHimDrLI9Ll8zfOJ0l2E=\ngithub.com/nakagami/firebirdsql v0.9.15 h1:Mf05jaFI8+kjy6sBstsAu76zOkJ44AGd6cpApWNrp/0=\ngithub.com/nakagami/firebirdsql v0.9.15/go.mod h1:bZKRs3rpHAjJgXAoc9YiPobTz3R22i41Zjo+llIS2B0=\ngithub.com/nathan-fiscaletti/consolesize-go v0.0.0-20220204101620-317176b6684d h1:NqRhLdNVlozULwM1B3VaHhcXYSgrOAv8V5BE65om+1Q=\ngithub.com/nathan-fiscaletti/consolesize-go v0.0.0-20220204101620-317176b6684d/go.mod h1:cxIIfNMTwff8f/ZvRouvWYF6wOoO7nj99neWSx2q/Es=\ngithub.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=\ngithub.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=\ngithub.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=\ngithub.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=\ngithub.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=\ngithub.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=\ngithub.com/opencontainers/runc v1.3.1 h1:c/yY0oh2wK7tzDuD56REnSxyU8ubh8hoAIOLGLrm4SM=\ngithub.com/opencontainers/runc v1.3.1/go.mod h1:9wbWt42gV+KRxKRVVugNP6D5+PQciRbenB4fLVsqGPs=\ngithub.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=\ngithub.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=\ngithub.com/paulmach/orb v0.12.0 h1:z+zOwjmG3MyEEqzv92UN49Lg1JFYx0L9GpGKNVDKk1s=\ngithub.com/paulmach/orb v0.12.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=\ngithub.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=\ngithub.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=\ngithub.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=\ngithub.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=\ngithub.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=\ngithub.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=\ngithub.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=\ngithub.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=\ngithub.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prestodb/presto-go-client v0.0.0-20240426182841-905ac40a1783 h1:1/uuAh1vatqywFmudA7PHVUc/Iu5W4iFft1r7MVubf8=\ngithub.com/prestodb/presto-go-client v0.0.0-20240426182841-905ac40a1783/go.mod h1:9mH1KvIoMeUe/OIs6WCJGvrR15FvC0y+SSMkIQQkF3M=\ngithub.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=\ngithub.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=\ngithub.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=\ngithub.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=\ngithub.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=\ngithub.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=\ngithub.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=\ngithub.com/proullon/ramsql v0.1.4 h1:yTFRTn46gFH/kPbzCx+mGjuFlyTBUeDr3h2ldwxddl0=\ngithub.com/proullon/ramsql v0.1.4/go.mod h1:CFGqeQHQpdRfWqYmWD3yXqPTEaHkF4zgXy1C6qDWc9E=\ngithub.com/rekby/fixenv v0.6.1 h1:jUFiSPpajT4WY2cYuc++7Y1zWrnCxnovGCIX72PZniM=\ngithub.com/rekby/fixenv v0.6.1/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c=\ngithub.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=\ngithub.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=\ngithub.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=\ngithub.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=\ngithub.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=\ngithub.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=\ngithub.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sclgo/impala-go v1.4.1 h1:31r2MQ2NL+ia0Ik0LoHFOzTE8akzQ5SiHakyUXMWUXI=\ngithub.com/sclgo/impala-go v1.4.1/go.mod h1:3qX0kRHejmoOjHkTtdLu/nxH0vitV9A+z+hGs15WTVw=\ngithub.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=\ngithub.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=\ngithub.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=\ngithub.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=\ngithub.com/sijms/go-ora/v2 v2.9.0 h1:+iQbUeTeCOFMb5BsOMgUhV8KWyrv9yjKpcK4x7+MFrg=\ngithub.com/sijms/go-ora/v2 v2.9.0/go.mod h1:QgFInVi3ZWyqAiJwzBQA+nbKYKH77tdp1PYoCqhR2dU=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/snowflakedb/gosnowflake v1.19.0 h1:Oy/w5/hXiSJV09kgG9zpFZFjNRNvF5Cet7r6vzd87OQ=\ngithub.com/snowflakedb/gosnowflake v1.19.0/go.mod h1:7D4+cLepOWrerVsH+tevW3zdMJ5/WrEN7ZceAC6xBv0=\ngithub.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=\ngithub.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=\ngithub.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=\ngithub.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=\ngithub.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=\ngithub.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=\ngithub.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=\ngithub.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/thda/tds v0.1.7 h1:s29kbnJK0agL3ps85A/sb9XS2uxgKF5UJ6AZjbyqXX4=\ngithub.com/thda/tds v0.1.7/go.mod h1:isLIF1oZdXfkqVMJM8RyNrsjlHPlTKnPlnsBs7ngZcM=\ngithub.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/trinodb/trino-go-client v0.333.0 h1:+bsW8/uLFNF00MEL9JZJym94LlUnle25VgDlWGPEZos=\ngithub.com/trinodb/trino-go-client v0.333.0/go.mod h1:91okdYtRUZoj3XJu/tqdzu11sNliQuN4A+vMFEB8GVE=\ngithub.com/uber-go/tally v3.3.17+incompatible h1:nFHIuW3VQ22wItiE9kPXic8dEgExWOsVOHwpmoIvsMw=\ngithub.com/uber-go/tally v3.3.17+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU=\ngithub.com/uber/athenadriver v1.1.15 h1:z/hivAcXmGgUCVoXgVvwwIzc4auTeF3TCmwyFTtd8NE=\ngithub.com/uber/athenadriver v1.1.15/go.mod h1:RnKD7+9Aup8iuFfhK+I26U+z137IXWeoLaEZDepd0Eg=\ngithub.com/vertica/vertica-sql-go v1.3.5 h1:IrfH2WIgzZ45yDHyjVFrXU2LuKNIjF5Nwi90a6cfgUI=\ngithub.com/vertica/vertica-sql-go v1.3.5/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=\ngithub.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/xo/dburl v0.24.2 h1:aK6ASamrFjKl76h/UCBecc0BPBi97+IVmw4YWxx0rno=\ngithub.com/xo/dburl v0.24.2/go.mod h1:uazlaAQxj4gkshhfuuYyvwCBouOmNnG2aDxTCFZpmL4=\ngithub.com/xo/echartsgoja v0.1.1 h1:9ZH7rhjovE/caGzWTFw1X5GItCsPw5eFV2Qwfgmjt7M=\ngithub.com/xo/echartsgoja v0.1.1/go.mod h1:u2iiKyIA1H3URjh9+8Nltj8cP33R19JHasXhSz/tWHI=\ngithub.com/xo/resvg v0.6.0 h1:GsovErv9JuOnGttOA8RhQcBI7DEEVpEiIEKBuJVRS4g=\ngithub.com/xo/resvg v0.6.0/go.mod h1:xsIgOmL6UD2xRHIm2Laepjm/b4auoPMxAAqOHkvbSes=\ngithub.com/xo/tblfmt v0.0.0-20190609041254-28c54ec42ce8/go.mod h1:3U5kKQdIhwACye7ml3acccHmjGExY9WmUGU7rnDWgv0=\ngithub.com/xo/tblfmt v0.16.0 h1:Bh1+mEtA/tggn0YL0oIHgrtsRTskWTLKurG7JWYuk6k=\ngithub.com/xo/tblfmt v0.16.0/go.mod h1:uA07YKaGqDkK0sRhtFleWAtzwF2i1pqFcM9zHI2S1g0=\ngithub.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=\ngithub.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=\ngithub.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ=\ngithub.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=\ngithub.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=\ngithub.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=\ngithub.com/ydb-platform/ydb-go-genproto v0.0.0-20260128080146-c4ed16b24b37 h1:kUXMT/fM/DpDT66WQgRUf3I8VOAWjypkMf52W5PChwA=\ngithub.com/ydb-platform/ydb-go-genproto v0.0.0-20260128080146-c4ed16b24b37/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I=\ngithub.com/ydb-platform/ydb-go-sdk/v3 v3.127.2 h1:GyG12uYgpxp9Oeux8XDTVA+zDOwa/WIh5fGNIF5meW4=\ngithub.com/ydb-platform/ydb-go-sdk/v3 v3.127.2/go.mod h1:stS1mQYjbJvwwYaYzKyFY9eMiuVXWWXQA6T+SpOLg9c=\ngithub.com/yookoala/realpath v1.0.0 h1:7OA9pj4FZd+oZDsyvXWQvjn5oBdcHRTV44PpdMSuImQ=\ngithub.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=\ngithub.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=\ngithub.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=\ngithub.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=\ngithub.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=\ngithub.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=\ngithub.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4=\ngitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs=\ngitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=\ngo.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=\ngo.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/detectors/gcp v1.41.0 h1:MBzEwqhroF0JK0DpTVYWDxsenxm6L4PqOEfA90uZ5AA=\ngo.opentelemetry.io/contrib/detectors/gcp v1.41.0/go.mod h1:5pSDD0v0t2HqUmPC5cBBc+nLQO4dLYWnzBNheXLBLgs=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.66.0 h1:w/o339tDd6Qtu3+ytwt+/jon2yjAs3Ot8Xq8pelfhSo=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.66.0/go.mod h1:pdhNtM9C4H5fRdrnwO7NjxzQWhKSSxCHk/KluVqDVC0=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0 h1:PnV4kVnw0zOmwwFkAzCN5O07fw1YOIQor120zrh0AVo=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.66.0/go.mod h1:ofAwF4uinaf8SXdVzzbL4OsxJ3VfeEg3f/F6CeF49/Y=\ngo.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=\ngo.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=\ngo.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=\ngo.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=\ngo.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8=\ngo.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90=\ngo.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8=\ngo.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y=\ngo.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=\ngo.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=\ngo.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/config v1.4.0/go.mod h1:aCyrMHmUAc/s2h9sv1koP84M9ZF/4K+g2oleyESO/Ig=\ngo.uber.org/dig v1.9.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw=\ngo.uber.org/fx v1.12.0/go.mod h1:egT3Kyg1JFYQkvKLZ3EsykxkNrZxgXS+gKoKo7abERY=\ngo.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=\ngo.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=\ngo.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=\ngo.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngo.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=\ngo.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=\ngo.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\ngo.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=\ngo.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=\ngolang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=\ngolang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=\ngolang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=\ngolang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=\ngolang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=\ngolang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=\ngolang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=\ngolang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=\ngolang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=\ngolang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=\ngolang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=\ngolang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=\ngolang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=\ngolang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=\ngolang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=\ngolang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1 h1:QNaHp8YvpPswfDNxlCmJyeesxbGOgaKf41iT9/QrErY=\ngolang.org/x/telemetry v0.0.0-20260213145524-e0ab670178e1/go.mod h1:NuITXsA9cTiqnXtVk+/wrBT2Ja4X5hsfGOYRJ6kgYjs=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=\ngolang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=\ngolang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=\ngolang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=\ngolang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=\ngolang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190802003818-e9bb7d36c060/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191104232314-dc038396d1f0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=\ngolang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=\ngolang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=\ngolang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngolang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngolang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=\ngolang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=\ngoogle.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=\ngoogle.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=\ngoogle.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=\ngoogle.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=\ngoogle.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=\ngoogle.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=\ngoogle.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=\ngoogle.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=\ngoogle.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=\ngoogle.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=\ngoogle.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=\ngoogle.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=\ngoogle.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=\ngoogle.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=\ngoogle.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=\ngoogle.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=\ngoogle.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=\ngoogle.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=\ngoogle.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=\ngoogle.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=\ngoogle.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=\ngoogle.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=\ngoogle.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=\ngoogle.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=\ngoogle.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=\ngoogle.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08=\ngoogle.golang.org/api v0.269.0 h1:qDrTOxKUQ/P0MveH6a7vZ+DNHxJQjtGm/uvdbdGXCQg=\ngoogle.golang.org/api v0.269.0/go.mod h1:N8Wpcu23Tlccl0zSHEkcAZQKDLdquxK+l9r2LkwAauE=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=\ngoogle.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=\ngoogle.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=\ngoogle.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=\ngoogle.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=\ngoogle.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=\ngoogle.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=\ngoogle.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=\ngoogle.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=\ngoogle.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=\ngoogle.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=\ngoogle.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=\ngoogle.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=\ngoogle.golang.org/genproto v0.0.0-20260226221140-a57be14db171 h1:RxhCsti413yL0IjU9dVvuTbCISo8gs3RW1jPMStck+4=\ngoogle.golang.org/genproto v0.0.0-20260226221140-a57be14db171/go.mod h1:uhvzakVEqAuXU3TC2JCsxIRe5f77l+JySE3EqPoMyqM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\ngoogle.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=\ngoogle.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=\ngoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=\ngopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=\ngopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=\ngopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=\ngopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI=\ngopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=\ngopkg.in/jcmturner/gokrb5.v6 v6.1.1 h1:n0KFjpbuM5pFMN38/Ay+Br3l91netGSVqHPHEXeWUqk=\ngopkg.in/jcmturner/gokrb5.v6 v6.1.1/go.mod h1:NFjHNLrHQiruory+EmqDXCGv6CrjkeYeA+bR9mIfNFk=\ngopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=\ngopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngorm.io/driver/bigquery v1.2.0 h1:E94oEXErYb4uImcR8oiCjE1SP2VdnrL5f3d78PtFWNk=\ngorm.io/driver/bigquery v1.2.0/go.mod h1:/5kcyb6RVIk/seff6YANAjB5aisE4oqY35x0Ix9iwXY=\ngorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=\ngorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=\ngorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=\ngorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=\ngorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=\ngotest.tools/gotestsum v1.13.0 h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo=\ngotest.tools/gotestsum v1.13.0/go.mod h1:7f0NS5hFb0dWr4NtcsAsF0y1kzjEFfAil0HiBQJE03Q=\ngotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=\ngotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhowett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=\nhowett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=\nhowett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=\nmodernc.org/b v1.1.0 h1:sFmr2MlofAtx5R0NC0btblNww5dqIHxXyT0SEiaTSIk=\nmodernc.org/b v1.1.0/go.mod h1:yF+wmBAFjebNdVqZNTeNfmnLaLqq91wozvDLcuXz+ck=\nmodernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=\nmodernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=\nmodernc.org/ccgo/v4 v4.31.0 h1:/bsaxqdgX3gy/0DboxcvWrc3NpzH+6wpFfI/ZaA/hrg=\nmodernc.org/ccgo/v4 v4.31.0/go.mod h1:jKe8kPBjIN/VdGTVqARTQ8N1gAziBmiISY8j5HoKwjg=\nmodernc.org/db v1.0.29 h1:irgsDp4SH0VqmdIeuyZmBtH79ETnxTqhC1O10nGe73g=\nmodernc.org/db v1.0.29/go.mod h1:6Q1hiJ/bHLUOOFhNMb9ujAes3GH10a7fTUTDQ7eMV5E=\nmodernc.org/file v1.0.19 h1:D0YRfPfuIgcGtozt+D8kwX8wN8TABAf1bV0mzhPUrrk=\nmodernc.org/file v1.0.19/go.mod h1:G9DjBo352Wgitb3GYSN1FAf3+N7IkpCjnvslO7knhA8=\nmodernc.org/fileutil v1.1.2/go.mod h1:HdjlliqRHrMAI4nVOvvpYVzVgvRSK7WnoCiG0GUWJNo=\nmodernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=\nmodernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=\nmodernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=\nmodernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=\nmodernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=\nmodernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=\nmodernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=\nmodernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=\nmodernc.org/golex v1.1.0 h1:dmSaksHMd+y6NkBsRsCShNPRaSNCNH+abrVm5/gZic8=\nmodernc.org/golex v1.1.0/go.mod h1:2pVlfqApurXhR1m0N+WDYu6Twnc4QuvO4+U8HnwoiRA=\nmodernc.org/internal v1.0.8/go.mod h1:km71QBJPWkc1+LUldg2U9TJsKT6Q2QKHIykdEeCy/jw=\nmodernc.org/internal v1.1.9 h1:ldAWZkxkOTXXLKXRmEgEGIax7kzE4Q4JoIh6ouvDKgM=\nmodernc.org/internal v1.1.9/go.mod h1:5s+KF0W41ZrnzvPkq/p/8yorM8gKw5OmcnzNNZHfNUs=\nmodernc.org/libc v1.69.0 h1:YQJ5QMSReTgQ3QFmI0dudfjXIjCcYTUxcH8/9P9f0D8=\nmodernc.org/libc v1.69.0/go.mod h1:YfLLduUEbodNV2xLU5JOnRHBTAHVHsVW3bVYGw0ZCV4=\nmodernc.org/lldb v1.0.8 h1:gM0Lpmgtw0h/ylWQSxABvzJ++TZKhf1Q/uPAGBAM6aU=\nmodernc.org/lldb v1.0.8/go.mod h1:ybOcsZ/RNZo3q8fiGadQFRnD+1Jc+RWGcTPdeilCnUk=\nmodernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=\nmodernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=\nmodernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=\nmodernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=\nmodernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=\nmodernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=\nmodernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=\nmodernc.org/ql v1.4.31 h1:GdUqcdhULwfr0FLzjdqpYeX433yjXzU7xjXAild37K4=\nmodernc.org/ql v1.4.31/go.mod h1:LEOgRQzURtIngtsUP46v20Yr49WCXv5UAoMPbnVSXAA=\nmodernc.org/sortutil v1.1.1/go.mod h1:DTj/8BqjEBLZFVPYvEGDfFFg94SsfPxQ70R+SQJ98qA=\nmodernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=\nmodernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=\nmodernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=\nmodernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=\nmodernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=\nmodernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=\nmodernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=\nmodernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=\nmodernc.org/zappy v1.0.9/go.mod h1:y2c4Hv5jzyBP179SxNmx5H/BM6cVgNIXPQv2bCeR6IM=\nmodernc.org/zappy v1.1.0 h1:cAf9HrymATNo2hYMc9c37y0tiZJYuKM2xa1ZAP8THUw=\nmodernc.org/zappy v1.1.0/go.mod h1:cxC0dWAgZuyMsJ+KL3ZBgo3twyKGBB/0By/umSZE2bQ=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsqlflow.org/gohive v0.0.0-20240730014249-8960223660e2 h1:zvkshqW4meDpDadU5rhwejwJrZVCfxo5qpJ4NoZeGbE=\nsqlflow.org/gohive v0.0.0-20240730014249-8960223660e2/go.mod h1:OAU0/vkmdKfZ363QgGTChI35KIBsS63sZWDNWcFFcBM=\nsqlflow.org/gomaxcompute v0.0.0-20210805062559-c14ae028b44c h1:Zo3qlfUn/rlMx9vWHpGE/luEtweuXHwrYbrFZwTG978=\nsqlflow.org/gomaxcompute v0.0.0-20210805062559-c14ae028b44c/go.mod h1:MxRFJp6UEk1OfnnVOIL3Jc7ROBH0dOpwF/J14A9LNdM=\n"
  },
  {
    "path": "handler/handler.go",
    "content": "// Package handler provides a input process handler implementation for usql.\npackage handler\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"maps\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/signal\"\n\t\"os/user\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\t\"unicode\"\n\n\t\"github.com/alecthomas/chroma/v2\"\n\t\"github.com/alecthomas/chroma/v2/formatters\"\n\t\"github.com/alecthomas/chroma/v2/styles\"\n\t\"github.com/go-git/go-billy/v5\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/dburl/passfile\"\n\t\"github.com/xo/echartsgoja\"\n\t\"github.com/xo/resvg\"\n\t\"github.com/xo/tblfmt\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/completer\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/metacmd\"\n\t\"github.com/xo/usql/metacmd/charts\"\n\t\"github.com/xo/usql/rline\"\n\t\"github.com/xo/usql/stmt\"\n\tustyles \"github.com/xo/usql/styles\"\n\t\"github.com/xo/usql/text\"\n)\n\n// Handler is a input process handler.\n//\n// Glues together usql's components to provide a \"read-eval-print loop\" (REPL)\n// for usql's interactive command-line and manages most of the core/high-level logic.\n//\n// Manages the active statement buffer, application IO, executing/querying SQL\n// statements, and handles backslash (\\) meta commands encountered in the input\n// stream.\ntype Handler struct {\n\t// l is the readline handler.\n\tl rline.IO\n\t// user is the user.\n\tuser *user.User\n\t// wd is the working directory.\n\twd string\n\t// charts is the charts filesystem.\n\tcharts billy.Filesystem\n\t// nopw indicates not asking for password.\n\tnopw bool\n\t// timing of every command executed.\n\ttiming bool\n\t// singleLineMode is single line mode.\n\tsingleLineMode bool\n\t// buf is the query statement buffer.\n\tbuf *stmt.Stmt\n\t// lastExec is the last executed query statement.\n\tlastExec string\n\t// lastExecPrefix is the last executed query statement prefix.\n\tlastExecPrefix string\n\t// lastPrint is the last executed printable query statement.\n\tlastPrint string\n\t// lastRaw is the last executed raw query statement.\n\tlastRaw string\n\t// batch indicates a batch has been started.\n\tbatch bool\n\t// batchEnd is the batch end string.\n\tbatchEnd string\n\t// bind are bound values for that will be used for statement execution.\n\tbind []interface{}\n\t// u is the active connection information.\n\tu *dburl.URL\n\t// db is the active database connection.\n\tdb *sql.DB\n\t// tx is the active transaction, if any.\n\ttx *sql.Tx\n\t// out file or pipe\n\tout io.WriteCloser\n}\n\n// New creates a new input handler.\nfunc New(l rline.IO, user *user.User, wd string, charts billy.Filesystem, nopw bool) *Handler {\n\tf, iactive := l.Next, l.Interactive()\n\tif iactive {\n\t\tf = func() ([]rune, error) {\n\t\t\t// next line\n\t\t\tr, err := l.Next()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\t// save history\n\t\t\t_ = l.Save(string(r))\n\t\t\treturn r, nil\n\t\t}\n\t}\n\th := &Handler{\n\t\tl:      l,\n\t\tuser:   user,\n\t\twd:     wd,\n\t\tcharts: charts,\n\t\tnopw:   nopw,\n\t\tbuf:    stmt.New(f),\n\t}\n\tif iactive {\n\t\tl.SetOutput(h.outputHighlighter)\n\t\tl.Completer(completer.NewDefaultCompleter(completer.WithConnStrings(h.connStrings())))\n\t}\n\treturn h\n}\n\n// GetTiming gets the timing toggle.\nfunc (h *Handler) GetTiming() bool {\n\treturn h.timing\n}\n\n// SetTiming sets the timing toggle.\nfunc (h *Handler) SetTiming(timing bool) {\n\th.timing = timing\n}\n\n// SetSingleLineMode sets the single line mode toggle.\nfunc (h *Handler) SetSingleLineMode(singleLineMode bool) {\n\th.singleLineMode = singleLineMode\n}\n\n// Run executes queries and commands.\nfunc (h *Handler) Run() error {\n\tstdout, stderr, iactive := h.l.Stdout(), h.l.Stderr(), h.l.Interactive()\n\t// display welcome info\n\tif iactive && env.Get(\"QUIET\") == \"off\" {\n\t\t// logo\n\t\tif typ := env.TermGraphics(); typ.Available() {\n\t\t\tif err := typ.Encode(stdout, text.Logo); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t// welcome text\n\t\tfmt.Fprintln(stdout, text.WelcomeDesc)\n\t\tfmt.Fprintln(stdout)\n\t}\n\tvar cmd string\n\tvar paramstr string\n\tvar err error\n\tvar opt metacmd.Option\n\tvar cont bool\n\tvar lastErr error\n\tvar execute bool\n\tfor {\n\t\texecute = false\n\t\t// set prompt\n\t\tif iactive {\n\t\t\th.l.Prompt(h.Prompt(env.Get(\"PROMPT1\")))\n\t\t}\n\t\t// read next statement/command\n\t\tswitch cmd, paramstr, err = h.buf.Next(env.Untick(h.user, env.Vars(), false)); {\n\t\tcase h.singleLineMode && err == nil:\n\t\t\texecute = h.buf.Len != 0\n\t\tcase err == rline.ErrInterrupt:\n\t\t\th.buf.Reset(nil)\n\t\t\tcontinue\n\t\tcase err == io.EOF:\n\t\t\treturn lastErr\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase cmd != \"\":\n\t\t\topt, cont, lastErr = h.apply(stdout, stderr, strings.TrimPrefix(cmd, `\\`), paramstr)\n\t\t}\n\t\tif cont {\n\t\t\tcontinue\n\t\t}\n\t\t// help, exit, quit intercept\n\t\tif iactive && len(h.buf.Buf) >= 4 {\n\t\t\ti, first := lastIndex(h.buf.Buf, '\\n'), false\n\t\t\tif i == -1 {\n\t\t\t\ti, first = 0, true\n\t\t\t}\n\t\t\tif s := strings.ToLower(helpQuitExitRE.FindString(string(h.buf.Buf[i:]))); s != \"\" {\n\t\t\t\tswitch s {\n\t\t\t\tcase \"help\":\n\t\t\t\t\ts = text.HelpDescShort\n\t\t\t\t\tif first {\n\t\t\t\t\t\ts = text.HelpDesc\n\t\t\t\t\t\th.buf.Reset(nil)\n\t\t\t\t\t}\n\t\t\t\tcase \"quit\", \"exit\":\n\t\t\t\t\ts = text.QuitDesc\n\t\t\t\t\tif first {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfmt.Fprintln(stdout, s)\n\t\t\t}\n\t\t}\n\t\t// quit\n\t\tif opt.Quit {\n\t\t\tif h.out != nil {\n\t\t\t\th.out.Close()\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\t// execute buf\n\t\tif execute || h.buf.Ready() || opt.Exec != metacmd.ExecNone {\n\t\t\t// intercept batch query\n\t\t\tif h.u != nil {\n\t\t\t\ttyp, end, batch := drivers.IsBatchQueryPrefix(h.u, h.buf.Prefix)\n\t\t\t\tswitch {\n\t\t\t\tcase h.batch && batch:\n\t\t\t\t\terr = fmt.Errorf(\"cannot perform %s in existing batch\", typ)\n\t\t\t\t\tlastErr = WrapErr(h.buf.String(), err)\n\t\t\t\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t// cannot use \\g* while accumulating statements for batch queries\n\t\t\t\tcase h.batch && typ != h.batchEnd && opt.Exec != metacmd.ExecNone:\n\t\t\t\t\terr = errors.New(\"cannot force batch execution\")\n\t\t\t\t\tlastErr = WrapErr(h.buf.String(), err)\n\t\t\t\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\t\t\t\tcontinue\n\t\t\t\tcase batch:\n\t\t\t\t\th.batch, h.batchEnd = true, end\n\t\t\t\tcase h.batch:\n\t\t\t\t\tvar lend string\n\t\t\t\t\tif len(h.lastExec) != 0 {\n\t\t\t\t\t\tlend = \"\\n\"\n\t\t\t\t\t}\n\t\t\t\t\t// append to last\n\t\t\t\t\th.lastExec += lend + h.buf.String()\n\t\t\t\t\th.lastExecPrefix = h.buf.Prefix\n\t\t\t\t\th.lastPrint += lend + h.buf.PrintString()\n\t\t\t\t\th.lastRaw += lend + h.buf.RawString()\n\t\t\t\t\th.buf.Reset(nil)\n\t\t\t\t\t// break\n\t\t\t\t\tif h.batchEnd != typ {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\th.lastExecPrefix = h.batchEnd\n\t\t\t\t\th.batch, h.batchEnd = false, \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t\tif h.buf.Len != 0 {\n\t\t\t\th.lastExec, h.lastExecPrefix, h.lastPrint, h.lastRaw = h.buf.String(), h.buf.Prefix, h.buf.PrintString(), h.buf.RawString()\n\t\t\t\th.buf.Reset(nil)\n\t\t\t}\n\t\t\t// log.Printf(\">> PROCESS EXECUTE: (%s) `%s`\", h.lastPrefix, h.last)\n\t\t\tif !h.batch && h.lastExec != \"\" && h.lastExec != \";\" {\n\t\t\t\t// force a transaction for batched queries for certain drivers\n\t\t\t\tvar forceBatch bool\n\t\t\t\tif h.u != nil {\n\t\t\t\t\t_, _, forceBatch = drivers.IsBatchQueryPrefix(h.u, stmt.FindPrefix(h.lastExec, true, true, true))\n\t\t\t\t\tforceBatch = forceBatch && drivers.BatchAsTransaction(h.u)\n\t\t\t\t}\n\t\t\t\t// execute\n\t\t\t\tout := stdout\n\t\t\t\tif h.out != nil {\n\t\t\t\t\tout = h.out\n\t\t\t\t}\n\t\t\t\tctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)\n\t\t\t\tif err = h.Execute(ctx, out, opt, h.lastExecPrefix, h.lastExec, forceBatch, h.unbind()...); err != nil {\n\t\t\t\t\tlastErr = WrapErr(h.lastExec, err)\n\t\t\t\t\tif env.Get(\"ON_ERROR_STOP\") == \"on\" {\n\t\t\t\t\t\tif iactive {\n\t\t\t\t\t\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\t\t\t\t\t\th.buf.Reset([]rune{}) // empty the buffer so no other statements are run\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tstop()\n\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstop()\n\t\t\t}\n\t\t}\n\t}\n}\n\n// apply applies the command against the handler.\nfunc (h *Handler) apply(stdout, stderr io.Writer, cmd, paramstr string) (metacmd.Option, bool, error) {\n\t// cmd = strings.TrimPrefix(cmd, `\\`)\n\tparams := stmt.NewParams(paramstr)\n\t// decode\n\tf, err := metacmd.Decode(cmd, params)\n\tif err != nil {\n\t\tswitch err = WrapErr(cmd, err); {\n\t\tcase err == text.ErrUnknownCommand:\n\t\t\tfmt.Fprintln(stderr, fmt.Sprintf(text.InvalidCommand, cmd))\n\t\tcase err == text.ErrMissingRequiredArgument:\n\t\t\tfmt.Fprintln(stderr, fmt.Sprintf(text.MissingRequiredArg, cmd))\n\t\tdefault:\n\t\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\t}\n\t\treturn metacmd.Option{}, true, err\n\t}\n\t// run\n\topt, err := f(h)\n\tif err != nil && err != rline.ErrInterrupt {\n\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\treturn metacmd.Option{}, true, WrapErr(cmd, err)\n\t}\nloop:\n\t// print unused command parameters\n\tfor {\n\t\tswitch arg, ok, err := params.Arg(); {\n\t\tcase err != nil:\n\t\t\tfmt.Fprintln(stderr, \"error:\", err)\n\t\tcase !ok:\n\t\t\tbreak loop\n\t\tdefault:\n\t\t\tfmt.Fprintln(stdout, fmt.Sprintf(text.ExtraArgumentIgnored, cmd, arg))\n\t\t}\n\t}\n\treturn opt, false, nil\n}\n\n// outputHighlighter returns s as a highlighted string, based on the current\n// buffer and syntax highlighting settings.\nfunc (h *Handler) outputHighlighter(s string) string {\n\t// bail when string is empty (ie, contains no printable, non-space\n\t// characters) or if syntax highlighting is not enabled\n\tif empty(s) || env.Get(\"SYNTAX_HL\") != \"true\" {\n\t\treturn s\n\t}\n\t// count end lines\n\tvar endl string\n\tif m := lineendRE.FindStringSubmatch(s); m != nil {\n\t\ts = strings.TrimSuffix(s, m[0])\n\t\tendl += m[0]\n\t}\n\t// leading whitespace\n\tvar leading string\n\t// capture current query statement buffer\n\torig := h.buf.RawString()\n\tfull := orig\n\tif full != \"\" {\n\t\tfull += \"\\n\"\n\t} else {\n\t\t// get leading whitespace\n\t\tif i := strings.IndexFunc(s, func(r rune) bool {\n\t\t\treturn !isSpaceOrControl(r)\n\t\t}); i != -1 {\n\t\t\tleading = s[:i]\n\t\t}\n\t}\n\tfull += s\n\t// setup statement parser\n\tst := drivers.NewStmt(h.u, func() func() ([]rune, error) {\n\t\ty := strings.Split(orig, \"\\n\")\n\t\tif y[0] == \"\" {\n\t\t\ty[0] = s\n\t\t} else {\n\t\t\ty = append(y, s)\n\t\t}\n\t\treturn func() ([]rune, error) {\n\t\t\tif len(y) > 0 {\n\t\t\t\tz := y[0]\n\t\t\t\ty = y[1:]\n\t\t\t\treturn []rune(z), nil\n\t\t\t}\n\t\t\treturn nil, io.EOF\n\t\t}\n\t}())\n\t// accumulate all \"active\" statements in buffer, breaking either at\n\t// EOF or when a \\ cmd has been encountered\n\tvar err error\n\tvar cmd, final string\nloop:\n\tfor {\n\t\tcmd, _, err = st.Next(env.Untick(h.user, env.Vars(), false))\n\t\tswitch {\n\t\tcase err != nil && err != io.EOF:\n\t\t\treturn s + endl\n\t\tcase err == io.EOF:\n\t\t\tbreak loop\n\t\t}\n\t\tif st.Ready() || cmd != \"\" {\n\t\t\tfinal += st.RawString()\n\t\t\tst.Reset(nil)\n\t\t\t// grab remaining whitespace to add to final\n\t\t\tl := len(final)\n\t\t\t// find first non empty character\n\t\t\tif i := strings.IndexFunc(full[l:], func(r rune) bool {\n\t\t\t\treturn !isSpaceOrControl(r)\n\t\t\t}); i != -1 {\n\t\t\t\tfinal += full[l : l+i]\n\t\t\t}\n\t\t}\n\t}\n\tif !st.Ready() && cmd == \"\" {\n\t\tfinal += st.RawString()\n\t}\n\tfinal = leading + final\n\t// determine whatever is remaining after \"active\"\n\tvar remaining string\n\tif fnl := len(final); fnl < len(full) {\n\t\tremaining = full[fnl:]\n\t}\n\t// this happens when a read line is empty and/or has only\n\t// whitespace and a \\ cmd\n\tif s == remaining {\n\t\treturn s + endl\n\t}\n\t// highlight entire final accumulated buffer\n\tb := new(bytes.Buffer)\n\tif err := h.Highlight(b, final); err != nil {\n\t\treturn s + endl\n\t}\n\tcolored := b.String()\n\t// return only last line plus whatever remaining string (ie, after\n\t// a \\ cmd) and the end line count\n\tss := strings.Split(colored, \"\\n\")\n\treturn lastcolor(colored) + ss[len(ss)-1] + remaining + endl\n}\n\n// Execute executes a query against the connected database.\nfunc (h *Handler) Execute(ctx context.Context, w io.Writer, opt metacmd.Option, prefix, sqlstr string, forceTrans bool, bind ...interface{}) error {\n\tif h.db == nil {\n\t\treturn text.ErrNotConnected\n\t}\n\t// determine type and pre process string\n\tprefix, sqlstr, qtyp, err := drivers.Process(h.u, prefix, sqlstr)\n\tif err != nil {\n\t\treturn drivers.WrapErr(h.u.Driver, err)\n\t}\n\t// start a transaction if forced\n\tif forceTrans {\n\t\tif err = h.BeginTx(ctx, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tf := h.doExecSingle\n\tswitch opt.Exec {\n\tcase metacmd.ExecExec:\n\t\tf = h.doExecExec\n\tcase metacmd.ExecSet:\n\t\tf = h.doExecSet\n\tcase metacmd.ExecWatch:\n\t\tf = h.doExecWatch\n\tcase metacmd.ExecChart:\n\t\tf = h.doExecChart\n\t}\n\tif err = drivers.WrapErr(h.u.Driver, f(ctx, w, opt, prefix, sqlstr, qtyp, bind)); err != nil {\n\t\tif forceTrans {\n\t\t\tdefer h.tx.Rollback()\n\t\t\th.tx = nil\n\t\t}\n\t\treturn err\n\t}\n\tif forceTrans {\n\t\treturn h.Commit()\n\t}\n\treturn nil\n}\n\n// Reset resets the handler's query statement buffer.\nfunc (h *Handler) Reset(r []rune) {\n\th.buf.Reset(r)\n\th.lastExec, h.lastExecPrefix, h.lastPrint, h.lastRaw, h.batch, h.batchEnd = \"\", \"\", \"\", \"\", false, \"\"\n}\n\n// Bind sets the bind parameters for the next query execution.\nfunc (h *Handler) Bind(bind []interface{}) {\n\th.bind = bind\n}\n\n// unbind returns the bind parameters.\nfunc (h *Handler) unbind() []interface{} {\n\tv := h.bind\n\th.bind = nil\n\treturn v\n}\n\n// Prompt parses a prompt.\n//\n// NOTE: the documentation below is INCORRECT, as it is just copied from\n// https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-PROMPTING\n//\n// TODO/FIXME: complete this functionality (from psql documentation):\n//\n//\t%M - The full host name (with domain name) of the database server, or\n//\t[local] if the connection is over a Unix domain socket, or\n//\t[local:/dir/name], if the Unix domain socket is not at the compiled in\n//\tdefault location.\n//\n//\t%m - The host name of the database server, truncated at the first dot, or\n//\t[local] if the connection is over a Unix domain socket.\n//\n//\t%> - The port number at which the database server is listening.\n//\n//\t%n - The database session user name. (The expansion of this value might\n//\tchange during a database session as the result of the command SET SESSION\n//\tAUTHORIZATION.)\n//\n//\t%/ - The name of the current database.\n//\n//\t%~ - Like %/, but the output is ~ (tilde) if the database is your default\n//\tdatabase.\n//\n//\t%# - If the session user is a database superuser, then a #, otherwise a >.\n//\t(The expansion of this value might change during a database session as the\n//\tresult of the command SET SESSION AUTHORIZATION.)\n//\n//\t%p - The process ID of the backend currently connected to.\n//\n//\t%R - In prompt 1 normally =, but @ if the session is in an inactive branch\n//\tof a conditional block, or ^ if in single-line mode, or ! if the session is\n//\tdisconnected from the database (which can happen if \\connect fails). In\n//\tprompt 2 %R is replaced by a character that depends on why psql expects\n//\tmore input: - if the command simply wasn't terminated yet, but * if there\n//\tis an unfinished /* ... */ comment, a single quote if there is an\n//\tunfinished quoted string, a double quote if there is an unfinished quoted\n//\tidentifier, a dollar sign if there is an unfinished dollar-quoted string,\n//\tor ( if there is an unmatched left parenthesis. In prompt 3 %R doesn't\n//\tproduce anything.\n//\n//\t%x - Transaction status: an empty string when not in a transaction block,\n//\tor * when in a transaction block, or ! when in a failed transaction block,\n//\tor ? when the transaction state is indeterminate (for example, because\n//\tthere is no connection).\n//\n//\t%l - The line number inside the current statement, starting from 1.\n//\n//\t%digits - The character with the indicated octal code is substituted.\n//\n//\t%:name: - The value of the psql variable name. See Variables, above, for\n//\tdetails.\n//\n//\t%`command` - The output of command, similar to ordinary “back-tick”\n//\tsubstitution.\n//\n//\t%[ ... %] - Prompts can contain terminal control characters which, for\n//\texample, change the color, background, or style of the prompt text, or\n//\tchange the title of the terminal window. In order for the line editing\n//\tfeatures of Readline to work properly, these non-printing control\n//\tcharacters must be designated as invisible by surrounding them with %[ and\n//\t%]. Multiple pairs of these can occur within the prompt. For example:\n//\n//\ttestdb=> \\set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# '\n//\n//\tresults in a boldfaced (1;) yellow-on-black (33;40) prompt on\n//\tVT100-compatible, color-capable terminals.\n//\n//\t%w - Whitespace of the same width as the most recent output of PROMPT1.\n//\tThis can be used as a PROMPT2 setting, so that multi-line statements are\n//\taligned with the first line, but there is no visible secondary prompt.\n//\n// To insert a percent sign into your prompt, write %%. The default prompts are\n// '%/%R%x%# ' for prompts 1 and 2, and '>> ' for prompt 3.\nfunc (h *Handler) Prompt(prompt string) string {\n\tr, connected := []rune(prompt), h.db != nil\n\tend := len(r)\n\tvar buf []byte\n\tfor i := 0; i < end; i++ {\n\t\tif r[i] != '%' {\n\t\t\tbuf = append(buf, string(r[i])...)\n\t\t\tcontinue\n\t\t}\n\t\tswitch grab(r, i+1, end) {\n\t\tcase '%': // literal\n\t\t\tbuf = append(buf, '%')\n\t\tcase 'S': // short driver name\n\t\t\tif connected {\n\t\t\t\ts := dburl.ShortAlias(h.u.Scheme)\n\t\t\t\tif s == \"\" {\n\t\t\t\t\ts = dburl.ShortAlias(h.u.Driver)\n\t\t\t\t}\n\t\t\t\tif s == \"\" {\n\t\t\t\t\ts = text.UnknownShortAlias\n\t\t\t\t}\n\t\t\t\tbuf = append(buf, s+\":\"...)\n\t\t\t} else {\n\t\t\t\tbuf = append(buf, text.NotConnected...)\n\t\t\t}\n\t\tcase 'u': // dburl short\n\t\t\tif connected {\n\t\t\t\tbuf = append(buf, h.u.Short()...)\n\t\t\t} else {\n\t\t\t\tbuf = append(buf, text.NotConnected...)\n\t\t\t}\n\t\tcase 'M': // full host name with domain\n\t\t\tif connected {\n\t\t\t\tbuf = append(buf, h.u.Hostname()...)\n\t\t\t}\n\t\tcase 'm': // host name truncated at first dot, or [local] if it's a domain socket\n\t\t\tif connected {\n\t\t\t\ts := h.u.Hostname()\n\t\t\t\tif i := strings.Index(s, \".\"); i != -1 {\n\t\t\t\t\ts = s[:i]\n\t\t\t\t}\n\t\t\t\tbuf = append(buf, s...)\n\t\t\t}\n\t\tcase '>': // the port number\n\t\t\tif connected {\n\t\t\t\ts := h.u.Port()\n\t\t\t\tif s != \"\" {\n\t\t\t\t\ts = \":\" + s\n\t\t\t\t}\n\t\t\t\tbuf = append(buf, s...)\n\t\t\t}\n\t\tcase 'N': // database user\n\t\t\tif connected && h.u.User != nil {\n\t\t\t\ts := h.u.User.Username()\n\t\t\t\tif s != \"\" {\n\t\t\t\t\tbuf = append(buf, s+\"@\"...)\n\t\t\t\t}\n\t\t\t}\n\t\tcase 'n': // database user\n\t\t\tif connected && h.u.User != nil {\n\t\t\t\tbuf = append(buf, h.u.User.Username()...)\n\t\t\t}\n\t\tcase '/': // database name\n\t\t\tswitch {\n\t\t\tcase connected && h.u.Opaque != \"\":\n\t\t\t\tbuf = append(buf, h.u.Opaque...)\n\t\t\tcase connected && h.u.Path != \"\" && h.u.Path != \"/\":\n\t\t\t\tbuf = append(buf, h.u.Path...)\n\t\t\t}\n\t\tcase 'O':\n\t\t\tif connected {\n\t\t\t\tbuf = append(buf, h.u.Opaque...)\n\t\t\t}\n\t\tcase 'o':\n\t\t\tif connected {\n\t\t\t\tbuf = append(buf, filepath.Base(h.u.Opaque)...)\n\t\t\t}\n\t\tcase 'P':\n\t\t\tif connected {\n\t\t\t\tbuf = append(buf, h.u.Path...)\n\t\t\t}\n\t\tcase 'p':\n\t\t\tif connected {\n\t\t\t\tbuf = append(buf, path.Base(h.u.Path)...)\n\t\t\t}\n\t\tcase '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':\n\t\t\tj := i + 1\n\t\t\tbase := 10\n\t\t\tif grab(r, j, end) == '0' {\n\t\t\t\tj++\n\t\t\t\tbase = 8\n\t\t\t}\n\t\t\tif grab(r, j, end) == 'x' {\n\t\t\t\tj++\n\t\t\t\tbase = 16\n\t\t\t}\n\t\t\ti = j\n\t\t\tfor unicode.IsDigit(grab(r, i+1, end)) {\n\t\t\t\ti++\n\t\t\t}\n\n\t\t\tn, err := strconv.ParseInt(string(r[j:i+1]), base, 16)\n\t\t\tif err == nil {\n\t\t\t\tbuf = append(buf, byte(n))\n\t\t\t}\n\t\t\ti--\n\t\tcase '~': // like %/ but ~ when default database\n\t\tcase '#': // when superuser, a #, otherwise >\n\t\t\tif h.tx != nil || h.batch {\n\t\t\t\tbuf = append(buf, '~')\n\t\t\t} else {\n\t\t\t\tbuf = append(buf, '>')\n\t\t\t}\n\t\t// case 'p': // the process id of the connected backend -- never going to be supported\n\t\tcase 'R': // statement state\n\t\t\tbuf = append(buf, h.buf.State()...)\n\t\tcase 'x': // empty when not in a transaction block, * in transaction block, ! in failed transaction block, or ? when indeterminate\n\t\tcase 'l': // line number\n\t\tcase ':': // variable value\n\t\tcase '`': // value of the evaluated command\n\t\tcase '[', ']':\n\t\tcase 'w':\n\t\t}\n\t\ti++\n\t}\n\treturn string(buf)\n}\n\n// IO returns the io for the handler.\nfunc (h *Handler) IO() rline.IO {\n\treturn h.l\n}\n\n// User returns the user for the handler.\nfunc (h *Handler) User() *user.User {\n\treturn h.user\n}\n\n// URL returns the URL for the handler.\nfunc (h *Handler) URL() *dburl.URL {\n\treturn h.u\n}\n\n// DB returns the sql.DB for the handler.\nfunc (h *Handler) DB() drivers.DB {\n\tif h.tx != nil {\n\t\treturn h.tx\n\t}\n\treturn h.db\n}\n\n// LastExec returns the last executed statement.\nfunc (h *Handler) LastExec() string {\n\treturn h.lastExec\n}\n\n// LastPrint returns the last printable statement.\nfunc (h *Handler) LastPrint() string {\n\treturn h.lastPrint\n}\n\n// LastRaw returns the last raw (non-interpolated) executed statement.\nfunc (h *Handler) LastRaw() string {\n\treturn h.lastRaw\n}\n\n// Buf returns the current query statement buffer.\nfunc (h *Handler) Buf() *stmt.Stmt {\n\treturn h.buf\n}\n\n// Highlight highlights using the current environment settings.\nfunc (h *Handler) Highlight(w io.Writer, buf string) error {\n\t// create lexer, formatter, styler\n\tl := chroma.Coalesce(drivers.Lexer(h.u))\n\tf := formatters.Get(env.Get(\"SYNTAX_HL_FORMAT\"))\n\ts := styles.Get(env.Get(\"SYNTAX_HL_STYLE\"))\n\t// override background\n\tif env.Get(\"SYNTAX_HL_OVERRIDE_BG\") != \"false\" {\n\t\ts = ustyles.Get(env.Get(\"SYNTAX_HL_STYLE\"))\n\t}\n\t// tokenize stream\n\tit, err := l.Tokenise(nil, buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// write formatted output\n\treturn f.Format(w, s, it)\n}\n\n// Open handles opening a specified database URL, passing either a single\n// string in the form of a URL, or more than one string, in which case the\n// first string is treated as a driver name, and the remaining strings are\n// joined (with a space) and passed as a DSN to sql.Open.\n//\n// If there is only one parameter, and it is not a well formatted URL, but\n// appears to be a file on disk, then an attempt will be made to open it with\n// an appropriate driver (mysql, postgres, sqlite3) depending on the type (unix\n// domain socket, directory, or regular file, respectively).\nfunc (h *Handler) Open(ctx context.Context, params ...string) error {\n\tif len(params) == 0 || params[0] == \"\" {\n\t\treturn nil\n\t}\n\tif h.tx != nil {\n\t\treturn text.ErrPreviousTransactionExists\n\t}\n\tif len(params) == 1 {\n\t\tif v, ok := env.Vars().GetConn(params[0]); ok {\n\t\t\tparams = v\n\t\t}\n\t}\n\tif len(params) < 2 {\n\t\tdsn := params[0]\n\t\t// parse dsn\n\t\tu, err := dburl.Parse(dsn)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\th.u = u\n\t\t// force parameters\n\t\th.forceParams(h.u)\n\t} else {\n\t\th.u = &dburl.URL{\n\t\t\tDriver: params[0],\n\t\t\tDSN:    strings.Join(params[1:], \" \"),\n\t\t}\n\t}\n\t// open connection\n\tvar err error\n\th.db, err = drivers.Open(ctx, h.u, h.GetOutput, h.l.Stderr)\n\tif err != nil && !drivers.IsPasswordErr(h.u, err) {\n\t\tdefer h.Close()\n\t\treturn err\n\t}\n\t// set buffer options\n\tdrivers.ConfigStmt(h.u, h.buf)\n\t// force error/check connection\n\tif err == nil {\n\t\tif err = drivers.Ping(ctx, h.u, h.db); err == nil {\n\t\t\tif h.l.Interactive() {\n\t\t\t\th.l.Completer(drivers.NewCompleter(ctx, h.u, h.db, readerOpts(), completer.WithConnStrings(h.connStrings())))\n\t\t\t}\n\t\t\treturn h.Version(ctx)\n\t\t}\n\t}\n\t// bail without getting password\n\tif h.nopw || !drivers.IsPasswordErr(h.u, err) || len(params) > 1 || !h.l.Interactive() {\n\t\tdefer h.Close()\n\t\treturn err\n\t}\n\t// print the error\n\tfmt.Fprintln(h.l.Stderr(), \"error:\", err)\n\t// otherwise, try to collect a password ...\n\tdsn, err := h.Password(params[0])\n\tif err != nil {\n\t\t// close connection\n\t\tdefer h.Close()\n\t\treturn err\n\t}\n\t// reconnect\n\treturn h.Open(ctx, dsn)\n}\n\nfunc (h *Handler) connStrings() []string {\n\tentries, err := passfile.Entries(h.user.HomeDir, text.PassfileName)\n\tif err != nil {\n\t\t// ignore the error as this is only used for completer\n\t\t// and it'll be reported again when trying to force params before opening a conn\n\t\tentries = nil\n\t}\n\tavailable := drivers.Available()\n\tnames := make([]string, 0, len(available)+len(entries))\n\tfor schema := range available {\n\t\t_, aliases := dburl.SchemeDriverAndAliases(schema)\n\t\t// TODO should we create all combinations of space, :, :// and +transport ?\n\t\tnames = append(names, schema)\n\t\tnames = append(names, aliases...)\n\t}\n\tfor _, entry := range entries {\n\t\tif entry.Protocol == \"*\" {\n\t\t\tcontinue\n\t\t}\n\t\tuser, host, port, dbname := \"\", \"\", \"\", \"\"\n\t\tif entry.Username != \"*\" {\n\t\t\tuser = entry.Username + \"@\"\n\t\t\tif entry.Host != \"*\" {\n\t\t\t\thost = entry.Host\n\t\t\t\tif entry.Port != \"*\" {\n\t\t\t\t\tport = \":\" + entry.Port\n\t\t\t\t}\n\t\t\t\tif entry.DBName != \"*\" {\n\t\t\t\t\tdbname = \"/\" + entry.DBName\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tnames = append(names, entry.Protocol+\"://\"+user+host+port+dbname)\n\t}\n\treturn append(names, slices.Sorted(maps.Keys(env.Vars().Conn()))...)\n}\n\n// forceParams forces connection parameters on a database URL, adding any\n// driver specific required parameters, and the username/password when a\n// matching entry exists in the PASS file.\nfunc (h *Handler) forceParams(u *dburl.URL) {\n\t// force driver parameters\n\tdrivers.ForceParams(u)\n\t// see if password entry is present\n\tuser, err := passfile.Match(u, h.user.HomeDir, text.PassfileName)\n\tswitch {\n\tcase err != nil:\n\t\tfmt.Fprintln(h.l.Stderr(), \"error:\", err)\n\tcase user != nil:\n\t\tu.User = user\n\t}\n\t// copy back to u\n\tz, _ := dburl.Parse(u.String())\n\t*u = *z\n}\n\n// Password collects a password from input, and returns a modified DSN\n// including the collected password.\nfunc (h *Handler) Password(dsn string) (string, error) {\n\tswitch conn, ok := env.Vars().GetConn(dsn); {\n\tcase dsn == \"\":\n\t\treturn \"\", text.ErrMissingDSN\n\tcase ok && len(conn) < 2:\n\t\treturn \"\", text.ErrNamedConnectionIsNotAURL\n\tcase ok:\n\t\tdsn = conn[0]\n\t}\n\tu, err := dburl.Parse(dsn)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tuser := h.user.Username\n\tif u.User != nil {\n\t\tuser = u.User.Username()\n\t}\n\tpass, err := h.l.Password(text.EnterPassword)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tu.User = url.UserPassword(user, pass)\n\treturn u.String(), nil\n}\n\n// Close closes the database connection if it is open.\nfunc (h *Handler) Close() error {\n\tif h.tx != nil {\n\t\treturn text.ErrPreviousTransactionExists\n\t}\n\tif h.db != nil {\n\t\terr := h.db.Close()\n\t\tdrv := h.u.Driver\n\t\th.db, h.u = nil, nil\n\t\treturn drivers.WrapErr(drv, err)\n\t}\n\treturn nil\n}\n\n// ReadVar reads a variable from the interactive prompt, saving it to\n// environment variables.\nfunc (h *Handler) ReadVar(typ, prompt string) (string, error) {\n\tvar masked bool\n\t// check type\n\tswitch typ {\n\tcase \"password\":\n\t\tmasked = true\n\tcase \"string\", \"int\", \"uint\", \"float\", \"bool\":\n\tdefault:\n\t\treturn \"\", text.ErrInvalidType\n\t}\n\tvar v string\n\tvar err error\n\tif masked {\n\t\tif prompt == \"\" {\n\t\t\tprompt = text.EnterPassword\n\t\t}\n\t\tv, err = h.l.Password(prompt)\n\t} else {\n\t\th.l.Prompt(prompt)\n\t\tvar r []rune\n\t\tr, err = h.l.Next()\n\t\tv = string(r)\n\t}\n\tswitch typ {\n\tcase \"int\":\n\t\t_, err = strconv.ParseInt(v, 10, 64)\n\tcase \"uint\":\n\t\t_, err = strconv.ParseUint(v, 10, 64)\n\tcase \"float\":\n\t\t_, err = strconv.ParseFloat(v, 64)\n\tcase \"bool\":\n\t\tvar b bool\n\t\tif b, err = strconv.ParseBool(v); err == nil {\n\t\t\tv = fmt.Sprintf(\"%t\", b)\n\t\t}\n\t}\n\tif err != nil {\n\t\terrstr := err.Error()\n\t\tif i := strings.LastIndex(errstr, \":\"); i != -1 {\n\t\t\terrstr = strings.TrimSpace(errstr[i+1:])\n\t\t}\n\t\treturn \"\", fmt.Errorf(text.InvalidValue, typ, v, errstr)\n\t}\n\treturn v, nil\n}\n\n// ChangePassword changes a password for the user.\nfunc (h *Handler) ChangePassword(user string) (string, error) {\n\tif h.db == nil {\n\t\treturn \"\", text.ErrNotConnected\n\t}\n\tif !h.l.Interactive() {\n\t\treturn \"\", text.ErrNotInteractive\n\t}\n\tvar err error\n\tif err = drivers.CanChangePassword(h.u); err != nil {\n\t\treturn \"\", err\n\t}\n\tvar newpw, newpw2, oldpw string\n\t// ask for previous password\n\tif user == \"\" && drivers.RequirePreviousPassword(h.u) {\n\t\toldpw, err = h.l.Password(text.EnterPreviousPassword)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\t// attempt to get passwords\n\tfor i := 0; i < 3; i++ {\n\t\tif newpw, err = h.l.Password(text.NewPassword); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif newpw2, err = h.l.Password(text.ConfirmPassword); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif newpw == newpw2 {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Fprintln(h.l.Stderr(), text.PasswordsDoNotMatch)\n\t}\n\t// verify passwords match\n\tif newpw != newpw2 {\n\t\treturn \"\", text.ErrPasswordAttemptsExhausted\n\t}\n\treturn drivers.ChangePassword(h.u, h.DB(), user, newpw, oldpw)\n}\n\n// Version prints the database version information after a successful connection.\nfunc (h *Handler) Version(ctx context.Context) error {\n\tif env.Get(\"SHOW_HOST_INFORMATION\") != \"true\" || !h.l.Interactive() {\n\t\treturn nil\n\t}\n\tif h.db == nil {\n\t\treturn text.ErrNotConnected\n\t}\n\tver, err := drivers.Version(ctx, h.u, h.DB())\n\tswitch {\n\tcase err != nil:\n\t\tver = fmt.Sprintf(\"<unknown, error: %v>\", err)\n\tcase ver == \"\":\n\t\tver = \"<unknown>\"\n\t}\n\th.Print(text.ConnInfo, h.u.Driver, ver)\n\treturn nil\n}\n\n// Print formats according to a format specifier and writes to handler's standard output.\nfunc (h *Handler) Print(s string, v ...interface{}) {\n\tif env.Get(\"QUIET\") == \"on\" {\n\t\treturn\n\t}\n\tfmt.Fprintln(h.l.Stdout(), fmt.Sprintf(s, v...))\n}\n\n// doExecWatch repeatedly executes a query against the database.\nfunc (h *Handler) doExecWatch(ctx context.Context, w io.Writer, opt metacmd.Option, prefix, sqlstr string, qtyp bool, bind []interface{}) error {\n\tfor {\n\t\t// the actual output that psql has: \"Mon Jan 2006 3:04:05 PM MST\" -- which is _slightly_ different than RFC1123\n\t\t// fmt.Fprintf(w, \"%s (every %fs)\\n\\n\", time.Now().Format(\"Mon Jan 2006 3:04:05 PM MST\"), float64(opt.Watch)/float64(time.Second))\n\t\tfmt.Fprintf(w, \"%s (every %v)\\n\", time.Now().Format(time.RFC1123), opt.Watch)\n\t\tfmt.Fprintln(w)\n\t\tif err := h.doExecSingle(ctx, w, opt, prefix, sqlstr, qtyp, bind); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tif err := ctx.Err(); err != nil && !errors.Is(err, context.Canceled) {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\tcase <-time.After(opt.Watch):\n\t\t}\n\t}\n}\n\n// doExecChart executes a single query against the database, displaying its output as a chart.\nfunc (h *Handler) doExecChart(ctx context.Context, w io.Writer, opt metacmd.Option, prefix, sqlstr string, qtyp bool, bind []interface{}) error {\n\tstdout, _, _ := h.l.Stdout(), h.l.Stderr(), h.l.Interactive()\n\ttyp := env.TermGraphics()\n\tif !typ.Available() {\n\t\treturn text.ErrGraphicsNotSupported\n\t}\n\tif _, ok := opt.Params[\"help\"]; ok {\n\t\tfmt.Fprintln(stdout, text.ChartUsage)\n\t\treturn nil\n\t}\n\tcfg, err := charts.ParseArgs(opt.Params)\n\tif err != nil {\n\t\treturn err\n\t}\n\tstart := time.Now()\n\t// query\n\trows, err := h.DB().QueryContext(ctx, sqlstr, bind...)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// get cols\n\tcols, err := drivers.Columns(h.u, rows)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// process row(s)\n\ttransposed := make([][]string, len(cols))\n\tclen, tfmt := len(cols), env.Vars().PrintTimeFormat()\n\tfor rows.Next() {\n\t\trow, err := h.scan(rows, clen, tfmt)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor i := range row {\n\t\t\ttransposed[i] = append(transposed[i], row[i])\n\t\t}\n\t}\n\t// display\n\tc, err := charts.MakeChart(cfg, cols, transposed)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdata, err := c.ToEcharts()\n\tif err != nil {\n\t\treturn err\n\t}\n\techarts := echartsgoja.New(echartsgoja.WithWidthHeight(cfg.W, cfg.H))\n\tres, err := echarts.RenderOptions(ctx, data)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif cfg.File != \"\" {\n\t\tfmt.Println(\"writing to\", cfg.File)\n\t\treturn os.WriteFile(cfg.File, []byte(res), 0o644)\n\t}\n\timg, err := resvg.Render([]byte(res), resvg.WithBackground(cfg.Background))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := typ.Encode(stdout, img); err != nil {\n\t\treturn err\n\t}\n\tif h.timing {\n\t\td := time.Since(start)\n\t\ts := text.TimingDesc\n\t\tv := []interface{}{float64(d.Microseconds()) / 1000}\n\t\tif d > 1*time.Second {\n\t\t\ts += \" (%v)\"\n\t\t\tv = append(v, d.Round(1*time.Millisecond))\n\t\t}\n\t\tfmt.Fprintln(h.l.Stdout(), fmt.Sprintf(s, v...))\n\t}\n\treturn nil\n}\n\n// doExecSingle executes a single query against the database based on its query type.\nfunc (h *Handler) doExecSingle(ctx context.Context, w io.Writer, opt metacmd.Option, prefix, sqlstr string, qtyp bool, bind []interface{}) error {\n\t// exec or query\n\tf := h.doExec\n\tif qtyp {\n\t\tf = h.doQuery\n\t}\n\t// exec\n\tstart := time.Now()\n\tif err := f(ctx, w, opt, prefix, sqlstr, bind); err != nil {\n\t\treturn err\n\t}\n\tif h.timing {\n\t\td := time.Since(start)\n\t\ts := text.TimingDesc\n\t\tv := []interface{}{float64(d.Microseconds()) / 1000}\n\t\tif d > 1*time.Second {\n\t\t\ts += \" (%v)\"\n\t\t\tv = append(v, d.Round(1*time.Millisecond))\n\t\t}\n\t\tfmt.Fprintln(h.l.Stdout(), fmt.Sprintf(s, v...))\n\t}\n\treturn nil\n}\n\n// doExecSet executes a SQL query, setting all returned columns as variables.\nfunc (h *Handler) doExecSet(ctx context.Context, w io.Writer, opt metacmd.Option, prefix, sqlstr string, _ bool, bind []interface{}) error {\n\t// query\n\trows, err := h.DB().QueryContext(ctx, sqlstr, bind...)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// get cols\n\tcols, err := drivers.Columns(h.u, rows)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// process row(s)\n\tvar i int\n\tvar row []string\n\tclen, tfmt := len(cols), env.Vars().PrintTimeFormat()\n\tfor rows.Next() {\n\t\tif i == 0 {\n\t\t\trow, err = h.scan(rows, clen, tfmt)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\ti++\n\t}\n\tif i > 1 {\n\t\treturn text.ErrTooManyRows\n\t}\n\t// set vars\n\tfor i, c := range cols {\n\t\tn := opt.Params[\"prefix\"] + c\n\t\tif err = env.ValidIdentifier(n); err != nil {\n\t\t\treturn fmt.Errorf(text.CouldNotSetVariable, n)\n\t\t}\n\t\t_ = env.Vars().Set(n, row[i])\n\t}\n\treturn nil\n}\n\n// doExecExec executes a query and re-executes all columns of all rows as if they\n// were their own queries.\nfunc (h *Handler) doExecExec(ctx context.Context, w io.Writer, _ metacmd.Option, prefix, sqlstr string, qtyp bool, bind []interface{}) error {\n\t// query\n\trows, err := h.DB().QueryContext(ctx, sqlstr, bind...)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// exec resulting rows\n\tif err := h.doExecRows(ctx, w, rows); err != nil {\n\t\treturn err\n\t}\n\t// check for additional result sets ...\n\tfor rows.NextResultSet() {\n\t\tif err := h.doExecRows(ctx, w, rows); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// doQuery executes a doQuery against the database.\nfunc (h *Handler) doQuery(ctx context.Context, w io.Writer, opt metacmd.Option, typ, sqlstr string, bind []interface{}) error {\n\t// run query\n\trows, err := h.DB().QueryContext(ctx, sqlstr, bind...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer rows.Close()\n\tparams := env.Vars().Print()\n\tparams[\"time\"] = env.Vars().PrintTimeFormat()\n\tfor k, v := range opt.Params {\n\t\tparams[k] = v\n\t}\n\tvar pipe io.WriteCloser\n\tvar cmd *exec.Cmd\n\tif pipeName := params[\"pipe\"]; pipeName != \"\" || h.out != nil {\n\t\tif params[\"expanded\"] == \"auto\" && params[\"columns\"] == \"\" {\n\t\t\t// don't rely on terminal size when piping output to a file or cmd\n\t\t\tparams[\"expanded\"] = \"off\"\n\t\t}\n\t\tif pipeName != \"\" {\n\t\t\tif pipeName[0] == '|' {\n\t\t\t\tpipe, cmd, err = env.Pipe(h.l.Stdout(), h.l.Stderr(), pipeName[1:])\n\t\t\t} else {\n\t\t\t\tpipe, err = os.OpenFile(pipeName, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0o644)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tw = pipe\n\t\t}\n\t} else if opt.Exec != metacmd.ExecWatch {\n\t\tparams[\"pager_cmd\"] = env.Get(\"PAGER\")\n\t}\n\t// set up column type config\n\tvar extra []tblfmt.Option\n\tswitch f := drivers.ColumnTypes(h.u); {\n\tcase f != nil:\n\t\textra = append(extra, tblfmt.WithColumnTypesFunc(f))\n\tcase drivers.UseColumnTypes(h.u):\n\t\textra = append(extra, tblfmt.WithUseColumnTypes(true))\n\t}\n\tresultSet := tblfmt.ResultSet(rows)\n\t// wrap query with crosstab\n\tif opt.Exec == metacmd.ExecCrosstab {\n\t\tvar err error\n\t\tif resultSet, err = tblfmt.NewCrosstabView(rows, append(extra, tblfmt.WithParams(opt.Crosstab...))...); err != nil {\n\t\t\treturn err\n\t\t}\n\t\textra = nil\n\t}\n\tif drivers.LowerColumnNames(h.u) {\n\t\tparams[\"lower_column_names\"] = \"true\"\n\t}\n\t// encode and handle error conditions\n\tswitch err := tblfmt.EncodeAll(w, resultSet, params, extra...); {\n\tcase err != nil && cmd != nil && errors.Is(err, syscall.EPIPE):\n\t\t// broken pipe means pager quit before consuming all data, which might be expected\n\t\treturn nil\n\tcase err != nil && h.u.Driver == \"sqlserver\" && err == tblfmt.ErrResultSetHasNoColumns && strings.HasPrefix(typ, \"EXEC\"):\n\t\t// sqlserver EXEC statements sometimes do not have results, fake that\n\t\t// it was executed as a exec and not a query\n\t\tfmt.Fprintln(w, typ)\n\tcase err != nil:\n\t\treturn err\n\tcase params[\"format\"] == \"aligned\":\n\t\tfmt.Fprintln(w)\n\t}\n\tif pipe != nil {\n\t\tpipe.Close()\n\t\tif cmd != nil {\n\t\t\tcmd.Wait()\n\t\t}\n\t}\n\treturn err\n}\n\n// doExecRows executes all the columns in the row.\nfunc (h *Handler) doExecRows(ctx context.Context, w io.Writer, rows *sql.Rows) error {\n\t// get columns\n\tcols, err := drivers.Columns(h.u, rows)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// process rows\n\tres := metacmd.Option{\n\t\tExec: metacmd.ExecOnly,\n\t}\n\tclen, tfmt := len(cols), env.Vars().PrintTimeFormat()\n\tfor rows.Next() {\n\t\tif clen != 0 {\n\t\t\trow, err := h.scan(rows, clen, tfmt)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// execute\n\t\t\tfor _, sqlstr := range row {\n\t\t\t\tif err = h.Execute(ctx, w, res, stmt.FindPrefix(sqlstr, true, true, true), sqlstr, false); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// scan scans a row.\nfunc (h *Handler) scan(rows *sql.Rows, clen int, tfmt string) ([]string, error) {\n\t// scan to []interface{}\n\tr := make([]interface{}, clen)\n\tfor i := range r {\n\t\tr[i] = new(interface{})\n\t}\n\tif err := rows.Scan(r...); err != nil {\n\t\treturn nil, err\n\t}\n\t// get conversion funcs\n\tcb, cm, cs, cd := drivers.ConvertBytes(h.u), drivers.ConvertMap(h.u), drivers.ConvertSlice(h.u), drivers.ConvertDefault(h.u)\n\trow := make([]string, clen)\n\tfor n, z := range r {\n\t\tj := z.(*interface{})\n\t\tswitch x := (*j).(type) {\n\t\tcase []byte:\n\t\t\tif x != nil {\n\t\t\t\tvar err error\n\t\t\t\tif row[n], err = cb(x, tfmt); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\tcase string:\n\t\t\trow[n] = x\n\t\tcase time.Time:\n\t\t\trow[n] = x.Format(tfmt)\n\t\tcase fmt.Stringer:\n\t\t\trow[n] = x.String()\n\t\tcase map[string]interface{}:\n\t\t\tif x != nil {\n\t\t\t\tvar err error\n\t\t\t\tif row[n], err = cm(x); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\tcase []interface{}:\n\t\t\tif x != nil {\n\t\t\t\tvar err error\n\t\t\t\tif row[n], err = cs(x); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tif x != nil {\n\t\t\t\tvar err error\n\t\t\t\tif row[n], err = cd(x); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn row, nil\n}\n\n// doExec does a database exec.\nfunc (h *Handler) doExec(ctx context.Context, w io.Writer, _ metacmd.Option, typ, sqlstr string, bind []interface{}) error {\n\tres, err := h.DB().ExecContext(ctx, sqlstr, bind...)\n\tif err != nil {\n\t\t_ = env.Vars().Set(\"ROW_COUNT\", \"0\")\n\t\treturn err\n\t}\n\t// get affected\n\tcount, err := drivers.RowsAffected(h.u, res)\n\tif err != nil {\n\t\t_ = env.Vars().Set(\"ROW_COUNT\", \"0\")\n\t\treturn err\n\t}\n\t// print name\n\tif env.Get(\"QUIET\") == \"off\" {\n\t\tfmt.Fprint(w, typ)\n\t\t// print count\n\t\tif count > 0 {\n\t\t\tfmt.Fprint(w, \" \", count)\n\t\t}\n\t\tfmt.Fprintln(w)\n\t}\n\treturn env.Vars().Set(\"ROW_COUNT\", strconv.FormatInt(count, 10))\n}\n\n// Begin begins a transaction.\nfunc (h *Handler) Begin(txOpts *sql.TxOptions) error {\n\treturn h.BeginTx(context.Background(), txOpts)\n}\n\n// Begin begins a transaction in a context.\nfunc (h *Handler) BeginTx(ctx context.Context, txOpts *sql.TxOptions) error {\n\tif h.db == nil {\n\t\treturn text.ErrNotConnected\n\t}\n\tif h.tx != nil {\n\t\treturn text.ErrPreviousTransactionExists\n\t}\n\tvar err error\n\th.tx, err = h.db.BeginTx(ctx, txOpts)\n\tif err != nil {\n\t\treturn drivers.WrapErr(h.u.Driver, err)\n\t}\n\treturn nil\n}\n\n// Commit commits a transaction.\nfunc (h *Handler) Commit() error {\n\tif h.db == nil {\n\t\treturn text.ErrNotConnected\n\t}\n\tif h.tx == nil {\n\t\treturn text.ErrNoPreviousTransactionExists\n\t}\n\ttx := h.tx\n\th.tx = nil\n\tif err := tx.Commit(); err != nil {\n\t\treturn drivers.WrapErr(h.u.Driver, err)\n\t}\n\treturn nil\n}\n\n// Rollback rollbacks a transaction.\nfunc (h *Handler) Rollback() error {\n\tif h.db == nil {\n\t\treturn text.ErrNotConnected\n\t}\n\tif h.tx == nil {\n\t\treturn text.ErrNoPreviousTransactionExists\n\t}\n\ttx := h.tx\n\th.tx = nil\n\tif err := tx.Rollback(); err != nil {\n\t\treturn drivers.WrapErr(h.u.Driver, err)\n\t}\n\treturn nil\n}\n\n// If starts an if block.\nfunc (h *Handler) If(ok bool) error {\n\treturn nil\n}\n\n// ElseIf starts an else if block.\nfunc (h *Handler) ElseIf(ok bool) error {\n\treturn nil\n}\n\n// Else starts an else block.\nfunc (h *Handler) Else(bool) error {\n\treturn nil\n}\n\n// EndIf closes an if block.\nfunc (h *Handler) EndIf(bool) error {\n\treturn nil\n}\n\n// IncludeReader includes the content of rdr.\nfunc (h *Handler) IncludeReader(rdr io.Reader, path string) error {\n\tr := bufio.NewReader(rdr)\n\t// setup rline\n\tl := &rline.Rline{\n\t\tN: func() ([]rune, error) {\n\t\t\tbuf := new(bytes.Buffer)\n\t\t\tvar b []byte\n\t\t\tvar isPrefix bool\n\t\t\tvar err error\n\t\t\tfor {\n\t\t\t\t// read\n\t\t\t\tb, isPrefix, err = r.ReadLine()\n\t\t\t\t// when not EOF\n\t\t\t\tif err != nil && err != io.EOF {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\t// append\n\t\t\t\tif _, werr := buf.Write(b); werr != nil {\n\t\t\t\t\treturn nil, werr\n\t\t\t\t}\n\t\t\t\t// end of line\n\t\t\t\tif !isPrefix || err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\t// peek and read possible line ending \\n or \\r\\n\n\t\t\tif err != io.EOF {\n\t\t\t\tif err := peekEnding(buf, r); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn []rune(buf.String()), err\n\t\t},\n\t\tOut: h.l.Stdout(),\n\t\tErr: h.l.Stderr(),\n\t\tPw:  h.l.Password,\n\t}\n\tp := New(l, h.user, filepath.Dir(path), h.charts, h.nopw)\n\tp.db, p.u = h.db, h.u\n\tdrivers.ConfigStmt(p.u, p.buf)\n\terr := p.Run()\n\th.db, h.u = p.db, p.u\n\treturn err\n}\n\n// Include includes the specified path.\nfunc (h *Handler) Include(path string, relative bool) error {\n\tif relative && !filepath.IsAbs(path) {\n\t\tpath = filepath.Join(h.wd, path)\n\t}\n\t// fmt.Fprintf(os.Stderr, \"include: %s relative: %t\\n\", path, relative)\n\t// open\n\tpath, f, err := env.OpenFile(h.user, path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\treturn h.IncludeReader(f, path)\n}\n\n// MetadataWriter loads the metadata writer for the\nfunc (h *Handler) MetadataWriter(ctx context.Context) (metadata.Writer, error) {\n\tif h.db == nil {\n\t\treturn nil, text.ErrNotConnected\n\t}\n\treturn drivers.NewMetadataWriter(ctx, h.u, h.db, h.l.Stdout(), readerOpts()...)\n}\n\n// GetOutput gets the output writer.\nfunc (h *Handler) GetOutput() io.Writer {\n\tif h.out == nil {\n\t\treturn h.l.Stdout()\n\t}\n\treturn h.out\n}\n\n// SetOutput sets the output writer.\nfunc (h *Handler) SetOutput(o io.WriteCloser) {\n\tif h.out != nil {\n\t\th.out.Close()\n\t}\n\th.out = o\n}\n\n// FS is the filesystem interface.\ntype FS interface{}\n\n// Error wraps handler errors.\ntype Error struct {\n\tBuf string\n\tErr error\n}\n\n// WrapErr wraps an [error] using the specified driver when err is not nil.\nfunc WrapErr(buf string, err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\t// avoid double wrapping error\n\tif _, ok := err.(*Error); ok {\n\t\treturn err\n\t}\n\treturn &Error{buf, err}\n}\n\n// Error satisfies the [error] interface, returning the original error message.\nfunc (e *Error) Error() string {\n\treturn e.Err.Error()\n}\n\n// Unwrap returns the original error.\nfunc (e *Error) Unwrap() error {\n\treturn e.Err\n}\n\nfunc readerOpts() []metadata.ReaderOption {\n\tvar opts []metadata.ReaderOption\n\tif env.Get(\"ECHO_HIDDEN\") == \"on\" || env.Get(\"ECHO_HIDDEN\") == \"noexec\" {\n\t\tif env.Get(\"ECHO_HIDDEN\") == \"noexec\" {\n\t\t\topts = append(opts, metadata.WithDryRun(true))\n\t\t}\n\t\topts = append(\n\t\t\topts,\n\t\t\tmetadata.WithLogger(log.New(os.Stdout, \"DEBUG: \", log.LstdFlags)),\n\t\t\tmetadata.WithTimeout(30*time.Second),\n\t\t)\n\t}\n\treturn opts\n}\n\n// peekEnding peeks to see if the next successive bytes in r is \\n or \\r\\n,\n// writing to w if it is. Does not advance r if the next bytes are not \\n or\n// \\r\\n.\nfunc peekEnding(w io.Writer, r *bufio.Reader) error {\n\t// peek first byte\n\tbuf, err := r.Peek(1)\n\tswitch {\n\tcase err != nil && err != io.EOF:\n\t\treturn err\n\tcase err == nil && buf[0] == '\\n':\n\t\tif _, rerr := r.ReadByte(); err != nil && err != io.EOF {\n\t\t\treturn rerr\n\t\t}\n\t\t_, werr := w.Write([]byte{'\\n'})\n\t\treturn werr\n\tcase err == nil && buf[0] != '\\r':\n\t\treturn nil\n\t}\n\t// peek second byte\n\tbuf, err = r.Peek(1)\n\tswitch {\n\tcase err != nil && err != io.EOF:\n\t\treturn err\n\tcase err == nil && buf[0] != '\\n':\n\t\treturn nil\n\t}\n\tif _, rerr := r.ReadByte(); err != nil && err != io.EOF {\n\t\treturn rerr\n\t}\n\t_, werr := w.Write([]byte{'\\n'})\n\treturn werr\n}\n\n// grab returns the i'th rune from r when i < end, otherwise 0.\nfunc grab(r []rune, i, end int) rune {\n\tif i < end {\n\t\treturn r[i]\n\t}\n\treturn 0\n}\n\n// empty reports whether s contains at least one printable, non-space\n// character.\nfunc empty(s string) bool {\n\treturn strings.IndexFunc(s, func(r rune) bool {\n\t\treturn unicode.IsPrint(r) && !unicode.IsSpace(r)\n\t}) == -1\n}\n\n// lastcolor returns the last defined color in s, if any.\nfunc lastcolor(s string) string {\n\tif i := strings.LastIndex(s, \"\\n\"); i != -1 {\n\t\ts = s[:i]\n\t}\n\tif i := strings.LastIndex(s, \"\\x1b[0m\"); i != -1 {\n\t\ts = s[i+4:]\n\t}\n\treturn strings.Join(ansiRE.FindAllString(s, -1), \"\")\n}\n\n// isSpaceOrControl returns true when r is a space or control character.\nfunc isSpaceOrControl(r rune) bool {\n\treturn unicode.IsSpace(r) || unicode.IsControl(r)\n}\n\n// lastIndex returns the last index in r of needle, or -1 if not found.\nfunc lastIndex(r []rune, needle rune) int {\n\tfor i := len(r) - 1; i >= 0; i-- {\n\t\tif r[i] == needle {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// ansiRE matches ansi escape (color) codes.\nvar ansiRE = regexp.MustCompile(`\\x1b[[0-9]+([:;][0-9]+)*m`)\n\n// lineendRE is the end of line terminal.\nvar lineendRE = regexp.MustCompile(`(?:\\r?\\n)+$`)\n\n// helpQuitExitRE is a regexp to use to match help, quit, or exit messages.\nvar helpQuitExitRE = regexp.MustCompile(`(?im)^+(` + strings.Join([]string{text.HelpPrefix, text.QuitPrefix, text.ExitPrefix}, \"|\") + `)\\s*$`)\n"
  },
  {
    "path": "internal/adodb.go",
    "content": "//go:build (all || most || adodb) && !no_adodb\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/adodb\" // Microsoft ADODB driver\n)\n"
  },
  {
    "path": "internal/athena.go",
    "content": "//go:build (all || most || athena) && !no_athena\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/athena\" // AWS Athena driver\n)\n"
  },
  {
    "path": "internal/avatica.go",
    "content": "//go:build (all || most || avatica) && !no_avatica\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/avatica\" // Apache Avatica driver\n)\n"
  },
  {
    "path": "internal/bigquery.go",
    "content": "//go:build (all || most || bigquery) && !no_bigquery\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/bigquery\" // Google BigQuery driver\n)\n"
  },
  {
    "path": "internal/cassandra.go",
    "content": "//go:build (all || most || cassandra) && !no_cassandra\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/cassandra\" // Cassandra driver\n)\n"
  },
  {
    "path": "internal/chai.go",
    "content": "//go:build (all || most || chai) && !no_chai\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/chai\" // ChaiSQL driver\n)\n"
  },
  {
    "path": "internal/clickhouse.go",
    "content": "//go:build (!no_base || clickhouse) && !no_clickhouse\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/clickhouse\" // ClickHouse driver\n)\n"
  },
  {
    "path": "internal/cosmos.go",
    "content": "//go:build (all || most || cosmos) && !no_cosmos\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/cosmos\" // Azure CosmosDB driver\n)\n"
  },
  {
    "path": "internal/couchbase.go",
    "content": "//go:build (all || most || couchbase) && !no_couchbase\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/couchbase\" // Couchbase driver\n)\n"
  },
  {
    "path": "internal/csvq.go",
    "content": "//go:build (!no_base || csvq) && !no_csvq\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/csvq\" // CSVQ driver\n)\n"
  },
  {
    "path": "internal/databend.go",
    "content": "//go:build (all || most || databend) && !no_databend\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/databend\" // Databend driver\n)\n"
  },
  {
    "path": "internal/databricks.go",
    "content": "//go:build (all || most || databricks) && !no_databricks\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/databricks\" // Databricks driver\n)\n"
  },
  {
    "path": "internal/duckdb.go",
    "content": "//go:build (all || most || duckdb) && !no_duckdb\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/duckdb\" // DuckDB driver\n)\n"
  },
  {
    "path": "internal/dynamodb.go",
    "content": "//go:build (all || most || dynamodb) && !no_dynamodb\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/dynamodb\" // DynamoDb driver\n)\n"
  },
  {
    "path": "internal/exasol.go",
    "content": "//go:build (all || most || exasol) && !no_exasol\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/exasol\" // Exasol driver\n)\n"
  },
  {
    "path": "internal/firebird.go",
    "content": "//go:build (all || most || firebird) && !no_firebird\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/firebird\" // Firebird driver\n)\n"
  },
  {
    "path": "internal/flightsql.go",
    "content": "//go:build (all || most || flightsql) && !no_flightsql\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/flightsql\" // FlightSQL driver\n)\n"
  },
  {
    "path": "internal/godror.go",
    "content": "//go:build (all || godror) && !no_godror\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/godror\" // GO DRiver for ORacle driver\n)\n"
  },
  {
    "path": "internal/h2.go",
    "content": "//go:build (all || most || h2) && !no_h2\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/h2\" // Apache H2 driver\n)\n"
  },
  {
    "path": "internal/hive.go",
    "content": "//go:build (all || most || hive) && !no_hive\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/hive\" // Apache Hive driver\n)\n"
  },
  {
    "path": "internal/ignite.go",
    "content": "//go:build (all || most || ignite) && !no_ignite\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/ignite\" // Apache Ignite driver\n)\n"
  },
  {
    "path": "internal/impala.go",
    "content": "//go:build (all || most || impala) && !no_impala\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/impala\" // Apache Impala driver\n)\n"
  },
  {
    "path": "internal/internal.go",
    "content": "// Package internal provides a way to obtain information about which database\n// drivers were included at build.\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\n// KnownBuildTags returns a map of known driver names to its respective build\n// tags.\nfunc KnownBuildTags() map[string]string {\n\treturn map[string]string{\n\t\t\"adodb\":         \"adodb\",         // github.com/mattn/go-adodb\n\t\t\"athena\":        \"awsathena\",     // github.com/uber/athenadriver/go\n\t\t\"avatica\":       \"avatica\",       // github.com/apache/calcite-avatica-go/v5\n\t\t\"bigquery\":      \"bigquery\",      // gorm.io/driver/bigquery/driver\n\t\t\"cassandra\":     \"cql\",           // github.com/MichaelS11/go-cql-driver\n\t\t\"chai\":          \"chai\",          // github.com/chaisql/chai\n\t\t\"clickhouse\":    \"clickhouse\",    // github.com/ClickHouse/clickhouse-go/v2\n\t\t\"cosmos\":        \"cosmos\",        // github.com/btnguyen2k/gocosmos\n\t\t\"couchbase\":     \"n1ql\",          // github.com/couchbase/go_n1ql\n\t\t\"csvq\":          \"csvq\",          // github.com/mithrandie/csvq-driver\n\t\t\"databend\":      \"databend\",      // github.com/datafuselabs/databend-go\n\t\t\"databricks\":    \"databricks\",    // github.com/databricks/databricks-sql-go\n\t\t\"duckdb\":        \"duckdb\",        // github.com/duckdb/duckdb-go/v2\n\t\t\"dynamodb\":      \"dynamodb\",      // github.com/btnguyen2k/godynamo\n\t\t\"exasol\":        \"exasol\",        // github.com/exasol/exasol-driver-go\n\t\t\"firebird\":      \"firebirdsql\",   // github.com/nakagami/firebirdsql\n\t\t\"flightsql\":     \"flightsql\",     // github.com/apache/arrow/go/v17/arrow/flight/flightsql/driver\n\t\t\"godror\":        \"godror\",        // github.com/godror/godror\n\t\t\"h2\":            \"h2\",            // github.com/jmrobles/h2go\n\t\t\"hive\":          \"hive\",          // sqlflow.org/gohive\n\t\t\"ignite\":        \"ignite\",        // github.com/amsokol/ignite-go-client/sql\n\t\t\"impala\":        \"impala\",        // github.com/sclgo/impala-go\n\t\t\"maxcompute\":    \"maxcompute\",    // sqlflow.org/gomaxcompute\n\t\t\"moderncsqlite\": \"moderncsqlite\", // modernc.org/sqlite\n\t\t\"mymysql\":       \"mymysql\",       // github.com/ziutek/mymysql/godrv\n\t\t\"mysql\":         \"mysql\",         // github.com/go-sql-driver/mysql\n\t\t\"netezza\":       \"nzgo\",          // github.com/IBM/nzgo/v12\n\t\t\"odbc\":          \"odbc\",          // github.com/alexbrainman/odbc\n\t\t\"oracle\":        \"oracle\",        // github.com/sijms/go-ora/v2\n\t\t\"ots\":           \"ots\",           // github.com/aliyun/aliyun-tablestore-go-sql-driver\n\t\t\"pgx\":           \"pgx\",           // github.com/jackc/pgx/v5/stdlib\n\t\t\"postgres\":      \"postgres\",      // github.com/lib/pq\n\t\t\"presto\":        \"presto\",        // github.com/prestodb/presto-go-client/presto\n\t\t\"ql\":            \"ql\",            // modernc.org/ql\n\t\t\"ramsql\":        \"ramsql\",        // github.com/proullon/ramsql/driver\n\t\t\"sapase\":        \"tds\",           // github.com/thda/tds\n\t\t\"saphana\":       \"hdb\",           // github.com/SAP/go-hdb/driver\n\t\t\"snowflake\":     \"snowflake\",     // github.com/snowflakedb/gosnowflake\n\t\t\"spanner\":       \"spanner\",       // github.com/googleapis/go-sql-spanner\n\t\t\"sqlite3\":       \"sqlite3\",       // github.com/mattn/go-sqlite3\n\t\t\"sqlserver\":     \"sqlserver\",     // github.com/microsoft/go-mssqldb\n\t\t\"trino\":         \"trino\",         // github.com/trinodb/trino-go-client/trino\n\t\t\"vertica\":       \"vertica\",       // github.com/vertica/vertica-sql-go\n\t\t\"voltdb\":        \"voltdb\",        // github.com/VoltDB/voltdb-client-go/voltdbclient\n\t\t\"ydb\":           \"ydb\",           // github.com/ydb-platform/ydb-go-sdk/v3\n\t}\n}\n"
  },
  {
    "path": "internal/maxcompute.go",
    "content": "//go:build (all || most || maxcompute) && !no_maxcompute\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/maxcompute\" // Alibaba MaxCompute driver\n)\n"
  },
  {
    "path": "internal/moderncsqlite.go",
    "content": "//go:build (all || most || moderncsqlite) && !no_moderncsqlite\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/moderncsqlite\" // ModernC SQLite3 driver\n)\n"
  },
  {
    "path": "internal/mymysql.go",
    "content": "//go:build (all || most || mymysql) && !no_mymysql\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/mymysql\" // MySQL MyMySQL driver\n)\n"
  },
  {
    "path": "internal/mysql.go",
    "content": "//go:build (!no_base || mysql) && !no_mysql\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/mysql\" // MySQL driver\n)\n"
  },
  {
    "path": "internal/netezza.go",
    "content": "//go:build (all || most || netezza) && !no_netezza\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/netezza\" // Netezza driver\n)\n"
  },
  {
    "path": "internal/odbc.go",
    "content": "//go:build (all || odbc) && !no_odbc\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/odbc\" // ODBC driver\n)\n"
  },
  {
    "path": "internal/oracle.go",
    "content": "//go:build (!no_base || oracle) && !no_oracle\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/oracle\" // Oracle Database driver\n)\n"
  },
  {
    "path": "internal/ots.go",
    "content": "//go:build (all || most || ots) && !no_ots\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/ots\" // Alibaba Tablestore driver\n)\n"
  },
  {
    "path": "internal/pgx.go",
    "content": "//go:build (all || most || pgx) && !no_pgx\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/pgx\" // PostgreSQL PGX driver\n)\n"
  },
  {
    "path": "internal/postgres.go",
    "content": "//go:build (!no_base || postgres) && !no_postgres\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/postgres\" // PostgreSQL driver\n)\n"
  },
  {
    "path": "internal/presto.go",
    "content": "//go:build (all || most || presto) && !no_presto\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/presto\" // Presto driver\n)\n"
  },
  {
    "path": "internal/ql.go",
    "content": "//go:build (all || most || ql) && !no_ql\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/ql\" // Cznic QL driver\n)\n"
  },
  {
    "path": "internal/ramsql.go",
    "content": "//go:build (all || most || ramsql) && !no_ramsql\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/ramsql\" // RamSQL driver\n)\n"
  },
  {
    "path": "internal/sapase.go",
    "content": "//go:build (all || most || sapase) && !no_sapase\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/sapase\" // SAP ASE driver\n)\n"
  },
  {
    "path": "internal/saphana.go",
    "content": "//go:build (all || most || saphana) && !no_saphana\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/saphana\" // SAP HANA driver\n)\n"
  },
  {
    "path": "internal/snowflake.go",
    "content": "//go:build (all || most || snowflake) && !no_snowflake\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/snowflake\" // Snowflake driver\n)\n"
  },
  {
    "path": "internal/spanner.go",
    "content": "//go:build (all || most || spanner) && !no_spanner\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/spanner\" // Google Spanner driver\n)\n"
  },
  {
    "path": "internal/sqlite3.go",
    "content": "//go:build (!no_base || sqlite3) && !no_sqlite3\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/sqlite3\" // SQLite3 driver\n)\n"
  },
  {
    "path": "internal/sqlserver.go",
    "content": "//go:build (!no_base || sqlserver) && !no_sqlserver\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/sqlserver\" // Microsoft SQL Server driver\n)\n"
  },
  {
    "path": "internal/trino.go",
    "content": "//go:build (all || most || trino) && !no_trino\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/trino\" // Trino driver\n)\n"
  },
  {
    "path": "internal/vertica.go",
    "content": "//go:build (all || most || vertica) && !no_vertica\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/vertica\" // Vertica driver\n)\n"
  },
  {
    "path": "internal/voltdb.go",
    "content": "//go:build (all || most || voltdb) && !no_voltdb\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/voltdb\" // VoltDB driver\n)\n"
  },
  {
    "path": "internal/ydb.go",
    "content": "//go:build (all || most || ydb) && !no_ydb\n\npackage internal\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t_ \"github.com/xo/usql/drivers/ydb\" // YDB driver\n)\n"
  },
  {
    "path": "internal/z.go",
    "content": "package internal\n\nimport (\n\t\"runtime\"\n\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n)\n\nfunc init() {\n\tif runtime.GOOS == \"windows\" {\n\t\t// if no odbc driver, but we have adodb, add 'odbc' (and related\n\t\t// aliases) as alias for oleodbc\n\t\tif drivers.Registered(\"adodb\") && !drivers.Registered(\"odbc\") {\n\t\t\told := dburl.Unregister(\"odbc\")\n\t\t\tdburl.RegisterAlias(\"oleodbc\", \"odbc\")\n\t\t\tfor _, alias := range old.Aliases {\n\t\t\t\tdburl.RegisterAlias(\"oleodbc\", alias)\n\t\t\t}\n\t\t}\n\t}\n\tif drivers.Registered(\"moderncsqlite\") && !drivers.Registered(\"sqlite3\") {\n\t\told := dburl.Unregister(\"sqlite3\")\n\t\tdburl.RegisterAlias(\"moderncsqlite\", \"sqlite3\")\n\t\tfor _, alias := range old.Aliases {\n\t\t\tdburl.RegisterAlias(\"moderncsqlite\", alias)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "main.go",
    "content": "// Command usql is the universal command-line interface for SQL databases.\n//\n//go:debug x509negativeserial=1\npackage main\n\n//go:generate go run gen.go\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/handler\"\n\t\"github.com/xo/usql/internal\"\n\t\"github.com/xo/usql/rline\"\n\t\"github.com/xo/usql/text\"\n)\n\nfunc main() {\n\t// get available drivers and known build tags\n\tavailable, known := drivers.Available(), internal.KnownBuildTags()\n\t// report if database is supported\n\tif len(os.Args) == 2 &&\n\t\tstrings.HasPrefix(os.Args[1], \"--has-\") &&\n\t\tstrings.HasSuffix(os.Args[1], \"-support\") {\n\t\tn := os.Args[1][6 : len(os.Args[1])-8]\n\t\tif v, ok := known[n]; ok {\n\t\t\tn = v\n\t\t}\n\t\tvar out int\n\t\tif _, ok := available[n]; ok {\n\t\t\tout = 1\n\t\t}\n\t\tfmt.Fprint(os.Stdout, out)\n\t\treturn\n\t}\n\t// run\n\tif err := New(os.Args).ExecuteContext(context.Background()); err != nil && err != io.EOF && err != rline.ErrInterrupt {\n\t\tvar he *handler.Error\n\t\tif !errors.As(err, &he) {\n\t\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\t}\n\t\tvar e *drivers.Error\n\t\tif errors.As(err, &e) && e.Err == text.ErrDriverNotAvailable {\n\t\t\tm := make(map[string]string, len(known))\n\t\t\tfor k, v := range known {\n\t\t\t\tm[v] = k\n\t\t\t}\n\t\t\ttag := e.Driver\n\t\t\tif t, ok := m[tag]; ok {\n\t\t\t\ttag = t\n\t\t\t}\n\t\t\trev := \"latest\"\n\t\t\tif text.CommandVersion == \"0.0.0-dev\" || strings.Contains(text.CommandVersion, \"-\") {\n\t\t\t\trev = \"master\"\n\t\t\t}\n\t\t\tfmt.Fprintf(os.Stderr, text.GoInstallHint, tag, rev)\n\t\t}\n\t\tswitch estr := err.Error(); {\n\t\tcase err == text.ErrWrongNumberOfArguments,\n\t\t\tstrings.HasPrefix(estr, \"unknown flag:\"),\n\t\t\tstrings.HasPrefix(estr, \"unknown shorthand flag:\"),\n\t\t\tstrings.HasPrefix(estr, \"bad flag syntax:\"),\n\t\t\tstrings.HasPrefix(estr, \"flag needs an argument:\"):\n\t\t\tfmt.Fprintln(os.Stderr, text.CommandHelpHint)\n\t\t}\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "main_test.go",
    "content": "package main\n\nimport (\n\t_ \"github.com/google/goexpect\"\n)\n"
  },
  {
    "path": "metacmd/charts/charts.go",
    "content": "package charts\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"image/color\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kenshaw/colors\"\n\t\"github.com/xo/usql/text\"\n)\n\ntype ChartConfig struct {\n\tTitle      string\n\tSubtitle   string\n\tW, H       int\n\tBackground color.Color\n\tType       string\n\tPrec       int\n\n\tFile string\n}\n\nfunc ParseArgs(opts map[string]string) (ChartConfig, error) {\n\tcfg := ChartConfig{\n\t\tTitle:      opts[\"title\"],\n\t\tSubtitle:   opts[\"subtitle\"],\n\t\tW:          800,\n\t\tH:          600,\n\t\tBackground: color.White,\n\t\tType:       opts[\"type\"],\n\t}\n\tif size, ok := opts[\"size\"]; ok {\n\t\tb, a, ok := strings.Cut(size, \"x\")\n\t\tif !ok {\n\t\t\treturn ChartConfig{}, fmt.Errorf(text.ChartParseFailed, \"size\", \"provide size as NxN\")\n\t\t}\n\t\tvar err error\n\t\tcfg.W, err = strconv.Atoi(b)\n\t\tif err != nil {\n\t\t\treturn ChartConfig{}, fmt.Errorf(text.ChartParseFailed, \"size\", err)\n\t\t}\n\t\tcfg.H, err = strconv.Atoi(a)\n\t\tif err != nil {\n\t\t\treturn ChartConfig{}, fmt.Errorf(text.ChartParseFailed, \"size\", err)\n\t\t}\n\t}\n\tif c, ok := opts[\"bg\"]; ok {\n\t\tvar err error\n\t\tcfg.Background, err = colors.Parse(c)\n\t\tif err != nil {\n\t\t\treturn ChartConfig{}, fmt.Errorf(text.ChartParseFailed, \"bg\", err)\n\t\t}\n\t}\n\tif prec, ok := opts[\"prec\"]; ok {\n\t\tp, err := strconv.Atoi(prec)\n\t\tif err != nil {\n\t\t\treturn ChartConfig{}, fmt.Errorf(text.ChartParseFailed, \"prec\", err)\n\t\t}\n\t\tcfg.Prec = p\n\t}\n\tif file, ok := opts[\"file\"]; ok {\n\t\tcfg.File = file\n\t}\n\treturn cfg, nil\n}\n\ntype Chart struct {\n\tTitle    string\n\tSubtitle string\n\tLegend   []string\n\tXAxis    Series\n\tYAxis    Series\n\tSeries   []Series\n}\n\ntype Series struct {\n\tName string\n\tType string\n\tData any\n}\n\nfunc MakeChart(cfg ChartConfig, cols []string, transposed [][]string) (*Chart, error) {\n\tnumCols := make([][]float64, len(cols))\n\tfor i, col := range transposed {\n\t\tfor _, v := range col {\n\t\t\tf, err := parseFloat(v, cfg.Prec)\n\t\t\tif err != nil {\n\t\t\t\tnumCols[i] = nil\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif numCols[i] == nil {\n\t\t\t\t// don't allocate slice unless we have at least some valid data\n\t\t\t\tnumCols[i] = make([]float64, 0, len(col))\n\t\t\t}\n\t\t\tnumCols[i] = append(numCols[i], f)\n\t\t}\n\t}\n\tfirstReg, firstNumeric := -1, -1\n\tfor i, c := range numCols {\n\t\tif firstReg == -1 && c == nil {\n\t\t\tfirstReg = i\n\t\t}\n\t\tif firstNumeric == -1 && c != nil {\n\t\t\tfirstNumeric = i\n\t\t}\n\t}\n\tc := &Chart{\n\t\tTitle:    cfg.Title,\n\t\tSubtitle: cfg.Subtitle,\n\t}\n\tvar x int\n\tvar chartType string\n\tswitch {\n\tcase firstNumeric == -1:\n\t\treturn nil, text.ErrNoNumericColumns\n\tcase firstReg >= 0:\n\t\tx = firstReg\n\t\tchartType = \"bar\"\n\tdefault:\n\t\tx = firstNumeric\n\t\tchartType = \"line\"\n\t}\n\tif cfg.Type != \"\" {\n\t\tchartType = cfg.Type\n\t}\n\tc.XAxis = Series{\n\t\tName: cols[x],\n\t\tType: \"category\",\n\t\tData: transposed[x],\n\t}\n\tc.YAxis = Series{\n\t\tType: \"value\",\n\t}\n\tfor i, col := range cols {\n\t\tif i == x {\n\t\t\tcontinue\n\t\t}\n\t\tc.Legend = append(c.Legend, col)\n\t\tc.Series = append(c.Series, Series{\n\t\t\tName: col,\n\t\t\tType: chartType,\n\t\t\tData: numCols[i],\n\t\t})\n\t}\n\treturn c, nil\n}\n\n/* echarts */\n\ntype echarts struct {\n\tTitle  *echartsTitle  `json:\"title,omitempty\"`\n\tLegend *echartsLegend `json:\"legend,omitempty\"`\n\tXAxis  *echartsAxis   `json:\"xAxis,omitempty\"`\n\tYAxis  *echartsAxis   `json:\"yAxis,omitempty\"`\n\tSeries []echartsAxis  `json:\"series,omitempty\"`\n}\n\ntype echartsTitle struct {\n\tTitle   string `json:\"text,omitempty\"`\n\tSubtext string `json:\"subtext,omitempty\"`\n}\n\ntype echartsLegend struct {\n\tData []string `json:\"data,omitempty\"`\n}\n\ntype echartsAxis struct {\n\tName string `json:\"name,omitempty\"`\n\tType string `json:\"type,omitempty\"`\n\tData any    `json:\"data,omitempty\"`\n}\n\nfunc (c Chart) ToEcharts() (string, error) {\n\tec := echarts{}\n\tif c.Title != \"\" || c.Subtitle != \"\" {\n\t\tec.Title = &echartsTitle{c.Title, c.Subtitle}\n\t}\n\tif len(c.Legend) > 0 {\n\t\tec.Legend = &echartsLegend{c.Legend}\n\t}\n\tif c.XAxis.Data != nil || c.YAxis.Type != \"\" {\n\t\tec.XAxis = &echartsAxis{\n\t\t\tName: c.XAxis.Name,\n\t\t\tType: c.XAxis.Type,\n\t\t\tData: c.XAxis.Data,\n\t\t}\n\t}\n\tif c.YAxis.Data != nil || c.YAxis.Type != \"\" {\n\t\tec.YAxis = &echartsAxis{\n\t\t\tName: c.YAxis.Name,\n\t\t\tType: c.YAxis.Type,\n\t\t\tData: c.YAxis.Data,\n\t\t}\n\t}\n\tif len(c.Series) > 0 {\n\t\tec.Series = make([]echartsAxis, 0, len(c.Series))\n\t\tfor _, s := range c.Series {\n\t\t\tec.Series = append(ec.Series, echartsAxis{\n\t\t\t\tName: s.Name,\n\t\t\t\tType: s.Type,\n\t\t\t\tData: s.Data,\n\t\t\t})\n\t\t}\n\t}\n\tbuf, err := json.Marshal(ec)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(buf), nil\n}\n\nfunc parseFloat(v string, prec int) (f float64, err error) {\n\tf, err = strconv.ParseFloat(v, 64)\n\tif err != nil || prec == 0 {\n\t\treturn\n\t}\n\tr := math.Pow(10, float64(prec))\n\treturn math.Round(f*r) / r, nil\n}\n\nconst basicBarTemplate = `\n{\n  \"title\": {\n    \"text\": {{ printf \"%q\" .Title }},\n    \"subtext\": {{ printf \"%q\" .Subtitle }}\n  },\n  {{- if .Legend }}\n  \"legend\": {\n    \"data\": [\n      {{ range .Legend }}{{ printf \"%q\" . }}{{ end }}\n    ]\n  },\n  {{- end }}\n  \"xAxis\": [\n    {\n      \"type\": \"category\",\n      \"data\": [\n        \"Jan\",\n        \"Feb\",\n        \"Mar\",\n        \"Apr\",\n        \"May\",\n        \"Jun\",\n        \"Jul\",\n        \"Aug\",\n        \"Sep\",\n        \"Oct\",\n        \"Nov\",\n        \"Dec\"\n      ]\n    }\n  ],\n  \"yAxis\": [\n    {\n      \"type\": \"value\"\n    }\n  ],\n  \"series\": [\n    {\n      \"name\": \"Rainfall\",\n      \"type\": \"bar\",\n      \"data\": [\n        2,\n        4.9,\n        7,\n        23.2,\n        25.6,\n        76.7,\n        135.6,\n        162.2,\n        32.6,\n        20,\n        6.4,\n        3.3\n      ],\n    },\n    {\n      \"name\": \"Evaporation\",\n      \"type\": \"bar\",\n      \"data\": [\n        2.6,\n        5.9,\n        9,\n        26.4,\n        28.7,\n        70.7,\n        175.6,\n        182.2,\n        48.7,\n        18.8,\n        6,\n        2.3\n      ],\n    }\n  ]\n}\n`\n"
  },
  {
    "path": "metacmd/cmds.go",
    "content": "package metacmd\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"os/signal\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/text\"\n)\n\n// Quit is a General meta command (\\q \\quit). Quits the application.\n//\n// Descs:\n//\n//\tq\tquit {{CommandName}}\n//\tquit\nfunc Quit(p *Params) error {\n\tp.Option.Quit = true\n\treturn nil\n}\n\n// Copyright is a General meta command (\\copyright). Writes the\n// application's copyright message to the output.\n//\n// Descs:\n//\n//\tcopyright\tshow usage and distribution terms for {{CommandName}}\nfunc Copyright(p *Params) error {\n\tstdout := p.Handler.IO().Stdout()\n\tif typ := env.TermGraphics(); typ.Available() {\n\t\ttyp.Encode(stdout, text.Logo)\n\t}\n\tfmt.Fprintln(stdout, text.Copyright)\n\treturn nil\n}\n\n// Drivers is a General meta command (\\drivers). Writes information about the\n// database drivers the application was built with to the output.\n//\n// Descs:\n//\n//\tdrivers\tshow database drivers available to {{CommandName}}\nfunc Drivers(p *Params) error {\n\tstdout, stderr := p.Handler.IO().Stdout(), p.Handler.IO().Stderr()\n\tvar cmd *exec.Cmd\n\tvar wc io.WriteCloser\n\tif pager := env.Get(\"PAGER\"); p.Handler.IO().Interactive() && pager != \"\" {\n\t\tvar err error\n\t\tif wc, cmd, err = env.Pipe(stdout, stderr, pager); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tstdout = wc\n\t}\n\tavailable := drivers.Available()\n\tnames := make([]string, len(available))\n\tvar z int\n\tfor k := range available {\n\t\tnames[z] = k\n\t\tz++\n\t}\n\tsort.Strings(names)\n\tfmt.Fprintln(stdout, text.AvailableDrivers)\n\tfor _, n := range names {\n\t\ts := \"  \" + n\n\t\tdriver, aliases := dburl.SchemeDriverAndAliases(n)\n\t\tif driver != n {\n\t\t\ts += \" (\" + driver + \")\"\n\t\t}\n\t\tif len(aliases) > 0 {\n\t\t\ts += \" [\" + strings.Join(aliases, \", \") + \"]\"\n\t\t}\n\t\tfmt.Fprintln(stdout, s)\n\t}\n\tif cmd != nil {\n\t\tif err := wc.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn cmd.Wait()\n\t}\n\treturn nil\n}\n\n// Help is a Help meta command (\\?). Writes a help message to the output.\n//\n// Descs:\n//\n//\t?\t[commands]\tshow help on {{CommandName}}'s meta (backslash) commands\n//\t?\toptions\tshow help on {{CommandName}} command-line options\n//\t?\tvariables\tshow help on special {{CommandName}} variables\nfunc Help(p *Params) error {\n\tname, err := p.Next(false)\n\tif err != nil {\n\t\treturn err\n\t}\n\tstdout, stderr := p.Handler.IO().Stdout(), p.Handler.IO().Stderr()\n\tvar cmd *exec.Cmd\n\tvar wc io.WriteCloser\n\tif pager := env.Get(\"PAGER\"); p.Handler.IO().Interactive() && pager != \"\" {\n\t\tif wc, cmd, err = env.Pipe(stdout, stderr, pager); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tstdout = wc\n\t}\n\tswitch name = strings.TrimSpace(strings.ToLower(name)); {\n\tcase name == \"options\":\n\t\ttext.Usage(stdout, true)\n\tcase name == \"variables\":\n\t\t_ = env.Listing(stdout)\n\tdefault:\n\t\t_ = Dump(stdout, name == \"commands\")\n\t}\n\tif cmd != nil {\n\t\tif err := wc.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn cmd.Wait()\n\t}\n\treturn nil\n}\n\n// Execute is a Query Execute meta command (\\g and variants). Executes the\n// active query on the open database connection.\n//\n// Descs:\n//\n//\tg\t[(OPTIONS)] [FILE] or ;\texecute query (and send results to file or |pipe)\n//\tgo:g\n//\tG\t[(OPTIONS)] [FILE]\tas \\g, but forces vertical output mode\n//\tego:G\n//\tgx\t[(OPTIONS)] [FILE]\tas \\g, but forces expanded output mode\n//\tgexec\texecute query and execute each value of the result\n//\tgset\t[PREFIX]\texecute query and store results in {{CommandName}} variables\nfunc Execute(p *Params) error {\n\tp.Option.Exec = ExecOnly\n\tswitch p.Name {\n\tcase \"g\", \"go\", \"G\", \"ego\", \"gx\", \"gset\":\n\t\tparams, err := p.All(true)\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase p.Name != \"gset\":\n\t\t\tp.Option.ParseParams(params, \"pipe\")\n\t\t}\n\t\tswitch p.Name {\n\t\tcase \"G\", \"ego\":\n\t\t\tp.Option.Params[\"format\"] = \"vertical\"\n\t\tcase \"gx\":\n\t\t\tp.Option.Params[\"expanded\"] = \"on\"\n\t\tcase \"gset\":\n\t\t\tp.Option.Exec = ExecSet\n\t\t\tp.Option.ParseParams(params, \"prefix\")\n\t\t}\n\tcase \"gexec\":\n\t\tp.Option.Exec = ExecExec\n\t}\n\treturn nil\n}\n\n// Bind is a Query Execute meta command (\\bind). Sets (or unsets) variables to\n// be used when executing a query.\n//\n// Descs:\n//\n//\tbind\t[PARAM]...\tset query parameters\nfunc Bind(p *Params) error {\n\tbind, err := p.All(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar v []interface{}\n\tif n := len(bind); n != 0 {\n\t\tv = make([]interface{}, len(bind))\n\t\tfor i := range n {\n\t\t\tv[i] = bind[i]\n\t\t}\n\t}\n\tp.Handler.Bind(v)\n\treturn nil\n}\n\n// Timing is a Query Execute meta command (\\timing). Sets (or toggles) writing\n// timing information for executed queries to the output.\n//\n// Descs:\n//\n//\ttiming\t[on|off]\ttoggle timing of commands\nfunc Timing(p *Params) error {\n\tv, err := p.Next(true)\n\tswitch {\n\tcase err != nil:\n\t\treturn err\n\tcase v == \"\":\n\t\tp.Handler.SetTiming(!p.Handler.GetTiming())\n\tdefault:\n\t\ts, err := env.ParseBool(v, `\\timing`)\n\t\tif err != nil {\n\t\t\tstderr := p.Handler.IO().Stderr()\n\t\t\tfmt.Fprintf(stderr, \"error: %v\", err)\n\t\t\tfmt.Fprintln(stderr)\n\t\t}\n\t\tvar b bool\n\t\tif s == \"on\" {\n\t\t\tb = true\n\t\t}\n\t\tp.Handler.SetTiming(b)\n\t}\n\tsetting := \"off\"\n\tif p.Handler.GetTiming() {\n\t\tsetting = \"on\"\n\t}\n\tp.Handler.Print(text.TimingSet, setting)\n\treturn nil\n}\n\n// Crosstab is a Query View meta command (\\crosstab). Executes the active query\n// on the open database connection and displays results in a crosstab view.\n//\n// Descs:\n//\n//\tcrosstab\t[(OPTIONS)] [COLUMNS]\texecute query and display results in crosstab\n//\tcrosstabview\n//\txtab\nfunc Crosstab(p *Params) error {\n\tp.Option.Exec = ExecCrosstab\n\tfor i := 0; i < 4; i++ {\n\t\tcol, ok, err := p.NextOK(true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tp.Option.Crosstab = append(p.Option.Crosstab, col)\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil\n}\n\n// Chart is a Query View meta command (\\chart). Executes the active query on\n// the open database connection and displays results in a chart view.\n//\n// Descs:\n//\n//\tchart\tCHART [(OPTIONS)]\texecute query and display results as a chart\nfunc Chart(p *Params) error {\n\tp.Option.Exec = ExecChart\n\tif p.Option.Params == nil {\n\t\tp.Option.Params = make(map[string]string, 1)\n\t}\n\tparams, err := p.All(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor i := 0; i < len(params); i++ {\n\t\tparam := params[i]\n\t\tif param == \"help\" {\n\t\t\tp.Option.Params[\"help\"] = \"\"\n\t\t\treturn nil\n\t\t}\n\t\tequal := strings.IndexByte(param, '=')\n\t\tswitch {\n\t\tcase equal == -1 && i >= len(params)-1:\n\t\t\treturn text.ErrWrongNumberOfArguments\n\t\tcase equal == -1:\n\t\t\ti++\n\t\t\tp.Option.Params[param] = params[i]\n\t\tdefault:\n\t\t\tp.Option.Params[param[:equal]] = param[equal+1:]\n\t\t}\n\t}\n\treturn nil\n}\n\n// Watch is a Query View meta command (\\watch). Executes (and re-executes) the\n// active query on the open database connection until canceled by the user.\n//\n// Descs:\n//\n//\twatch\t[(OPTIONS)] [INTERVAL]\texecute query every specified interval\nfunc Watch(p *Params) error {\n\tp.Option.Exec = ExecWatch\n\tp.Option.Watch = 2 * time.Second\n\tswitch s, ok, err := p.NextOK(true); {\n\tcase err != nil:\n\t\treturn err\n\tcase ok:\n\t\td, err := time.ParseDuration(s)\n\t\tif err != nil {\n\t\t\tif f, err := strconv.ParseFloat(s, 64); err == nil {\n\t\t\t\td = time.Duration(f * float64(time.Second))\n\t\t\t}\n\t\t}\n\t\tif d == 0 {\n\t\t\treturn text.ErrInvalidWatchDuration\n\t\t}\n\t\tp.Option.Watch = d\n\t}\n\treturn nil\n}\n\n// Connect is a Connection meta command (\\c, \\connect). Opens (connects) a\n// database connection.\n//\n// Descs:\n//\n//\tc\tDSN or \\c NAME\tconnect to dsn or named database connection\n//\tc\tDRIVER PARAMS...\tconnect to database with driver and parameters\n//\tconnect\nfunc Connect(p *Params) error {\n\tvals, err := p.All(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)\n\tdefer cancel()\n\treturn p.Handler.Open(ctx, vals...)\n}\n\n// Disconnect is a Connection meta command (\\Z). Closes (disconnects) the\n// current database connection.\n//\n// Descs:\n//\n//\tZ\tclose (disconnect) database connection\n//\tdisconnect\nfunc Disconnect(p *Params) error {\n\treturn p.Handler.Close()\n}\n\n// Password is a Connection meta command (\\password). Changes the database\n// user's password.\n//\n// Descs:\n//\n//\tpassword\t[USER]\tchange password for user\n//\tpasswd\nfunc Password(p *Params) error {\n\tusername, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tuser, err := p.Handler.ChangePassword(username)\n\tswitch {\n\tcase err == text.ErrPasswordNotSupportedByDriver || err == text.ErrNotConnected:\n\t\treturn err\n\tcase err != nil:\n\t\treturn fmt.Errorf(text.PasswordChangeFailed, user, err)\n\t}\n\t// p.Handler.Print(text.PasswordChangeSucceeded, user)\n\treturn nil\n}\n\n// ConnectionInfo is a Connection meta command (\\conninfo). Writes information\n// about the connection to the output.\n//\n// Descs:\n//\n//\tconninfo\tdisplay information about the current database connection\nfunc ConnectionInfo(p *Params) error {\n\ts := text.NotConnected\n\tif db, u := p.Handler.DB(), p.Handler.URL(); db != nil && u != nil {\n\t\ts = fmt.Sprintf(text.ConnInfo, u.Driver, u.DSN)\n\t}\n\tfmt.Fprintln(p.Handler.IO().Stdout(), s)\n\treturn nil\n}\n\n// Edit is a Query Buffer meta command (\\e \\edit). Opens the query buffer for\n// editing in an external application.\n//\n// Descs:\n//\n//\te\t[-raw|-exec] [FILE] [LINE]\tedit the query buffer, raw (non-interpolated) buffer, the exec buffer, or a file with external editor\n//\tedit\nfunc Edit(p *Params) error {\n\tvar exec bool\n\tpath, ok, err := p.NextOpt(true)\n\tif ok {\n\t\tif path != \"exec\" {\n\t\t\treturn fmt.Errorf(text.InvalidOption, path)\n\t\t}\n\t\texec = true\n\t\tif path, err = p.Next(true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// get last statement\n\ts, buf := \"\", p.Handler.Buf()\n\tswitch {\n\tcase buf.Len != 0 && exec:\n\t\ts = buf.String()\n\tcase buf.Len != 0:\n\t\ts = buf.RawString()\n\tcase exec:\n\t\ts = p.Handler.LastExec()\n\tdefault:\n\t\ts = p.Handler.LastRaw()\n\t}\n\tline, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// reset if no error\n\tout, err := env.EditFile(p.Handler.User(), path, line, []byte(s))\n\tif err != nil {\n\t\treturn err\n\t}\n\t// save edited buffer to history\n\tp.Handler.IO().Save(string(out))\n\tbuf.Reset([]rune(string(out)))\n\treturn nil\n}\n\n// Print is a Query Buffer meta command (\\p, \\print, \\raw, \\exec). Writes the\n// query buffer to the output.\n//\n// Descs:\n//\n//\tp\t[-raw|-exec]\tshow the contents of the query buffer, the raw (non-interpolated) buffer or the exec buffer\n//\tprint\n//\traw\n//\texec\nfunc Print(p *Params) error {\n\t// get last statement\n\tvar s string\n\tswitch buf := p.Handler.Buf(); {\n\tcase buf.Len != 0 && p.Name == \"exec\":\n\t\ts = buf.String()\n\tcase buf.Len != 0 && p.Name == \"raw\":\n\t\ts = buf.RawString()\n\tcase buf.Len != 0:\n\t\ts = buf.PrintString()\n\tcase p.Name == \"exec\":\n\t\ts = p.Handler.LastExec()\n\tcase p.Name == \"raw\":\n\t\ts = p.Handler.LastRaw()\n\tdefault:\n\t\ts = p.Handler.LastPrint()\n\t}\n\tswitch {\n\tcase s == \"\":\n\t\ts = text.QueryBufferEmpty\n\tcase p.Handler.IO().Interactive() && env.Get(\"SYNTAX_HL\") == \"true\":\n\t\tb := new(bytes.Buffer)\n\t\tif p.Handler.Highlight(b, s) == nil {\n\t\t\ts = b.String()\n\t\t}\n\t}\n\tfmt.Fprintln(p.Handler.IO().Stdout(), s)\n\treturn nil\n}\n\n// Write is a Query Buffer meta command (\\w \\write). Writes the query buffer to\n// a file.\n//\n// Descs:\n//\n//\tw\t[-raw|-exec] FILE\twrite the contents of the query buffer, raw (non-interpolated) buffer, or exec buffer to file\n//\twrite\nfunc Write(p *Params) error {\n\t// get last statement\n\ts, buf := p.Handler.LastExec(), p.Handler.Buf()\n\tif buf.Len != 0 {\n\t\ts = buf.String()\n\t}\n\tname, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn os.WriteFile(name, []byte(strings.TrimSuffix(s, \"\\n\")+\"\\n\"), 0o644)\n}\n\n// Reset is a Query Buffer meta command (\\r, \\reset). Clears (resets) the query\n// buffer.\n//\n// Descs:\n//\n//\tr\treset (clear) the query buffer\n//\treset\nfunc Reset(p *Params) error {\n\tp.Handler.Reset(nil)\n\tp.Handler.Print(text.QueryBufferReset)\n\treturn nil\n}\n\n// Echo is a Input/Output meta command (\\echo, \\warn, \\qecho). Writes a message\n// to the output.\n//\n// Descs:\n//\n//\techo\t[-n] [MESSAGE]...\twrite message to standard output (-n for no newline)\n//\tqecho\t[-n] [MESSAGE]...\twrite message to \\o output stream (-n for no newline)\n//\twarn\t[-n] [MESSAGE]...\twrite message to standard error (-n for no newline)\nfunc Echo(p *Params) error {\n\tn, ok, err := p.NextOpt(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf := fmt.Fprintln\n\tvar vals []string\n\tswitch {\n\tcase ok && n == \"n\":\n\t\tf = fmt.Fprint\n\tcase ok:\n\t\tvals = append(vals, \"-\"+n)\n\tdefault:\n\t\tvals = append(vals, n)\n\t}\n\tv, err := p.All(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tout := p.Handler.IO().Stdout()\n\tswitch o := p.Handler.GetOutput(); {\n\tcase p.Name == \"qecho\" && o != nil:\n\t\tout = o\n\tcase p.Name == \"warn\":\n\t\tout = p.Handler.IO().Stderr()\n\t}\n\tf(out, strings.Join(append(vals, v...), \" \"))\n\treturn nil\n}\n\n// Out is a Input/Output meta command (\\o \\out). Sets (redirects) the output to\n// a file or a command.\n//\n// Descs:\n//\n//\to\t[FILE]\tsend all query results to file or |pipe\n//\tout\nfunc Out(p *Params) error {\n\tp.Handler.SetOutput(nil)\n\tparams, err := p.All(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpipe := strings.Join(params, \" \")\n\tif pipe == \"\" {\n\t\treturn nil\n\t}\n\tvar out io.WriteCloser\n\tif pipe[0] == '|' {\n\t\tout, _, err = env.Pipe(p.Handler.IO().Stdout(), p.Handler.IO().Stderr(), pipe[1:])\n\t} else {\n\t\tout, err = os.OpenFile(pipe, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0o644)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.Handler.SetOutput(out)\n\treturn nil\n}\n\n// Copy is a Input/Output meta command (\\copy). Copies data between databases.\n//\n// Descs:\n//\n//\tcopy\tSRC DST QUERY TABLE\tcopy results of query from source database into table on destination database\n//\tcopy\tSRC DST QUERY TABLE(A,...)\tcopy results of query from source database into table's columns on destination database\nfunc Copy(p *Params) error {\n\tsrcstr, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsrc, err := dburl.Parse(srcstr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdeststr, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdest, err := dburl.Parse(deststr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tquery, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttable, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx := context.Background()\n\tstdout, stderr := p.Handler.IO().Stdout, p.Handler.IO().Stderr\n\tsrcDb, err := drivers.Open(ctx, src, stdout, stderr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer srcDb.Close()\n\tdestDb, err := drivers.Open(ctx, dest, stdout, stderr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer destDb.Close()\n\tctx, cancel := signal.NotifyContext(ctx, os.Interrupt)\n\tdefer cancel()\n\t// get the result set\n\tr, err := srcDb.QueryContext(ctx, query)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer r.Close()\n\tn, err := drivers.Copy(ctx, dest, stdout, stderr, r, table)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.Handler.Print(\"COPY %d\", n)\n\treturn nil\n}\n\n// Include is a Control/Conditional meta command (\\i, \\include and variants).\n// Includes (runs) the specified file in the current execution environment.\n//\n// Descs:\n//\n//\ti\tFILE\texecute commands from file\n//\tinclude:i\n//\tir\tFILE\tas \\i, but relative to location of current script\n//\tinclude_relative:ir\nfunc Include(p *Params) error {\n\tpath, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\trelative := p.Name == \"ir\" || p.Name == \"include_relative\"\n\tif err := p.Handler.Include(path, relative); err != nil {\n\t\treturn fmt.Errorf(\"%s: %v\", path, err)\n\t}\n\treturn nil\n}\n\n// Transact is a Transaction meta command (\\begin, \\commit, \\rollback). Begins,\n// commits, or aborts (rollback) the current database transaction on the open\n// database connection.\n//\n// Descs:\n//\n//\tbegin\t[-read-only [ISOLATION]]\tbegin transaction, with optional isolation level\n//\tcommit\tcommit current transaction\n//\trollback\trollback (abort) current transaction\n//\tabort:rollback\nfunc Transact(p *Params) error {\n\tswitch p.Name {\n\tcase \"commit\":\n\t\treturn p.Handler.Commit()\n\tcase \"rollback\", \"abort\":\n\t\treturn p.Handler.Rollback()\n\t}\n\t// read begin params\n\treadOnly := false\n\tn, ok, err := p.NextOpt(true)\n\tif ok {\n\t\tif n != \"read-only\" {\n\t\t\treturn fmt.Errorf(text.InvalidOption, n)\n\t\t}\n\t\treadOnly = true\n\t\tif n, err = p.Next(true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// build tx options\n\tvar txOpts *sql.TxOptions\n\tif readOnly || n != \"\" {\n\t\tisolation := sql.LevelDefault\n\t\tswitch strings.ToLower(n) {\n\t\tcase \"default\", \"\":\n\t\tcase \"read-uncommitted\":\n\t\t\tisolation = sql.LevelReadUncommitted\n\t\tcase \"read-committed\":\n\t\t\tisolation = sql.LevelReadCommitted\n\t\tcase \"write-committed\":\n\t\t\tisolation = sql.LevelWriteCommitted\n\t\tcase \"repeatable-read\":\n\t\t\tisolation = sql.LevelRepeatableRead\n\t\tcase \"snapshot\":\n\t\t\tisolation = sql.LevelSnapshot\n\t\tcase \"serializable\":\n\t\t\tisolation = sql.LevelSerializable\n\t\tcase \"linearizable\":\n\t\t\tisolation = sql.LevelLinearizable\n\t\tdefault:\n\t\t\treturn text.ErrInvalidIsolationLevel\n\t\t}\n\t\ttxOpts = &sql.TxOptions{\n\t\t\tIsolation: isolation,\n\t\t\tReadOnly:  readOnly,\n\t\t}\n\t}\n\t// begin\n\treturn p.Handler.Begin(txOpts)\n}\n\n// Set is a Variables meta command (\\set). Sets (or shows) the application variables.\n//\n// Descs:\n//\n//\tset\t[NAME [VALUE]]\tset {{CommandName}} application variable, or show all {{CommandName}} application variables if no parameters\nfunc Set(p *Params) error {\n\tswitch n, ok, err := p.NextOK(true); {\n\tcase err != nil:\n\t\treturn err\n\tcase ok:\n\t\tvals, err := p.All(true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn env.Vars().Set(n, strings.Join(vals, \" \"))\n\t}\n\treturn env.Vars().Dump(p.Handler.IO().Stdout())\n}\n\n// Unset is a Variables meta command (\\unset). Unsets a application variable.\n//\n// Descs:\n//\n//\tunset\tNAME\tunset (delete) {{CommandName}} application variable\nfunc Unset(p *Params) error {\n\tn, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn env.Vars().Unset(n)\n}\n\n// SetPrint is a Variables meta command (\\pset, \\a, \\C, \\f, \\H, \\t, \\T, \\x).\n// Sets, toggles, or displays the application's print formatting variables.\n//\n// Descs:\n//\n//\tpset\t[NAME [VALUE]]\tset table print formatting option, or show all print formatting options if no parameters\n//\ta\t\ttoggle between unaligned and aligned output mode\tDEPRECATED\n//\tC\t[TITLE]\tset table title, or unset if none\tDEPRECATED\n//\tf\t[SEPARATOR]\tshow or set field separator for unaligned query output\tDEPRECATED\n//\tH\t\ttoggle HTML output mode\tDEPRECATED\n//\tT\t[ATTRIBUTES]\tset HTML <table> tag attributes, or unset if none\tDEPRECATED\n//\tt\t[on|off]\tshow only rows\tDEPRECATED\n//\tx\t[on|off|auto]\ttoggle expanded output\tDEPRECATED\nfunc SetPrint(p *Params) error {\n\tvar ok bool\n\tvar val string\n\tvar err error\n\tswitch p.Name {\n\tcase \"a\", \"H\":\n\tdefault:\n\t\tif val, ok, err = p.NextOK(true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// display variables\n\tif p.Name == \"pset\" && !ok {\n\t\treturn env.Vars().DumpPrint(p.Handler.IO().Stdout())\n\t}\n\tvar field, extra string\n\tswitch p.Name {\n\tcase \"pset\":\n\t\tfield = val\n\t\tif val, ok, err = p.NextOK(true); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase \"a\":\n\t\tfield = \"format\"\n\tcase \"C\":\n\t\tfield = \"title\"\n\tcase \"f\":\n\t\tfield = \"fieldsep\"\n\tcase \"H\":\n\t\tfield, extra = \"format\", \"html\"\n\tcase \"t\":\n\t\tfield = \"tuples_only\"\n\tcase \"T\":\n\t\tfield = \"tableattr\"\n\tcase \"x\":\n\t\tfield = \"expanded\"\n\t}\n\tif !ok {\n\t\tif val, err = env.Vars().TogglePrint(field, extra); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif val, err = env.Vars().SetPrint(field, val); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// special replacement name for expanded field, when 'auto'\n\tif field == \"expanded\" && val == \"auto\" {\n\t\tfield = \"expanded_auto\"\n\t}\n\t// format output\n\tmask := text.FormatFieldNameSetMap[field]\n\tunsetMask := text.FormatFieldNameUnsetMap[field]\n\tswitch {\n\tcase strings.Contains(mask, \"%d\"):\n\t\ti, _ := strconv.Atoi(val)\n\t\tp.Handler.Print(mask, i)\n\tcase unsetMask != \"\" && val == \"\":\n\t\tp.Handler.Print(unsetMask)\n\tcase !strings.Contains(mask, \"%\"):\n\t\tp.Handler.Print(mask)\n\tdefault:\n\t\tif field == \"time\" {\n\t\t\tval = fmt.Sprintf(\"%q\", val)\n\t\t\tif tfmt := env.Vars().PrintTimeFormat(); tfmt != val {\n\t\t\t\tval = fmt.Sprintf(\"%s (%q)\", val, tfmt)\n\t\t\t}\n\t\t}\n\t\tp.Handler.Print(mask, val)\n\t}\n\treturn nil\n}\n\n// SetConn is a Variables meta command (\\cset). Sets a connection variable.\n//\n// Descs:\n//\n//\tcset\t[NAME [URL]]\tset named connection, or show all named connections if no parameters\n//\tcset\tNAME DRIVER PARAMS...\tset named connection for driver and parameters\nfunc SetConn(p *Params) error {\n\tswitch n, ok, err := p.NextOK(true); {\n\tcase err != nil:\n\t\treturn err\n\tcase ok:\n\t\tvals, err := p.All(true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn env.Vars().SetConn(n, vals...)\n\t}\n\treturn env.Vars().DumpConn(p.Handler.IO().Stdout())\n}\n\n// Prompt is a Variables meta command (\\prompt). Prompts the user for input,\n// setting a application variable to the user's response.\n//\n// Descs:\n//\n//\tprompt\t[-TYPE] VAR [PROMPT]\tprompt user to set application variable\nfunc Prompt(p *Params) error {\n\ttyp := \"string\"\n\tn, ok, err := p.NextOpt(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif ok {\n\t\ttyp = n\n\t\tn, err = p.Next(true)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif n == \"\" {\n\t\treturn text.ErrMissingRequiredArgument\n\t}\n\tif err := env.ValidIdentifier(n); err != nil {\n\t\treturn err\n\t}\n\tvals, err := p.All(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tv, err := p.Handler.ReadVar(typ, strings.Join(vals, \" \"))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn env.Vars().Set(n, v)\n}\n\n// Describe is a Informational meta command (\\d and variants). Queries the open\n// database connection for information about the database schema and writes the\n// information to the output.\n//\n// Descs:\n//\n//\td[S+]\t[NAME]\tlist tables, views, and sequences or describe table, view, sequence, or index\n//\tda[S+]\t[PATTERN]\tlist aggregates\n//\tdf[S+]\t[PATTERN]\tlist functions\n//\tdi[S+]\t[PATTERN]\tlist indexes\n//\tdm[S+]\t[PATTERN]\tlist materialized views\n//\tdn[S+]\t[PATTERN]\tlist schemas\n//\tdp[S]\t[PATTERN]\tlist table, view, and sequence access privileges\n//\tds[S+]\t[PATTERN]\tlist sequences\n//\tdt[S+]\t[PATTERN]\tlist tables\n//\tdv[S+]\t[PATTERN]\tlist views\n//\tl[+]\tlist databases\nfunc Describe(p *Params) error {\n\tctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)\n\tdefer cancel()\n\tm, err := p.Handler.MetadataWriter(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tverbose := strings.ContainsRune(p.Name, '+')\n\tshowSystem := strings.ContainsRune(p.Name, 'S')\n\tname := strings.TrimRight(p.Name, \"S+\")\n\tpattern, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tswitch name {\n\tcase \"d\":\n\t\tif pattern != \"\" {\n\t\t\treturn m.DescribeTableDetails(p.Handler.URL(), pattern, verbose, showSystem)\n\t\t}\n\t\treturn m.ListTables(p.Handler.URL(), \"tvmsE\", pattern, verbose, showSystem)\n\tcase \"df\", \"da\":\n\t\treturn m.DescribeFunctions(p.Handler.URL(), name, pattern, verbose, showSystem)\n\tcase \"dt\", \"dtv\", \"dtm\", \"dts\", \"dv\", \"dm\", \"ds\":\n\t\treturn m.ListTables(p.Handler.URL(), name, pattern, verbose, showSystem)\n\tcase \"dn\":\n\t\treturn m.ListSchemas(p.Handler.URL(), pattern, verbose, showSystem)\n\tcase \"di\":\n\t\treturn m.ListIndexes(p.Handler.URL(), pattern, verbose, showSystem)\n\tcase \"l\":\n\t\treturn m.ListAllDbs(p.Handler.URL(), pattern, verbose)\n\tcase \"dp\":\n\t\treturn m.ListPrivilegeSummaries(p.Handler.URL(), pattern, showSystem)\n\t}\n\treturn nil\n}\n\n// Stats is a Informational meta command (\\ss and variants). Queries the open\n// database connection for stats and writes it to the output.\n//\n// Descs:\n//\n//\tss[+]\t[TABLE|QUERY] [k]\tshow stats for a table or a query\nfunc Stats(p *Params) error {\n\tctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)\n\tdefer cancel()\n\tm, err := p.Handler.MetadataWriter(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tverbose := strings.ContainsRune(p.Name, '+')\n\tname := strings.TrimRight(p.Name, \"+\")\n\tpattern, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tk := 0\n\tif verbose {\n\t\tk = 3\n\t}\n\tif name == \"ss\" {\n\t\tname = \"sswnulhmkf\"\n\t}\n\tval, ok, err := p.NextOK(true)\n\tswitch {\n\tcase err != nil:\n\t\treturn err\n\tcase ok:\n\t\tverbose = true\n\t\tif k, err = strconv.Atoi(val); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn m.ShowStats(p.Handler.URL(), name, pattern, verbose, k)\n}\n\n// Conditional is a Control/Conditional meta command (\\if, \\elif, \\else,\n// \\endif). Starts, closes, and ends a conditional block within the\n// application.\n//\n// Descs:\n//\n//\tif\tEXPR\tbegin conditional block\n//\telif\tEXPR\talternative within current conditional block\n//\telse\tfinal alternative within current conditional block\n//\tendif\tend conditional block\nfunc Conditional(p *Params) error {\n\tswitch p.Name {\n\tcase \"if\":\n\tcase \"elif\":\n\tcase \"else\":\n\tcase \"endif\":\n\t}\n\treturn nil\n}\n\n// Shell is a Operating System/Environment meta command (\\!). Executes a\n// command using the Operating System/Environment's shell.\n//\n// Descs:\n//\n//\t!\t[COMMAND]\texecute command in shell or start interactive shell\nfunc Shell(p *Params) error {\n\treturn env.Shell(p.Raw())\n}\n\n// Chdir is a Operating System/Environment meta command (\\cd). Changes the\n// current directory for the Operating System/Environment.\n//\n// Descs:\n//\n//\tcd\t[DIR]\tchange the current working directory\nfunc Chdir(p *Params) error {\n\tdir, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn env.Chdir(p.Handler.User(), dir)\n}\n\n// Getenv is a Operating System/Environment meta command (\\getenv). Sets the\n// application's variable value returned from the Operating\n// System/Environment's variables.\n//\n// Descs:\n//\n//\tgetenv\tVARNAME ENVVAR\tfetch environment variable\nfunc Getenv(p *Params) error {\n\tn, err := p.Next(true)\n\tswitch {\n\tcase err != nil:\n\t\treturn err\n\tcase n == \"\":\n\t\treturn text.ErrMissingRequiredArgument\n\t}\n\tv, err := p.Next(true)\n\tswitch {\n\tcase err != nil:\n\t\treturn err\n\tcase v == \"\":\n\t\treturn text.ErrMissingRequiredArgument\n\t}\n\tvalue, _ := env.Getenv(v)\n\treturn env.Vars().Set(n, value)\n}\n\n// Setenv is a Operating System/Environment meta command (\\setenv). Sets (or\n// unsets) a Operating System/Environment variable. Environment variables set\n// this way will be passed to any child processes.\n//\n// Descs:\n//\n//\tsetenv\tNAME [VALUE]\tset or unset environment variable\nfunc Setenv(p *Params) error {\n\tn, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\tv, err := p.Next(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn os.Setenv(n, v)\n}\n"
  },
  {
    "path": "metacmd/descs.go",
    "content": "package metacmd\n\n// Code generated by gen.go. DO NOT EDIT.\n\nimport (\n\t\"github.com/xo/usql/text\"\n)\n\n// sections are the command description sections.\nvar sections = []string{\n\t\"General\",\n\t\"Help\",\n\t\"Connection\",\n\t\"Query Execute\",\n\t\"Query View\",\n\t\"Query Buffer\",\n\t\"Informational\",\n\t\"Variables\",\n\t\"Input/Output\",\n\t\"Control/Conditional\",\n\t\"Transaction\",\n\t\"Operating System/Environment\",\n}\n\n// descs are the command descriptions.\nvar descs [][]desc\n\n// cmds are the command lookup map.\nvar cmds map[string]func(*Params) error\n\nfunc init() {\n\tdescs = [][]desc{\n\t\t// General\n\t\t{\n\t\t\t{Quit, `q`, ``, `quit ` + text.CommandName + ``, false, false},\n\t\t\t{Quit, `quit`, ``, `alias for \\q`, true, false},\n\t\t\t{Copyright, `copyright`, ``, `show usage and distribution terms for ` + text.CommandName + ``, false, false},\n\t\t\t{Drivers, `drivers`, ``, `show database drivers available to ` + text.CommandName + ``, false, false},\n\t\t},\n\t\t// Help\n\t\t{\n\t\t\t{Help, `?`, `[commands]`, `show help on ` + text.CommandName + `'s meta (backslash) commands`, false, false},\n\t\t\t{Help, `?`, `options`, `show help on ` + text.CommandName + ` command-line options`, false, false},\n\t\t\t{Help, `?`, `variables`, `show help on special ` + text.CommandName + ` variables`, false, false},\n\t\t},\n\t\t// Connection\n\t\t{\n\t\t\t{Connect, `c`, `DSN or \\c NAME`, `connect to dsn or named database connection`, false, false},\n\t\t\t{Connect, `c`, `DRIVER PARAMS...`, `connect to database with driver and parameters`, false, false},\n\t\t\t{Connect, `connect`, ``, `alias for \\c`, true, false},\n\t\t\t{Disconnect, `Z`, ``, `close (disconnect) database connection`, false, false},\n\t\t\t{Disconnect, `disconnect`, ``, `alias for \\Z`, true, false},\n\t\t\t{Password, `password`, `[USER]`, `change password for user`, false, false},\n\t\t\t{Password, `passwd`, ``, `alias for \\password`, true, false},\n\t\t\t{ConnectionInfo, `conninfo`, ``, `display information about the current database connection`, false, false},\n\t\t},\n\t\t// Query Execute\n\t\t{\n\t\t\t{Execute, `g`, `[(OPTIONS)] [FILE] or ;`, `execute query (and send results to file or |pipe)`, false, false},\n\t\t\t{Execute, `go`, ``, `alias for \\g`, true, false},\n\t\t\t{Execute, `G`, `[(OPTIONS)] [FILE]`, `as \\g, but forces vertical output mode`, false, false},\n\t\t\t{Execute, `ego`, ``, `alias for \\G`, true, false},\n\t\t\t{Execute, `gx`, `[(OPTIONS)] [FILE]`, `as \\g, but forces expanded output mode`, false, false},\n\t\t\t{Execute, `gexec`, ``, `execute query and execute each value of the result`, false, false},\n\t\t\t{Execute, `gset`, `[PREFIX]`, `execute query and store results in ` + text.CommandName + ` variables`, false, false},\n\t\t\t{Bind, `bind`, `[PARAM]...`, `set query parameters`, false, false},\n\t\t\t{Timing, `timing`, `[on|off]`, `toggle timing of commands`, false, false},\n\t\t},\n\t\t// Query View\n\t\t{\n\t\t\t{Crosstab, `crosstab`, `[(OPTIONS)] [COLUMNS]`, `execute query and display results in crosstab`, false, false},\n\t\t\t{Crosstab, `crosstabview`, ``, `alias for \\crosstab`, true, false},\n\t\t\t{Crosstab, `xtab`, ``, `alias for \\crosstab`, true, false},\n\t\t\t{Chart, `chart`, `CHART [(OPTIONS)]`, `execute query and display results as a chart`, false, false},\n\t\t\t{Watch, `watch`, `[(OPTIONS)] [INTERVAL]`, `execute query every specified interval`, false, false},\n\t\t},\n\t\t// Query Buffer\n\t\t{\n\t\t\t{Edit, `e`, `[-raw|-exec] [FILE] [LINE]`, `edit the query buffer, raw (non-interpolated) buffer, the exec buffer, or a file with external editor`, false, false},\n\t\t\t{Edit, `edit`, ``, `alias for \\e`, true, false},\n\t\t\t{Print, `p`, `[-raw|-exec]`, `show the contents of the query buffer, the raw (non-interpolated) buffer or the exec buffer`, false, false},\n\t\t\t{Print, `print`, ``, `alias for \\p`, true, false},\n\t\t\t{Print, `raw`, ``, `alias for \\p`, true, false},\n\t\t\t{Print, `exec`, ``, `alias for \\p`, true, false},\n\t\t\t{Write, `w`, `[-raw|-exec] FILE`, `write the contents of the query buffer, raw (non-interpolated) buffer, or exec buffer to file`, false, false},\n\t\t\t{Write, `write`, ``, `alias for \\w`, true, false},\n\t\t\t{Reset, `r`, ``, `reset (clear) the query buffer`, false, false},\n\t\t\t{Reset, `reset`, ``, `alias for \\r`, true, false},\n\t\t},\n\t\t// Informational\n\t\t{\n\t\t\t{Describe, `d[S+]`, `[NAME]`, `list tables, views, and sequences or describe table, view, sequence, or index`, false, false},\n\t\t\t{Describe, `da[S+]`, `[PATTERN]`, `list aggregates`, false, false},\n\t\t\t{Describe, `df[S+]`, `[PATTERN]`, `list functions`, false, false},\n\t\t\t{Describe, `di[S+]`, `[PATTERN]`, `list indexes`, false, false},\n\t\t\t{Describe, `dm[S+]`, `[PATTERN]`, `list materialized views`, false, false},\n\t\t\t{Describe, `dn[S+]`, `[PATTERN]`, `list schemas`, false, false},\n\t\t\t{Describe, `dp[S]`, `[PATTERN]`, `list table, view, and sequence access privileges`, false, false},\n\t\t\t{Describe, `ds[S+]`, `[PATTERN]`, `list sequences`, false, false},\n\t\t\t{Describe, `dt[S+]`, `[PATTERN]`, `list tables`, false, false},\n\t\t\t{Describe, `dv[S+]`, `[PATTERN]`, `list views`, false, false},\n\t\t\t{Describe, `l[+]`, ``, `list databases`, false, false},\n\t\t\t{Stats, `ss[+]`, `[TABLE|QUERY] [k]`, `show stats for a table or a query`, false, false},\n\t\t},\n\t\t// Variables\n\t\t{\n\t\t\t{Set, `set`, `[NAME [VALUE]]`, `set ` + text.CommandName + ` application variable, or show all ` + text.CommandName + ` application variables if no parameters`, false, false},\n\t\t\t{Unset, `unset`, `NAME`, `unset (delete) ` + text.CommandName + ` application variable`, false, false},\n\t\t\t{SetPrint, `pset`, `[NAME [VALUE]]`, `set table print formatting option, or show all print formatting options if no parameters`, false, false},\n\t\t\t{SetPrint, `a`, ``, `toggle between unaligned and aligned output mode`, false, true},\n\t\t\t{SetPrint, `C`, `[TITLE]`, `set table title, or unset if none`, false, true},\n\t\t\t{SetPrint, `f`, `[SEPARATOR]`, `show or set field separator for unaligned query output`, false, true},\n\t\t\t{SetPrint, `H`, ``, `toggle HTML output mode`, false, true},\n\t\t\t{SetPrint, `T`, `[ATTRIBUTES]`, `set HTML <table> tag attributes, or unset if none`, false, true},\n\t\t\t{SetPrint, `t`, `[on|off]`, `show only rows`, false, true},\n\t\t\t{SetPrint, `x`, `[on|off|auto]`, `toggle expanded output`, false, true},\n\t\t\t{SetConn, `cset`, `[NAME [URL]]`, `set named connection, or show all named connections if no parameters`, false, false},\n\t\t\t{SetConn, `cset`, `NAME DRIVER PARAMS...`, `set named connection for driver and parameters`, false, false},\n\t\t\t{Prompt, `prompt`, `[-TYPE] VAR [PROMPT]`, `prompt user to set application variable`, false, false},\n\t\t},\n\t\t// Input/Output\n\t\t{\n\t\t\t{Echo, `echo`, `[-n] [MESSAGE]...`, `write message to standard output (-n for no newline)`, false, false},\n\t\t\t{Echo, `qecho`, `[-n] [MESSAGE]...`, `write message to \\o output stream (-n for no newline)`, false, false},\n\t\t\t{Echo, `warn`, `[-n] [MESSAGE]...`, `write message to standard error (-n for no newline)`, false, false},\n\t\t\t{Out, `o`, `[FILE]`, `send all query results to file or |pipe`, false, false},\n\t\t\t{Out, `out`, ``, `alias for \\o`, true, false},\n\t\t\t{Copy, `copy`, `SRC DST QUERY TABLE`, `copy results of query from source database into table on destination database`, false, false},\n\t\t\t{Copy, `copy`, `SRC DST QUERY TABLE(A,...)`, `copy results of query from source database into table's columns on destination database`, false, false},\n\t\t},\n\t\t// Control/Conditional\n\t\t{\n\t\t\t{Include, `i`, `FILE`, `execute commands from file`, false, false},\n\t\t\t{Include, `include`, ``, `alias for \\i`, true, false},\n\t\t\t{Include, `ir`, `FILE`, `as \\i, but relative to location of current script`, false, false},\n\t\t\t{Include, `include_relative`, ``, `alias for \\ir`, true, false},\n\t\t\t{Conditional, `if`, `EXPR`, `begin conditional block`, false, false},\n\t\t\t{Conditional, `elif`, `EXPR`, `alternative within current conditional block`, false, false},\n\t\t\t{Conditional, `else`, ``, `final alternative within current conditional block`, false, false},\n\t\t\t{Conditional, `endif`, ``, `end conditional block`, false, false},\n\t\t},\n\t\t// Transaction\n\t\t{\n\t\t\t{Transact, `begin`, `[-read-only [ISOLATION]]`, `begin transaction, with optional isolation level`, false, false},\n\t\t\t{Transact, `commit`, ``, `commit current transaction`, false, false},\n\t\t\t{Transact, `rollback`, ``, `rollback (abort) current transaction`, false, false},\n\t\t\t{Transact, `abort`, ``, `alias for \\rollback`, true, false},\n\t\t},\n\t\t// Operating System/Environment\n\t\t{\n\t\t\t{Shell, `!`, `[COMMAND]`, `execute command in shell or start interactive shell`, false, false},\n\t\t\t{Chdir, `cd`, `[DIR]`, `change the current working directory`, false, false},\n\t\t\t{Getenv, `getenv`, `VARNAME ENVVAR`, `fetch environment variable`, false, false},\n\t\t\t{Setenv, `setenv`, `NAME [VALUE]`, `set or unset environment variable`, false, false},\n\t\t},\n\t}\n\tcmds = make(map[string]func(*Params) error)\n\tfor i := range sections {\n\t\tfor _, desc := range descs[i] {\n\t\t\tfor _, n := range desc.Names() {\n\t\t\t\tcmds[n] = desc.Func\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "metacmd/metacmd.go",
    "content": "// Package metacmd contains meta information and implementation for usql's\n// backslash (\\) commands.\npackage metacmd\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"fmt\"\n\t\"io\"\n\t\"os/user\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/mattn/go-runewidth\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/drivers\"\n\t\"github.com/xo/usql/drivers/metadata\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/rline\"\n\t\"github.com/xo/usql/stmt\"\n\t\"github.com/xo/usql/text\"\n)\n\n// Handler is the shared interface for a command handler.\ntype Handler interface {\n\t// IO handles the handler's IO.\n\tIO() rline.IO\n\t// User returns the current user.\n\tUser() *user.User\n\t// URL returns the current database URL.\n\tURL() *dburl.URL\n\t// DB returns the current database connection.\n\tDB() drivers.DB\n\t// LastExec returns the last executed query.\n\tLastExec() string\n\t// LastPrint returns the last executed printable query.\n\tLastPrint() string\n\t// LastRaw returns the last raw (non-interpolated) query.\n\tLastRaw() string\n\t// Buf returns the current query buffer.\n\tBuf() *stmt.Stmt\n\t// Reset resets the last and current query buffer.\n\tReset([]rune)\n\t// Bind binds query parameters.\n\tBind([]interface{})\n\t// Open opens a database connection.\n\tOpen(context.Context, ...string) error\n\t// Close closes the current database connection.\n\tClose() error\n\t// ChangePassword changes the password for a user.\n\tChangePassword(string) (string, error)\n\t// ReadVar reads a variable of a specified type.\n\tReadVar(string, string) (string, error)\n\t// Include includes a file.\n\tInclude(string, bool) error\n\t// Begin begins a transaction.\n\tBegin(*sql.TxOptions) error\n\t// Commit commits the current transaction.\n\tCommit() error\n\t// Rollback aborts the current transaction.\n\tRollback() error\n\t// Highlight highlights the statement.\n\tHighlight(io.Writer, string) error\n\t// GetTiming mode.\n\tGetTiming() bool\n\t// SetTiming mode.\n\tSetTiming(bool)\n\t// GetOutput writer.\n\tGetOutput() io.Writer\n\t// SetOutput writer.\n\tSetOutput(io.WriteCloser)\n\t// MetadataWriter retrieves the metadata writer for the handler.\n\tMetadataWriter(context.Context) (metadata.Writer, error)\n\t// Print formats according to a format specifier and writes to handler's standard output.\n\tPrint(string, ...interface{})\n}\n\n// Dump writes the command descriptions to w, separated by section.\nfunc Dump(w io.Writer, hidden bool) error {\n\tn := 0\n\tfor i := range sections {\n\t\tfor _, desc := range descs[i] {\n\t\t\tif (!desc.Hidden && !desc.Deprecated) || hidden {\n\t\t\t\tn = max(n, runewidth.StringWidth(desc.Name)+1+runewidth.StringWidth(desc.Params))\n\t\t\t}\n\t\t}\n\t}\n\tfor i, s := range sections {\n\t\tif i != 0 {\n\t\t\tfmt.Fprintln(w)\n\t\t}\n\t\tfmt.Fprintln(w, s)\n\t\tfor _, desc := range descs[i] {\n\t\t\tif (!desc.Hidden && !desc.Deprecated) || hidden {\n\t\t\t\t_, _ = fmt.Fprintf(w, \"  \\\\%- *s  %s\\n\", n, desc.Name+\" \"+desc.Params, wrap(desc.Desc, 95, n+5))\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// Decode converts a command name (or alias) into a Runner.\nfunc Decode(name string, params *stmt.Params) (func(Handler) (Option, error), error) {\n\tf, ok := cmds[name]\n\tif !ok || name == \"\" {\n\t\treturn nil, text.ErrUnknownCommand\n\t}\n\treturn func(h Handler) (Option, error) {\n\t\tp := &Params{\n\t\t\tHandler: h,\n\t\t\tName:    name,\n\t\t\tParams:  params,\n\t\t}\n\t\terr := f(p)\n\t\treturn p.Option, err\n\t}, nil\n}\n\n// Params wraps metacmd parameters.\ntype Params struct {\n\t// Handler is the process handler.\n\tHandler Handler\n\t// Name is the name of the metacmd.\n\tName string\n\t// Params are the actual statement parameters.\n\tParams *stmt.Params\n\t// Option contains resulting command execution options.\n\tOption Option\n}\n\n// Next returns the next command parameter, using env.Untick.\nfunc (p *Params) Next(exec bool) (string, error) {\n\tv, _, err := p.Params.Next(env.Untick(\n\t\tp.Handler.User(),\n\t\tenv.Vars(),\n\t\texec,\n\t))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn v, nil\n}\n\n// NextOK returns the next command parameter, using env.Untick.\nfunc (p *Params) NextOK(exec bool) (string, bool, error) {\n\treturn p.Params.Next(env.Untick(\n\t\tp.Handler.User(),\n\t\tenv.Vars(),\n\t\texec,\n\t))\n}\n\n// NextOpt returns the next command parameter, using env.Untick. Returns true\n// when the value is prefixed with a \"-\", along with the value sans the \"-\"\n// prefix. Otherwise returns false and the value.\nfunc (p *Params) NextOpt(exec bool) (string, bool, error) {\n\tv, err := p.Next(exec)\n\tswitch {\n\tcase err != nil:\n\t\treturn \"\", false, err\n\tcase len(v) > 0 && v[0] == '-':\n\t\treturn v[1:], true, nil\n\t}\n\treturn v, false, nil\n}\n\n// All gets all remaining command parameters using env.Untick.\nfunc (p *Params) All(exec bool) ([]string, error) {\n\treturn p.Params.All(env.Untick(\n\t\tp.Handler.User(),\n\t\tenv.Vars(),\n\t\texec,\n\t))\n}\n\n// Raw returns the remaining command parameters as a raw string.\n//\n// Note: no other processing is done to interpolate variables or to decode\n// string values.\nfunc (p *Params) Raw() string {\n\treturn p.Params.Raw()\n}\n\n// Option contains parsed result options of a metacmd.\ntype Option struct {\n\t// Quit instructs the handling code to quit.\n\tQuit bool\n\t// Exec informs the handling code of the type of execution.\n\tExec ExecType\n\t// Params are accompanying string parameters for execution.\n\tParams map[string]string\n\t// Crosstab are the crosstab column parameters.\n\tCrosstab []string\n\t// Watch is the watch duration interval.\n\tWatch time.Duration\n}\n\nfunc (opt *Option) ParseParams(params []string, defaultKey string) error {\n\tif opt.Params == nil {\n\t\topt.Params = make(map[string]string, len(params))\n\t}\n\tformatOpts := false\n\tfor i, param := range params {\n\t\tif len(param) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif !formatOpts {\n\t\t\tif param[0] == '(' {\n\t\t\t\tformatOpts = true\n\t\t\t} else {\n\t\t\t\topt.Params[defaultKey] = strings.Join(params[i:], \" \")\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tparts := strings.SplitN(param, \"=\", 2)\n\t\tif len(parts) == 1 {\n\t\t\treturn text.ErrInvalidFormatOption\n\t\t}\n\t\topt.Params[strings.TrimLeft(parts[0], \"(\")] = strings.TrimRight(parts[1], \")\")\n\t\tif formatOpts && param[len(param)-1] == ')' {\n\t\t\tformatOpts = false\n\t\t}\n\t}\n\treturn nil\n}\n\n// ExecType represents the type of execution requested.\ntype ExecType int\n\nconst (\n\t// ExecNone indicates no execution.\n\tExecNone ExecType = iota\n\t// ExecOnly indicates plain execution only (\\g).\n\tExecOnly\n\t// ExecPipe indicates execution and piping results (\\g |file)\n\tExecPipe\n\t// ExecSet indicates execution and setting the resulting columns as\n\t// variables (\\gset).\n\tExecSet\n\t// ExecExec indicates execution and executing the resulting rows (\\gexec).\n\tExecExec\n\t// ExecCrosstab indicates execution using crosstabview (\\crosstabview).\n\tExecCrosstab\n\t// ExecChart indicates execution using chart (\\chart).\n\tExecChart\n\t// ExecWatch indicates repeated execution with a fixed time interval.\n\tExecWatch\n)\n\n// desc wraps a meta command description.\ntype desc struct {\n\tFunc       func(*Params) error\n\tName       string\n\tParams     string\n\tDesc       string\n\tHidden     bool\n\tDeprecated bool\n}\n\n// Names returns the names for the command.\nfunc (d desc) Names() []string {\n\tswitch i := strings.Index(d.Name, \"[\"); {\n\tcase i == -1:\n\t\treturn []string{d.Name}\n\tcase !strings.HasSuffix(d.Name, \"]\"):\n\t\tpanic(fmt.Sprintf(\"invalid command %q\", d.Name))\n\tdefault:\n\t\tname := d.Name[:i]\n\t\tv := []string{name}\n\t\tfor _, s := range d.Name[i+1 : len(d.Name)-1] {\n\t\t\tv = append(v, name+string(s))\n\t\t}\n\t\treturn v\n\t}\n}\n\n// wrap wraps a line of text to the specified width, and adding the prefix to\n// each wrapped line.\nfunc wrap(s string, width, prefixWidth int) string {\n\twords := strings.Fields(strings.TrimSpace(s))\n\tif len(words) == 0 {\n\t\treturn \"\"\n\t}\n\tprefix, wrapped := strings.Repeat(\" \", prefixWidth), words[0]\n\tleft := width - prefixWidth - len(wrapped)\n\tfor _, word := range words[1:] {\n\t\tif left < len(word)+1 {\n\t\t\twrapped += \"\\n\" + prefix + word\n\t\t\tleft = width - len(word)\n\t\t} else {\n\t\t\twrapped += \" \" + word\n\t\t\tleft -= 1 + len(word)\n\t\t}\n\t}\n\treturn wrapped\n}\n"
  },
  {
    "path": "rline/rline.go",
    "content": "// Package rline provides a readline implementation for usql.\npackage rline\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/gohxs/readline\"\n)\n\nvar (\n\t// ErrInterrupt is the interrupt error.\n\tErrInterrupt = readline.ErrInterrupt\n\t// ErrPasswordNotAvailable is the password not available error.\n\tErrPasswordNotAvailable = errors.New(\"password not available\")\n)\n\n// IO is the common input/output interface.\ntype IO interface {\n\t// Next returns the next line of runes (excluding '\\n') from the input.\n\tNext() ([]rune, error)\n\t// Close closes the IO.\n\tClose() error\n\t// Stdout is the IO's standard out.\n\tStdout() io.Writer\n\t// Stderr is the IO's standard error out.\n\tStderr() io.Writer\n\t// Interactive determines if the IO is an interactive terminal.\n\tInteractive() bool\n\t// Cygwin determines if the IO is a Cygwin interactive terminal.\n\tCygwin() bool\n\t// Prompt sets the prompt for the next interactive line read.\n\tPrompt(string)\n\t// Completer sets the auto-completer.\n\tCompleter(readline.AutoCompleter)\n\t// Save saves a line of history.\n\tSave(string) error\n\t// Password prompts for a password.\n\tPassword(string) (string, error)\n\t// SetOutput sets the output filter func.\n\tSetOutput(func(string) string)\n}\n\n// Rline provides a type compatible with the IO interface.\ntype Rline struct {\n\tInst *readline.Instance\n\tN    func() ([]rune, error)\n\tC    func() error\n\tOut  io.Writer\n\tErr  io.Writer\n\tInt  bool\n\tCyg  bool\n\tP    func(string)\n\tA    func(readline.AutoCompleter)\n\tS    func(string) error\n\tPw   func(string) (string, error)\n}\n\n// Next returns the next line of runes (excluding '\\n') from the input.\nfunc (l *Rline) Next() ([]rune, error) {\n\tif l.N != nil {\n\t\treturn l.N()\n\t}\n\treturn nil, io.EOF\n}\n\n// Close closes the IO.\nfunc (l *Rline) Close() error {\n\tif l.C != nil {\n\t\treturn l.C()\n\t}\n\treturn nil\n}\n\n// Stdout is the IO's standard out.\nfunc (l *Rline) Stdout() io.Writer {\n\treturn l.Out\n}\n\n// Stderr is the IO's standard error out.\nfunc (l *Rline) Stderr() io.Writer {\n\treturn l.Err\n}\n\n// Interactive determines if the IO is an interactive terminal.\nfunc (l *Rline) Interactive() bool {\n\treturn l.Int\n}\n\n// Cygwin determines if the IO is a Cygwin interactive terminal.\nfunc (l *Rline) Cygwin() bool {\n\treturn l.Cyg\n}\n\n// Prompt sets the prompt for the next interactive line read.\nfunc (l *Rline) Prompt(s string) {\n\tif l.P != nil {\n\t\tl.P(s)\n\t}\n}\n\n// Completer sets the auto-completer.\nfunc (l *Rline) Completer(a readline.AutoCompleter) {\n\tif l.A != nil {\n\t\tl.A(a)\n\t}\n}\n\n// Save saves a line of history.\nfunc (l *Rline) Save(s string) error {\n\tif l.S != nil {\n\t\treturn l.S(s)\n\t}\n\treturn nil\n}\n\n// Password prompts for a password.\nfunc (l *Rline) Password(prompt string) (string, error) {\n\tif l.Pw != nil {\n\t\treturn l.Pw(prompt)\n\t}\n\treturn \"\", ErrPasswordNotAvailable\n}\n\n// SetOutput sets the output format func.\nfunc (l *Rline) SetOutput(f func(string) string) {\n\tl.Inst.Config.Output = f\n}\n\n// New creates a new readline input/output handler.\nfunc New(interactive, cygwin, forceNonInteractive bool, out, histfile string) (IO, error) {\n\tvar closers []func() error\n\t// configure stdin\n\tvar stdin io.ReadCloser\n\tswitch {\n\tcase forceNonInteractive:\n\t\tinteractive, cygwin = false, false\n\tcase cygwin:\n\t\tstdin = os.Stdin\n\tdefault:\n\t\tstdin = readline.Stdin\n\t}\n\t// configure stdout\n\tvar stdout io.WriteCloser\n\tswitch {\n\tcase out != \"\":\n\t\tvar err error\n\t\tstdout, err = os.OpenFile(out, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0o644)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tclosers = append(closers, stdout.Close)\n\t\tinteractive = false\n\tcase cygwin:\n\t\tstdout = os.Stdout\n\tdefault:\n\t\tstdout = readline.Stdout\n\t}\n\t// configure stderr\n\tvar stderr io.Writer = os.Stderr\n\tif !cygwin {\n\t\tstderr = readline.Stderr\n\t}\n\tif interactive {\n\t\t// wrap it with cancelable stdin\n\t\tstdin = readline.NewCancelableStdin(stdin)\n\t}\n\t// create readline instance\n\tl, err := readline.NewEx(&readline.Config{\n\t\tHistoryFile:            histfile,\n\t\tDisableAutoSaveHistory: true,\n\t\tInterruptPrompt:        \"^C\",\n\t\tHistorySearchFold:      true,\n\t\tStdin:                  stdin,\n\t\tStdout:                 stdout,\n\t\tStderr:                 stderr,\n\t\tFuncIsTerminal: func() bool {\n\t\t\treturn interactive || cygwin\n\t\t},\n\t\tFuncFilterInputRune: func(r rune) (rune, bool) {\n\t\t\tif r == readline.CharCtrlZ {\n\t\t\t\treturn r, false\n\t\t\t}\n\t\t\treturn r, true\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclosers = append(closers, l.Close)\n\tn := l.Operation.Runes\n\tpw := func(prompt string) (string, error) {\n\t\tbuf, err := l.ReadPassword(prompt)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn string(buf), nil\n\t}\n\tif forceNonInteractive {\n\t\tn, pw = nil, nil\n\t}\n\treturn &Rline{\n\t\tInst: l,\n\t\tN:    n,\n\t\tC: func() error {\n\t\t\tfor _, f := range closers {\n\t\t\t\t_ = f()\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tOut: stdout,\n\t\tErr: stderr,\n\t\tInt: interactive || cygwin,\n\t\tCyg: cygwin,\n\t\tP:   l.SetPrompt,\n\t\tA: func(a readline.AutoCompleter) {\n\t\t\tcfg := l.Config.Clone()\n\t\t\tcfg.AutoComplete = a\n\t\t\tl.SetConfig(cfg)\n\t\t},\n\t\tS:  l.SaveHistory,\n\t\tPw: pw,\n\t}, nil\n}\n"
  },
  {
    "path": "run.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/user\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/go-git/go-billy/v5\"\n\t\"github.com/go-git/go-billy/v5/memfs\"\n\t\"github.com/go-git/go-billy/v5/osfs\"\n\t\"github.com/mattn/go-isatty\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/pflag\"\n\t\"github.com/spf13/viper\"\n\t\"github.com/xo/dburl\"\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/handler\"\n\t\"github.com/xo/usql/rline\"\n\t\"github.com/xo/usql/text\"\n)\n\n// ContextExecutor is the command context.\ntype ContextExecutor interface {\n\tExecuteContext(context.Context) error\n}\n\n// New builds the command context.\nfunc New(cliargs []string) ContextExecutor {\n\targs := &Args{}\n\tvar (\n\t\tbashCompletion       bool\n\t\tzshCompletion        bool\n\t\tfishCompletion       bool\n\t\tpowershellCompletion bool\n\t\tnoDescriptions       bool\n\t\tbadHelp              bool\n\t)\n\tv := viper.New()\n\tc := &cobra.Command{\n\t\tUse:                text.CommandName + \" [flags]... [DSN]\",\n\t\tShort:              text.Short(),\n\t\tVersion:            text.CommandVersion,\n\t\tSilenceErrors:      true,\n\t\tSilenceUsage:       true,\n\t\tDisableAutoGenTag:  true,\n\t\tDisableSuggestions: true,\n\t\tArgs: func(_ *cobra.Command, cliargs []string) error {\n\t\t\tif len(cliargs) > 1 {\n\t\t\t\treturn text.ErrWrongNumberOfArguments\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tPersistentPreRunE: func(cmd *cobra.Command, _ []string) error {\n\t\t\tcommandUpper := text.CommandUpper()\n\t\t\tconfigFile := strings.TrimSpace(os.Getenv(commandUpper + \"_CONFIG\"))\n\t\t\tcmd.Flags().VisitAll(func(f *pflag.Flag) {\n\t\t\t\tif s := strings.TrimSpace(f.Value.String()); f.Name == \"config\" && s != \"\" {\n\t\t\t\t\tconfigFile = s\n\t\t\t\t}\n\t\t\t})\n\t\t\tif configFile != \"\" {\n\t\t\t\tv.SetConfigFile(configFile)\n\t\t\t} else {\n\t\t\t\tv.SetConfigName(text.ConfigName)\n\t\t\t\tif configDir, err := os.UserConfigDir(); err == nil {\n\t\t\t\t\tv.AddConfigPath(filepath.Join(configDir, text.CommandName))\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := v.ReadInConfig(); err != nil {\n\t\t\t\tif _, ok := err.(viper.ConfigFileNotFoundError); !ok {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tv.SetEnvPrefix(commandUpper)\n\t\t\tv.AutomaticEnv()\n\t\t\tcmd.Flags().VisitAll(func(f *pflag.Flag) {\n\t\t\t\tif f.Name == \"config\" {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t_ = v.BindEnv(f.Name, commandUpper+\"_\"+strings.ToUpper(strings.ReplaceAll(f.Name, \"-\", \"_\")))\n\t\t\t\tif !f.Changed && v.IsSet(f.Name) {\n\t\t\t\t\t_ = cmd.Flags().Set(f.Name, fmt.Sprintf(\"%v\", v.Get(f.Name)))\n\t\t\t\t}\n\t\t\t})\n\t\t\t// unhide params\n\t\t\tswitch {\n\t\t\tcase bashCompletion,\n\t\t\t\tzshCompletion,\n\t\t\t\tfishCompletion,\n\t\t\t\tpowershellCompletion,\n\t\t\t\tcmd.Name() == \"__complete\":\n\t\t\t\tflags := cmd.Root().Flags()\n\t\t\t\tfor _, name := range []string{\"no-psqlrc\", \"no-\" + text.CommandName + \"rc\", \"var\", \"variable\"} {\n\t\t\t\t\tflags.Lookup(name).Hidden = false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\tRunE: func(cmd *cobra.Command, cliargs []string) error {\n\t\t\t// completions and short circuits\n\t\t\tswitch {\n\t\t\tcase bashCompletion:\n\t\t\t\treturn cmd.GenBashCompletionV2(os.Stdout, !noDescriptions)\n\t\t\tcase zshCompletion:\n\t\t\t\tif noDescriptions {\n\t\t\t\t\treturn cmd.GenZshCompletionNoDesc(os.Stdout)\n\t\t\t\t}\n\t\t\t\treturn cmd.GenZshCompletion(os.Stdout)\n\t\t\tcase fishCompletion:\n\t\t\t\treturn cmd.GenFishCompletion(os.Stdout, !noDescriptions)\n\t\t\tcase powershellCompletion:\n\t\t\t\tif noDescriptions {\n\t\t\t\t\treturn cmd.GenPowerShellCompletion(os.Stdout)\n\t\t\t\t}\n\t\t\t\treturn cmd.GenPowerShellCompletionWithDesc(os.Stdout)\n\t\t\tcase badHelp:\n\t\t\t\treturn errors.New(\"unknown shorthand flag: 'h' in -h\")\n\t\t\t}\n\t\t\t// run\n\t\t\tif len(cliargs) > 0 {\n\t\t\t\targs.DSN = cliargs[0]\n\t\t\t}\n\t\t\t// create charts chroot\n\t\t\tvar err error\n\t\t\tif args.Charts, err = chartsFS(v); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\t// fmt.Fprintf(os.Stderr, \"\\n\\n%v\\n\\n\", args.Charts)\n\t\t\targs.Connections = v.GetStringMap(\"connections\")\n\t\t\targs.Init = v.GetString(\"init\")\n\t\t\targs.ConfigFileUsed = v.ConfigFileUsed()\n\t\t\treturn Run(cmd.Context(), args)\n\t\t},\n\t}\n\n\tc.SetVersionTemplate(\"{{ .Name }} {{ .Version }}\\n\")\n\tc.SetArgs(cliargs[1:])\n\tc.SetUsageTemplate(text.UsageTemplate)\n\ttext.UsageString = c.UsageString\n\n\tflags := c.Flags()\n\tflags.SortFlags = false\n\n\t// completions / short circuits\n\tflags.BoolVar(&bashCompletion, \"completion-script-bash\", false, \"output bash completion script and exit\")\n\tflags.BoolVar(&zshCompletion, \"completion-script-zsh\", false, \"output zsh completion script and exit\")\n\tflags.BoolVar(&fishCompletion, \"completion-script-fish\", false, \"output fish completion script and exit\")\n\tflags.BoolVar(&powershellCompletion, \"completion-script-powershell\", false, \"output powershell completion script and exit\")\n\tflags.BoolVar(&noDescriptions, \"no-descriptions\", false, \"disable descriptions in completion scripts\")\n\tflags.BoolVarP(&badHelp, \"bad-help\", \"h\", false, \"bad help\")\n\n\t// command / file flags\n\tflags.VarP(commandOrFile{args, true}, \"command\", \"c\", \"run only single command (SQL or internal) and exit\")\n\tflags.VarP(commandOrFile{args, false}, \"file\", \"f\", \"execute commands from file and exit\")\n\n\t// general flags\n\tflags.BoolVarP(&args.NoPassword, \"no-password\", \"w\", false, \"never prompt for password\")\n\tflags.BoolVarP(&args.NoInit, \"no-init\", \"X\", false, \"do not execute initialization scripts (aliases: --no-rc --no-psqlrc --no-\"+text.CommandName+\"rc)\")\n\tflags.BoolVar(&args.NoInit, \"no-rc\", false, \"do not read startup file\")\n\tflags.BoolVar(&args.NoInit, \"no-psqlrc\", false, \"do not read startup file\")\n\tflags.BoolVar(&args.NoInit, \"no-\"+text.CommandName+\"rc\", false, \"do not read startup file\")\n\tflags.VarP(filevar{&args.Out}, \"out\", \"o\", \"output file\")\n\tflags.BoolVarP(&args.ForcePassword, \"password\", \"W\", false, \"force password prompt (should happen automatically)\")\n\tflags.BoolVarP(&args.SingleTransaction, \"single-transaction\", \"1\", false, \"execute as a single transaction (if non-interactive)\")\n\n\t// set\n\tsf(flags, &args.Vars, \"set\", \"v\", `set variable NAME to VALUE (see \\set command, aliases: --var --variable)`, \"NAME=VALUE\")\n\tsf(flags, &args.Vars, \"var\", \"\", \"set variable NAME to VALUE\", \"NAME=VALUE\")\n\tsf(flags, &args.Vars, \"variable\", \"\", \"set variable NAME to VALUE\", \"NAME=VALUE\")\n\t// cset\n\tsf(flags, &args.Cvars, \"cset\", \"N\", `set named connection NAME to DSN (see \\cset command)`, \"NAME=DSN\")\n\t// pset\n\tsf(flags, &args.Pvars, \"pset\", \"P\", `set printing option VAR to ARG (see \\pset command)`, \"VAR=ARG\")\n\t// pset flags\n\tsf(flags, &args.Pvars, \"field-separator\", \"F\", `field separator for unaligned and CSV output (default \"|\" and \",\")`, \"FIELD-SEPARATOR\", \"fieldsep=%q\", \"csv_fieldsep=%q\")\n\tsf(flags, &args.Pvars, \"record-separator\", \"R\", `record separator for unaligned and CSV output (default \\n)`, \"RECORD-SEPARATOR\", \"recordsep=%q\")\n\tsf(flags, &args.Pvars, \"table-attr\", \"T\", \"set HTML table tag attributes (e.g., width, border)\", \"TABLE-ATTR\", \"tableattr=%q\")\n\t// pset bools\n\tsf(flags, &args.Pvars, \"no-align\", \"A\", \"unaligned table output mode\", \"\", \"format=unaligned\")\n\tsf(flags, &args.Pvars, \"html\", \"H\", \"HTML table output mode\", \"\", \"format=html\")\n\tsf(flags, &args.Pvars, \"tuples-only\", \"t\", \"print rows only\", \"\", \"tuples_only=on\")\n\tsf(flags, &args.Pvars, \"expanded\", \"x\", \"turn on expanded table output\", \"\", \"expanded=on\")\n\tsf(flags, &args.Pvars, \"field-separator-zero\", \"z\", \"set field separator for unaligned and CSV output to zero byte\", \"\", \"fieldsep_zero=on\")\n\tsf(flags, &args.Pvars, \"record-separator-zero\", \"0\", \"set record separator for unaligned and CSV output to zero byte\", \"\", \"recordsep_zero=on\")\n\tsf(flags, &args.Pvars, \"json\", \"J\", \"JSON output mode\", \"\", \"format=json\")\n\tsf(flags, &args.Pvars, \"csv\", \"C\", \"CSV output mode\", \"\", \"format=csv\")\n\tsf(flags, &args.Pvars, \"vertical\", \"G\", \"vertical output mode\", \"\", \"format=vertical\")\n\t// set bools\n\tsf(flags, &args.Vars, \"quiet\", \"q\", \"run quietly (no messages, only query output)\", \"\", \"QUIET=on\")\n\n\t// app config\n\t_ = flags.StringP(\"config\", \"\", \"\", \"config file\")\n\n\t// manually set --version, see github.com/spf13/cobra/command.go\n\t_ = flags.BoolP(\"version\", \"V\", false, \"output version information, then exit\")\n\t_ = flags.SetAnnotation(\"version\", cobra.FlagSetByCobraAnnotation, []string{\"true\"})\n\n\t// manually set --help, see github.com/spf13/cobra/command.go\n\t_ = flags.BoolP(\"help\", \"?\", false, \"show this help, then exit\")\n\t_ = c.Flags().SetAnnotation(\"help\", cobra.FlagSetByCobraAnnotation, []string{\"true\"})\n\n\t// mark hidden\n\tfor _, name := range []string{\n\t\t\"no-rc\", \"no-psqlrc\", \"no-\" + text.CommandName + \"rc\", \"var\", \"variable\",\n\t\t\"completion-script-bash\", \"completion-script-zsh\", \"completion-script-fish\",\n\t\t\"completion-script-powershell\", \"no-descriptions\",\n\t\t\"bad-help\",\n\t} {\n\t\tflags.Lookup(name).Hidden = true\n\t}\n\n\treturn c\n}\n\n// Run runs the application.\nfunc Run(ctx context.Context, args *Args) error {\n\t// get user\n\tu, err := user.Current()\n\tif err != nil {\n\t\treturn err\n\t}\n\t// get working directory\n\twd, err := os.Getwd()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// determine if interactive\n\tinteractive := isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stdin.Fd())\n\tcygwin := isatty.IsCygwinTerminal(os.Stdout.Fd()) && isatty.IsCygwinTerminal(os.Stdin.Fd())\n\tforceNonInteractive := len(args.CommandOrFiles) != 0\n\n\t// enable term graphics\n\tif !forceNonInteractive && interactive && !cygwin {\n\t\t// NOTE: this is done here and not in the env.init() package, because\n\t\t// NOTE: we need to determine if it is interactive first, otherwise it\n\t\t// NOTE: could mess up the non-interactive output with control characters\n\t\tvar typ string\n\t\tif s, _ := env.Getenv(text.CommandUpper()+\"_TERM_GRAPHICS\", \"TERM_GRAPHICS\"); s != \"\" {\n\t\t\ttyp = s\n\t\t}\n\t\tif err := env.Vars().Set(\"TERM_GRAPHICS\", typ); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// configured named connections\n\tfor name, v := range args.Connections {\n\t\tif err := setConn(name, v); err != nil && !forceNonInteractive && interactive {\n\t\t\tfmt.Fprintln(os.Stderr, fmt.Sprintf(text.InvalidNamedConnection, name, err))\n\t\t}\n\t}\n\n\t// fmt.Fprintf(os.Stdout, \"VARS: %v\\nCVARS: %v\\nPVARS: %v\\n\", args.Vars, args.Cvars, args.Pvars)\n\n\t// set vars\n\tfor _, v := range args.Vars {\n\t\tif i := strings.Index(v, \"=\"); i != -1 {\n\t\t\t_ = env.Vars().Set(v[:i], v[i+1:])\n\t\t} else {\n\t\t\t_ = env.Vars().Unset(v)\n\t\t}\n\t}\n\t// set cvars\n\tfor _, v := range args.Cvars {\n\t\tif i := strings.Index(v, \"=\"); i != -1 {\n\t\t\ts := v[i+1:]\n\t\t\tif c := s[0]; c == '\\'' || c == '\"' {\n\t\t\t\tif s, err = env.Unquote(s); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err = env.Vars().SetConn(v[:i], s); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif err = env.Vars().SetConn(v, \"\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\t// set pvars\n\tfor _, v := range args.Pvars {\n\t\tif i := strings.Index(v, \"=\"); i != -1 {\n\t\t\ts := v[i+1:]\n\t\t\tif c := s[0]; c == '\\'' || c == '\"' {\n\t\t\t\tif s, err = env.Unquote(s); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif _, err = env.Vars().SetPrint(v[:i], s); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tif _, err = env.Vars().TogglePrint(v, \"\"); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\t// create input/output\n\tl, err := rline.New(interactive, cygwin, forceNonInteractive, args.Out, env.HistoryFile(u))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer l.Close()\n\t// create handler\n\th := handler.New(l, u, wd, args.Charts, args.NoPassword)\n\t// force password\n\tdsn := args.DSN\n\tif args.ForcePassword {\n\t\tif dsn, err = h.Password(dsn); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// open dsn\n\tif err = h.Open(ctx, dsn); err != nil {\n\t\treturn err\n\t}\n\t// start transaction\n\tif args.SingleTransaction {\n\t\tif h.IO().Interactive() {\n\t\t\treturn text.ErrSingleTransactionCannotBeUsedWithInteractiveMode\n\t\t}\n\t\tif err = h.BeginTx(ctx, nil); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t// init script\n\tif !args.NoInit {\n\t\t// rc file\n\t\tif rc := env.RCFile(u); rc != \"\" {\n\t\t\tif err = h.Include(rc, false); err != nil && err != text.ErrNoSuchFileOrDirectory {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif args.Init != \"\" {\n\t\t\tif err = h.IncludeReader(strings.NewReader(args.Init), args.ConfigFileUsed); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\t// setup runner\n\tf := h.Run\n\tif len(args.CommandOrFiles) != 0 {\n\t\tf = runCommandOrFiles(h, args.CommandOrFiles)\n\t}\n\t// run\n\tif err = f(); err != nil {\n\t\treturn err\n\t}\n\t// commit\n\tif args.SingleTransaction {\n\t\treturn h.Commit()\n\t}\n\treturn nil\n}\n\n// Args are the command line arguments.\ntype Args struct {\n\tDSN               string\n\tCommandOrFiles    []CommandOrFile\n\tOut               string\n\tForcePassword     bool\n\tNoPassword        bool\n\tNoInit            bool\n\tSingleTransaction bool\n\tVars              []string\n\tCvars             []string\n\tPvars             []string\n\tCharts            billy.Filesystem\n\tConnections       map[string]interface{}\n\tInit              string\n\tConfigFileUsed    string\n}\n\n// CommandOrFile is a special type to deal with interspersed -c, -f,\n// command-line options, to ensure proper order execution.\ntype CommandOrFile struct {\n\tCommand bool\n\tValue   string\n}\n\n// commandOrFile provides a [pflag.Value] to wrap the command or file value in\n// [Args].\ntype commandOrFile struct {\n\targs    *Args\n\tcommand bool\n}\n\n// Set satisfies the [pflag.Value] interface.\nfunc (c commandOrFile) Set(value string) error {\n\tc.args.CommandOrFiles = append(c.args.CommandOrFiles, CommandOrFile{\n\t\tCommand: c.command,\n\t\tValue:   value,\n\t})\n\treturn nil\n}\n\n// String satisfies the [pflag.Value] interface.\nfunc (c commandOrFile) String() string {\n\treturn \"\"\n}\n\n// Type satisfies the [pflag.Value] interface.\nfunc (c commandOrFile) Type() string {\n\tif c.command {\n\t\treturn \"COMMAND\"\n\t}\n\treturn \"FILE\"\n}\n\n// vs handles setting vars with predefined values.\ntype vs struct {\n\tvars *[]string\n\tvals []string\n\ttyp  string\n}\n\n// Set satisfies the [pflag.Value] interface.\nfunc (p vs) Set(value string) error {\n\tif len(p.vals) != 0 {\n\t\tfor _, v := range p.vals {\n\t\t\tif strings.Contains(v, \"%\") {\n\t\t\t\t*p.vars = append(*p.vars, fmt.Sprintf(v, value))\n\t\t\t} else {\n\t\t\t\t*p.vars = append(*p.vars, v)\n\t\t\t}\n\t\t}\n\t} else {\n\t\t*p.vars = append(*p.vars, value)\n\t}\n\treturn nil\n}\n\n// String satisfies the [pflag.Value] interface.\nfunc (vs) String() string {\n\treturn \"\"\n}\n\n// Type satisfies the [pflag.Value] interface.\nfunc (p vs) Type() string {\n\tif p.typ == \"\" {\n\t\treturn \"bool\"\n\t}\n\treturn p.typ\n}\n\n// filevar is a file var.\ntype filevar struct {\n\tv *string\n}\n\n// Set satisfies the [pflag.Value] interface.\nfunc (p filevar) Set(value string) error {\n\t*p.v = value\n\treturn nil\n}\n\n// String satisfies the [pflag.Value] interface.\nfunc (filevar) String() string {\n\treturn \"\"\n}\n\n// Type satisfies the [pflag.Value] interface.\nfunc (filevar) Type() string {\n\treturn \"FILE\"\n}\n\n// chartsFS creates a filesystem for charts.\nfunc chartsFS(v *viper.Viper) (billy.Filesystem, error) {\n\tvar configDir string\n\tif s := v.ConfigFileUsed(); s != \"\" {\n\t\tconfigDir = filepath.Dir(s)\n\t} else {\n\t\tvar err error\n\t\tif configDir, err = os.UserConfigDir(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tconfigDir = filepath.Join(configDir, text.CommandName)\n\t}\n\tchartsPath := \"charts\"\n\tif s := v.GetString(\"charts_path\"); s != \"\" {\n\t\tchartsPath = s\n\t}\n\tfs := osfs.New(configDir, osfs.WithBoundOS())\n\tswitch fi, err := fs.Stat(chartsPath); {\n\tcase err != nil && os.IsNotExist(err) && chartsPath == \"charts\":\n\t\treturn memfs.New(), nil\n\tcase err != nil && os.IsNotExist(err):\n\t\tfmt.Fprintln(os.Stderr, fmt.Sprintf(text.ChartsPathDoesNotExist, chartsPath))\n\t\treturn memfs.New(), nil\n\tcase err != nil:\n\t\treturn nil, err\n\tcase !fi.IsDir():\n\t\tfmt.Fprintln(os.Stderr, fmt.Sprintf(text.ChartsPathIsNotADirectory, chartsPath))\n\t\treturn memfs.New(), nil\n\t}\n\treturn fs.Chroot(chartsPath)\n}\n\n// setConn sets a connection name to a DSN built from the passed value.\nfunc setConn(name string, value interface{}) error {\n\tswitch x := value.(type) {\n\tcase string:\n\t\treturn env.Vars().SetConn(name, x)\n\tcase []interface{}:\n\t\treturn env.Vars().SetConn(name, convSlice(x)...)\n\tcase map[string]interface{}:\n\t\turlstr, err := dburl.BuildURL(x)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn env.Vars().SetConn(name, urlstr)\n\t}\n\treturn text.ErrInvalidConfig\n}\n\n// runCommandOrFiles processes all the supplied commands or files.\nfunc runCommandOrFiles(h *handler.Handler, commandsOrFiles []CommandOrFile) func() error {\n\treturn func() error {\n\t\tfor _, c := range commandsOrFiles {\n\t\t\th.SetSingleLineMode(c.Command)\n\t\t\tif c.Command {\n\t\t\t\th.Reset([]rune(c.Value))\n\t\t\t\tif err := h.Run(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := h.Include(c.Value, false); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// sf sets a flag.\nfunc sf(flags *pflag.FlagSet, v *[]string, name, short, usage, placeholder string, vals ...string) {\n\tf := flags.VarPF(vs{v, vals, placeholder}, name, short, usage)\n\tif placeholder == \"\" {\n\t\tf.DefValue, f.NoOptDefVal = \"true\", \"true\"\n\t}\n}\n\n// convSlice converts a generic slice to a string slice.\nfunc convSlice(v []interface{}) []string {\n\ts := make([]string, len(v))\n\tfor i, x := range v {\n\t\ts[i] = fmt.Sprintf(\"%s\", x)\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "stmt/params.go",
    "content": "package stmt\n\nimport (\n\t\"unicode\"\n\n\t\"github.com/xo/usql/text\"\n)\n\n// Params holds information about command parameters.\ntype Params struct {\n\tR   []rune\n\tLen int\n}\n\n// NewParams creates command parameters.\nfunc NewParams(params string) *Params {\n\tr := []rune(params)\n\treturn &Params{\n\t\tR:   r,\n\t\tLen: len(r),\n\t}\n}\n\n// Raw reads all remaining runes. No substitution or whitespace removal is\n// performed.\nfunc (p *Params) Raw() string {\n\ts := string(p.R)\n\tp.R, p.Len = p.R[:0], 0\n\treturn s\n}\n\n// Next reads the next command parameter using the provided substitution func.\n// True indicates there are runes remaining in the command parameters to\n// process.\nfunc (p *Params) Next(unquote func(string, bool) (string, bool, error)) (string, bool, error) {\n\ti, _ := findNonSpace(p.R, 0, p.Len)\n\tif i >= p.Len {\n\t\treturn \"\", false, nil\n\t}\n\tvar ok bool\n\tvar quote rune\n\tstart := i\nloop:\n\tfor ; i < p.Len; i++ {\n\t\tc, next := p.R[i], grab(p.R, i+1, p.Len)\n\t\tswitch {\n\t\tcase quote != 0:\n\t\t\tstart := i - 1\n\t\t\ti, ok = readString(p.R, i, p.Len, quote, \"\")\n\t\t\tif !ok {\n\t\t\t\tbreak loop\n\t\t\t}\n\t\t\tswitch z, ok, err := unquote(string(p.R[start:i+1]), false); {\n\t\t\tcase err != nil:\n\t\t\t\treturn \"\", false, err\n\t\t\tcase ok:\n\t\t\t\tp.R, p.Len = substitute(p.R, start, p.Len, i-start+1, z)\n\t\t\t\ti = start + len([]rune(z)) - 1\n\t\t\t}\n\t\t\tquote = 0\n\t\t// start of single, double, or backtick string\n\t\tcase c == '\\'' || c == '\"' || c == '`':\n\t\t\tquote = c\n\t\tcase c == ':' && next != ':':\n\t\t\tif v := readVar(p.R, i, p.Len, next); v != nil {\n\t\t\t\tswitch z, ok, err := unquote(v.Name, true); {\n\t\t\t\tcase err != nil:\n\t\t\t\t\treturn \"\", false, err\n\t\t\t\tcase ok || v.Quote == '?':\n\t\t\t\t\tp.R, p.Len = v.Substitute(p.R, z, ok)\n\t\t\t\t\ti += v.Len - 1\n\t\t\t\tdefault:\n\t\t\t\t\ti = v.End - 1\n\t\t\t\t}\n\t\t\t}\n\t\tcase unicode.IsSpace(c):\n\t\t\tbreak loop\n\t\t}\n\t}\n\tif quote != 0 {\n\t\treturn \"\", false, text.ErrUnterminatedQuotedString\n\t}\n\ts := string(p.R[start:i])\n\tp.R = p.R[i:]\n\tp.Len = len(p.R)\n\treturn s, true, nil\n}\n\n// All retrieves all remaining command parameters using the provided\n// substitution func. Will return on the first encountered error.\nfunc (p *Params) All(unquote func(string, bool) (string, bool, error)) ([]string, error) {\n\tvar v []string\nloop:\n\tfor {\n\t\tswitch s, ok, err := p.Next(unquote); {\n\t\tcase err != nil:\n\t\t\treturn v, err\n\t\tcase !ok:\n\t\t\tbreak loop\n\t\tdefault:\n\t\t\tv = append(v, s)\n\t\t}\n\t}\n\treturn v, nil\n}\n\n// Arg retrieves the next argument, without decoding.\nfunc (p *Params) Arg() (string, bool, error) {\n\treturn p.Next(func(s string, _ bool) (string, bool, error) {\n\t\treturn s, true, nil\n\t})\n}\n"
  },
  {
    "path": "stmt/params_test.go",
    "content": "package stmt\n\nimport (\n\t\"os/user\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/xo/usql/env\"\n\t\"github.com/xo/usql/text\"\n)\n\nfunc TestParamsGetRaw(t *testing.T) {\n\tconst exp = `  'a string'  \"another string\"   `\n\tp := NewParams(exp)\n\ts := p.Raw()\n\tif s != exp {\n\t\tt.Errorf(\"expected %q, got: %q\", exp, s)\n\t}\n\tu, err := user.Current()\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error, got: %v\", err)\n\t}\n\tunquote := testUnquote(t, u)\n\tswitch s, ok, err := p.Next(unquote); {\n\tcase err != nil:\n\t\tt.Fatalf(\"expected no error, got: %v\", err)\n\tcase s != \"\":\n\t\tt.Errorf(\"expected empty string, got: %q\", s)\n\tcase ok:\n\t\tt.Errorf(\"expected ok=false, got: %t\", ok)\n\t}\n\tswitch v, err := p.All(unquote); {\n\tcase err != nil:\n\t\tt.Fatalf(\"expected no error, got: %v\", err)\n\tcase len(v) != 0:\n\t\tt.Errorf(\"expected v to have length 0, got: %d\", len(v))\n\t}\n}\n\nfunc TestParamsGetAll(t *testing.T) {\n\tu, err := user.Current()\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error, got: %v\", err)\n\t}\n\ttests := []struct {\n\t\ts   string\n\t\texp []string\n\t\terr error\n\t}{\n\t\t{``, nil, nil},\n\t\t{` `, nil, nil},\n\t\t{` :foo`, []string{`bar`}, nil},\n\t\t{` :'foo`, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :'型示師`, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :\"型示師`, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :'型示師 `, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :\"型示師 `, nil, text.ErrUnterminatedQuotedString},\n\t\t{`:'foo'`, []string{`'bar'`}, nil},\n\t\t{` :'foo' `, []string{`'bar'`}, nil},\n\t\t{`:'foo':foo`, []string{`'bar'bar`}, nil},\n\t\t{`:'foo':foo:\"foo\"`, []string{`'bar'bar\"bar\"`}, nil},\n\t\t{`:'foo':foo:foo`, []string{`'bar'barbar`}, nil},\n\t\t{` :'foo':foo:foo`, []string{`'bar'barbar`}, nil},\n\t\t{` :'foo':yes:foo`, []string{`'bar':yesbar`}, nil},\n\t\t{` :foo `, []string{`bar`}, nil},\n\t\t{`:foo:foo`, []string{`barbar`}, nil},\n\t\t{` :foo:foo `, []string{`barbar`}, nil},\n\t\t{`  :foo:foo  `, []string{`barbar`}, nil},\n\t\t{`'hello'`, []string{`hello`}, nil},\n\t\t{`  'hello''yes'  `, []string{`hello'yes`}, nil},\n\t\t{`  'hello\\'...\\'yes'  `, []string{`hello'...'yes`}, nil},\n\t\t{`  \"hello\\'...\\'yes\"  `, nil, text.ErrInvalidQuotedString},\n\t\t{`  \"hello\\\"...\\\"yes\"  `, nil, text.ErrInvalidQuotedString},\n\t\t{`  'hello':'yes'  `, []string{`hello:'yes'`}, nil},\n\t\t{` :'foo `, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :'foo bar`, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :'foo  bar`, nil, text.ErrUnterminatedQuotedString},\n\t\t{` :'foo  bar `, nil, text.ErrUnterminatedQuotedString},\n\t\t{\" `foo\", nil, text.ErrUnterminatedQuotedString},\n\t\t{\" `foo bar`\", []string{\"foo bar\"}, nil},\n\t\t{\" `foo  :foo`\", []string{\"foo  :foo\"}, nil},\n\t\t{` :'foo':\"foo\"`, []string{`'bar'\"bar\"`}, nil},\n\t\t{` :'foo' :\"foo\" `, []string{`'bar'`, `\"bar\"`}, nil},\n\t\t{` :'foo' :\"foo\"`, []string{`'bar'`, `\"bar\"`}, nil},\n\t\t{` :'foo'  :\"foo\"`, []string{`'bar'`, `\"bar\"`}, nil},\n\t\t{` :'foo'  :\"foo\" `, []string{`'bar'`, `\"bar\"`}, nil},\n\t\t{` :'foo'  :\"foo\"  :foo `, []string{`'bar'`, `\"bar\"`, `bar`}, nil},\n\t\t{` :'foo':foo:\"foo\" `, []string{`'bar'bar\"bar\"`}, nil},\n\t\t{` :'foo''yes':'foo' `, []string{`'bar'yes'bar'`}, nil},\n\t\t{` :'foo' 'yes' :'foo' `, []string{`'bar'`, `yes`, `'bar'`}, nil},\n\t\t{` 'yes':'foo':\"foo\"'blah''no' \"\\ntest\" `, []string{`yes'bar'\"bar\"blah'no`, \"\\ntest\"}, nil},\n\t\t{`:型示師:'型示師':\"型示師\"`, []string{`:型示師:'型示師':\"型示師\"`}, nil},\n\t\t{`:型示師 :'型示師' :\"型示師\"`, []string{`:型示師`, `:'型示師'`, `:\"型示師\"`}, nil},\n\t\t{` :型示師 :'型示師' :\"型示師\" `, []string{`:型示師`, `:'型示師'`, `:\"型示師\"`}, nil},\n\t\t{` :{?foo} `, []string{`TRUE`}, nil},\n\t\t{` :{?foo_} `, []string{`FALSE`}, nil},\n\t\t{` :{?型示} `, []string{`TRUE`}, nil},\n\t\t{` :{?型示師} `, []string{`FALSE`}, nil},\n\t\t{` :{?型示師 } `, []string{`:{?型示師`, `}`}, nil},\n\t\t{` :{?foo }`, []string{`:{?foo`, `}`}, nil},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tvals, err := NewParams(test.s).All(testUnquote(t, u))\n\t\t\tif err != test.err {\n\t\t\t\tt.Fatalf(\"expected error %v, got: %v\", test.err, err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(vals, test.exp) {\n\t\t\t\tt.Errorf(\"expected %v, got: %v\", test.exp, vals)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc testUnquote(t *testing.T, u *user.User) func(string, bool) (string, bool, error) {\n\tt.Helper()\n\tvars := env.NewVars()\n\tvars.Set(\"foo\", \"bar\")\n\tvars.Set(\"型示\", \"yes\")\n\tf := env.Untick(u, vars, false)\n\treturn func(s string, isvar bool) (string, bool, error) {\n\t\t// t.Logf(\"test %d %q s: %q, isvar: %t\", i, teststr, s, isvar)\n\t\treturn f(s, isvar)\n\t}\n}\n"
  },
  {
    "path": "stmt/parse.go",
    "content": "package stmt\n\nimport (\n\t\"regexp\"\n\t\"unicode\"\n)\n\n// prefixCount is the number of words to extract from a prefix.\nconst prefixCount = 6\n\n// maxVarNameLen is the maximum var name length.\nconst maxVarNameLen = 128\n\n// grab returns the i'th rune from r when i < end, otherwise 0.\nfunc grab(r []rune, i, end int) rune {\n\tif i < end {\n\t\treturn r[i]\n\t}\n\treturn 0\n}\n\n// findSpace finds first space rune in r, returning end if not found.\nfunc findSpace(r []rune, i, end int) (int, bool) {\n\tfor ; i < end; i++ {\n\t\tif isSpaceOrControl(r[i]) {\n\t\t\treturn i, true\n\t\t}\n\t}\n\treturn i, false\n}\n\n// findNonSpace finds first non space rune in r, returning end if not found.\nfunc findNonSpace(r []rune, i, end int) (int, bool) {\n\tfor ; i < end; i++ {\n\t\tif !isSpaceOrControl(r[i]) {\n\t\t\treturn i, true\n\t\t}\n\t}\n\treturn i, false\n}\n\n// findRune finds the next rune c in r, returning end if not found.\nfunc findRune(r []rune, i, end int, c rune) (int, bool) {\n\tfor ; i < end; i++ {\n\t\tif r[i] == c {\n\t\t\treturn i, true\n\t\t}\n\t}\n\treturn i, false\n}\n\n// isEmptyLine returns true when r is empty or composed of only whitespace.\nfunc isEmptyLine(r []rune, i, end int) bool {\n\t_, ok := findNonSpace(r, i, end)\n\treturn !ok\n}\n\n// identifierRE is a regexp that matches dollar tag identifiers ($tag$).\nvar identifierRE = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_]{0,127}$`)\n\n// readDollarAndTag reads a dollar style $tag$ in r, starting at i, returning\n// the enclosed \"tag\" and position, or -1 if the dollar and tag was invalid.\nfunc readDollarAndTag(r []rune, i, end int) (string, int, bool) {\n\tstart, found := i, false\n\ti++\n\tfor ; i < end; i++ {\n\t\tif r[i] == '$' {\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t\tif i-start > 128 {\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\treturn \"\", i, false\n\t}\n\t// check valid identifier\n\tid := string(r[start+1 : i])\n\tif id != \"\" && !identifierRE.MatchString(id) {\n\t\treturn \"\", i, false\n\t}\n\treturn id, i, true\n}\n\n// readString seeks to the end of a string returning the position and whether\n// or not the string's end was found.\n//\n// If the string's terminator was not found, then the result will be the passed\n// end.\nfunc readString(r []rune, i, end int, quote rune, tag string) (int, bool) {\n\tvar prev, c, next rune\n\tfor ; i < end; i++ {\n\t\tc, next = r[i], grab(r, i+1, end)\n\t\tswitch {\n\t\tcase quote == '\\'' && c == '\\\\':\n\t\t\ti++\n\t\t\tprev = 0\n\t\t\tcontinue\n\t\tcase quote == '\\'' && c == '\\'' && next == '\\'':\n\t\t\ti++\n\t\t\tcontinue\n\t\tcase quote == '\\'' && c == '\\'' && prev != '\\'',\n\t\t\tquote == '\"' && c == '\"',\n\t\t\tquote == '`' && c == '`':\n\t\t\treturn i, true\n\t\tcase quote == '$' && c == '$':\n\t\t\tif id, pos, ok := readDollarAndTag(r, i, end); ok && tag == id {\n\t\t\t\treturn pos, true\n\t\t\t}\n\t\t}\n\t\tprev = c\n\t}\n\treturn end, false\n}\n\n// readMultilineComment finds the end of a multiline comment (ie, '*/').\nfunc readMultilineComment(r []rune, i, end int) (int, bool) {\n\ti++\n\tfor ; i < end; i++ {\n\t\tif r[i-1] == '*' && r[i] == '/' {\n\t\t\treturn i, true\n\t\t}\n\t}\n\treturn end, false\n}\n\n// readVar reads variable from r in the form of :var_name :'var_name'\n// :\"var_name\" and :{?var_name}.\nfunc readVar(r []rune, i, end int, next rune) *Var {\n\to := 1\n\tswitch next {\n\tcase '\\'', '\"':\n\t\to = 2\n\tcase '{':\n\t\tif grab(r, i+2, end) != '?' {\n\t\t\treturn nil\n\t\t}\n\t\to, next = 3, '}'\n\tdefault:\n\t\tnext = 0\n\t}\n\tn, q := readVarName(r, i+o, end, next)\n\tv := n\n\tswitch {\n\tcase n-i < o+1, next != 0 && next != q:\n\t\treturn nil\n\tcase next != 0:\n\t\tv++\n\t}\n\tif next == '}' {\n\t\tnext = '?'\n\t}\n\treturn &Var{\n\t\tI:     i,\n\t\tEnd:   v,\n\t\tQuote: next,\n\t\tName:  string(r[i+o : n]),\n\t}\n}\n\n// readVarName reads a variable name up to maxVarNameLen.\nfunc readVarName(r []rune, i, end int, q rune) (int, rune) {\n\tc := rune(-1)\n\tfor n := 0; i < end && n < maxVarNameLen && c != q; i, n = i+1, n+1 {\n\t\tswitch c = grab(r, i, end); {\n\t\tcase c == 0, c == q, c != '_' && !unicode.IsLetter(c) && !unicode.IsNumber(c):\n\t\t\treturn i, c\n\t\t}\n\t}\n\treturn i, 0\n}\n\n// readCommand reads the command and any parameters from r, returning the\n// offset from i for the end of command, and the end of the command parameters.\n//\n// A command is defined as the first non-blank text after \\, followed by\n// parameters up to either the next \\ or a control character (for example, \\n):\nfunc readCommand(r []rune, i, end int) (int, int) {\n\t// find end of command\n\tc, next := rune(0), rune(0)\ncommand:\n\tfor ; i < end; i++ {\n\t\tswitch next = grab(r, i+1, end); {\n\t\tcase next == 0:\n\t\t\treturn end, end\n\t\tcase next == '\\\\' || unicode.IsControl(next):\n\t\t\ti++\n\t\t\treturn i, i\n\t\tcase unicode.IsSpace(next):\n\t\t\ti++\n\t\t\tbreak command\n\t\t}\n\t}\n\tcmd, quote := i, rune(0)\nparams:\n\t// find end of params\n\tfor ; i < end; i++ {\n\t\tswitch c, next = r[i], grab(r, i+1, end); {\n\t\tcase next == 0:\n\t\t\treturn cmd, end\n\t\tcase quote == 0 && (c == '\\'' || c == '\"' || c == '`'):\n\t\t\tquote = c\n\t\tcase quote != 0 && c == quote:\n\t\t\tquote = 0\n\t\t// skip escaped\n\t\tcase quote != 0 && c == '\\\\' && (next == quote || next == '\\\\'):\n\t\t\ti++\n\t\tcase quote == 0 && (c == '\\\\' || unicode.IsControl(c)):\n\t\t\tbreak params\n\t\t}\n\t}\n\t// log.Printf(\">>> params: %q remaining: %q\", string(r[cmd:i]), string(r[i:end]))\n\treturn cmd, i\n}\n\n// findPrefix finds the prefix in r up to n words.\nfunc findPrefix(r []rune, n int, allowCComments, allowHashComments, allowMultilineComments bool) string {\n\tvar s []rune\n\tvar words int\nloop:\n\tfor i, end := 0, len(r); i < end; i++ {\n\t\t// skip space + control characters\n\t\tif j, _ := findNonSpace(r, i, end); i != j {\n\t\t\tr, end, i = r[j:], end-j, 0\n\t\t}\n\t\t// grab current and next character\n\t\tc, next := grab(r, i, end), grab(r, i+1, end)\n\t\tswitch {\n\t\t// do nothing\n\t\tcase c == 0:\n\t\t// statement terminator\n\t\tcase c == ';':\n\t\t\tbreak loop\n\t\t// single line comments '--' and '//'\n\t\tcase c == '-' && next == '-', c == '/' && next == '/' && allowCComments, c == '#' && allowHashComments:\n\t\t\tif i != 0 {\n\t\t\t\ts, words = appendUpperRunes(s, r[:i], ' '), words+1\n\t\t\t}\n\t\t\t// find line end\n\t\t\tif i, _ = findRune(r, i, end, '\\n'); i >= end {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tr, end, i = r[i+1:], end-i-1, -1\n\t\t// multiline comments '/*' '*/'\n\t\tcase c == '/' && next == '*' && allowMultilineComments:\n\t\t\tif i != 0 {\n\t\t\t\ts, words = appendUpperRunes(s, r[:i]), words+1\n\t\t\t}\n\t\t\t// find comment end '*/'\n\t\t\tfor i += 2; i < end; i++ {\n\t\t\t\tif grab(r, i, end) == '*' && grab(r, i+1, end) == '/' {\n\t\t\t\t\tr, end, i = r[i+2:], end-i-2, -1\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\t// add space when remaining runes begin with space, and previous\n\t\t\t// captured word did not\n\t\t\tif sl := len(s); end > 0 && sl != 0 && isSpaceOrControl(r[0]) && !isSpaceOrControl(s[sl-1]) {\n\t\t\t\ts = append(s, ' ')\n\t\t\t}\n\t\t// end of statement, max words, or punctuation that can be ignored\n\t\tcase words == n || !unicode.IsLetter(c):\n\t\t\tbreak loop\n\t\t// ignore remaining, as no prefix can come after\n\t\tcase next != '/' && !unicode.IsLetter(next):\n\t\t\ts, words = appendUpperRunes(s, r[:i+1], ' '), words+1\n\t\t\tif next == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif next == ';' {\n\t\t\t\tbreak loop\n\t\t\t}\n\t\t\tr, end, i = r[i+2:], end-i-2, -1\n\t\t}\n\t}\n\t// trim right ' ', if any\n\tif sl := len(s); sl != 0 && s[sl-1] == ' ' {\n\t\treturn string(s[:sl-1])\n\t}\n\treturn string(s)\n}\n\n// FindPrefix finds the first 6 prefix words in s.\nfunc FindPrefix(s string, allowCComments, allowHashComments, allowMultilineComments bool) string {\n\treturn findPrefix([]rune(s), prefixCount, allowCComments, allowHashComments, allowMultilineComments)\n}\n\n// substitute substitutes n runes in r starting at i with the runes in s.\n// Dynamically grows r if necessary.\nfunc substitute(r []rune, i, end, n int, s string) ([]rune, int) {\n\tsr, rcap := []rune(s), cap(r)\n\tsn := len(sr)\n\t// grow ...\n\ttlen := len(r) + sn - n\n\tif tlen > rcap {\n\t\tz := make([]rune, tlen)\n\t\tcopy(z, r)\n\t\tr = z\n\t} else {\n\t\tr = r[:rcap]\n\t}\n\t// substitute\n\tcopy(r[i+sn:], r[i+n:])\n\tcopy(r[i:], sr)\n\treturn r[:tlen], tlen\n}\n\n// appendUpperRunes creates a new []rune from s, with the runes in r on the end\n// converted to upper case. extra runes will be appended to the final []rune.\nfunc appendUpperRunes(s []rune, r []rune, extra ...rune) []rune {\n\tsl, rl, el := len(s), len(r), len(extra)\n\tsre := make([]rune, sl+rl+el)\n\tcopy(sre[:sl], s)\n\tfor i := 0; i < rl; i++ {\n\t\tsre[sl+i] = unicode.ToUpper(r[i])\n\t}\n\tcopy(sre[sl+rl:], extra)\n\treturn sre\n}\n"
  },
  {
    "path": "stmt/parse_test.go",
    "content": "package stmt\n\nimport (\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGrab(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp rune\n\t}{\n\t\t{\"\", 0, 0},\n\t\t{\"a\", 0, 'a'},\n\t\t{\" a\", 0, ' '},\n\t\t{\"a \", 1, ' '},\n\t\t{\"a\", 1, 0},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tz := []rune(test.s)\n\t\t\tr := grab(z, test.i, len(z))\n\t\t\tif r != test.exp {\n\t\t\t\tt.Errorf(\"expected %c, got: %c\", test.exp, r)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFindSpace(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp int\n\t\tb   bool\n\t}{\n\t\t{\"\", 0, 0, false},\n\t\t{\" \", 0, 0, true},\n\t\t{\"a\", 0, 1, false},\n\t\t{\"a \", 0, 1, true},\n\t\t{\" a \", 0, 0, true},\n\t\t{\"aaa\", 0, 3, false},\n\t\t{\" a \", 1, 2, true},\n\t\t{\"aaa\", 1, 3, false},\n\t\t{\" aaa\", 1, 4, false},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tz := []rune(test.s)\n\t\t\tn, b := findSpace(z, test.i, len(z))\n\t\t\tif n != test.exp {\n\t\t\t\tt.Errorf(\"expected %d, got: %d\", test.exp, n)\n\t\t\t}\n\t\t\tif b != test.b {\n\t\t\t\tt.Errorf(\"expected %t, got: %t\", test.b, b)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFindNonSpace(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp int\n\t\tb   bool\n\t}{\n\t\t{\"\", 0, 0, false},\n\t\t{\" \", 0, 1, false},\n\t\t{\"a\", 0, 0, true},\n\t\t{\"a \", 0, 0, true},\n\t\t{\" a \", 0, 1, true},\n\t\t{\"    \", 0, 4, false},\n\t\t{\" a \", 1, 1, true},\n\t\t{\"aaa\", 1, 1, true},\n\t\t{\" aaa\", 1, 1, true},\n\t\t{\"  aa\", 1, 2, true},\n\t\t{\"    \", 1, 4, false},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tz := []rune(test.s)\n\t\t\tn, b := findNonSpace(z, test.i, len(z))\n\t\t\tif n != test.exp {\n\t\t\t\tt.Errorf(\"expected %d, got: %d\", test.exp, n)\n\t\t\t}\n\t\t\tif b != test.b {\n\t\t\t\tt.Errorf(\"expected %t, got: %t\", test.b, b)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsEmptyLine(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp bool\n\t}{\n\t\t{\"\", 0, true},\n\t\t{\"a\", 0, false},\n\t\t{\" a\", 0, false},\n\t\t{\" a \", 0, false},\n\t\t{\" \\na\", 0, false},\n\t\t{\" \\n\\ta\", 0, false},\n\t\t{\"a \", 1, true},\n\t\t{\" a\", 1, false},\n\t\t{\" a \", 1, false},\n\t\t{\" \\na\", 1, false},\n\t\t{\" \\n\\t \", 1, true},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tz := []rune(test.s)\n\t\t\tb := isEmptyLine(z, test.i, len(z))\n\t\t\tif b != test.exp {\n\t\t\t\tt.Errorf(\"expected %t, got: %t\", test.exp, b)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadString(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp string\n\t\tok  bool\n\t}{\n\t\t{`'`, 0, ``, false},\n\t\t{` '`, 1, ``, false},\n\t\t{`''`, 0, `''`, true},\n\t\t{`'foo' `, 0, `'foo'`, true},\n\t\t{` 'foo' `, 1, `'foo'`, true},\n\t\t{`\"foo\"`, 0, `\"foo\"`, true},\n\t\t{\"`foo`\", 0, \"`foo`\", true},\n\t\t{\"`'foo'`\", 0, \"`'foo'`\", true},\n\t\t{`'foo''foo'`, 0, `'foo''foo'`, true},\n\t\t{` 'foo''foo' `, 1, `'foo''foo'`, true},\n\t\t{` \"foo''foo\" `, 1, `\"foo''foo\"`, true},\n\t\t// escaped \\\" aren't allowed in strings, so the second \" would be next\n\t\t// double quoted string\n\t\t{`\"foo\\\"\"`, 0, `\"foo\\\"`, true},\n\t\t{` \"foo\\\"\" `, 1, `\"foo\\\"`, true},\n\t\t{`''''`, 0, `''''`, true},\n\t\t{` '''' `, 1, `''''`, true},\n\t\t{`''''''`, 0, `''''''`, true},\n\t\t{` '''''' `, 1, `''''''`, true},\n\t\t{`'''`, 0, ``, false},\n\t\t{` ''' `, 1, ``, false},\n\t\t{`'''''`, 0, ``, false},\n\t\t{` ''''' `, 1, ``, false},\n\t\t{`\"fo'o\"`, 0, `\"fo'o\"`, true},\n\t\t{` \"fo'o\" `, 1, `\"fo'o\"`, true},\n\t\t{`\"fo''o\"`, 0, `\"fo''o\"`, true},\n\t\t{` \"fo''o\" `, 1, `\"fo''o\"`, true},\n\t\t{`'本門台初埼本門台初埼'`, 0, `'本門台初埼本門台初埼'`, true},\n\t\t{` '本門台初埼本門台初埼' `, 1, `'本門台初埼本門台初埼'`, true},\n\t\t{`\"本門台初埼本門台初埼\"`, 0, `\"本門台初埼本門台初埼\"`, true},\n\t\t{` \"本門台初埼本門台初埼\" `, 1, `\"本門台初埼本門台初埼\"`, true},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tr := []rune(test.s)\n\t\t\tc, end := rune(strings.TrimSpace(test.s)[0]), len(r)\n\t\t\tif c != '\\'' && c != '\"' && c != '`' {\n\t\t\t\tt.Fatal(\"incorrect!\")\n\t\t\t}\n\t\t\tpos, ok := readString(r, test.i+1, end, c, \"\")\n\t\t\tif ok != test.ok {\n\t\t\t\tt.Fatalf(\"expected ok %t, got: %t\", test.ok, ok)\n\t\t\t}\n\t\t\tif !test.ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif r[pos] != c {\n\t\t\t\tt.Fatalf(\"expected last character to be %c, got: %c\", c, r[pos])\n\t\t\t}\n\t\t\tv := string(r[test.i : pos+1])\n\t\t\tif n := len(v); n < 2 {\n\t\t\t\tt.Fatalf(\"expected result of at least length 2, got: %d\", n)\n\t\t\t}\n\t\t\tif v != test.exp {\n\t\t\t\tt.Errorf(\"expected %q, got: %q\", test.exp, v)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadCommand(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp string\n\t}{\n\t\t{`\\c foo bar z`, 0, `\\c| foo bar z|`},\n\t\t{`\\c foo bar z `, 0, `\\c| foo bar z |`},\n\t\t{`\\c foo bar z  `, 0, `\\c| foo bar z  |`},\n\t\t{`\\c    foo    bar    z  `, 0, `\\c|    foo    bar    z  |`},\n\t\t{`\\c    pg://blah    bar    z  `, 0, `\\c|    pg://blah    bar    z  |`},\n\t\t{`\\foo    pg://blah    bar    z  `, 0, `\\foo|    pg://blah    bar    z  |`},\n\t\t{`\\a\\b`, 0, `\\a||\\b`},\n\t\t{`\\a \\b`, 0, `\\a| |\\b`},\n\t\t{\"\\\\a \\n\\\\b\", 0, \"\\\\a| |\\n\\\\b\"},\n\t\t{` \\ab \\bc \\cd `, 5, `\\bc| |\\cd `},\n\t\t{`\\p foo \\p`, 0, `\\p| foo |\\p`},\n\t\t{`\\p foo   \\p bar`, 0, `\\p| foo   |\\p bar`},\n\t\t{`\\p\\p`, 0, `\\p||\\p`},\n\t\t{`\\p \\r foo`, 0, `\\p| |\\r foo`},\n\t\t{`\\print   \\reset    foo`, 0, `\\print|   |\\reset    foo`},\n\t\t{`\\print   \\reset    foo`, 9, `\\reset|    foo|`},\n\t\t{`\\print   \\reset    foo  `, 9, `\\reset|    foo  |`},\n\t\t{`\\print   \\reset    foo  bar  `, 9, `\\reset|    foo  bar  |`},\n\t\t{`\\c 'foo bar' z`, 0, `\\c| 'foo bar' z|`},\n\t\t{`\\c foo \"bar \" z `, 0, `\\c| foo \"bar \" z |`},\n\t\t{\"\\\\c `foo bar z  `  \", 0, \"\\\\c| `foo bar z  `  |\"},\n\t\t{`\\c 'foob':foo:bar'test'  `, 0, `\\c| 'foob':foo:bar'test'  |`},\n\t\t{\"\\\\a \\n\\\\b\\\\c\\n\", 0, \"\\\\a| |\\n\\\\b\\\\c\\n\"},\n\t\t{`\\a'foob' \\b`, 0, `\\a'foob'| |\\b`},\n\t\t{`\\foo 'test' \"bar\"\\print`, 0, `\\foo| 'test' \"bar\"|\\print`},\n\t\t{`\\foo 'test' \"bar\"  \\print`, 0, `\\foo| 'test' \"bar\"  |\\print`},\n\t\t{`\\afoob' \\b`, 0, `\\afoob'| |\\b`},\n\t\t{`\\afoob' '\\b  `, 0, `\\afoob'| '\\b  |`},\n\t\t{`\\afoob' '\\b  '\\print`, 0, `\\afoob'| '\\b  '|\\print`},\n\t\t{`\\afoob' '\\b  ' \\print`, 0, `\\afoob'| '\\b  ' |\\print`},\n\t\t{`\\afoob' '\\b  ' \\print `, 0, `\\afoob'| '\\b  ' |\\print `},\n\t\t{\"\\\\foo `foob'foob'\\\\print\", 0, \"\\\\foo| `foob'foob'\\\\print|\"},\n\t\t{\"\\\\foo `foob'foob'  \\\\print\", 0, \"\\\\foo| `foob'foob'  \\\\print|\"},\n\t\t{`\\foo \"foob'foob'\\\\print`, 0, `\\foo| \"foob'foob'\\\\print|`},\n\t\t{`\\foo \"foob'foob'  \\\\print`, 0, `\\foo| \"foob'foob'  \\\\print|`},\n\t\t{`\\foo \"\\\"\"\\print`, 0, `\\foo| \"\\\"\"|\\print`},\n\t\t{`\\foo \"\\\"'\"\\print`, 0, `\\foo| \"\\\"'\"|\\print`},\n\t\t{`\\foo \"\\\"''\"\\print`, 0, `\\foo| \"\\\"''\"|\\print`},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tz := []rune(test.s)\n\t\t\tif !strings.Contains(test.exp, \"|\") {\n\t\t\t\tt.Fatalf(\"expected value is invalid (missing |): %q\", test.exp)\n\t\t\t}\n\t\t\tv := strings.Split(test.exp, \"|\")\n\t\t\tif len(v) != 3 {\n\t\t\t\tt.Fatalf(\"should have 3 expected values, has: %d\", len(v))\n\t\t\t}\n\t\t\tcmd, params := readCommand(z, test.i, len(z))\n\t\t\tif s := string(z[test.i:cmd]); s != v[0] {\n\t\t\t\tt.Errorf(\"expected command to be %q, got: %q [%d, %d]\", v[0], s, cmd, params)\n\t\t\t}\n\t\t\tif s := string(z[cmd:params]); s != v[1] {\n\t\t\t\tt.Errorf(\"expected params to be %q, got: %q [%d, %d]\", v[1], s, cmd, params)\n\t\t\t}\n\t\t\tif s := string(z[params:]); s != v[2] {\n\t\t\t\tt.Errorf(\"expected remaining to be %q, got: %q\", v[2], s)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFindPrefix(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\tw   int\n\t\texp string\n\t}{\n\t\t{\"\", 4, \"\"},\n\t\t{\"  \", 4, \"\"},\n\t\t{\"  \", 4, \"\"},\n\t\t{\" select \", 4, \"SELECT\"},\n\t\t{\" select to \", 4, \"SELECT TO\"},\n\t\t{\" select to \", 4, \"SELECT TO\"},\n\t\t{\" select   to   \", 4, \"SELECT TO\"},\n\t\t{\"select into from\", 2, \"SELECT INTO\"},\n\t\t{\"select into * from\", 4, \"SELECT INTO\"},\n\t\t{\" select into  *   from  \", 4, \"SELECT INTO\"},\n\t\t{\" select   \\t  into \\n *  \\t\\t\\n\\n\\n  from     \", 4, \"SELECT INTO\"},\n\t\t{\"  select\\n\\n\\tb\\t\\tzfrom j\\n\\n  \", 2, \"SELECT B\"},\n\t\t{\"select/* foob  */into\", 4, \"SELECTINTO\"},\n\t\t{\"select/* foob  */\\tinto\", 4, \"SELECT INTO\"},\n\t\t{\"select/* foob  */ into\", 4, \"SELECT INTO\"},\n\t\t{\"select/* foob  */ into \", 4, \"SELECT INTO\"},\n\t\t{\"select /* foob  */ into \", 4, \"SELECT INTO\"},\n\t\t{\"   select /* foob  */ into \", 4, \"SELECT INTO\"},\n\t\t{\" select * --test\\n from where \\n\\nfff\", 4, \"SELECT\"},\n\t\t{\"/*idreamedital*/foo//bar\\n/*  nothing */test\\n\\n\\nwe made /*\\n\\n\\n\\n*/   \\t   it    \", 5, \"FOO TEST WE MADE IT\"},\n\t\t{\" --yes\\n//no\\n\\n\\t/*whatever*/ \", 4, \"\"},\n\t\t{\"/*/*test*/*/ select \", 4, \"\"},\n\t\t{\"/*/*test*/*/ select \", 4, \"\"},\n\t\t{\"//\", 4, \"\"},\n\t\t{\"-\", 4, \"\"},\n\t\t{\"* select\", 4, \"\"},\n\t\t{\"/**/\", 4, \"\"},\n\t\t{\"--\\n\\t\\t\\thello,\\t--\", 4, \"HELLO\"},\n\t\t{\"/*   */\\n\\n\\n\\tselect/*--\\n*/\\t\\b\\bzzz\", 4, \"SELECT ZZZ\"},\n\t\t{\"n\\nn\\n\\nn\\tn\", 7, \"N N N N\"},\n\t\t{\"n\\nn\\n\\nn\\tn\", 1, \"N\"},\n\t\t{\"--\\n/* */n/* */\\nn\\n--\\nn\\tn\", 7, \"N N N N\"},\n\t\t{\"--\\n/* */n\\n/* */\\nn\\n--\\nn\\tn\", 7, \"N N N N\"},\n\t\t{\"\\n\\n/* */\\nn n\", 7, \"N N\"},\n\t\t{\"\\n\\n/* */\\nn/* */n\", 7, \"NN\"},\n\t\t{\"\\n\\n/* */\\nn /* */n\", 7, \"N N\"},\n\t\t{\"\\n\\n/* */\\nn/* */\\nn\", 7, \"N N\"},\n\t\t{\"\\n\\n/* */\\nn/* */ n\", 7, \"N N\"},\n\t\t{\"*/foob\", 7, \"\"},\n\t\t{\"*/ \\n --\\nfoob\", 7, \"\"},\n\t\t{\"--\\n\\n--\\ntest\", 7, \"TEST\"},\n\t\t{\"\\b\\btest\", 7, \"TEST\"},\n\t\t{\"select/*\\r\\n\\r\\n*/blah\", 7, \"SELECTBLAH\"},\n\t\t{\"\\r\\n\\r\\nselect from where\", 8, \"SELECT FROM WHERE\"},\n\t\t{\"\\r\\n\\b\\bselect 1;create 2;\", 8, \"SELECT\"},\n\t\t{\"\\r\\n\\bbegin transaction;\\ncreate x where;\", 8, \"BEGIN TRANSACTION\"},\n\t\t{\"begin;test;create;awesome\", 3, \"BEGIN\"},\n\t\t{\" /* */ ; begin; \", 5, \"\"},\n\t\t{\" /* foo */ test; test\", 5, \"TEST\"},\n\t\t{\";test\", 5, \"\"},\n\t\t{\"\\b\\b\\t;test\", 5, \"\"},\n\t\t{\"\\b\\t; test\", 5, \"\"},\n\t\t{\"\\b\\tfoob; test\", 5, \"FOOB\"},\n\t\t{\"  TEST /*\\n\\t\\b*/\\b\\t;foob\", 10, \"TEST\"},\n\t\t{\"begin transaction\\n\\tinsert into x;\\ncommit;\", 6, \"BEGIN TRANSACTION INSERT INTO X\"},\n\t\t{\"--\\nbegin /* */transaction/* */\\n/* */\\tinsert into x;--/* */\\ncommit;\", 6, \"BEGIN TRANSACTION INSERT INTO X\"},\n\t\t{\"#\\nbegin /* */transaction/* */\\n/* */\\t#\\ninsert into x;#\\n--/* */\\ncommit;\", 6, \"BEGIN TRANSACTION INSERT INTO X\"},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tif p := findPrefix([]rune(test.s), test.w, true, true, true); p != test.exp {\n\t\t\t\tt.Errorf(\"%q expected %q, got: %q\", test.s, test.exp, p)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadVar(t *testing.T) {\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\texp *Var\n\t}{\n\t\t{``, 0, nil},\n\t\t{`:`, 0, nil},\n\t\t{` :`, 0, nil},\n\t\t{`a:`, 0, nil},\n\t\t{`a:a`, 0, nil},\n\t\t{`: `, 0, nil},\n\t\t{`: a `, 0, nil},\n\t\t{`:'ab  ' `, 0, nil},\n\t\t{`:\"ab  \" `, 0, nil},\n\t\t{`:{?ab  } `, 0, nil},\n\t\t{`:a`, 0, v(0, `a`)},\n\t\t{`:ab`, 0, v(0, `ab`)},\n\t\t{`:a `, 0, v(0, `a`)},\n\t\t{`:a_ `, 0, v(0, `a_`)},\n\t\t{\":a_\\t \", 0, v(0, `a_`)},\n\t\t{\":a_\\n \", 0, v(0, `a_`)},\n\t\t{`:a9`, 0, v(0, `a9`)},\n\t\t{`:ab9`, 0, v(0, `ab9`)},\n\t\t{`:a 9`, 0, v(0, `a`)},\n\t\t{`:a_9 `, 0, v(0, `a_9`)},\n\t\t{\":a_9\\t \", 0, v(0, `a_9`)},\n\t\t{\":a_9\\n \", 0, v(0, `a_9`)},\n\t\t{`:a_;`, 0, v(0, `a_`)},\n\t\t{`:a_\\`, 0, v(0, `a_`)},\n\t\t{`:a_$`, 0, v(0, `a_`)},\n\t\t{`:a_'`, 0, v(0, `a_`)},\n\t\t{`:a_\"`, 0, v(0, `a_`)},\n\t\t{`:ab `, 0, v(0, `ab`)},\n\t\t{`:ab123 `, 0, v(0, `ab123`)},\n\t\t{`:ab123`, 0, v(0, `ab123`)},\n\t\t{`:'`, 0, nil},\n\t\t{`:' `, 0, nil},\n\t\t{`:' a`, 0, nil},\n\t\t{`:' a `, 0, nil},\n\t\t{`:\"`, 0, nil},\n\t\t{`:\" `, 0, nil},\n\t\t{`:\" a`, 0, nil},\n\t\t{`:\" a `, 0, nil},\n\t\t{`:''`, 0, nil},\n\t\t{`:'' `, 0, nil},\n\t\t{`:'' a`, 0, nil},\n\t\t{`:\"\"`, 0, nil},\n\t\t{`:\"\" `, 0, nil},\n\t\t{`:\"\" a`, 0, nil},\n\t\t{`:'     `, 0, nil},\n\t\t{`:'       `, 0, nil},\n\t\t{`:\"     `, 0, nil},\n\t\t{`:\"       `, 0, nil},\n\t\t{`:'a'`, 0, v(0, `a`, `'`)},\n\t\t{`:'a' `, 0, v(0, `a`, `'`)},\n\t\t{`:'ab'`, 0, v(0, `ab`, `'`)},\n\t\t{`:'ab' `, 0, v(0, `ab`, `'`)},\n\t\t{`:\"a\"`, 0, v(0, `a`, `\"`)},\n\t\t{`:\"a\" `, 0, v(0, `a`, `\"`)},\n\t\t{`:\"ab\"`, 0, v(0, `ab`, `\"`)},\n\t\t{`:\"ab\" `, 0, v(0, `ab`, `\"`)},\n\t\t{`:型`, 0, v(0, \"型\")},\n\t\t{`:'型'`, 0, v(0, \"型\", `'`)},\n\t\t{`:\"型\"`, 0, v(0, \"型\", `\"`)},\n\t\t{` :型 `, 1, v(1, \"型\")},\n\t\t{` :'型' `, 1, v(1, \"型\", `'`)},\n\t\t{` :\"型\" `, 1, v(1, \"型\", `\"`)},\n\t\t{`:型示師`, 0, v(0, \"型示師\")},\n\t\t{`:'型示師'`, 0, v(0, \"型示師\", `'`)},\n\t\t{`:\"型示師\"`, 0, v(0, \"型示師\", `\"`)},\n\t\t{` :型示師 `, 1, v(1, \"型示師\")},\n\t\t{` :'型示師' `, 1, v(1, \"型示師\", `'`)},\n\t\t{` :\"型示師\" `, 1, v(1, \"型示師\", `\"`)},\n\t\t{`:{?a}`, 0, v(0, \"a\", `?`)},\n\t\t{` :{?a} `, 1, v(1, \"a\", `?`)},\n\t\t{`:{?a_b} `, 0, v(0, \"a_b\", `?`)},\n\t\t{` :{?a_b} `, 1, v(1, \"a_b\", `?`)},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tt.Logf(\"parsing %q\", test.s)\n\t\t\tz := []rune(test.s)\n\t\t\tv := readVar(z, test.i, len(z), grab(z, test.i+1, len(z)))\n\t\t\tif !reflect.DeepEqual(v, test.exp) {\n\t\t\t\tt.Errorf(\"\\nexpected: %#v\\n     got: %#v\", test.exp, v)\n\t\t\t}\n\t\t\tif test.exp != nil && v != nil {\n\t\t\t\tn := string(z[v.I+1 : v.End])\n\t\t\t\tswitch v.Quote {\n\t\t\t\tcase '\\'', '\"':\n\t\t\t\t\tif c := rune(n[0]); c != v.Quote {\n\t\t\t\t\t\tt.Errorf(\"expected var to start with quote %c, got: %c\", c, v.Quote)\n\t\t\t\t\t}\n\t\t\t\t\tif c := rune(n[len(n)-1]); c != v.Quote {\n\t\t\t\t\t\tt.Errorf(\"expected var to end with quote %c, got: %c\", c, v.Quote)\n\t\t\t\t\t}\n\t\t\t\t\tn = n[1 : len(n)-1]\n\t\t\t\tcase '?':\n\t\t\t\t\tif !strings.HasPrefix(n, \"{?\") {\n\t\t\t\t\t\tt.Errorf(\"expected var %q to start with {?\", n)\n\t\t\t\t\t}\n\t\t\t\t\tif !strings.HasSuffix(n, \"}\") {\n\t\t\t\t\t\tt.Errorf(\"expected var %q to end with }\", n)\n\t\t\t\t\t}\n\t\t\t\t\tn = n[2 : len(n)-1]\n\t\t\t\t}\n\t\t\t\tif n != test.exp.Name {\n\t\t\t\t\tt.Errorf(\"expected var name of %q, got: %q\", test.exp.Name, n)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSubstitute(t *testing.T) {\n\ta512 := sl(512, 'a')\n\tb512 := sl(512, 'a')\n\tb512 = b512[:1] + \"b\" + b512[2:]\n\tif len(b512) != 512 {\n\t\tt.Fatalf(\"b512 should be length 512, is: %d\", len(b512))\n\t}\n\ttests := []struct {\n\t\ts   string\n\t\ti   int\n\t\tn   int\n\t\tt   string\n\t\texp string\n\t}{\n\t\t{\"\", 0, 0, \"\", \"\"},\n\t\t{\"a\", 0, 1, \"b\", \"b\"},\n\t\t{\"ab\", 1, 1, \"cd\", \"acd\"},\n\t\t{\"\", 0, 0, \"ab\", \"ab\"},\n\t\t{\"abc\", 1, 2, \"d\", \"ad\"},\n\t\t{a512, 1, 1, \"b\", b512},\n\t\t{\"foo\", 0, 1, \"bar\", \"baroo\"},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tr := []rune(test.s)\n\t\t\tr, rlen := substitute(r, test.i, len(r), test.n, test.t)\n\t\t\tif rlen != len(test.exp) {\n\t\t\t\tt.Errorf(\"expected length %d, got: %d\", len(test.exp), rlen)\n\t\t\t}\n\t\t\tif s := string(r); s != test.exp {\n\t\t\t\tt.Errorf(\"expected %q, got %q\", test.exp, s)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "stmt/stmt.go",
    "content": "// Package stmt contains a statement buffer implementation.\npackage stmt\n\nimport (\n\t\"bytes\"\n\t\"unicode\"\n)\n\n// minCapIncrease is the minimum amount by which to grow a Stmt.Buf.\nconst minCapIncrease = 512\n\n// Stmt is a reusable statement buffer that handles reading and parsing\n// SQL-like statements.\ntype Stmt struct {\n\t// f is the rune source.\n\tf func() ([]rune, error)\n\t// allowDollar allows dollar quoted strings (ie, $$ ... $$ or $tag$ ... $tag$).\n\tallowDollar bool\n\t// allowMultilineComments allows multiline comments (ie, /* ... */)\n\tallowMultilineComments bool\n\t// allowCComments allows C-style comments (ie, // ... )\n\tallowCComments bool\n\t// allowHashComments allows hash comments (ie, # ... )\n\tallowHashComments bool\n\t// Buf is the statement buffer\n\tBuf []rune\n\t// Len is the current len of any statement in Buf.\n\tLen int\n\t// Prefix is the detected prefix of the statement.\n\tPrefix string\n\t// Vars is the list of encountered variables.\n\tVars []*Var\n\t// r is the unprocessed runes.\n\tr []rune\n\t// rlen is the number of unprocessed runes.\n\trlen int\n\t// quote indicates currently parsing a quoted string.\n\tquote rune\n\t// quoteDollarTag is the parsed tag of a dollar quoted string\n\tquoteDollarTag string\n\t// multilineComment is state of multiline comment processing\n\tmultilineComment bool\n\t// balanceCount is the balanced paren count\n\tbalanceCount int\n\t// ready indicates that a complete statement has been parsed\n\tready bool\n}\n\n// New creates a new Stmt using the supplied rune source f.\nfunc New(f func() ([]rune, error), opts ...Option) *Stmt {\n\tb := &Stmt{\n\t\tf: f,\n\t}\n\t// apply opts\n\tfor _, o := range opts {\n\t\to(b)\n\t}\n\treturn b\n}\n\n// String satisfies fmt.Stringer.\nfunc (b *Stmt) String() string {\n\treturn string(b.Buf)\n}\n\n// PrintString returns a print string of the statement buffer, which is the\n// statement buffer but with escaped variables re-interpolated.\nfunc (b *Stmt) PrintString() string {\n\tif b.Len == 0 {\n\t\treturn \"\"\n\t}\n\ti, s, w := 0, string(b.Buf), new(bytes.Buffer)\n\t// deinterpolate vars\n\tfor _, v := range b.Vars {\n\t\tif v.Quote != '\\\\' {\n\t\t\tcontinue\n\t\t}\n\t\tif len(s) > i {\n\t\t\tw.WriteString(s[i:v.I])\n\t\t}\n\t\tw.WriteString(v.String())\n\t\ti = v.I + v.Len\n\t}\n\t// add remaining\n\tif len(s) > i {\n\t\tw.WriteString(s[i:])\n\t}\n\treturn w.String()\n}\n\n// RawString returns the non-interpolated version of the statement buffer.\nfunc (b *Stmt) RawString() string {\n\tif b.Len == 0 {\n\t\treturn \"\"\n\t}\n\ti, s, z := 0, string(b.Buf), new(bytes.Buffer)\n\t// deinterpolate vars\n\tfor _, v := range b.Vars {\n\t\tif !v.Defined && v.Quote != '\\\\' {\n\t\t\tcontinue\n\t\t}\n\t\tif len(s) > i {\n\t\t\tz.WriteString(s[i:v.I])\n\t\t}\n\t\tz.WriteString(v.String())\n\t\ti = v.I + v.Len\n\t}\n\t// add remaining\n\tif len(s) > i {\n\t\tz.WriteString(s[i:])\n\t}\n\treturn z.String()\n}\n\n// Ready returns true when the statement buffer contains a non empty, balanced\n// statement that has been properly terminated (ie, ended with a semicolon).\nfunc (b *Stmt) Ready() bool {\n\treturn b.ready\n}\n\n// Reset resets the statement buffer.\nfunc (b *Stmt) Reset(r []rune) {\n\t// reset buf\n\tb.Buf, b.Len, b.Prefix, b.Vars = nil, 0, \"\", b.Vars[:0]\n\t// quote state\n\tb.quote, b.quoteDollarTag = 0, \"\"\n\t// multicomment state\n\tb.multilineComment = false\n\t// balance state\n\tb.balanceCount = 0\n\t// ready state\n\tb.ready = false\n\tif r != nil {\n\t\tb.r, b.rlen = r, len(r)\n\t}\n}\n\n// Next reads the next statement from the rune source, returning when either\n// the statement has been terminated, or a meta command has been read from the\n// rune source. After a call to Next, the collected statement is available in\n// Stmt.Buf, or call Stmt.String() to convert it to a string.\n//\n// After a call to Next, Reset should be called if the extracted statement was\n// executed (ie, processed). Note that the rune source supplied to New will be\n// called again only after any remaining collected runes have been processed.\n//\n// Example:\n//\n//\tbuf := stmt.New(runeSrc)\n//\tfor {\n//\t    cmd, params, err := buf.Next(unquoteFunc)\n//\t    if err { /* ... */ }\n//\n//\t    execute, quit := buf.Ready() || cmd == \"g\", cmd == \"q\"\n//\n//\t    // process command ...\n//\t    switch cmd {\n//\t        /* ... */\n//\t    }\n//\n//\t    if quit {\n//\t        break\n//\t    }\n//\n//\t    if execute {\n//\t       s := buf.String()\n//\t       res, err := db.Query(s)\n//\t       /* handle database ... */\n//\t       buf.Reset(nil)\n//\t    }\n//\t}\nfunc (b *Stmt) Next(unquote func(string, bool) (string, bool, error)) (string, string, error) {\n\tvar err error\n\tvar i int\n\t// no runes to process, grab more\n\tif b.rlen == 0 {\n\t\tb.r, err = b.f()\n\t\tif err != nil {\n\t\t\treturn \"\", \"\", err\n\t\t}\n\t\tb.rlen = len(b.r)\n\t}\n\tvar cmd, params string\n\tvar ok bool\nparse:\n\tfor ; i < b.rlen; i++ {\n\t\t// fmt.Fprintf(os.Stderr, \"> %d: `%s`\\n\", i, string(b.r[i:]))\n\t\t// grab c, next\n\t\tc, next := b.r[i], grab(b.r, i+1, b.rlen)\n\t\tswitch {\n\t\t// find end of string\n\t\tcase b.quote != 0:\n\t\t\ti, ok = readString(b.r, i, b.rlen, b.quote, b.quoteDollarTag)\n\t\t\tif ok {\n\t\t\t\tb.quote, b.quoteDollarTag = 0, \"\"\n\t\t\t}\n\t\t// find end of multiline comment\n\t\tcase b.multilineComment:\n\t\t\ti, ok = readMultilineComment(b.r, i, b.rlen)\n\t\t\tb.multilineComment = !ok\n\t\t// start of single or double quoted string\n\t\tcase c == '\\'' || c == '\"':\n\t\t\tb.quote = c\n\t\t// start of dollar quoted string literal (postgres)\n\t\tcase b.allowDollar && c == '$' && (next == '$' || next == '_' || unicode.IsLetter(next)):\n\t\t\tvar id string\n\t\t\tid, i, ok = readDollarAndTag(b.r, i, b.rlen)\n\t\t\tif ok {\n\t\t\t\tb.quote, b.quoteDollarTag = '$', id\n\t\t\t}\n\t\t// start of sql comment, skip to end of line\n\t\tcase c == '-' && next == '-':\n\t\t\ti = b.rlen\n\t\t// start of c-style comment, skip to end of line\n\t\tcase b.allowCComments && c == '/' && next == '/':\n\t\t\ti = b.rlen\n\t\t// start of hash comment, skip to end of line\n\t\tcase b.allowHashComments && c == '#':\n\t\t\ti = b.rlen\n\t\t// start of multiline comment\n\t\tcase b.allowMultilineComments && c == '/' && next == '*':\n\t\t\tb.multilineComment = true\n\t\t\ti++\n\t\t// variable declaration\n\t\tcase c == ':' && next != ':':\n\t\t\tif v := readVar(b.r, i, b.rlen, next); v != nil {\n\t\t\t\tb.Vars = append(b.Vars, v)\n\t\t\t\tz, ok, _ := unquote(v.Name, true)\n\t\t\t\tif v.Defined = ok || v.Quote == '?'; v.Defined {\n\t\t\t\t\tb.r, b.rlen = v.Substitute(b.r, z, ok)\n\t\t\t\t}\n\t\t\t\tif b.Len != 0 {\n\t\t\t\t\tv.I += b.Len + 1\n\t\t\t\t}\n\t\t\t}\n\t\t// skip escaped backslash, semicolon, colon\n\t\tcase c == '\\\\' && (next == '\\\\' || next == ';' || next == ':'):\n\t\t\tv := &Var{\n\t\t\t\tI:     i,\n\t\t\t\tEnd:   i + 2,\n\t\t\t\tQuote: '\\\\',\n\t\t\t\tName:  string(next),\n\t\t\t}\n\t\t\tb.Vars = append(b.Vars, v)\n\t\t\tif b.r, b.rlen = v.Substitute(b.r, string(next), false); b.Len != 0 {\n\t\t\t\tv.I += b.Len + 1\n\t\t\t}\n\t\t// unbalance\n\t\tcase c == '(':\n\t\t\tb.balanceCount++\n\t\t// balance\n\t\tcase c == ')':\n\t\t\tb.balanceCount = max(0, b.balanceCount-1)\n\t\t// continue processing quoted string, multiline comment, or unbalanced statements\n\t\tcase b.quote != 0 || b.multilineComment || b.balanceCount != 0:\n\t\t// start of command\n\t\tcase c == '\\\\':\n\t\t\t// parse command and params end positions\n\t\t\tcend, pend := readCommand(b.r, i, b.rlen)\n\t\t\tcmd, params = string(b.r[i:cend]), string(b.r[cend:pend])\n\t\t\t// remove command and params from r\n\t\t\tb.r = append(b.r[:i], b.r[pend:]...)\n\t\t\tb.rlen = len(b.r)\n\t\t\tbreak parse\n\t\t// terminated\n\t\tcase c == ';':\n\t\t\tb.ready = true\n\t\t\ti++\n\t\t\tbreak parse\n\t\t}\n\t}\n\t// fix i -- i will be +1 when passing the length, which is a problem as the\n\t// '\\n' will get copied from the source.\n\ti = min(i, b.rlen)\n\t// append line to buf when:\n\t// 1. in a quoted string (ie, ', \", or $)\n\t// 2. in a multiline comment\n\t// 3. line is not empty\n\t//\n\t// DO NOT append to buf when:\n\t// 1. line is empty/whitespace and not in a string/multiline comment\n\tempty := isEmptyLine(b.r, 0, i)\n\tappendLine := b.quote != 0 || b.multilineComment || !empty\n\tif !b.multilineComment && cmd != \"\" && empty {\n\t\tappendLine = false\n\t}\n\tif appendLine {\n\t\t// skip leading space when empty\n\t\tst := 0\n\t\tif b.Len == 0 {\n\t\t\tst, _ = findNonSpace(b.r, 0, i)\n\t\t}\n\t\t// log.Printf(\">> appending: `%s`\", string(r[st:i]))\n\t\tb.Append(b.r[st:i], lineend)\n\t}\n\t// set prefix\n\tb.Prefix = findPrefix(b.Buf, prefixCount, b.allowCComments, b.allowHashComments, b.allowMultilineComments)\n\t// reset r\n\tb.r = b.r[i:]\n\tb.rlen = len(b.r)\n\t/*\n\t\tfmt.Fprintf(os.Stderr, \"\\n------------------------------\\n\")\n\t\tfmt.Fprintf(os.Stderr, \"    NEXT: `%s`\\n\", string(b.Buf))\n\t\tfmt.Fprintf(os.Stderr, \"  REMAIN: `%s`\\n\", string(b.r))\n\t\tfmt.Fprintf(os.Stderr, \"     CMD: `%s`\\n\", cmd)\n\t\tfmt.Fprintf(os.Stderr, \"  PARAMS: %v\\n\", params)\n\t*/\n\treturn cmd, params, nil\n}\n\n// Append appends r to b.Buf separated by sep when b.Buf is not already empty.\n//\n// Dynamically grows b.Buf as necessary to accommodate r and the separator.\n// Specifically, when b.Buf is not empty, b.Buf will grow by increments of\n// MinCapIncrease.\n//\n// After a call to Append, b.Len will be len(b.Buf)+len(sep)+len(r). Call Reset\n// to reset the Buf.\nfunc (b *Stmt) Append(r, sep []rune) {\n\trlen := len(r)\n\t// initial\n\tif b.Buf == nil {\n\t\tb.Buf, b.Len = r, rlen\n\t\treturn\n\t}\n\tblen, seplen := b.Len, len(sep)\n\ttlen := blen + rlen + seplen\n\t// grow\n\tif bcap := cap(b.Buf); tlen > bcap {\n\t\tn := tlen + 2*rlen\n\t\tn += minCapIncrease - (n % minCapIncrease)\n\t\tz := make([]rune, blen, n)\n\t\tcopy(z, b.Buf)\n\t\tb.Buf = z\n\t}\n\tb.Buf = b.Buf[:tlen]\n\tcopy(b.Buf[blen:], sep)\n\tcopy(b.Buf[blen+seplen:], r)\n\tb.Len = tlen\n}\n\n// AppendString is a util func wrapping Append.\nfunc (b *Stmt) AppendString(s, sep string) {\n\tb.Append([]rune(s), []rune(sep))\n}\n\n// State returns a string representing the state of statement parsing.\nfunc (b *Stmt) State() string {\n\tswitch {\n\tcase b.quote != 0:\n\t\treturn string(b.quote)\n\tcase b.multilineComment:\n\t\treturn \"*\"\n\tcase b.balanceCount != 0:\n\t\treturn \"(\"\n\tcase b.Len != 0:\n\t\treturn \"-\"\n\t}\n\treturn \"=\"\n}\n\n// Var holds information about a variable.\ntype Var struct {\n\t// I is where the variable starts (ie, ':') in Stmt.Buf.\n\tI int\n\t// End is where the variable ends in Stmt.Buf.\n\tEnd int\n\t// Quote is the quote character used if the variable was quoted, 0\n\t// otherwise.\n\tQuote rune\n\t// Name is the actual variable name excluding ':' and any enclosing quote\n\t// characters.\n\tName string\n\t// Len is the length of the replaced variable.\n\tLen int\n\t// Defined indicates whether the variable has been defined.\n\tDefined bool\n}\n\n// String satisfies the fmt.Stringer interface.\nfunc (v *Var) String() string {\n\tswitch v.Quote {\n\tcase '\\\\':\n\t\treturn \"\\\\\" + v.Name\n\tcase '\\'', '\"':\n\t\treturn \":\" + string(v.Quote) + v.Name + string(v.Quote)\n\tcase '?':\n\t\treturn \":{?\" + v.Name + \"}\"\n\t}\n\treturn \":\" + v.Name\n}\n\n// Substitute substitutes part of r, with s.\nfunc (v *Var) Substitute(r []rune, s string, ok bool) ([]rune, int) {\n\tswitch v.Quote {\n\tcase '?':\n\t\ts = trueFalse(ok)\n\tcase '\\'', '\"':\n\t\ts = string(v.Quote) + s + string(v.Quote)\n\t}\n\t// fmt.Fprintf(os.Stderr, \"orig: %q repl: %q\\n\", string(r), s)\n\tsr, rcap := []rune(s), cap(r)\n\tv.Len = len(sr)\n\t// grow ...\n\ttlen := len(r) + v.Len - (v.End - v.I)\n\tif tlen > rcap {\n\t\tz := make([]rune, tlen)\n\t\tcopy(z, r)\n\t\tr = z\n\t} else {\n\t\tr = r[:rcap]\n\t}\n\t// substitute\n\tcopy(r[v.I+v.Len:], r[v.End:])\n\tcopy(r[v.I:v.I+v.Len], sr)\n\treturn r[:tlen], tlen\n}\n\n// Option is a statement buffer option.\ntype Option func(*Stmt)\n\n// WithAllowDollar is a statement buffer option to set allowing dollar strings (ie,\n// $$text$$ or $tag$text$tag$).\nfunc WithAllowDollar(enable bool) Option {\n\treturn func(b *Stmt) {\n\t\tb.allowDollar = enable\n\t}\n}\n\n// WithAllowMultilineComments is a statement buffer option to set allowing multiline comments\n// (ie, /* ... */).\nfunc WithAllowMultilineComments(enable bool) Option {\n\treturn func(b *Stmt) {\n\t\tb.allowMultilineComments = enable\n\t}\n}\n\n// WithAllowCComments is a statement buffer option to set allowing C-style comments\n// (ie, // ...).\nfunc WithAllowCComments(enable bool) Option {\n\treturn func(b *Stmt) {\n\t\tb.allowCComments = enable\n\t}\n}\n\n// WithAllowHashComments is a statement buffer option to set allowing hash comments\n// (ie, # ...).\nfunc WithAllowHashComments(enable bool) Option {\n\treturn func(b *Stmt) {\n\t\tb.allowHashComments = enable\n\t}\n}\n\n// isSpaceOrControl is a special test for either a space or a control (ie, \\b)\n// characters.\nfunc isSpaceOrControl(r rune) bool {\n\treturn unicode.IsSpace(r) || unicode.IsControl(r)\n}\n\n// lastIndex returns the last index in r of needle, or -1 if not found.\nfunc lastIndex(r []rune, needle rune) int {\n\tfor i := len(r) - 1; i >= 0; i-- {\n\t\tif r[i] == needle {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// trueFalse returns TRUE or FALSE.\nfunc trueFalse(ok bool) string {\n\tif ok {\n\t\treturn \"TRUE\"\n\t}\n\treturn \"FALSE\"\n}\n\n// lineend is the slice to use when appending a line.\nvar lineend = []rune{'\\n'}\n"
  },
  {
    "path": "stmt/stmt_test.go",
    "content": "package stmt\n\nimport (\n\t\"io\"\n\t\"os/user\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/xo/usql/env\"\n)\n\nfunc TestAppend(t *testing.T) {\n\ta512 := sl(512, 'a')\n\t// b1024 := sl(1024, 'b')\n\ttests := []struct {\n\t\ts   []string\n\t\texp string\n\t\tl   int\n\t\tc   int\n\t}{\n\t\t{[]string{\"\"}, \"\", 0, 0},\n\t\t{[]string{\"\", \"\"}, \"\\n\", 1, minCapIncrease},\n\t\t{[]string{\"\", \"\", \"\"}, \"\\n\\n\", 2, minCapIncrease},\n\t\t{[]string{\"\", \"\", \"\", \"\"}, \"\\n\\n\\n\", 3, minCapIncrease},\n\t\t{[]string{\"a\", \"\"}, \"a\\n\", 2, 2},\n\t\t{[]string{\"a\", \"b\", \"\"}, \"a\\nb\\n\", 4, minCapIncrease},\n\t\t{[]string{\"a\", \"b\", \"c\", \"\"}, \"a\\nb\\nc\\n\", 6, minCapIncrease},\n\t\t{[]string{\"\", \"a\", \"\"}, \"\\na\\n\", 3, minCapIncrease},\n\t\t{[]string{\"\", \"a\", \"b\", \"\"}, \"\\na\\nb\\n\", 5, minCapIncrease},\n\t\t{[]string{\"\", \"a\", \"b\", \"c\", \"\"}, \"\\na\\nb\\nc\\n\", 7, minCapIncrease},\n\t\t{[]string{\"\", \"foo\"}, \"\\nfoo\", 4, minCapIncrease},\n\t\t{[]string{\"\", \"foo\", \"\"}, \"\\nfoo\\n\", 5, minCapIncrease},\n\t\t{[]string{\"foo\", \"\", \"bar\"}, \"foo\\n\\nbar\", 8, minCapIncrease},\n\t\t{[]string{\"\", \"foo\", \"bar\"}, \"\\nfoo\\nbar\", 8, minCapIncrease},\n\t\t{[]string{a512}, a512, 512, 512},\n\t\t{[]string{a512, a512}, a512 + \"\\n\" + a512, 1025, 5 * minCapIncrease},\n\t\t{[]string{a512, a512, a512}, a512 + \"\\n\" + a512 + \"\\n\" + a512, 1538, 5 * minCapIncrease},\n\t\t{[]string{a512, \"\"}, a512 + \"\\n\", 513, 2 * minCapIncrease},\n\t\t{[]string{a512, \"\", \"foo\"}, a512 + \"\\n\\nfoo\", 517, 2 * minCapIncrease},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tb := new(Stmt)\n\t\t\tfor _, s := range test.s {\n\t\t\t\tb.AppendString(s, \"\\n\")\n\t\t\t}\n\t\t\tif s := b.String(); s != test.exp {\n\t\t\t\tt.Errorf(\"expected result of %q, got: %q\", test.exp, s)\n\t\t\t}\n\t\t\tif b.Len != test.l {\n\t\t\t\tt.Errorf(\"expected resulting len of %d, got: %d\", test.l, b.Len)\n\t\t\t}\n\t\t\tif c := cap(b.Buf); c != test.c {\n\t\t\t\tt.Errorf(\"expected resulting cap of %d, got: %d\", test.c, c)\n\t\t\t}\n\t\t\tb.Reset(nil)\n\t\t\tif b.Len != 0 {\n\t\t\t\tt.Errorf(\"expected after reset len of 0, got: %d\", b.Len)\n\t\t\t}\n\t\t\tb.AppendString(\"\", \"\\n\")\n\t\t\tif s := b.String(); s != \"\" {\n\t\t\t\tt.Errorf(\"expected after reset appending an empty string would result in empty string, got: %q\", s)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestVariedSeparator(t *testing.T) {\n\tb := new(Stmt)\n\tb.AppendString(\"foo\", \"\\n\")\n\tb.AppendString(\"foo\", \"bar\")\n\tif b.Len != 9 {\n\t\tt.Errorf(\"expected len of 9, got: %d\", b.Len)\n\t}\n\tif s := b.String(); s != \"foobarfoo\" {\n\t\tt.Errorf(\"expected %q, got: %q\", \"foobarfoo\", s)\n\t}\n\tif c := cap(b.Buf); c != minCapIncrease {\n\t\tt.Errorf(\"expected cap of %d, got: %d\", minCapIncrease, c)\n\t}\n}\n\nfunc TestNextResetState(t *testing.T) {\n\tu, err := user.Current()\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error, got: %v\", err)\n\t}\n\tunquote := env.Untick(u, env.NewVars(), false)\n\ttests := []struct {\n\t\ts     string\n\t\tstmts []string\n\t\tcmds  []string\n\t\tstate string\n\t\tvars  []string\n\t}{\n\t\t{``, nil, []string{`|`}, `=`, nil},\n\t\t{`;`, []string{`;`}, []string{`|`}, `=`, nil},\n\t\t{` ; `, []string{`;`}, []string{`|`, `|`}, `=`, nil},\n\t\t{` \\v `, nil, []string{`\\v| `}, `=`, nil},\n\t\t{` \\v \\p`, nil, []string{`\\v| `, `\\p|`}, `=`, nil},\n\t\t{` \\v   foo   \\p`, nil, []string{`\\v|   foo   `, `\\p|`}, `=`, nil},\n\t\t{` \\v   foo   bar  \\p   zz`, nil, []string{`\\v|   foo   bar  `, `\\p|   zz`}, `=`, nil},\n\t\t{` \\very   foo   bar  \\print   zz`, nil, []string{`\\very|   foo   bar  `, `\\print|   zz`}, `=`, nil},\n\t\t{`select 1;`, []string{`select 1;`}, []string{`|`}, `=`, nil},\n\t\t{`select 1\\g`, []string{`select 1`}, []string{`\\g|`}, `=`, nil},\n\t\t{`select 1 \\g`, []string{`select 1 `}, []string{`\\g|`}, `=`, nil},\n\t\t{` select 1 \\g`, []string{`select 1 `}, []string{`\\g|`}, `=`, nil},\n\t\t{` select 1   \\g  `, []string{`select 1   `}, []string{`\\g|  `}, `=`, nil},\n\t\t{`select 1; select 1\\g`, []string{`select 1;`, `select 1`}, []string{`|`, `\\g|`}, `=`, nil},\n\t\t{\"select 1\\n\\\\g\", []string{`select 1`}, []string{`|`, `\\g|`}, `=`, nil},\n\t\t{\"select 1 \\\\g\\n\\n\\n\\n\\\\v\", []string{`select 1 `}, []string{`\\g|`, `|`, `|`, `|`, `\\v|`}, `=`, nil},\n\t\t{\"select 1 \\\\g\\n\\n\\n\\n\\\\v foob \\\\p zzz \\n\\n\", []string{`select 1 `}, []string{`\\g|`, `|`, `|`, `|`, `\\v| foob `, `\\p| zzz `, `|`, `|`}, `=`, nil},\n\t\t{\" select 1 \\\\g \\\\p \\n select (15)\\\\g\", []string{`select 1 `, `select (15)`}, []string{`\\g| `, `\\p| `, `\\g|`}, `=`, nil},\n\t\t{\" select 1 (  \\\\g ) \\n ;\", []string{\"select 1 (  \\\\g ) \\n ;\"}, []string{`|`, `|`}, `=`, nil},\n\t\t{\n\t\t\t\" select 1\\n;select 2\\\\g  select 3;  \\\\p   \\\\z  foo bar \",\n\t\t\t[]string{\"select 1\\n;\", \"select 2\"},\n\t\t\t[]string{`|`, `|`, `\\g|  select 3;  `, `\\p|   `, `\\z|  foo bar `},\n\t\t\t\"=\", nil,\n\t\t},\n\t\t{\n\t\t\t\" select 1\\\\g\\n\\n\\tselect 2\\\\g\\n select 3;  \\\\p   \\\\z  foo bar \\\\p\\\\p select * from;  \\n\\\\p\",\n\t\t\t[]string{`select 1`, `select 2`, `select 3;`},\n\t\t\t[]string{`\\g|`, `|`, `\\g|`, `|`, `\\p|   `, `\\z|  foo bar `, `\\p|`, `\\p| select * from;  `, `\\p|`},\n\t\t\t\"=\", nil,\n\t\t},\n\t\t{\"select '';\", []string{\"select '';\"}, []string{\"|\"}, \"=\", nil},\n\t\t{\"select 'a''b\\nz';\", []string{\"select 'a''b\\nz';\"}, []string{\"|\", \"|\"}, \"=\", nil},\n\t\t{\"select 'a' 'b\\nz';\", []string{\"select 'a' 'b\\nz';\"}, []string{\"|\", \"|\"}, \"=\", nil},\n\t\t{\"select \\\"\\\";\", []string{\"select \\\"\\\";\"}, []string{\"|\"}, \"=\", nil},\n\t\t{\"select \\\"\\n\\\";\", []string{\"select \\\"\\n\\\";\"}, []string{\"|\", \"|\"}, \"=\", nil},\n\t\t{\"select $$$$;\", []string{\"select $$$$;\"}, []string{\"|\"}, \"=\", nil},\n\t\t{\"select $$\\nfoob(\\n$$;\", []string{\"select $$\\nfoob(\\n$$;\"}, []string{\"|\", \"|\", \"|\"}, \"=\", nil},\n\t\t{\"select $tag$$tag$;\", []string{\"select $tag$$tag$;\"}, []string{\"|\"}, \"=\", nil},\n\t\t{\"select $tag$\\n\\n$tag$;\", []string{\"select $tag$\\n\\n$tag$;\"}, []string{\"|\", \"|\", \"|\"}, \"=\", nil},\n\t\t{\"select $tag$\\n(\\n$tag$;\", []string{\"select $tag$\\n(\\n$tag$;\"}, []string{\"|\", \"|\", \"|\"}, \"=\", nil},\n\t\t{\"select $tag$\\n\\\\v(\\n$tag$;\", []string{\"select $tag$\\n\\\\v(\\n$tag$;\"}, []string{\"|\", \"|\", \"|\"}, \"=\", nil},\n\t\t{\"select $tag$\\n\\\\v(\\n$tag$\\\\g\", []string{\"select $tag$\\n\\\\v(\\n$tag$\"}, []string{\"|\", \"|\", `\\g|`}, \"=\", nil},\n\t\t{\"select $$\\n\\\\v(\\n$tag$$zz$$\\\\g$$\\\\g\", []string{\"select $$\\n\\\\v(\\n$tag$$zz$$\\\\g$$\"}, []string{\"|\", \"|\", `\\g|`}, \"=\", nil},\n\t\t{\"select * --\\n\\\\v\", nil, []string{\"|\", `\\v|`}, \"-\", nil},\n\t\t{\"select--\", nil, []string{\"|\"}, \"-\", nil},\n\t\t{\"select --\", nil, []string{\"|\"}, \"-\", nil},\n\t\t{\"select /**/\", nil, []string{\"|\"}, \"-\", nil},\n\t\t{\"select/* */\", nil, []string{\"|\"}, \"-\", nil},\n\t\t{\"select/*\", nil, []string{\"|\"}, \"*\", nil},\n\t\t{\"select /*\", nil, []string{\"|\"}, \"*\", nil},\n\t\t{\"select * /**/\", nil, []string{\"|\"}, \"-\", nil},\n\t\t{\"select * /* \\n\\n\\n--*/\\n;\", []string{\"select * /* \\n\\n\\n--*/\\n;\"}, []string{\"|\", \"|\", \"|\", \"|\", \"|\"}, \"=\", nil},\n\t\t{\"select * /* \\n\\n\\n--*/\\n\", nil, []string{\"|\", \"|\", \"|\", \"|\", \"|\"}, \"-\", nil},\n\t\t{\"select * /* \\n\\n\\n--\\n\", nil, []string{\"|\", \"|\", \"|\", \"|\", \"|\"}, \"*\", nil},\n\t\t{\"\\\\p \\\\p\\nselect (\", nil, []string{`\\p| `, `\\p|`, \"|\"}, \"(\", nil},\n\t\t{\"\\\\p \\\\p\\nselect ()\", nil, []string{`\\p| `, `\\p|`, \"|\"}, \"-\", nil},\n\t\t{\"\\n             \\t\\t               \\n\", nil, []string{\"|\", \"|\", \"|\"}, \"=\", nil},\n\t\t{\"\\n   foob      \\t\\t               \\n\", nil, []string{\"|\", \"|\", \"|\"}, \"-\", nil},\n\t\t{\"$$\", nil, []string{\"|\"}, \"$\", nil},\n\t\t{\"$$foo\", nil, []string{\"|\"}, \"$\", nil},\n\t\t{\"'\", nil, []string{\"|\"}, \"'\", nil},\n\t\t{\"(((()()\", nil, []string{\"|\"}, \"(\", nil},\n\t\t{\"\\\"\", nil, []string{\"|\"}, \"\\\"\", nil},\n\t\t{\"\\\"foo\", nil, []string{\"|\"}, \"\\\"\", nil},\n\t\t{\":a :b\", nil, []string{\"|\"}, \"-\", []string{\"a\", \"b\"}},\n\t\t{\":{?a_b} :{?_foo_bar_}\", nil, []string{\"|\"}, \"-\", []string{\"a_b\", \"_foo_bar_\"}},\n\t\t{`select :'a_b' :\"foo_bar_\"`, nil, []string{\"|\"}, \"-\", []string{\"a_b\", \"foo_bar_\"}},\n\t\t{`select :a:b;`, []string{\"select :a:b;\"}, []string{\"|\"}, \"=\", []string{\"a\", \"b\"}},\n\t\t{\"select :'a\\n:foo:bar\", nil, []string{\"|\", \"|\"}, \"'\", nil},\n\t\t{\"select :''\\n:foo:bar\\\\g\", []string{\"select :''\\n:foo:bar\"}, []string{\"|\", `\\g|`}, \"=\", []string{\"foo\", \"bar\"}},\n\t\t{\"select :''\\n:foo :bar\\\\g\", []string{\"select :''\\n:foo :bar\"}, []string{\"|\", `\\g|`}, \"=\", []string{\"foo\", \"bar\"}},\n\t\t{\"select :''\\n :foo :bar \\\\g\", []string{\"select :''\\n :foo :bar \"}, []string{\"|\", `\\g|`}, \"=\", []string{\"foo\", \"bar\"}},\n\t\t{\"select :'a\\n:'foo':\\\"bar\\\"\", nil, []string{\"|\", \"|\"}, \"'\", nil},\n\t\t{\"select :''\\n:'foo':\\\"bar\\\"\\\\g\", []string{\"select :''\\n:'foo':\\\"bar\\\"\"}, []string{\"|\", `\\g|`}, \"=\", []string{\"foo\", \"bar\"}},\n\t\t{\"select :''\\n:'foo' :\\\"bar\\\"\\\\g\", []string{\"select :''\\n:'foo' :\\\"bar\\\"\"}, []string{\"|\", `\\g|`}, \"=\", []string{\"foo\", \"bar\"}},\n\t\t{\"select :''\\n :'foo' :\\\"bar\\\" \\\\g\", []string{\"select :''\\n :'foo' :\\\"bar\\\" \"}, []string{\"|\", `\\g|`}, \"=\", []string{\"foo\", \"bar\"}},\n\t\t{`select 1\\echo 'pg://':foo'/':bar`, nil, []string{`\\echo| 'pg://':foo'/':bar`}, \"-\", nil},\n\t\t{`select :'foo'\\echo 'pg://':bar'/' `, nil, []string{`\\echo| 'pg://':bar'/' `}, \"-\", []string{\"foo\"}},\n\t\t{`select 1\\g '\\g`, []string{`select 1`}, []string{`\\g| '\\g`}, \"=\", nil},\n\t\t{`select 1\\g \"\\g`, []string{`select 1`}, []string{`\\g| \"\\g`}, \"=\", nil},\n\t\t{\"select 1\\\\g `\\\\g\", []string{`select 1`}, []string{\"\\\\g| `\\\\g\"}, \"=\", nil},\n\t\t{`select 1\\g '\\g `, []string{`select 1`}, []string{`\\g| '\\g `}, \"=\", nil},\n\t\t{`select 1\\g \"\\g `, []string{`select 1`}, []string{`\\g| \"\\g `}, \"=\", nil},\n\t\t{\"select 1\\\\g `\\\\g \", []string{`select 1`}, []string{\"\\\\g| `\\\\g \"}, \"=\", nil},\n\t\t{\"select $$\\\\g$$\\\\g\", []string{`select $$\\g$$`}, []string{`\\g|`}, \"=\", nil},\n\t\t{\"select $1\\\\bind a b c\\\\g\", []string{`select $1`}, []string{`\\bind| a b c`, `\\g|`}, \"=\", nil},\n\t\t{\"select $1 \\\\bind a b c \\\\g\", []string{`select $1 `}, []string{`\\bind| a b c `, `\\g|`}, \"=\", nil},\n\t\t{\"select $2, $a$ foo $a$, $1 \\\\bind a b \\\\g\", []string{`select $2, $a$ foo $a$, $1 `}, []string{`\\bind| a b `, `\\g|`}, \"=\", nil},\n\t\t{\"select \\\\;\\\\\\\\\\\\:\\\\; \\n;\", []string{\"select ;\\\\:; \\n;\"}, []string{`|`, `|`}, `=`, []string{`;`, `\\`, `:`, `;`}},\n\t\t{\"select \\\\;\\\\;\\\\;\\\\; \\n;\", []string{\"select ;;;; \\n;\"}, []string{`|`, `|`}, `=`, []string{`;`, `;`, `;`, `;`}},\n\t\t{`select \\;\\;\\;\\;;`, []string{`select ;;;;;`}, []string{`|`}, `=`, []string{`;`, `;`, `;`, `;`}},\n\t\t{`select \\\\\\;\\\\\\;\\\\\\;\\\\\\;;`, []string{`select \\;\\;\\;\\;;`}, []string{`|`}, `=`, []string{`\\`, `;`, `\\`, `;`, `\\`, `;`, `\\`, `;`}},\n\t\t{`select \\:foo;`, []string{`select :foo;`}, []string{`|`}, `=`, []string{\":\"}},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tt.Logf(\"statement: %q\", test.s)\n\t\t\tb := New(\n\t\t\t\tsp(test.s, \"\\n\"),\n\t\t\t\tWithAllowDollar(true),\n\t\t\t\tWithAllowMultilineComments(true),\n\t\t\t\tWithAllowCComments(true),\n\t\t\t)\n\t\t\tvar stmts, cmds, aparams []string\n\t\t\tvar vars []*Var\n\t\tloop:\n\t\t\tfor {\n\t\t\t\tcmd, params, err := b.Next(unquote)\n\t\t\t\tt.Logf(\"next buf:%q\", string(b.Buf))\n\t\t\t\tt.Logf(\"next cmd:%q params:%q\", cmd, params)\n\t\t\t\tswitch {\n\t\t\t\tcase err == io.EOF:\n\t\t\t\t\tbreak loop\n\t\t\t\tcase err != nil:\n\t\t\t\t\tt.Fatalf(\"expected no error, got: %v\", err)\n\t\t\t\t}\n\t\t\t\tif b.Ready() || cmd == `\\g` {\n\t\t\t\t\tstmts = append(stmts, b.String())\n\t\t\t\t\tvars = append(vars, b.Vars...)\n\t\t\t\t\tb.Reset(nil)\n\t\t\t\t}\n\t\t\t\tcmds = append(cmds, cmd)\n\t\t\t\taparams = append(aparams, params)\n\t\t\t}\n\t\t\tvars = append(vars, b.Vars...)\n\t\t\tif len(stmts) != len(test.stmts) || !reflect.DeepEqual(stmts, test.stmts) {\n\t\t\t\tt.Errorf(\"expected %d statements, got: %d\", len(test.stmts), len(stmts))\n\t\t\t\tt.Logf(\"expected:\")\n\t\t\t\tfor _, s := range test.stmts {\n\t\t\t\t\tt.Logf(\" %q\", s)\n\t\t\t\t}\n\t\t\t\tt.Logf(\"got:\")\n\t\t\t\tfor _, s := range stmts {\n\t\t\t\t\tt.Logf(\" %q\", s)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif cz := cc(cmds, aparams); !reflect.DeepEqual(cz, test.cmds) {\n\t\t\t\tt.Logf(\">> cmds: %#v, aparams: %#v, cz: %#v, test.cmds: %#v\", cmds, aparams, cz, test.cmds)\n\t\t\t\tt.Errorf(\"commands do not match\")\n\t\t\t\tt.Logf(\"expected:\")\n\t\t\t\tfor _, s := range test.cmds {\n\t\t\t\t\tt.Logf(\" %q\", s)\n\t\t\t\t}\n\t\t\t\tt.Logf(\"got:\")\n\t\t\t\tfor _, s := range cz {\n\t\t\t\t\tt.Logf(\" %q\", s)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif st := b.State(); st != test.state {\n\t\t\t\tt.Errorf(\"expected end parse state %q, got: %q\", test.state, st)\n\t\t\t}\n\t\t\tif len(vars) != len(test.vars) {\n\t\t\t\tt.Errorf(\"expected %d vars, got: %d\", len(test.vars), len(vars))\n\t\t\t\tt.Logf(\"expected:\")\n\t\t\t\tfor _, v := range test.vars {\n\t\t\t\t\tt.Logf(\" %#v\", v)\n\t\t\t\t}\n\t\t\t\tt.Logf(\"got:\")\n\t\t\t\tfor _, v := range vars {\n\t\t\t\t\tt.Logf(\" %#v\", v)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor i, v := range test.vars {\n\t\t\t\tif len(vars) < i {\n\t\t\t\t\tt.Logf(\"expected var %d: %#v\", i, v)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif vars[i].Name != v {\n\t\t\t\t\tt.Errorf(\"expected var %d: %q, got: %q\", i, v, vars[i].Name)\n\t\t\t\t}\n\t\t\t}\n\t\t\tb.Reset(nil)\n\t\t\tif len(b.Buf) != 0 {\n\t\t\t\tt.Errorf(\"after reset b.Buf should have len %d, got: %d\", 0, len(b.Buf))\n\t\t\t}\n\t\t\tif b.Len != 0 {\n\t\t\t\tt.Errorf(\"after reset should have len %d, got: %d\", 0, b.Len)\n\t\t\t}\n\t\t\tif len(b.Vars) != 0 {\n\t\t\t\tt.Errorf(\"after reset should have len(vars) == 0, got: %d\", len(b.Vars))\n\t\t\t}\n\t\t\tif b.Prefix != \"\" {\n\t\t\t\tt.Errorf(\"after reset should have empty prefix, got: %s\", b.Prefix)\n\t\t\t}\n\t\t\tif b.quote != 0 || b.quoteDollarTag != \"\" || b.multilineComment || b.balanceCount != 0 {\n\t\t\t\tt.Fatal(\"after reset should have a cleared parse state\")\n\t\t\t}\n\t\t\tif st := b.State(); st != \"=\" {\n\t\t\t\tt.Errorf(\"after reset should have state `=`, got: %q\", st)\n\t\t\t}\n\t\t\tif b.ready {\n\t\t\t\tt.Fatal(\"after reset should not be ready\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEmptyVariablesRawString(t *testing.T) {\n\tstmt := new(Stmt)\n\tstmt.AppendString(\"select \", \"\\n\")\n\tstmt.Prefix = \"SELECT\"\n\tv := &Var{\n\t\tI:    7,\n\t\tEnd:  9,\n\t\tName: \"a\",\n\t\tLen:  0,\n\t}\n\tstmt.Vars = append(stmt.Vars, v)\n\n\tif exp, got := \"select \", stmt.RawString(); exp != got {\n\t\tt.Fatalf(\"Defined=false, expected: %s, got: %s\", exp, got)\n\t}\n\n\tv.Defined = true\n\tif exp, got := \"select :a\", stmt.RawString(); exp != got {\n\t\tt.Fatalf(\"Defined=true, expected: %s, got: %s\", exp, got)\n\t}\n}\n\nfunc TestVarSubstitute(t *testing.T) {\n\ta512 := sl(512, 'a')\n\ttests := []struct {\n\t\ts   string\n\t\tv   *Var\n\t\tsub string\n\t\texp string\n\t}{\n\t\t{`:a`, v(0, `a`), `x`, `x`},\n\t\t{` :a`, v(1, `a`), `x`, ` x`},\n\t\t{`:a `, v(0, `a`), `x`, `x `},\n\t\t{` :a `, v(1, `a`), `x`, ` x `},\n\t\t{` :'a' `, v(1, `a`, `'`), `x`, ` 'x' `},\n\t\t{` :\"a\" `, v(1, \"a\", `\"`), `x`, ` \"x\" `},\n\t\t{`:a`, v(0, `a`), ``, ``},\n\t\t{` :a`, v(1, `a`), ``, ` `},\n\t\t{`:a `, v(0, `a`), ``, ` `},\n\t\t{` :a `, v(1, `a`), ``, `  `},\n\t\t{` :'a' `, v(1, `a`, `'`), ``, ` '' `},\n\t\t{` :\"a\" `, v(1, \"a\", `\"`), \"\", ` \"\" `},\n\t\t{` :aaa `, v(1, \"aaa\"), \"\", \"  \"},\n\t\t{` :aaa `, v(1, \"aaa\"), a512, \" \" + a512 + \" \"},\n\t\t{` :` + a512 + ` `, v(1, a512), \"\", \"  \"},\n\t\t{`:foo`, v(0, \"foo\"), \"这是一个\", `这是一个`},\n\t\t{`:foo `, v(0, \"foo\"), \"这是一个\", `这是一个 `},\n\t\t{` :foo`, v(1, \"foo\"), \"这是一个\", ` 这是一个`},\n\t\t{` :foo `, v(1, \"foo\"), \"这是一个\", ` 这是一个 `},\n\t\t{`:'foo'`, v(0, `foo`, `'`), `这是一个`, `'这是一个'`},\n\t\t{`:'foo' `, v(0, `foo`, `'`), `这是一个`, `'这是一个' `},\n\t\t{` :'foo'`, v(1, `foo`, `'`), `这是一个`, ` '这是一个'`},\n\t\t{` :'foo' `, v(1, `foo`, `'`), `这是一个`, ` '这是一个' `},\n\t\t{`:\"foo\"`, v(0, `foo`, `\"`), `这是一个`, `\"这是一个\"`},\n\t\t{`:\"foo\" `, v(0, `foo`, `\"`), `这是一个`, `\"这是一个\" `},\n\t\t{` :\"foo\"`, v(1, `foo`, `\"`), `这是一个`, ` \"这是一个\"`},\n\t\t{` :\"foo\" `, v(1, `foo`, `\"`), `这是一个`, ` \"这是一个\" `},\n\t\t{`:型`, v(0, `型`), `x`, `x`},\n\t\t{` :型`, v(1, `型`), `x`, ` x`},\n\t\t{`:型 `, v(0, `型`), `x`, `x `},\n\t\t{` :型 `, v(1, `型`), `x`, ` x `},\n\t\t{` :'型' `, v(1, `型`, `'`), `x`, ` 'x' `},\n\t\t{` :\"型\" `, v(1, \"型\", `\"`), `x`, ` \"x\" `},\n\t\t{`:型`, v(0, `型`), ``, ``},\n\t\t{` :型`, v(1, `型`), ``, ` `},\n\t\t{`:型 `, v(0, `型`), ``, ` `},\n\t\t{` :型 `, v(1, `型`), ``, `  `},\n\t\t{` :'型' `, v(1, `型`, `'`), ``, ` '' `},\n\t\t{` :\"型\" `, v(1, \"型\", `\"`), \"\", ` \"\" `},\n\t\t{`:型示師`, v(0, `型示師`), `本門台初埼本門台初埼`, `本門台初埼本門台初埼`},\n\t\t{` :型示師`, v(1, `型示師`), `本門台初埼本門台初埼`, ` 本門台初埼本門台初埼`},\n\t\t{`:型示師 `, v(0, `型示師`), `本門台初埼本門台初埼`, `本門台初埼本門台初埼 `},\n\t\t{` :型示師 `, v(1, `型示師`), `本門台初埼本門台初埼`, ` 本門台初埼本門台初埼 `},\n\t\t{` :型示師 `, v(1, `型示師`), `本門台初埼本門台初埼`, ` 本門台初埼本門台初埼 `},\n\t\t{` :'型示師' `, v(1, `型示師`, `'`), `a`, ` 'a' `},\n\t\t{` :\"型示師\" `, v(1, `型示師`, `\"`), `b`, ` \"b\" `},\n\t\t{` :'型示師' `, v(1, `型示師`, `'`), `本門台初埼本門台初埼`, ` '本門台初埼本門台初埼' `},\n\t\t{` :\"型示師\" `, v(1, `型示師`, `\"`), `本門台初埼本門台初埼`, ` \"本門台初埼本門台初埼\" `},\n\t\t{`:{?foo}`, v(0, `foo`, `?`), ``, `TRUE`},\n\t\t{`:{?foo_}`, v(0, `foo_`, `?`), ``, `FALSE`},\n\t\t{` :{?foo} `, v(1, `foo`, `?`), ``, ` TRUE `},\n\t\t{` :{?foo_} `, v(1, `foo_`, `?`), ``, ` FALSE `},\n\t}\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tt.Logf(\"`%s` `%s`: `%s` --> `%s`\", test.v.Name, test.sub, test.s, test.exp)\n\t\t\tt.Logf(\"[]byte: %d\", len([]byte(test.s)))\n\t\t\tt.Logf(\"[]rune: %d\", len([]rune(test.s)))\n\t\t\tt.Logf(\"v.I:%d v.End:%d\", test.v.I, test.v.End)\n\t\t\tr, n := test.v.Substitute([]rune(test.s), test.sub, test.v.Name == \"foo\")\n\t\t\tif exp := len([]rune(test.exp)); n != exp {\n\t\t\t\tt.Errorf(\"expected n to be %d, got: %d\", exp, n)\n\t\t\t}\n\t\t\tif s := string(r); s != test.exp {\n\t\t\t\tt.Errorf(\"expected %q, got: %q\", test.exp, s)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc v(i int, name string, x ...string) *Var {\n\tz := &Var{\n\t\tI:    i,\n\t\tName: name,\n\t\tEnd:  i + len([]rune(name)) + 1, // :name\n\t}\n\tif len(x) != 0 {\n\t\tz.Quote = []rune(x[0])[0]\n\t\tswitch z.Quote {\n\t\tcase '\\'', '\"':\n\t\t\tz.End += 2 // '', \"\"\n\t\tcase '?':\n\t\t\tz.End += 3 // {?}\n\t\t}\n\t}\n\treturn z\n}\n\n// cc combines commands with params.\nfunc cc(cmds []string, params []string) []string {\n\tif len(cmds) == 0 {\n\t\treturn []string{\"|\"}\n\t}\n\tz := make([]string, len(cmds))\n\tif len(cmds) != len(params) {\n\t\tpanic(\"length of params should be same as cmds\")\n\t}\n\tfor i := 0; i < len(cmds); i++ {\n\t\tz[i] = cmds[i] + \"|\" + params[i]\n\t}\n\treturn z\n}\n\nfunc sp(a, sep string) func() ([]rune, error) {\n\ts := strings.Split(a, sep)\n\treturn func() ([]rune, error) {\n\t\tif len(s) > 0 {\n\t\t\tz := s[0]\n\t\t\ts = s[1:]\n\t\t\treturn []rune(z), nil\n\t\t}\n\t\treturn nil, io.EOF\n\t}\n}\n\nfunc sl(n int, r rune) string {\n\tz := make([]rune, n)\n\tfor i := 0; i < n; i++ {\n\t\tz[i] = r\n\t}\n\treturn string(z)\n}\n"
  },
  {
    "path": "styles/styles.go",
    "content": "// Package styles provides chroma styles based on the chroma styles but removing\n// the backgrounds.\npackage styles\n\nimport (\n\t\"sync\"\n\n\t\"github.com/alecthomas/chroma/v2\"\n\tcstyles \"github.com/alecthomas/chroma/v2/styles\"\n)\n\n// styles is the set of styles with their background colors removed.\nvar styles = struct {\n\tstyles map[string]*chroma.Style\n\tsync.Mutex\n}{\n\tstyles: make(map[string]*chroma.Style),\n}\n\n// Get retrieves the equivalent chroma style.\nfunc Get(name string) *chroma.Style {\n\tstyles.Lock()\n\tdefer styles.Unlock()\n\tif _, ok := styles.styles[name]; !ok {\n\t\t// get original style\n\t\ts := cstyles.Get(name)\n\t\t// create new entry map\n\t\tm := make(chroma.StyleEntries)\n\t\tfor _, typ := range s.Types() {\n\t\t\t// skip background\n\t\t\tif typ == chroma.Background {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tz := s.Get(typ)\n\t\t\t// unset background\n\t\t\tz.Background = chroma.Colour(0)\n\t\t\tm[typ] = z.String()\n\t\t}\n\t\tstyles.styles[name] = chroma.MustNewStyle(s.Name, m)\n\t}\n\treturn styles.styles[name]\n}\n"
  },
  {
    "path": "testcli.go",
    "content": "//go:build ignore\n\n// Command testcli runs goexpect tests against a built usql binary.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"regexp\"\n\t\"time\"\n\n\tgexpect \"github.com/google/goexpect\"\n)\n\nfunc main() {\n\tbinpath := flag.String(\"binpath\", \"./usql\", \"bin path\")\n\tdeadline := flag.Duration(\"deadline\", 5*time.Minute, \"total execution deadline\")\n\ttimeout := flag.Duration(\"timeout\", 2*time.Minute, \"individual test timeout\")\n\tre := flag.String(\"run\", \"\", \"test name regexp to run\")\n\tflag.Parse()\n\tif err := run(context.Background(), *binpath, *deadline, *timeout, *re); err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"error: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n}\n\nfunc run(ctx context.Context, binpath string, deadline, timeout time.Duration, re string) error {\n\tctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(deadline))\n\tdefer cancel()\n\ttests, err := cliTests()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar nameRE *regexp.Regexp\n\tif re != \"\" {\n\t\tnameRE, err = regexp.Compile(re)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tfor _, test := range tests {\n\t\tif nameRE != nil && !nameRE.MatchString(test.name) {\n\t\t\tlog.Printf(\">>> SKIPPING: %s\", test.name)\n\t\t\tcontinue\n\t\t}\n\t\tlog.Printf(\">>> RUNNING: %s\", test.name)\n\t\tif err := test.do(ctx, binpath, timeout); err != nil {\n\t\t\treturn fmt.Errorf(\"test %s: %v\", test.name, err)\n\t\t}\n\t\tlog.Printf(\">>> COMPLETED: %s\", test.name)\n\t}\n\treturn nil\n}\n\ntype Test struct {\n\tname   string\n\tscript string\n\targs   []string\n\tenv    []string\n}\n\nfunc cliTests() ([]Test, error) {\n\tenv := append(os.Environ(), \"TERM=xterm-256color\")\n\treturn []Test{\n\t\t{\n\t\t\t\"complex/postgres\",\n\t\t\t\"./contrib/postgres/test.sql\",\n\t\t\t[]string{\"pgsql://postgres:P4ssw0rd@localhost\", \"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t\t{\n\t\t\t\"complex/mysql\",\n\t\t\t\"./contrib/mysql/test.sql\",\n\t\t\t[]string{\"my://root:P4ssw0rd@localhost\", \"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t\t{\n\t\t\t\"complex/sqlite3\",\n\t\t\t\"./contrib/sqlite3/test.sql\",\n\t\t\t[]string{\"sqlite:./testdata/sqlite3_test.db\", \"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t\t{\n\t\t\t\"complex/moderncsqlite\",\n\t\t\t\"./contrib/sqlite3/test.sql\",\n\t\t\t[]string{\"mq:./testdata/moderncsqlite_test.db\", \"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t\t{\n\t\t\t\"complex/sqlserver\",\n\t\t\t\"./contrib/sqlserver/test.sql\",\n\t\t\t[]string{\"sqlserver://sa:Adm1nP@ssw0rd@localhost/\", \"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t\t{\n\t\t\t\"complex/cassandra\",\n\t\t\t\"./contrib/cassandra/test.sql\",\n\t\t\t[]string{\"ca://cassandra:cassandra@localhost\", \"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t\t{\n\t\t\t\"copy/a_bit_of_everything\",\n\t\t\t\"./testdata/copy.sql\",\n\t\t\t[]string{\"--set=PAGER=''\", \"--pset=pager=off\"},\n\t\t\tenv,\n\t\t},\n\t}, nil\n}\n\nfunc (test Test) do(ctx context.Context, binpath string, timeout time.Duration) error {\n\texp, errch, err := gexpect.SpawnWithArgs(\n\t\tappend([]string{binpath}, test.args...),\n\t\ttimeout,\n\t\tgexpect.SetEnv(test.env),\n\t\tgexpect.Tee(&noopWriteCloser{os.Stdout}),\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuf, err := os.ReadFile(test.script)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, line := range bytes.Split(buf, []byte{'\\n'}) {\n\t\tif err := exp.Send(string(line) + \"\\n\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tselect {\n\tcase <-ctx.Done():\n\t\tdefer exp.Close()\n\t\treturn ctx.Err()\n\tcase err := <-errch:\n\t\tdefer exp.Close()\n\t\treturn err\n\t}\n}\n\ntype noopWriteCloser struct {\n\tio.Writer\n}\n\nfunc (*noopWriteCloser) Close() error {\n\treturn nil\n}\n"
  },
  {
    "path": "testdata/copy.sql",
    "content": "\\set PGDB pg://postgres:P4ssw0rd@localhost\n\\set MYDB my://root:P4ssw0rd@localhost\n\\set SQDB sq:./testdata/copy_test.db\n\\set MSDB ms://sa:Adm1nP@ssw0rd@localhost/\n\n\\connect :PGDB\ndrop table if exists a_bit_of_everything;\ncreate table a_bit_of_everything (\n  a_id serial primary key,\n  a_blob bytea,\n  a_bool boolean,\n  a_date timestamp with time zone,\n  a_double double precision,\n  a_int integer,\n  a_text text\n);\ninsert into a_bit_of_everything\n  (a_blob, a_bool, a_date, a_double, a_int, a_text)\nvalues\n  (E'more\\ntext'::bytea, true, now(), 32.0, 0, 'some text'),\n  (E'other\\ntext'::bytea, false, now()+interval '3 days', 64.0, 128, 'foobar')\n;\nselect * from a_bit_of_everything;\n\n\\connect :MYDB\ndrop database if exists testdb;\ncreate database testdb;\nuse testdb;\ndrop table if exists a_bit_of_everything;\ncreate table a_bit_of_everything (\n  a_id integer not null auto_increment primary key,\n  a_blob blob,\n  a_bool boolean,\n  a_date datetime,\n  a_double double,\n  a_int integer,\n  a_text text\n);\n\\copy :PGDB :MYDB/testdb 'select * from a_bit_of_everything' 'a_bit_of_everything(a_id, a_blob, a_bool, a_date, a_double, a_int, a_text)'\n\\connect :MYDB/testdb\nselect * from a_bit_of_everything;\n\n\\! rm -f ./testdata/test3.db\n\\connect :SQDB\ncreate table a_bit_of_everything (\n  a_id integer primary key autoincrement,\n  a_blob blob,\n  a_bool boolean,\n  a_date datetime,\n  a_double double precision,\n  a_int integer,\n  a_text text\n);\n\\copy :PGDB :SQDB 'select * from a_bit_of_everything' 'a_bit_of_everything(a_id, a_blob, a_bool, a_date, a_double, a_int, a_text)'\n\\connect :SQDB\nselect * from a_bit_of_everything;\n\n\\connect :MSDB\ndrop table  if exists a_bit_of_everything;\ncreate table a_bit_of_everything (\n  -- a_id integer identity(1,1) primary key, -- doesn't work currently\n  a_id integer,\n  a_blob varbinary(max),\n  a_bool bit,\n  a_date datetime2,\n  a_double double precision,\n  a_int integer,\n  a_text text\n);\n\\copy :PGDB :MSDB 'select * from a_bit_of_everything' 'a_bit_of_everything(a_id, a_blob, a_bool, a_date, a_double, a_int, a_text)'\n\\connect :MSDB\nselect * from a_bit_of_everything;\n\n\\quit\n"
  },
  {
    "path": "testdata/inc_test.sql",
    "content": "select 'testdata/inc_test.sql';\n\n\\i sub/inc_test2.sql\n"
  },
  {
    "path": "testdata/inc_test_z.sql",
    "content": "select 'testdata/inc_test_z.sql';\n"
  },
  {
    "path": "testdata/numbers.sql",
    "content": "select '1251258098.1555901285'::numeric;\nselect '1251258098.1555901285'::float4;\nselect '1251258098.1555901285'::float8;\nselect '1251258098.1555901285'::double precision;\n"
  },
  {
    "path": "testdata/quotes.sql",
    "content": "-- echo all\n\\set ECHO all\n-- conditional variables display FALSE when name is not set\n\\unset foo\n\\echo :{?foo}\n-- conditional variables display TRUE when name is set\n\\set foo 'bar'\n\\echo :{?foo}\n-- single quoted strings will decode '' as ' and decode \\n, \\t, \\b, \\r, \\f, \\digits octals, \\xdigits (standard escapes)\n\\set foo 'bar''bar\\r\\n'\n-- single quoted variables escape ' but does not escape special characters\n\\echo :'foo'\n-- double quoted variables do not escape ' or special characters\n\\echo :\"foo\"\n-- single quoted strings decode any other standard escape (\\<char>) as literal\n\\set foo 'bar\\'''bar'\n\\echo :foo\n\\echo :'foo'\n-- single quoted variables escape \\ with E'' style strings\n\\set foo 'bar\\\\\\''\n\\echo :foo\n\\echo :'foo'\n\\echo :\"foo\"\n-- backticks interpolate unquoted variables\n\\set foo 'bar'\n\\echo `echo :foo`\n-- backticks interpolate single quoted variables\n\\echo `echo :'foo'`\n-- backticks do not interpolate double quoted variables\n\\echo `echo :\"foo\"`\n-- backticks have error messages for single quoted variables containing \\r or \\n when using :'' syntax\n\\set foo 'bar\\r\\n'\n\\echo `echo :'foo'`\n"
  },
  {
    "path": "testdata/sub/inc_test2.sql",
    "content": "select 'testdata/sub/inc_test2.sql';\n\nselect 'from testdata/sub/inc_test2.sql, doing: \\i inc_test_z.sql';\n\n\\i inc_test_z.sql\n\nselect 'from testdata/sub/inc_test2.sql, doing: \\ir inc_test_z.sql';\n\n\\ir inc_test_z.sql\n"
  },
  {
    "path": "testdata/sub/inc_test_z.sql",
    "content": "select 'testdata/sub/inc_test_z.sql'\n"
  },
  {
    "path": "text/errors.go",
    "content": "package text\n\nimport (\n\t\"errors\"\n)\n\nvar (\n\t// ErrNotConnected is the not connected error.\n\tErrNotConnected = errors.New(`not connected`)\n\t// ErrNoSuchFileOrDirectory is the no such file or directory error.\n\tErrNoSuchFileOrDirectory = errors.New(`no such file or directory`)\n\t// ErrCannotIncludeDirectories is the cannot include directories error.\n\tErrCannotIncludeDirectories = errors.New(`cannot include directories`)\n\t// ErrMissingDSN is the missing dsn error.\n\tErrMissingDSN = errors.New(`missing dsn`)\n\t// ErrNoPreviousTransactionExists is the no previous transaction exists error.\n\tErrNoPreviousTransactionExists = errors.New(`no previous transaction exists`)\n\t// ErrPreviousTransactionExists is the previous transaction exists error.\n\tErrPreviousTransactionExists = errors.New(`previous transaction exists`)\n\t// ErrPasswordAttemptsExhausted is the exhausted password attempts error.\n\tErrPasswordAttemptsExhausted = errors.New(`password attempts exhausted`)\n\t// ErrSingleTransactionCannotBeUsedWithInteractiveMode is the single transaction cannot be used with interactive mode error.\n\tErrSingleTransactionCannotBeUsedWithInteractiveMode = errors.New(`--single-transaction cannot be used with interactive mode`)\n\t// ErrNoEditorDefined is the no editor defined error.\n\tErrNoEditorDefined = errors.New(`no editor defined`)\n\t// ErrUnknownCommand is the unknown command error.\n\tErrUnknownCommand = errors.New(`unknown command`)\n\t// ErrMissingRequiredArgument is the missing required argument error.\n\tErrMissingRequiredArgument = errors.New(`missing required argument`)\n\t// ErrDriverNotAvailable is the driver not available error.\n\tErrDriverNotAvailable = errors.New(`driver not available`)\n\t// ErrPasswordNotSupportedByDriver is the password not supported by driver error.\n\tErrPasswordNotSupportedByDriver = errors.New(`\\password not supported by driver`)\n\t// ErrUnterminatedQuotedString is the unterminated quoted string error.\n\tErrUnterminatedQuotedString = errors.New(`unterminated quoted string`)\n\t// ErrNoShellAvailable is the no SHELL available error.\n\tErrNoShellAvailable = errors.New(`no SHELL available`)\n\t// ErrNotInteractive is the not interactive error.\n\tErrNotInteractive = errors.New(`not interactive`)\n\t// ErrInvalidType is the invalid type error.\n\tErrInvalidType = errors.New(`invalid -TYPE: TYPE must be password, string, int, uint, float, or bool`)\n\t// ErrInvalidIdentifier is the invalid identifier error.\n\tErrInvalidIdentifier = errors.New(`invalid identifier`)\n\t// ErrInvalidValue is the invalid value error.\n\tErrInvalidValue = errors.New(`invalid value`)\n\t// ErrTooManyRows is the too many rows error.\n\tErrTooManyRows = errors.New(`too many rows`)\n\t// ErrInvalidFormatType is the invalid format type error.\n\tErrInvalidFormatType = errors.New(`\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms, json, csv`)\n\t// ErrInvalidFormatPagerType is the invalid format pager error.\n\tErrInvalidFormatPagerType = errors.New(`\\pset: allowed pager values are on, off, always`)\n\t// ErrInvalidFormatExpandedType is the invalid format expanded error.\n\tErrInvalidFormatExpandedType = errors.New(`\\pset: allowed expanded values are on, off, auto`)\n\t// ErrInvalidFormatLineStyle is the invalid format line style error.\n\tErrInvalidFormatLineStyle = errors.New(`\\pset: allowed line styles are ascii, old-ascii, unicode`)\n\t// ErrInvalidFormatBorderLineStyle is the invalid format border line style error.\n\tErrInvalidFormatBorderLineStyle = errors.New(`\\pset: allowed Unicode border line styles are single, double`)\n\t// ErrInvalidTimezoneLocation is the invalid timezone location error.\n\tErrInvalidTimezoneLocation = errors.New(`\\pset: invalid timezone location`)\n\t// ErrGraphicsNotSupported is the graphics not supported error.\n\tErrGraphicsNotSupported = errors.New(`\\chart: graphics not supported in terminal`)\n\t// ErrNoNumericColumns is the no numeric columns error.\n\tErrNoNumericColumns = errors.New(`\\chart: no numeric columns found`)\n\t// ErrInvalidQuotedString is the invalid quoted string error.\n\tErrInvalidQuotedString = errors.New(`invalid quoted string`)\n\t// ErrInvalidFormatOption is the invalid format option error.\n\tErrInvalidFormatOption = errors.New(`invalid format option`)\n\t// ErrInvalidWatchDuration is the invalid watch duration error.\n\tErrInvalidWatchDuration = errors.New(`invalid watch duration`)\n\t// ErrUnableToNormalizeURL is the unable to normalize URL error.\n\tErrUnableToNormalizeURL = errors.New(`unable to normalize URL`)\n\t// ErrInvalidIsolationLevel is the invalid isolation level error.\n\tErrInvalidIsolationLevel = errors.New(`invalid isolation level`)\n\t// ErrNotSupported is the not supported error.\n\tErrNotSupported = errors.New(`not supported`)\n\t// ErrWrongNumberOfArguments is the wrong number of arguments error.\n\tErrWrongNumberOfArguments = errors.New(`wrong number of arguments`)\n\t// ErrUnknownFileType is the unknown file type error.\n\tErrUnknownFileType = errors.New(`unknown file type`)\n\t// ErrNamedConnectionIsNotAURL is the named connection is not a url error.\n\tErrNamedConnectionIsNotAURL = errors.New(`named connection is not a url`)\n\t// ErrInvalidConfig is the invalid config error.\n\tErrInvalidConfig = errors.New(`invalid config`)\n\t// ErrIfEscaped is the if escaped error.\n\tErrIfEscaped = errors.New(`\\if escaped`)\n\t// ErrEndIfNoMatchingIf is the endif no matching if error.\n\tErrEndIfNoMatchingIf = errors.New(`\\endif: no matching \\if`)\n)\n"
  },
  {
    "path": "text/license.go",
    "content": "package text\n\n// Code generated by gen.go. DO NOT EDIT.\n\n// License contains the license text for usql.\nconst License = `The MIT License (MIT)\n\nCopyright (c) 2015-2025 Kenneth Shaw\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.`\n"
  },
  {
    "path": "text/text.go",
    "content": "// Package text contains the text (and eventually translations) for the usql\n// application.\npackage text\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"image\"\n\t\"image/png\"\n\t\"io\"\n\t\"regexp\"\n\t\"strings\"\n)\n\n// Various usql text bits.\nvar (\n\tCommandName              = `usql`\n\tCommandVersion           = `0.0.0-dev`\n\tPassfileName             = CommandName + `pass`\n\tConfigName               = \"config\"\n\tBanner                   = `the universal command-line interface for SQL databases`\n\tCommandHelpHint          = `hint: try \"` + CommandName + ` --help\" for more information.`\n\tGoInstallHint            = \"\\ntry:\\n\\n  go install -tags 'most %s' github.com/xo/usql@%s\\n\\n\"\n\tNotConnected             = `(not connected)`\n\tHelpPrefix               = `help`\n\tQuitPrefix               = `quit`\n\tExitPrefix               = `exit`\n\tWelcomeDesc              = `Type \"` + HelpPrefix + `\" for help.`\n\tQueryBufferEmpty         = `Query buffer is empty.`\n\tQueryBufferReset         = `Query buffer reset (cleared).`\n\tInvalidCommand           = `Invalid command \\%s. Try \\? for help.`\n\tExtraArgumentIgnored     = `\\%s: extra argument %q ignored`\n\tMissingRequiredArg       = `\\%s: missing required argument`\n\tCopyright                = CommandName + \", \" + Banner + \".\\n\\n\" + License\n\tRowCount                 = `(%d rows)`\n\tAvailableDrivers         = `Available Drivers:`\n\tConnInfo                 = `Connected with driver %s (%s)`\n\tEnterPassword            = `Enter password: `\n\tEnterPreviousPassword    = `Enter previous password: `\n\tPasswordsDoNotMatch      = `Passwords do not match, trying again ...`\n\tNewPassword              = `Enter new password: `\n\tConfirmPassword          = `Confirm password: `\n\tPasswordChangeFailed     = `\\password for %q failed: %v`\n\tCouldNotSetVariable      = `could not set variable %q`\n\tChartParseFailed         = `\\chart: invalid argument for %q: %v`\n\tCommandIgnoredUseEndIf   = `%s command ignored; use \\endif or %s to exit current \\if block`\n\tUnrecognizedValueForCond = `unrecognized value %q for \"\\%s expression\": Boolean expected`\n\t// PasswordChangeSucceeded = `\\password succeeded for %q`\n\tHelpDesc          string\n\tHelpDescShort     = `Use \\? for help or press control-C to clear the input buffer.`\n\tHelpBanner        = `You are using ` + CommandName + \", \" + Banner + `.`\n\tHelpCommandPrefix = `Type:  `\n\tHelpCommands      = [][]string{\n\t\t{`copyright`, `for distribution terms`},\n\t\t//{`h`, `for help with SQL commands`},\n\t\t{`?`, `for help with ` + CommandName + ` commands`},\n\t\t{`g`, `or terminate with semicolon to execute query`},\n\t\t{`q`, `to quit`},\n\t}\n\tQuitDesc                = `Use \\q to quit.`\n\tUnknownFormatFieldName  = `unknown option: %s`\n\tFormatFieldInvalid      = `unrecognized value %q for \"%s\"`\n\tFormatFieldInvalidValue = `unrecognized value %q for \"%s\": %s expected`\n\tFormatFieldNameSetMap   = map[string]string{\n\t\t`border`:                   `Border style is %d.`,\n\t\t`columns`:                  `Target width is %d.`,\n\t\t`expanded`:                 `Expanded display is %s.`,\n\t\t`expanded_auto`:            `Expanded display is used automatically.`,\n\t\t`fieldsep`:                 `Field separator is %q.`,\n\t\t`fieldsep_zero`:            `Field separator is zero byte.`,\n\t\t`footer`:                   `Default footer is %s.`,\n\t\t`format`:                   `Output format is %s.`,\n\t\t`linestyle`:                `Line style is %s.`,\n\t\t`locale`:                   `Locale is %q.`,\n\t\t`null`:                     `Null display is %q.`,\n\t\t`numericlocale`:            `Locale-adjusted numeric output is %s.`,\n\t\t`pager`:                    `Pager usage is %s.`,\n\t\t`pager_min_lines`:          `Pager won't be used for less than %d line(s).`,\n\t\t`recordsep`:                `Field separator is %q.`,\n\t\t`recordsep_zero`:           `Record separator is zero byte.`,\n\t\t`tableattr`:                `Table attributes are %q.`,\n\t\t`time`:                     `Time display is %s.`,\n\t\t`title`:                    `Title is %q.`,\n\t\t`tuples_only`:              `Tuples only is %s.`,\n\t\t`unicode_border_linestyle`: `Unicode border line style is %q.`,\n\t\t`unicode_column_linestyle`: `Unicode column line style is %q.`,\n\t\t`unicode_header_linestyle`: `Unicode header line style is %q.`,\n\t}\n\tFormatFieldNameUnsetMap = map[string]string{\n\t\t`tableattr`: `Table attributes unset.`,\n\t\t`title`:     `Title is unset.`,\n\t}\n\tTimingSet                 = `Timing is %s.`\n\tTimingDesc                = `Time: %0.3f ms`\n\tInvalidValue              = `invalid -%s value %q: %s`\n\tNotSupportedByDriver      = `%s not supported by %s driver`\n\tRelationNotFound          = `Did not find any relation named \"%s\".`\n\tInvalidOption             = `invalid option %q`\n\tNotificationReceived      = `Asynchronous notification %q %sreceived from server process with PID %d.`\n\tNotificationPayload       = `with payload %q `\n\tUnknownShortAlias         = `(unk)`\n\tInvalidNamedConnection    = `warning: named connection %q was not defined: %v`\n\tChartsPathDoesNotExist    = `warning: charts_path %q does not exist`\n\tChartsPathIsNotADirectory = `warning: charts_path %q is not a directory`\n\tUsageTemplate             = `Usage:\n  {{.UseLine}}\n\nArguments:\n  DSN   database url or connection name\n\nFlags:\n{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}\n`\n\tChartUsage = `\\chart: create and display charts from SQL data\nusage: \\chart [opts]\n\navailable options:\n\nhelp\ntitle    [title]     chart title\nsubtitle [subtitle]  chart subtitle\nsize     NxN         chart size (width x height)\nbg       [color]     chart background color\ntype     [bar|line]  chart type\nprec     [num]       data decimal precision\nfile     [path]      write chart to file (svg)`\n)\n\nfunc init() {\n\t// setup help description\n\tcmds := make([]string, len(HelpCommands))\n\tfor i, h := range HelpCommands {\n\t\tcmds[i] = `\\` + h[0] + \" \" + h[1]\n\t}\n\tHelpDesc = HelpBanner +\n\t\t\"\\n\" + HelpCommandPrefix +\n\t\tstrings.Join(cmds, \"\\n\"+strings.Repeat(\" \", len(HelpCommandPrefix)))\n}\n\nvar spaceRE = regexp.MustCompile(`\\s+`)\n\n// Command returns the command name without spaces.\nvar Command = func() string {\n\treturn spaceRE.ReplaceAllString(CommandName, \"\")\n}\n\n// CommandLower returns the lower case command name without spaces.\nvar CommandLower = func() string {\n\treturn strings.ToLower(Command())\n}\n\n// CommandUpper returns the upper case command name without spaces.\nvar CommandUpper = func() string {\n\treturn strings.ToUpper(Command())\n}\n\n// Short returns the command name and banner.\nvar Short = func() string {\n\treturn Command() + \", \" + Banner\n}\n\n// UsageString is used to return the\nvar UsageString = func() string {\n\treturn \"\"\n}\n\n// Usage displays writes the command line options to w, optionally including a\n// banner.\nfunc Usage(w io.Writer, banner bool) {\n\tif banner {\n\t\t_, _ = w.Write([]byte(Short() + \"\\n\\n\"))\n\t}\n\t_, _ = w.Write([]byte(UsageString()))\n}\n\n// Logo is the logo.\nvar Logo image.Image\n\n// LogoPng is the embedded logo.\n//\n//go:embed logo.png\nvar LogoPng []byte\n\nfunc init() {\n\tvar err error\n\tif Logo, err = png.Decode(bytes.NewReader(LogoPng)); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "update-deps.sh",
    "content": "#!/bin/bash\n\nSRC=$(realpath $(cd -P \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd))\n\nset -e\n\npushd $SRC &> /dev/null\n(set -x;\n  go get -u -v -x $@ $(go list -tags 'all test' -f '{{ join .Imports \"\\n\" }}' ./internal/...)\n)\nPKGS=$(go list -tags 'all test' -f '{{ join .Imports \"\\n\" }}'|grep 'github.com/xo/usql'|grep -v drivers|grep -v internal)\n(set -x;\n  go get -u -v -x $@ $PKGS\n)\n(set -x;\n  go mod tidy\n)\npopd &> /dev/null\n"
  }
]